mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	Compare commits
	
		
			131 Commits
		
	
	
		
			FluxEngine
			...
			FluxEngine
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | d528978667 | ||
|  | 827fcf69d2 | ||
|  | 711ff545e0 | ||
|  | 5befa31050 | ||
|  | 8e5c2d0ebb | ||
|  | f95fceeb3d | ||
|  | 003b20dbf0 | ||
|  | cd9bbaa4b6 | ||
|  | 71e622bf72 | ||
|  | 2a065a08df | ||
|  | 6087228378 | ||
|  | efd74e0d7b | ||
|  | b68a9dcc4f | ||
|  | 008855daa9 | ||
|  | 7a9d36de2a | ||
|  | c56e982c9a | ||
|  | 002cc171a2 | ||
|  | 32e721b47a | ||
|  | 1e82f697a9 | ||
|  | fa09631e32 | ||
|  | e06436ce1e | ||
|  | b2f443e1ad | ||
|  | 2e07be0cf7 | ||
|  | bf0b14d094 | ||
|  | c9f5803194 | ||
|  | 5293560c02 | ||
|  | c49823aa9d | ||
|  | c4ef4882ae | ||
|  | a8eca06cf0 | ||
|  | 065257b5aa | ||
|  | 29bdfc043a | ||
|  | 933ffe7ab4 | ||
|  | e517f28563 | ||
|  | 91ffcf59c3 | ||
|  | 51c618f325 | ||
|  | 9dc1067032 | ||
|  | 9e75dc3af1 | ||
|  | efa4c933b3 | ||
|  | 6af80d1e5e | ||
|  | 0c48897814 | ||
|  | 60e5e35947 | ||
|  | 86c4e959ca | ||
|  | b0c675c589 | ||
|  | d77841c3b7 | ||
|  | 4ed1fb6bac | ||
|  | bcc9e9d9a5 | ||
|  | ec327e25a4 | ||
|  | d0ed5b32f7 | ||
|  | 7c66e1b0d4 | ||
|  | 4475e9f085 | ||
|  | 5c9639ec5a | ||
|  | 792cc88192 | ||
|  | 21fe586724 | ||
|  | 5a0fb2761a | ||
|  | ef4581ed39 | ||
|  | 73419704c2 | ||
|  | a8b92d4780 | ||
|  | 98140b0646 | ||
|  | 4429ce1f84 | ||
|  | 1f50941a2c | ||
|  | a7de04848c | ||
|  | c264fec6e9 | ||
|  | 4488b2542f | ||
|  | 2f1a5189d6 | ||
|  | effaeff51e | ||
|  | 1210549f59 | ||
|  | 7200de9702 | ||
|  | 5dd5c8516a | ||
|  | f7fb2a844b | ||
|  | 20b1b2a4a8 | ||
|  | f8b8bc2295 | ||
|  | 2d4d56d09f | ||
|  | 39599b76c8 | ||
|  | c2c40ccfbb | ||
|  | ab42eb23f4 | ||
|  | 05eff0e528 | ||
|  | 23311b4b68 | ||
|  | 5e97df8d15 | ||
|  | 898e8c551c | ||
|  | ad69c6bd27 | ||
|  | 661399cc83 | ||
|  | edbb4b1daa | ||
|  | 6389e8a756 | ||
|  | c187b79d80 | ||
|  | edbe624c5a | ||
|  | 44e2334815 | ||
|  | b448ab7917 | ||
|  | 072a097003 | ||
|  | a66e704bab | ||
|  | ed0d578b18 | ||
|  | 32bb956710 | ||
|  | f436d6b582 | ||
|  | d2f8c27cb6 | ||
|  | eaa3c57425 | ||
|  | 549f12a2ab | ||
|  | aea254fbe7 | ||
|  | 8ee6eed4dc | ||
|  | 3094c5c919 | ||
|  | 1e012699af | ||
|  | 91d6e9aeb9 | ||
|  | a40b26ff46 | ||
|  | ebcb9c4bb0 | ||
|  | 2520834b18 | ||
|  | a1f3087046 | ||
|  | e9670e205e | ||
|  | 658e2b7295 | ||
|  | 7b4a8d6de2 | ||
|  | e8f7b51aef | ||
|  | 9d6bc57a5f | ||
|  | 73766f92b4 | ||
|  | 80badf3b54 | ||
|  | 116529f85a | ||
|  | 5a2b2bc07a | ||
|  | 41070395c0 | ||
|  | 4304d1eede | ||
|  | 46f1b0aef4 | ||
|  | 9923d67a7c | ||
|  | 99335a84fd | ||
|  | c266779433 | ||
|  | bdcc12cd53 | ||
|  | 7988d0fe24 | ||
|  | 27f5c294b1 | ||
|  | b9a53e0d1c | ||
|  | f8b6d5e6fb | ||
|  | 04ff31c348 | ||
|  | 77b4aebd1b | ||
|  | 4056364300 | ||
|  | 60bfe050d3 | ||
|  | 28d0ce765e | ||
|  | 4954d33307 | ||
|  | 55f3354287 | 
| @@ -15,7 +15,7 @@ install: | ||||
|  | ||||
| build_script: | ||||
|   - make | ||||
|   - zip -9 fluxengine.zip fluxengine.exe brother120tool.exe | ||||
|   - zip -9 fluxengine.zip fluxengine.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex | ||||
|  | ||||
| artifacts: | ||||
|   - path: fluxengine.zip | ||||
|   | ||||
							
								
								
									
										41
									
								
								.github/workflows/ccpp.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								.github/workflows/ccpp.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| name: C/C++ CI | ||||
|  | ||||
| on: [push] | ||||
|  | ||||
| jobs: | ||||
|   build-linux: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v1 | ||||
|       with: | ||||
|         fetch-depth: 1 | ||||
|     - name: apt | ||||
|       run: sudo apt install libusb-1.0-0-dev libsqlite3-dev ninja-build | ||||
|     - name: make | ||||
|       run: make | ||||
|  | ||||
|   build-macos: | ||||
|     runs-on: macos-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v1 | ||||
|       with: | ||||
|         fetch-depth: 1 | ||||
|     - name: brew | ||||
|       run: brew install sqlite pkg-config libusb ninja | ||||
|     - name: make | ||||
|       run: make | ||||
|  | ||||
| #  build-windows: | ||||
| #    runs-on: windows-latest | ||||
| #    steps: | ||||
| #    - uses: numworks/setup-msys2@v1 | ||||
| #      with: | ||||
| #        path-type: inherit | ||||
| #    - uses: actions/checkout@v1 | ||||
| #    - name: pacman | ||||
| #      run: | | ||||
| #        msys2do pacman -S --noconfirm --needed make ninja mingw-w64-i686-libusb mingw-w64-i686-sqlite3 mingw-w64-i686-zlib mingw-w64-i686-gcc zip | ||||
| #    - name: build | ||||
| #      run: | | ||||
| #        msys2do make | ||||
|  | ||||
							
								
								
									
										39
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,39 +0,0 @@ | ||||
| language: shell | ||||
| git: | ||||
|     depth: 1 | ||||
|  | ||||
| matrix: | ||||
|     include: | ||||
|         - | ||||
|             os: linux | ||||
|             sudo: false | ||||
|             dist: xenial | ||||
|             compiler: gcc | ||||
|             env: CXX=g++-8 | ||||
|             script: | ||||
|             - make | ||||
|         - | ||||
|             os: osx | ||||
|             osx_image: xcode10.2 | ||||
|             compiler: clang | ||||
|             env: | ||||
|             - HOMEBREW_NO_INSTALL_CLEANUP=1 | ||||
|  | ||||
| addons: | ||||
|     apt: | ||||
|         sources: | ||||
|         - llvm-toolchain-precise-3.8 | ||||
|         - ubuntu-toolchain-r-test | ||||
|         packages: | ||||
|         - ninja-build | ||||
|         - libusb-1.0-0-dev | ||||
|         - libsqlite3-dev | ||||
|         - g++-8 | ||||
|     homebrew: | ||||
|         packages: | ||||
|         - ninja | ||||
|  | ||||
| script: | ||||
| - make | ||||
|  | ||||
|  | ||||
							
								
								
									
										4626
									
								
								FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4626
									
								
								FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										27
									
								
								FluxEngine.cydsn/FIFOin/API/c.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								FluxEngine.cydsn/FIFOin/API/c.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #include "cyfitter_cfg.h" | ||||
| #include "cydevice_trm.h" | ||||
| #include "cyfitter.h" | ||||
| #include "`$INSTANCE_NAME`_h.h" | ||||
|  | ||||
| void `$INSTANCE_NAME`_Start() | ||||
| { | ||||
|    `$INSTANCE_NAME`_Init(); | ||||
| }     | ||||
|  | ||||
| void `$INSTANCE_NAME`_Stop() | ||||
| { | ||||
|     `$INSTANCE_NAME`_Disable(); | ||||
| } | ||||
|  | ||||
| void `$INSTANCE_NAME`_Init() | ||||
| {     | ||||
|     `$INSTANCE_NAME`_Enable(); | ||||
|      | ||||
| } | ||||
| void `$INSTANCE_NAME`_Enable() | ||||
| { | ||||
| } | ||||
|  | ||||
| void `$INSTANCE_NAME`_Disable() | ||||
| { | ||||
| } | ||||
							
								
								
									
										50
									
								
								FluxEngine.cydsn/FIFOin/API/h.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								FluxEngine.cydsn/FIFOin/API/h.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| #if !defined(`$INSTANCE_NAME`_H) | ||||
| #define `$INSTANCE_NAME`_H | ||||
|  | ||||
| #include "cytypes.h" | ||||
| #include "cyfitter.h" | ||||
| #include "CyLib.h"  | ||||
|  | ||||
| #define `$INSTANCE_NAME`_FIFO_PTR	         ((reg8 *) `$INSTANCE_NAME`_dp__F0_REG) | ||||
|  | ||||
|     /* Macros to clear DP FIFOs.*/ | ||||
| #define `$INSTANCE_NAME`_CLEAR do { \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x01u | \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)));\ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfeu & \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)));\ | ||||
|     } while(0) | ||||
|  | ||||
| /* Macros to set FIFO level mode. See the TRM for details */ | ||||
| #define `$INSTANCE_NAME`_SET_LEVEL_NORMAL \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfbu & \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
| #define `$INSTANCE_NAME`_SET_LEVEL_MID \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x04u | \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
|  | ||||
| /* Macros to set FIFO to single-buffer mode. */ | ||||
| #define `$INSTANCE_NAME`_SINGLE_BUFFER_SET \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x01u | \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
|  | ||||
| /* Macros to return the FIFO to normal mode. */ | ||||
| #define `$INSTANCE_NAME`_SINGLE_BUFFER_UNSET \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfeu & \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
|      | ||||
| void `$INSTANCE_NAME`_Enable(); | ||||
| void `$INSTANCE_NAME`_Disable(); | ||||
| void `$INSTANCE_NAME`_Start(); | ||||
| void `$INSTANCE_NAME`_Stop(); | ||||
| void `$INSTANCE_NAME`_Init(); | ||||
|  | ||||
| #endif | ||||
|  | ||||
| /* [] END OF FILE */ | ||||
							
								
								
									
										
											BIN
										
									
								
								FluxEngine.cydsn/FIFOin/FIFOin.cysym
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								FluxEngine.cydsn/FIFOin/FIFOin.cysym
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										128
									
								
								FluxEngine.cydsn/FIFOin/FIFOin.v
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								FluxEngine.cydsn/FIFOin/FIFOin.v
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
|  | ||||
| //`#start header` -- edit after this line, do not edit this line | ||||
| `include "cypress.v" | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
|  | ||||
| /* Ultra-simple FIFO in component: a byte is shifted in every clock when req | ||||
|  * is high. */ | ||||
|   | ||||
| module FIFOin (drq, clk, d, req); | ||||
| 	output  drq; | ||||
| 	input   clk; | ||||
| 	input  [7:0] d; | ||||
| 	input  req; | ||||
|  | ||||
| //`#start body` -- edit after this line, do not edit this line | ||||
|  | ||||
| wire [7:0] pi; | ||||
| assign pi = d; | ||||
|  | ||||
| wire load; | ||||
| assign load = req; | ||||
|  | ||||
| cy_psoc3_dp #(.cy_dpconfig( | ||||
| { | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM0:    */ | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM1:     */ | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM2:     */ | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM3:     */ | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM4:     */ | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM5:     */ | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM6:     */ | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM7:     */ | ||||
| 	8'hFF, 8'h00,	/*CFG9:     */ | ||||
| 	8'hFF, 8'hFF,	/*CFG11-10:     */ | ||||
| 	`SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH, | ||||
| 	`SC_CI_A_ARITH, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL, | ||||
| 	`SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI, | ||||
| 	`SC_SI_A_DEFSI, /*CFG13-12:     */ | ||||
| 	`SC_A0_SRC_PIN, `SC_SHIFT_SL, 1'h0, | ||||
| 	1'h0, `SC_FIFO1_BUS, `SC_FIFO0_ALU, | ||||
| 	`SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN, | ||||
| 	`SC_FB_NOCHN, `SC_CMP1_NOCHN, | ||||
| 	`SC_CMP0_NOCHN, /*CFG15-14:     */ | ||||
| 	10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX, | ||||
| 	`SC_FIFO_LEVEL,`SC_FIFO__SYNC,`SC_EXTCRC_DSBL, | ||||
| 	`SC_WRK16CAT_DSBL /*CFG17-16:     */ | ||||
| } | ||||
| )) dp( | ||||
| 	/* input          */ .clk(clk), | ||||
| 	/* input [02:00]  */ .cs_addr(3'b0),    // Program counter | ||||
| 	/* input          */ .route_si(1'b0),   // Shift in | ||||
| 	/* input          */ .route_ci(1'b0),   // Carry in | ||||
| 	/* input          */ .f0_load(load),    // Load FIFO 0 | ||||
| 	/* input          */ .f1_load(1'b0), 	// Load FIFO 1 | ||||
| 	/* input          */ .d0_load(1'b0), 	// Load Data Register 0 | ||||
| 	/* input          */ .d1_load(1'b0), 	// Load Data Register 1 | ||||
| 	/* output         */ .ce0(), 			// Accumulator 0 = Data register 0 | ||||
| 	/* output         */ .cl0(), 			// Accumulator 0 < Data register 0 | ||||
| 	/* output         */ .z0(), 			// Accumulator 0 = 0 | ||||
| 	/* output         */ .ff0(), 			// Accumulator 0 = FF | ||||
| 	/* output         */ .ce1(), 			// Accumulator [0|1] = Data register 1 | ||||
| 	/* output         */ .cl1(), 			// Accumulator [0|1] < Data register 1 | ||||
| 	/* output         */ .z1(), 			// Accumulator 1 = 0 | ||||
| 	/* output         */ .ff1(), 			// Accumulator 1 = FF | ||||
| 	/* output         */ .ov_msb(), 		// Operation over flow | ||||
| 	/* output         */ .co_msb(), 		// Carry out | ||||
| 	/* output         */ .cmsb(), 			// Carry out | ||||
| 	/* output         */ .so(), 			// Shift out | ||||
|     /* output         */ .f0_bus_stat(drq), // not empty | ||||
| 	/* output         */ .f0_blk_stat(full),// full | ||||
| 	/* output         */ .f1_bus_stat(), 	// FIFO 1 status to uP | ||||
| 	/* output         */ .f1_blk_stat(), 	// FIFO 1 status to DP | ||||
| 	/* input          */ .ci(1'b0), 		// Carry in from previous stage | ||||
| 	/* output         */ .co(), 			// Carry out to next stage | ||||
| 	/* input          */ .sir(1'b0), 		// Shift in from right side | ||||
| 	/* output         */ .sor(), 			// Shift out to right side | ||||
| 	/* input          */ .sil(1'b0), 		// Shift in from left side | ||||
| 	/* output         */ .sol(), 			// Shift out to left side | ||||
| 	/* input          */ .msbi(1'b0), 		// MSB chain in | ||||
| 	/* output         */ .msbo(), 			// MSB chain out | ||||
| 	/* input [01:00]  */ .cei(2'b0),        // Compare equal in from prev stage | ||||
| 	/* output [01:00] */ .ceo(),            // Compare equal out to next stage | ||||
| 	/* input [01:00]  */ .cli(2'b0), 	    // Compare less than in from prv stage | ||||
| 	/* output [01:00] */ .clo(),            // Compare less than out to next stage | ||||
| 	/* input [01:00]  */ .zi(2'b0),         // Zero detect in from previous stage | ||||
| 	/* output [01:00] */ .zo(),             // Zero detect out to next stage | ||||
| 	/* input [01:00]  */ .fi(2'b0), 		// 0xFF detect in from previous stage | ||||
| 	/* output [01:00] */ .fo(), 	        // 0xFF detect out to next stage | ||||
| 	/* input [01:00]  */ .capi(2'b0),	    // Capture in from previous stage | ||||
| 	/* output [01:00] */ .capo(),		    // Capture out to next stage | ||||
| 	/* input          */ .cfbi(1'b0), 		// CRC Feedback in from previous stage | ||||
| 	/* output         */ .cfbo(), 			// CRC Feedback out to next stage | ||||
| 	/* input [07:00]  */ .pi(pi), 		    // Parallel data port | ||||
| 	/* output [07:00] */ .po()              // Parallel data port | ||||
| ); | ||||
|  | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
| endmodule | ||||
| //`#start footer` -- edit after this line, do not edit this line | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										27
									
								
								FluxEngine.cydsn/FIFOout/API/c.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								FluxEngine.cydsn/FIFOout/API/c.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #include "cyfitter_cfg.h" | ||||
| #include "cydevice_trm.h" | ||||
| #include "cyfitter.h" | ||||
| #include "`$INSTANCE_NAME`_h.h" | ||||
|  | ||||
| void `$INSTANCE_NAME`_Start() | ||||
| { | ||||
|    `$INSTANCE_NAME`_Init(); | ||||
| }     | ||||
|  | ||||
| void `$INSTANCE_NAME`_Stop() | ||||
| { | ||||
|     `$INSTANCE_NAME`_Disable(); | ||||
| } | ||||
|  | ||||
| void `$INSTANCE_NAME`_Init() | ||||
| {     | ||||
|     `$INSTANCE_NAME`_Enable(); | ||||
|      | ||||
| } | ||||
| void `$INSTANCE_NAME`_Enable() | ||||
| { | ||||
| } | ||||
|  | ||||
| void `$INSTANCE_NAME`_Disable() | ||||
| { | ||||
| } | ||||
							
								
								
									
										50
									
								
								FluxEngine.cydsn/FIFOout/API/h.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								FluxEngine.cydsn/FIFOout/API/h.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| #if !defined(`$INSTANCE_NAME`_H) | ||||
| #define `$INSTANCE_NAME`_H | ||||
|  | ||||
| #include "cytypes.h" | ||||
| #include "cyfitter.h" | ||||
| #include "CyLib.h"  | ||||
|  | ||||
| #define `$INSTANCE_NAME`_FIFO_PTR	         ((reg8 *) `$INSTANCE_NAME`_dp__F0_REG) | ||||
|  | ||||
|     /* Macros to clear DP FIFOs.*/ | ||||
| #define `$INSTANCE_NAME`_CLEAR do { \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x01u | \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)));\ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfeu & \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)));\ | ||||
|     } while(0) | ||||
|  | ||||
| /* Macros to set FIFO level mode. See the TRM for details */ | ||||
| #define `$INSTANCE_NAME`_SET_LEVEL_NORMAL \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfbu & \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
| #define `$INSTANCE_NAME`_SET_LEVEL_MID \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x04u | \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
|  | ||||
| /* Macros to set FIFO to single-buffer mode. */ | ||||
| #define `$INSTANCE_NAME`_SINGLE_BUFFER_SET \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x01u | \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
|  | ||||
| /* Macros to return the FIFO to normal mode. */ | ||||
| #define `$INSTANCE_NAME`_SINGLE_BUFFER_UNSET \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfeu & \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
|      | ||||
| void `$INSTANCE_NAME`_Enable(); | ||||
| void `$INSTANCE_NAME`_Disable(); | ||||
| void `$INSTANCE_NAME`_Start(); | ||||
| void `$INSTANCE_NAME`_Stop(); | ||||
| void `$INSTANCE_NAME`_Init(); | ||||
|  | ||||
| #endif | ||||
|  | ||||
| /* [] END OF FILE */ | ||||
							
								
								
									
										
											BIN
										
									
								
								FluxEngine.cydsn/FIFOout/FIFOout.cysym
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								FluxEngine.cydsn/FIFOout/FIFOout.cysym
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										168
									
								
								FluxEngine.cydsn/FIFOout/FIFOout.v
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								FluxEngine.cydsn/FIFOout/FIFOout.v
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,168 @@ | ||||
|  | ||||
| //`#start header` -- edit after this line, do not edit this line | ||||
| `include "cypress.v" | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
| // Generated on 11/16/2017 at 15:44 | ||||
| // Component: FIFOout | ||||
| module FIFOout ( | ||||
|     input req, | ||||
| 	input clk, | ||||
|     output [7:0] d, | ||||
| 	output drq, | ||||
| 	output empty, | ||||
|     output ack | ||||
| ); | ||||
|  | ||||
| //`#start body` -- edit after this line, do not edit this line | ||||
|  | ||||
| /* Reads from the FIFO are done based on the FIFO being not empty. */ | ||||
|   | ||||
| wire [7:0] po; | ||||
| assign d = po; | ||||
|  | ||||
| localparam STATE_WAIT = 1'b0; | ||||
| localparam STATE_READ = 1'b1; | ||||
|  | ||||
| reg state; | ||||
| reg oldreq; | ||||
|  | ||||
| assign ack = (state != STATE_READ); | ||||
|  | ||||
| always @(posedge clk) | ||||
| begin | ||||
|     case (state) | ||||
|         STATE_WAIT: | ||||
|         begin | ||||
|             if (!empty) | ||||
|             begin | ||||
|                 if (req && !oldreq) | ||||
|                 begin | ||||
|                     state <= STATE_READ; | ||||
|                 end | ||||
|                 oldreq <= req; | ||||
|             end | ||||
|         end | ||||
|          | ||||
|         STATE_READ: | ||||
|         begin | ||||
|             state <= STATE_WAIT; | ||||
|         end | ||||
|     endcase | ||||
| end | ||||
|              | ||||
| cy_psoc3_dp #(.cy_dpconfig( | ||||
| { | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM0: STATE_WAITFORREQ*/ | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC___F0, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM1: STATE_LOAD*/ | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM2:           */ | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM3:           */ | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM4:           */ | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM5:           */ | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM6:           */ | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM7:           */ | ||||
|     8'hFF, 8'h00,  /*CFG9:           */ | ||||
|     8'hFF, 8'hFF,  /*CFG11-10:           */ | ||||
|     `SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH, | ||||
|     `SC_CI_A_ARITH, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL, | ||||
|     `SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI, | ||||
|     `SC_SI_A_DEFSI, /*CFG13-12:           */ | ||||
|     `SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0, | ||||
|     1'h0, `SC_FIFO1_BUS, `SC_FIFO0_BUS, | ||||
|     `SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN, | ||||
|     `SC_FB_NOCHN, `SC_CMP1_NOCHN, | ||||
|     `SC_CMP0_NOCHN, /*CFG15-14:           */ | ||||
|     10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX, | ||||
|     `SC_FIFO_LEVEL,`SC_FIFO_ASYNC,`SC_EXTCRC_DSBL, | ||||
|     `SC_WRK16CAT_DSBL /*CFG17-16:           */ | ||||
| } | ||||
| )) dp( | ||||
|         /*  input                   */  .reset(1'b0), | ||||
|         /*  input                   */  .clk(clk), | ||||
|         /*  input   [02:00]         */  .cs_addr({2'b0, state}), | ||||
|         /*  input                   */  .route_si(1'b0), | ||||
|         /*  input                   */  .route_ci(1'b0), | ||||
|         /*  input                   */  .f0_load(1'b0), | ||||
|         /*  input                   */  .f1_load(1'b0), | ||||
|         /*  input                   */  .d0_load(1'b0), | ||||
|         /*  input                   */  .d1_load(1'b0), | ||||
|         /*  output                  */  .ce0(), | ||||
|         /*  output                  */  .cl0(), | ||||
|         /*  output                  */  .z0(), | ||||
|         /*  output                  */  .ff0(), | ||||
|         /*  output                  */  .ce1(), | ||||
|         /*  output                  */  .cl1(), | ||||
|         /*  output                  */  .z1(), | ||||
|         /*  output                  */  .ff1(), | ||||
|         /*  output                  */  .ov_msb(), | ||||
|         /*  output                  */  .co_msb(), | ||||
|         /*  output                  */  .cmsb(), | ||||
|         /*  output                  */  .so(), | ||||
|         /*  output                  */  .f0_bus_stat(drq), // not full | ||||
|         /*  output                  */  .f0_blk_stat(empty), // empty | ||||
|         /*  output                  */  .f1_bus_stat(), | ||||
|         /*  output                  */  .f1_blk_stat(), | ||||
|          | ||||
|         /* input                    */  .ci(1'b0),     // Carry in from previous stage | ||||
|         /* output                   */  .co(),// Carry out to next stage | ||||
|         /* input                    */  .sir(1'b0),    // Shift in from right side | ||||
|         /* output                   */  .sor(),        // Shift out to right side | ||||
|         /* input                    */  .sil(1'b0),    // Shift in from left side | ||||
|         /* output                   */  .sol(),        // Shift out to left side | ||||
|         /* input                    */  .msbi(1'b0),   // MSB chain in | ||||
|         /* output                   */  .msbo(),       // MSB chain out | ||||
|         /* input [01:00]            */  .cei(2'b0),    // Compare equal in from prev stage | ||||
|         /* output [01:00]           */  .ceo(),        // Compare equal out to next stage | ||||
|         /* input [01:00]            */  .cli(2'b0),    // Compare less than in from prv stage | ||||
|         /* output [01:00]           */  .clo(),        // Compare less than out to next stage | ||||
|         /* input [01:00]            */  .zi(2'b0),     // Zero detect in from previous stage | ||||
|         /* output [01:00]           */  .zo(),         // Zero detect out to next stage | ||||
|         /* input [01:00]            */  .fi(2'b0),     // 0xFF detect in from previous stage | ||||
|         /* output [01:00]           */  .fo(),         // 0xFF detect out to next stage | ||||
|         /* input [01:00]            */  .capi(2'b0),   // Software capture from previous stage | ||||
|         /* output [01:00]           */  .capo(),       // Software capture to next stage | ||||
|         /* input                    */  .cfbi(1'b0),   // CRC Feedback in from previous stage | ||||
|         /* output                   */  .cfbo(),       // CRC Feedback out to next stage | ||||
|         /* input [07:00]            */  .pi(8'b0),     // Parallel data port | ||||
|         /* output [07:00]           */  .po(po)       // Parallel data port | ||||
| ); | ||||
|  | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
| endmodule | ||||
| //`#start footer` -- edit after this line, do not edit this line | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -28,6 +28,54 @@ | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="1a7e8637-3b6b-4e84-839c-0dfc18fdaf5b"> | ||||
|         <Data key="check_tolerance" value="True" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
|         <Data key="derive_type" value="NAMED_DIVIDER" /> | ||||
|         <Data key="desired_freq" value="0" /> | ||||
|         <Data key="desired_unit" value="15" /> | ||||
|         <Data key="divider" value="0" /> | ||||
|         <Data key="domain" value="DIGITAL" /> | ||||
|         <Data key="enabled" value="True" /> | ||||
|         <Data key="minus_accuracy" value="0.25" /> | ||||
|         <Data key="minus_tolerance" value="5" /> | ||||
|         <Data key="name" value="Clock_5" /> | ||||
|         <Data key="named_src_direct_connect" value="True" /> | ||||
|         <Data key="netlist_name" value="Clock_5" /> | ||||
|         <Data key="placement" value="AUTO" /> | ||||
|         <Data key="plus_accuracy" value="0.25" /> | ||||
|         <Data key="plus_tolerance" value="5" /> | ||||
|         <Data key="scope" value="LOCAL" /> | ||||
|         <Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" /> | ||||
|         <Data key="src_clk_name" value="BUS_CLK" /> | ||||
|         <Data key="start_on_reset" value="True" /> | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="3f3708ae-fb62-4012-919b-9a3b9a1dfbc2"> | ||||
|         <Data key="check_tolerance" value="True" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
|         <Data key="derive_type" value="NAMED_DIVIDER" /> | ||||
|         <Data key="desired_freq" value="0" /> | ||||
|         <Data key="desired_unit" value="15" /> | ||||
|         <Data key="divider" value="0" /> | ||||
|         <Data key="domain" value="DIGITAL" /> | ||||
|         <Data key="enabled" value="True" /> | ||||
|         <Data key="minus_accuracy" value="0.25" /> | ||||
|         <Data key="minus_tolerance" value="5" /> | ||||
|         <Data key="name" value="Clock_8" /> | ||||
|         <Data key="named_src_direct_connect" value="True" /> | ||||
|         <Data key="netlist_name" value="Clock_8" /> | ||||
|         <Data key="placement" value="AUTO" /> | ||||
|         <Data key="plus_accuracy" value="0.25" /> | ||||
|         <Data key="plus_tolerance" value="5" /> | ||||
|         <Data key="scope" value="LOCAL" /> | ||||
|         <Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" /> | ||||
|         <Data key="src_clk_name" value="BUS_CLK" /> | ||||
|         <Data key="start_on_reset" value="True" /> | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="4eef02b9-8ad1-43c4-85f1-b3335faa5fc4"> | ||||
|         <Data key="check_tolerance" value="True" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
| @@ -147,6 +195,54 @@ | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="71bc291d-84a7-40a8-b7b2-1c8a34326a31"> | ||||
|         <Data key="check_tolerance" value="True" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
|         <Data key="derive_type" value="NAMED_FREQ" /> | ||||
|         <Data key="desired_freq" value="300" /> | ||||
|         <Data key="desired_unit" value="0" /> | ||||
|         <Data key="divider" value="65536" /> | ||||
|         <Data key="domain" value="DIGITAL" /> | ||||
|         <Data key="enabled" value="True" /> | ||||
|         <Data key="minus_accuracy" value="0.25" /> | ||||
|         <Data key="minus_tolerance" value="5" /> | ||||
|         <Data key="name" value="CLOCK300" /> | ||||
|         <Data key="named_src_direct_connect" value="False" /> | ||||
|         <Data key="netlist_name" value="CLOCK300" /> | ||||
|         <Data key="placement" value="AUTO" /> | ||||
|         <Data key="plus_accuracy" value="0.25" /> | ||||
|         <Data key="plus_tolerance" value="5" /> | ||||
|         <Data key="scope" value="LOCAL" /> | ||||
|         <Data key="src_clk_id" value="CEF43CFB-0213-49b9-B980-2FFAB81C5B47" /> | ||||
|         <Data key="src_clk_name" value="IMO" /> | ||||
|         <Data key="start_on_reset" value="True" /> | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="90ce0c72-9f10-44ef-a049-f0f525d59bea"> | ||||
|         <Data key="check_tolerance" value="True" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
|         <Data key="derive_type" value="NAMED_FREQ" /> | ||||
|         <Data key="desired_freq" value="128" /> | ||||
|         <Data key="desired_unit" value="0" /> | ||||
|         <Data key="divider" value="65536" /> | ||||
|         <Data key="domain" value="DIGITAL" /> | ||||
|         <Data key="enabled" value="True" /> | ||||
|         <Data key="minus_accuracy" value="0.25" /> | ||||
|         <Data key="minus_tolerance" value="5" /> | ||||
|         <Data key="name" value="CLOCK8" /> | ||||
|         <Data key="named_src_direct_connect" value="False" /> | ||||
|         <Data key="netlist_name" value="CLOCK8" /> | ||||
|         <Data key="placement" value="AUTO" /> | ||||
|         <Data key="plus_accuracy" value="0.25" /> | ||||
|         <Data key="plus_tolerance" value="5" /> | ||||
|         <Data key="scope" value="LOCAL" /> | ||||
|         <Data key="src_clk_id" value="CEF43CFB-0213-49b9-B980-2FFAB81C5B47" /> | ||||
|         <Data key="src_clk_name" value="IMO" /> | ||||
|         <Data key="start_on_reset" value="True" /> | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="349ffa20-8576-4ac3-9a6f-34ef606de6cf"> | ||||
|         <Data key="check_tolerance" value="True" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
| @@ -170,6 +266,29 @@ | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="4033c29d-f4bc-4e94-ac95-aa587e869f88/696a0979-21fc-4185-bf38-6c79febcde7a"> | ||||
|         <Data key="check_tolerance" value="False" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
|         <Data key="derive_type" value="AUTO" /> | ||||
|         <Data key="desired_freq" value="1600000" /> | ||||
|         <Data key="desired_unit" value="0" /> | ||||
|         <Data key="divider" value="40" /> | ||||
|         <Data key="domain" value="DIGITAL" /> | ||||
|         <Data key="enabled" value="True" /> | ||||
|         <Data key="minus_accuracy" value="0.25" /> | ||||
|         <Data key="minus_tolerance" value="5" /> | ||||
|         <Data key="name" value="OUTPUT_VOLTAGE_ADC_theACLK" /> | ||||
|         <Data key="netlist_name" value="\OUTPUT_VOLTAGE_ADC:theACLK\" /> | ||||
|         <Data key="placement" value="AUTO" /> | ||||
|         <Data key="plus_accuracy" value="0.25" /> | ||||
|         <Data key="plus_tolerance" value="5" /> | ||||
|         <Data key="scope" value="LOCAL" /> | ||||
|         <Data key="src_clk_id" value="61737EF6-3B74-48f9-8B91-F7473A442AE7" /> | ||||
|         <Data key="src_clk_name" value="MASTER_CLK" /> | ||||
|         <Data key="start_on_reset" value="True" /> | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="6616e828-6611-4893-a674-66c861d79d6c"> | ||||
|         <Data key="check_tolerance" value="True" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
| @@ -241,6 +360,53 @@ | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="09974428-e912-491f-8d2f-361ba50e7599"> | ||||
|         <Data key="check_tolerance" value="True" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
|         <Data key="derive_type" value="NAMED_DIVIDER" /> | ||||
|         <Data key="desired_freq" value="0" /> | ||||
|         <Data key="desired_unit" value="15" /> | ||||
|         <Data key="divider" value="0" /> | ||||
|         <Data key="domain" value="DIGITAL" /> | ||||
|         <Data key="enabled" value="True" /> | ||||
|         <Data key="minus_accuracy" value="0.25" /> | ||||
|         <Data key="minus_tolerance" value="5" /> | ||||
|         <Data key="name" value="Clock_6" /> | ||||
|         <Data key="named_src_direct_connect" value="True" /> | ||||
|         <Data key="netlist_name" value="Clock_6" /> | ||||
|         <Data key="placement" value="AUTO" /> | ||||
|         <Data key="plus_accuracy" value="0.25" /> | ||||
|         <Data key="plus_tolerance" value="5" /> | ||||
|         <Data key="scope" value="LOCAL" /> | ||||
|         <Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" /> | ||||
|         <Data key="src_clk_name" value="BUS_CLK" /> | ||||
|         <Data key="start_on_reset" value="True" /> | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="a5825a94-fa18-4e4f-a843-bc687cacbd56/696a0979-21fc-4185-bf38-6c79febcde7a"> | ||||
|         <Data key="check_tolerance" value="False" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
|         <Data key="derive_type" value="AUTO" /> | ||||
|         <Data key="desired_freq" value="1600000" /> | ||||
|         <Data key="desired_unit" value="0" /> | ||||
|         <Data key="divider" value="40" /> | ||||
|         <Data key="domain" value="DIGITAL" /> | ||||
|         <Data key="enabled" value="True" /> | ||||
|         <Data key="minus_accuracy" value="0.25" /> | ||||
|         <Data key="minus_tolerance" value="5" /> | ||||
|         <Data key="name" value="INPUT_VOLTAGE_ADC_theACLK" /> | ||||
|         <Data key="netlist_name" value="\INPUT_VOLTAGE_ADC:theACLK\" /> | ||||
|         <Data key="placement" value="AUTO" /> | ||||
|         <Data key="plus_accuracy" value="0.25" /> | ||||
|         <Data key="plus_tolerance" value="5" /> | ||||
|         <Data key="scope" value="LOCAL" /> | ||||
|         <Data key="src_clk_id" value="61737EF6-3B74-48f9-8B91-F7473A442AE7" /> | ||||
|         <Data key="src_clk_name" value="MASTER_CLK" /> | ||||
|         <Data key="start_on_reset" value="True" /> | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="b762c287-7f87-4b21-982e-84be01dc5115"> | ||||
|         <Data key="check_tolerance" value="True" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
| @@ -288,6 +454,30 @@ | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="b722443b-8f81-46dc-bf9b-c95eb62bc181"> | ||||
|         <Data key="check_tolerance" value="True" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
|         <Data key="derive_type" value="NAMED_DIVIDER" /> | ||||
|         <Data key="desired_freq" value="0" /> | ||||
|         <Data key="desired_unit" value="15" /> | ||||
|         <Data key="divider" value="0" /> | ||||
|         <Data key="domain" value="DIGITAL" /> | ||||
|         <Data key="enabled" value="True" /> | ||||
|         <Data key="minus_accuracy" value="0.25" /> | ||||
|         <Data key="minus_tolerance" value="5" /> | ||||
|         <Data key="name" value="Clock_1" /> | ||||
|         <Data key="named_src_direct_connect" value="True" /> | ||||
|         <Data key="netlist_name" value="Clock_1" /> | ||||
|         <Data key="placement" value="AUTO" /> | ||||
|         <Data key="plus_accuracy" value="0.25" /> | ||||
|         <Data key="plus_tolerance" value="5" /> | ||||
|         <Data key="scope" value="LOCAL" /> | ||||
|         <Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" /> | ||||
|         <Data key="src_clk_name" value="BUS_CLK" /> | ||||
|         <Data key="start_on_reset" value="True" /> | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="cb7e877c-9fb4-4fc1-a708-f1e48eb5a68c"> | ||||
|         <Data key="check_tolerance" value="True" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
| @@ -312,6 +502,30 @@ | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="d3075dc6-05c8-4dc9-9959-cf7014c0e66f"> | ||||
|         <Data key="check_tolerance" value="True" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
|         <Data key="derive_type" value="NAMED_DIVIDER" /> | ||||
|         <Data key="desired_freq" value="0" /> | ||||
|         <Data key="desired_unit" value="15" /> | ||||
|         <Data key="divider" value="0" /> | ||||
|         <Data key="domain" value="DIGITAL" /> | ||||
|         <Data key="enabled" value="True" /> | ||||
|         <Data key="minus_accuracy" value="0.25" /> | ||||
|         <Data key="minus_tolerance" value="5" /> | ||||
|         <Data key="name" value="Clock_7" /> | ||||
|         <Data key="named_src_direct_connect" value="True" /> | ||||
|         <Data key="netlist_name" value="Clock_7" /> | ||||
|         <Data key="placement" value="AUTO" /> | ||||
|         <Data key="plus_accuracy" value="0.25" /> | ||||
|         <Data key="plus_tolerance" value="5" /> | ||||
|         <Data key="scope" value="LOCAL" /> | ||||
|         <Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" /> | ||||
|         <Data key="src_clk_name" value="BUS_CLK" /> | ||||
|         <Data key="start_on_reset" value="True" /> | ||||
|         <Data key="sync_with_bus_clk" value="True" /> | ||||
|         <Data key="user_set_domain" value="False" /> | ||||
|       </Group> | ||||
|       <Group key="e4a53a4c-40e1-4747-a72a-10193ffdf31c"> | ||||
|         <Data key="check_tolerance" value="True" /> | ||||
|         <Data key="clock_version" value="v1" /> | ||||
| @@ -609,18 +823,27 @@ | ||||
|   <Group key="DWRInstGuidMapping"> | ||||
|     <Group key="Clock"> | ||||
|       <Data key="0b2f9bbb-00ce-4115-a788-ffb9d046a9e5" value="Clock_4" /> | ||||
|       <Data key="1a7e8637-3b6b-4e84-839c-0dfc18fdaf5b" value="Clock_5" /> | ||||
|       <Data key="3f3708ae-fb62-4012-919b-9a3b9a1dfbc2" value="Clock_8" /> | ||||
|       <Data key="4eef02b9-8ad1-43c4-85f1-b3335faa5fc4" value="Clock_3" /> | ||||
|       <Data key="06c4d5d4-f15f-4b29-a1d0-c24b2e38b1ec" value="CounterClock" /> | ||||
|       <Data key="24cd38f7-f472-4403-837f-86807c8f5333" value="PULSE_CLOCK" /> | ||||
|       <Data key="63ed4137-0b09-4256-8a27-35c9a2653f1a" value="Clock_2" /> | ||||
|       <Data key="66f14071-bddd-4b4d-a9aa-a129cceaa7b6" value="Clock_3" /> | ||||
|       <Data key="71bc291d-84a7-40a8-b7b2-1c8a34326a31" value="CLOCK300" /> | ||||
|       <Data key="90ce0c72-9f10-44ef-a049-f0f525d59bea" value="CLOCK8" /> | ||||
|       <Data key="349ffa20-8576-4ac3-9a6f-34ef606de6cf" value="Clock_1" /> | ||||
|       <Data key="4033c29d-f4bc-4e94-ac95-aa587e869f88/696a0979-21fc-4185-bf38-6c79febcde7a" value="OUTPUT_VOLTAGE_ADC_theACLK" /> | ||||
|       <Data key="6616e828-6611-4893-a674-66c861d79d6c" value="SignalSamplingClock" /> | ||||
|       <Data key="12664fc6-9d70-44b1-8a49-887a292e1b7f" value="Clock_3" /> | ||||
|       <Data key="75187c05-9501-4450-b306-6ccdd3bb77db" value="Clock_5" /> | ||||
|       <Data key="09974428-e912-491f-8d2f-361ba50e7599" value="Clock_6" /> | ||||
|       <Data key="a5825a94-fa18-4e4f-a843-bc687cacbd56/696a0979-21fc-4185-bf38-6c79febcde7a" value="INPUT_VOLTAGE_ADC_theACLK" /> | ||||
|       <Data key="b762c287-7f87-4b21-982e-84be01dc5115" value="Clock_2" /> | ||||
|       <Data key="b0162966-0060-4af5-82d1-fcb491ad7619/be0a0e37-ad17-42ca-b5a1-1a654d736358" value="UART_IntClock" /> | ||||
|       <Data key="b722443b-8f81-46dc-bf9b-c95eb62bc181" value="Clock_1" /> | ||||
|       <Data key="cb7e877c-9fb4-4fc1-a708-f1e48eb5a68c" value="CounterClock" /> | ||||
|       <Data key="d3075dc6-05c8-4dc9-9959-cf7014c0e66f" value="Clock_7" /> | ||||
|       <Data key="e4a53a4c-40e1-4747-a72a-10193ffdf31c" value="Clock_1" /> | ||||
|       <Data key="efd5f185-0c32-4824-ba72-3ceb5356f5a7" value="Clock_1" /> | ||||
|     </Group> | ||||
| @@ -638,16 +861,18 @@ | ||||
|       <Data key="a5d987c6-e45b-45b9-ad93-656fab06d720" value="TRK00" /> | ||||
|       <Data key="a93ef5b3-00f4-42c0-8fad-0e275a7e2537" value="MOTEB" /> | ||||
|       <Data key="b8380fb7-fdb8-449f-bd8d-c4ca96cdf55a" value="DEBUG_PINS" /> | ||||
|       <Data key="bc2e8987-db82-469c-bf6f-22fd3464cc70" value="DEBUG_PINS" /> | ||||
|       <Data key="bc5d52a1-1b25-4aa0-9ba9-3f81d122772f" value="DEBUG_PINS" /> | ||||
|       <Data key="beca5e2d-f70f-4900-a4db-7eca1ed3126e/8b77a6c4-10a0-4390-971c-672353e2a49c" value="USBFS_Dm" /> | ||||
|       <Data key="beca5e2d-f70f-4900-a4db-7eca1ed3126e/618a72fc-5ddd-4df5-958f-a3d55102db42" value="USBFS_Dp" /> | ||||
|       <Data key="c5367cde-21d5-4866-9a32-d16abfea0c61" value="WPT" /> | ||||
|       <Data key="d19368c5-6855-41bb-a9ff-808938abef00" value="INDEX" /> | ||||
|       <Data key="e9f14b5a-b2bf-49b8-98f3-d7b5a43ace8d" value="DRVSB" /> | ||||
|       <Data key="e851a3b9-efb8-48be-bbb8-b303b216c393" value="LED" /> | ||||
|       <Data key="e851a3b9-efb8-48be-bbb8-b303b216c393" value="INDEX300" /> | ||||
|       <Data key="e51063a9-4fad-40c7-a06b-7cc4b137dc18" value="DSKCHG" /> | ||||
|       <Data key="ea7ee228-8b3f-426c-8bb8-cd7a81937769" value="DIR" /> | ||||
|       <Data key="ed092b9b-d398-4703-be89-cebf998501f6" value="UartTx" /> | ||||
|       <Data key="f9a7371a-8a7d-4144-8b08-69e3d2a3a663" value="INDEX360" /> | ||||
|       <Data key="fbd1f839-40f9-498e-a48b-5f3048ea5c3d/52f31aa9-2f0a-497d-9a1f-1424095e13e6" value="UART_tx" /> | ||||
|       <Data key="fede1767-f3fd-4021-b3d7-8f9d88f36f9b" value="DRVSA" /> | ||||
|       <Data key="fff78075-035e-43d7-8577-bc5be4d21926" value="WGATE" /> | ||||
| @@ -3859,6 +4084,32 @@ | ||||
|         <Data key="Port Format" value="2,3" /> | ||||
|       </Group> | ||||
|     </Group> | ||||
|     <Group key="bc2e8987-db82-469c-bf6f-22fd3464cc70"> | ||||
|       <Group key="0"> | ||||
|         <Data key="Port Format" value="0,0" /> | ||||
|       </Group> | ||||
|       <Group key="1"> | ||||
|         <Data key="Port Format" value="0,1" /> | ||||
|       </Group> | ||||
|       <Group key="2"> | ||||
|         <Data key="Port Format" value="0,2" /> | ||||
|       </Group> | ||||
|       <Group key="3"> | ||||
|         <Data key="Port Format" value="0,3" /> | ||||
|       </Group> | ||||
|       <Group key="4"> | ||||
|         <Data key="Port Format" value="0,4" /> | ||||
|       </Group> | ||||
|       <Group key="5"> | ||||
|         <Data key="Port Format" value="0,5" /> | ||||
|       </Group> | ||||
|       <Group key="6"> | ||||
|         <Data key="Port Format" value="0,6" /> | ||||
|       </Group> | ||||
|       <Group key="7"> | ||||
|         <Data key="Port Format" value="0,7" /> | ||||
|       </Group> | ||||
|     </Group> | ||||
|     <Group key="bc5d52a1-1b25-4aa0-9ba9-3f81d122772f"> | ||||
|       <Group key="0"> | ||||
|         <Data key="Port Format" value="0,5" /> | ||||
| @@ -3894,7 +4145,7 @@ | ||||
|     </Group> | ||||
|     <Group key="e851a3b9-efb8-48be-bbb8-b303b216c393"> | ||||
|       <Group key="0"> | ||||
|         <Data key="Port Format" value="2,1" /> | ||||
|         <Data key="Port Format" value="3,0" /> | ||||
|       </Group> | ||||
|     </Group> | ||||
|     <Group key="e51063a9-4fad-40c7-a06b-7cc4b137dc18"> | ||||
| @@ -3912,9 +4163,14 @@ | ||||
|         <Data key="Port Format" value="12,7" /> | ||||
|       </Group> | ||||
|     </Group> | ||||
|     <Group key="f9a7371a-8a7d-4144-8b08-69e3d2a3a663"> | ||||
|       <Group key="0"> | ||||
|         <Data key="Port Format" value="3,1" /> | ||||
|       </Group> | ||||
|     </Group> | ||||
|     <Group key="fbd1f839-40f9-498e-a48b-5f3048ea5c3d/52f31aa9-2f0a-497d-9a1f-1424095e13e6"> | ||||
|       <Group key="0"> | ||||
|         <Data key="Port Format" value="2,5" /> | ||||
|         <Data key="Port Format" value="12,7" /> | ||||
|       </Group> | ||||
|     </Group> | ||||
|     <Group key="fede1767-f3fd-4021-b3d7-8f9d88f36f9b"> | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										134
									
								
								FluxEngine.cydsn/Sampler/Sampler.v
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								FluxEngine.cydsn/Sampler/Sampler.v
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | ||||
|  | ||||
| //`#start header` -- edit after this line, do not edit this line | ||||
| `include "cypress.v" | ||||
| `include "../SuperCounter/SuperCounter.v" | ||||
|  | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
| // Generated on 12/11/2019 at 21:18 | ||||
| // Component: Sampler | ||||
| module Sampler ( | ||||
| 	output [2:0] debug_state, | ||||
| 	output reg [7:0] opcode, | ||||
| 	output  req, | ||||
| 	input   clock, | ||||
| 	input   index, | ||||
| 	input   rdata, | ||||
| 	input   reset, | ||||
| 	input   sampleclock | ||||
| ); | ||||
|  | ||||
| //`#start body` -- edit after this line, do not edit this line | ||||
|  | ||||
| localparam STATE_RESET = 0; | ||||
| localparam STATE_WAITING = 1; | ||||
| localparam STATE_INTERVAL = 2; | ||||
| localparam STATE_DISPATCH = 3; | ||||
| localparam STATE_OPCODE = 4; | ||||
| localparam STATE_COUNTING = 5; | ||||
|  | ||||
| reg [2:0] state; | ||||
| wire [6:0] counter; | ||||
|  | ||||
| wire countnow; | ||||
| assign countnow = (state == STATE_COUNTING); | ||||
|  | ||||
| wire counterreset; | ||||
| assign counterreset = (state == STATE_INTERVAL) || (state == STATE_OPCODE); | ||||
|  | ||||
| SuperCounter #(.Delta(1), .ResetValue(0)) Counter | ||||
| ( | ||||
|     /* input */ .clk(clock), | ||||
|     /* input */ .reset(counterreset), | ||||
|     /* input */ .count(countnow), | ||||
|     /* output */ .d(counter) | ||||
| ); | ||||
|  | ||||
| reg oldsampleclock; | ||||
| wire sampleclocked; | ||||
| assign sampleclocked = !oldsampleclock && sampleclock; | ||||
|  | ||||
| reg oldindex; | ||||
| wire indexed; | ||||
| assign indexed = !oldindex && index; | ||||
|  | ||||
| wire rdataed; | ||||
| reg oldrdata; | ||||
| assign rdataed = !oldrdata && rdata; | ||||
|  | ||||
| assign req = (state == STATE_INTERVAL) || (state == STATE_OPCODE); | ||||
|  | ||||
| always @(posedge clock) | ||||
| begin | ||||
|     if (reset) | ||||
|     begin | ||||
|         state <= STATE_RESET; | ||||
|         opcode <= 0; | ||||
|         oldsampleclock <= 0; | ||||
|         oldindex <= 0; | ||||
|         oldrdata <= 0; | ||||
|     end | ||||
|     else | ||||
|         case (state) | ||||
|             STATE_RESET: | ||||
|                 state <= STATE_WAITING; | ||||
|              | ||||
|             STATE_WAITING: | ||||
|             begin | ||||
|                 if (rdataed || indexed) | ||||
|                 begin | ||||
|                     opcode <= {0, counter}; | ||||
|                     state <= STATE_INTERVAL; | ||||
|                 end | ||||
|                 else if (sampleclocked) | ||||
|                 begin | ||||
|                     oldsampleclock <= 1; | ||||
|                     if (counter == 7'h7f) | ||||
|                     begin | ||||
|                         opcode <= {0, counter}; | ||||
|                         state <= STATE_OPCODE; | ||||
|                     end | ||||
|                     else | ||||
|                         state <= STATE_COUNTING; | ||||
|                 end | ||||
|                  | ||||
|                 if (oldrdata && !rdata) | ||||
|                     oldrdata <= 0; | ||||
|                 if (oldindex && !index) | ||||
|                     oldindex <= 0; | ||||
|                 if (oldsampleclock && !sampleclock) | ||||
|                     oldsampleclock <= 0; | ||||
|             end | ||||
|              | ||||
|             STATE_INTERVAL: /* interval byte sent here; counter reset */ | ||||
|                 state <= STATE_DISPATCH; | ||||
|                  | ||||
|             STATE_DISPATCH: /* relax after interval byte, dispatch for opcode */ | ||||
|             begin | ||||
|                 if (rdataed) | ||||
|                 begin | ||||
|                     oldrdata <= 1; | ||||
|                     opcode <= 8'h80; | ||||
|                     state <= STATE_OPCODE; | ||||
|                 end | ||||
|                 else if (indexed) | ||||
|                 begin | ||||
|                     oldindex <= 1; | ||||
|                     opcode <= 8'h81; | ||||
|                     state <= STATE_OPCODE; | ||||
|                 end | ||||
|                 else | ||||
|                     state <= STATE_WAITING; | ||||
|             end | ||||
|              | ||||
|             STATE_OPCODE: /* opcode byte sent here */ | ||||
|                 state <= STATE_WAITING; | ||||
|                              | ||||
|             STATE_COUNTING: /* counter changes here */ | ||||
|                 state <= STATE_WAITING; | ||||
|         endcase | ||||
| end | ||||
|  | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
| endmodule | ||||
| //`#start footer` -- edit after this line, do not edit this line | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										103
									
								
								FluxEngine.cydsn/Sequencer/Sequencer.v
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								FluxEngine.cydsn/Sequencer/Sequencer.v
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
|  | ||||
| //`#start header` -- edit after this line, do not edit this line | ||||
| `include "cypress.v" | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
| // Generated on 11/24/2019 at 17:25 | ||||
| // Component: Sequencer | ||||
| module Sequencer ( | ||||
| 	output req, /* request new data on leading edge */ | ||||
| 	output wdata, | ||||
|     output [2:0] debug_state, | ||||
| 	input clock, | ||||
| 	input dataclock, /* incoming data on leading edge */ | ||||
| 	input [7:0] opcode, | ||||
|     input index, | ||||
|     input sampleclock, | ||||
|     input reset | ||||
| ); | ||||
|  | ||||
| //`#start body` -- edit after this line, do not edit this line | ||||
|  | ||||
| localparam STATE_IDLE = 0; | ||||
| localparam STATE_LOAD = 1; | ||||
| localparam STATE_WAITING = 2; | ||||
| localparam STATE_PULSING = 3; | ||||
| localparam STATE_INDEXING = 4; | ||||
|  | ||||
| localparam OPCODE_PULSE = 8'h80; | ||||
| localparam OPCODE_INDEX = 8'h81; | ||||
|  | ||||
| reg [2:0] state; | ||||
| reg [6:0] countdown; | ||||
|  | ||||
| assign req = (state == STATE_LOAD); | ||||
| assign wdata = (state == STATE_PULSING); | ||||
| assign debug_state = state; | ||||
|  | ||||
| reg olddataclock; | ||||
| wire dataclocked; | ||||
| always @(posedge clock) olddataclock <= dataclock; | ||||
| assign dataclocked = !olddataclock && dataclock; | ||||
|  | ||||
| reg oldsampleclock; | ||||
| wire sampleclocked; | ||||
| always @(posedge clock) oldsampleclock <= sampleclock; | ||||
| assign sampleclocked = !oldsampleclock && sampleclock; | ||||
|  | ||||
| reg oldindex; | ||||
| wire indexed; | ||||
| always @(posedge clock) oldindex <= index; | ||||
| assign indexed = !oldindex && index; | ||||
|  | ||||
| always @(posedge clock) | ||||
| begin | ||||
|     if (reset) | ||||
|     begin | ||||
|         state <= STATE_IDLE; | ||||
|         countdown <= 0; | ||||
|     end | ||||
|     else | ||||
|         case (state) | ||||
|             STATE_IDLE: | ||||
|                 state <= STATE_LOAD; | ||||
|              | ||||
|             STATE_LOAD: | ||||
|                 if (dataclocked) | ||||
|                     case (opcode) | ||||
|                         OPCODE_PULSE: | ||||
|                             state <= STATE_PULSING; | ||||
|                          | ||||
|                         OPCODE_INDEX: | ||||
|                             state <= STATE_INDEXING; | ||||
|                          | ||||
|                         default: | ||||
|                         begin | ||||
|                             countdown <= opcode[6:0]; | ||||
|                             state <= STATE_WAITING; | ||||
|                         end | ||||
|                     endcase | ||||
|              | ||||
|             STATE_WAITING: | ||||
|                 if (sampleclocked) | ||||
|                 begin | ||||
|                     if (countdown == 0) | ||||
|                         state <= STATE_LOAD; | ||||
|                     else | ||||
|                         countdown <= countdown - 1; | ||||
|                 end | ||||
|              | ||||
|             STATE_PULSING: | ||||
|                 state <= STATE_LOAD; | ||||
|              | ||||
|             STATE_INDEXING: | ||||
|                 if (indexed) | ||||
|                     state <= STATE_LOAD; | ||||
|                 else | ||||
|                     state <= STATE_INDEXING; | ||||
|         endcase | ||||
| end | ||||
|  | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
| endmodule | ||||
| //`#start footer` -- edit after this line, do not edit this line | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
							
								
								
									
										
											BIN
										
									
								
								FluxEngine.cydsn/SuperCounter/SuperCounter.cysym
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								FluxEngine.cydsn/SuperCounter/SuperCounter.cysym
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										156
									
								
								FluxEngine.cydsn/SuperCounter/SuperCounter.v
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								FluxEngine.cydsn/SuperCounter/SuperCounter.v
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | ||||
|  | ||||
| //`#start header` -- edit after this line, do not edit this line | ||||
| `include "cypress.v" | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
| // Generated on 11/16/2017 at 15:44 | ||||
| // Component: FIFOout | ||||
| module SuperCounter ( | ||||
| 	input clk, | ||||
|     input reset, | ||||
|     input count, | ||||
|     output [7:0] d, | ||||
| 	output drq, | ||||
| 	output empty, | ||||
|     output ack | ||||
| ); | ||||
|  | ||||
| //`#start body` -- edit after this line, do not edit this line | ||||
|  | ||||
| parameter ResetValue = 0; | ||||
| parameter Delta = 1; | ||||
|      | ||||
| wire [7:0] po; | ||||
| assign d = po; | ||||
|  | ||||
| localparam STATE_RESET = 0; | ||||
| localparam STATE_WAIT = 1; | ||||
| localparam STATE_ADD = 2; | ||||
|  | ||||
| reg oldcount; | ||||
| wire counted; | ||||
| assign counted = count && !oldcount; | ||||
|  | ||||
| always @(posedge clk) oldcount <= count; | ||||
|  | ||||
| wire [2:0] cs; | ||||
| assign cs = reset ? STATE_RESET : (counted ? STATE_ADD : STATE_WAIT); | ||||
|              | ||||
| cy_psoc3_dp #(.d0_init(ResetValue), .d1_init(Delta),  | ||||
| .cy_dpconfig( | ||||
| { | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC___D0, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM0:  STATE_RESET*/ | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM1:  STATE_WAIT*/ | ||||
|     `CS_ALU_OP__ADD, `CS_SRCA_A0, `CS_SRCB_D1, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM2:  STATE_ADD*/ | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM3:             */ | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM4:             */ | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM5:             */ | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM6:             */ | ||||
|     `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
|     `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
|     `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
|     `CS_CMP_SEL_CFGA, /*CFGRAM7:             */ | ||||
|     8'hFF, 8'h00,  /*CFG9:             */ | ||||
|     8'hFF, 8'hFF,  /*CFG11-10:             */ | ||||
|     `SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH, | ||||
|     `SC_CI_A_ARITH, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL, | ||||
|     `SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI, | ||||
|     `SC_SI_A_DEFSI, /*CFG13-12:             */ | ||||
|     `SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0, | ||||
|     1'h0, `SC_FIFO1_BUS, `SC_FIFO0_BUS, | ||||
|     `SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN, | ||||
|     `SC_FB_NOCHN, `SC_CMP1_NOCHN, | ||||
|     `SC_CMP0_NOCHN, /*CFG15-14:             */ | ||||
|     10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX, | ||||
|     `SC_FIFO_LEVEL,`SC_FIFO_ASYNC,`SC_EXTCRC_DSBL, | ||||
|     `SC_WRK16CAT_DSBL /*CFG17-16:             */ | ||||
| } | ||||
| )) dp( | ||||
|         /*  input                   */  .reset(1'b0), | ||||
|         /*  input                   */  .clk(clk), | ||||
|         /*  input   [02:00]         */  .cs_addr(cs), | ||||
|         /*  input                   */  .route_si(1'b0), | ||||
|         /*  input                   */  .route_ci(1'b0), | ||||
|         /*  input                   */  .f0_load(1'b0), | ||||
|         /*  input                   */  .f1_load(1'b0), | ||||
|         /*  input                   */  .d0_load(1'b0), | ||||
|         /*  input                   */  .d1_load(1'b0), | ||||
|         /*  output                  */  .ce0(), | ||||
|         /*  output                  */  .cl0(), | ||||
|         /*  output                  */  .z0(), | ||||
|         /*  output                  */  .ff0(), | ||||
|         /*  output                  */  .ce1(), | ||||
|         /*  output                  */  .cl1(), | ||||
|         /*  output                  */  .z1(), | ||||
|         /*  output                  */  .ff1(), | ||||
|         /*  output                  */  .ov_msb(), | ||||
|         /*  output                  */  .co_msb(), | ||||
|         /*  output                  */  .cmsb(), | ||||
|         /*  output                  */  .so(), | ||||
|         /*  output                  */  .f0_bus_stat(), | ||||
|         /*  output                  */  .f0_blk_stat(), | ||||
|         /*  output                  */  .f1_bus_stat(), | ||||
|         /*  output                  */  .f1_blk_stat(), | ||||
|          | ||||
|         /* input                    */  .ci(1'b0),     // Carry in from previous stage | ||||
|         /* output                   */  .co(),// Carry out to next stage | ||||
|         /* input                    */  .sir(1'b0),    // Shift in from right side | ||||
|         /* output                   */  .sor(),        // Shift out to right side | ||||
|         /* input                    */  .sil(1'b0),    // Shift in from left side | ||||
|         /* output                   */  .sol(),        // Shift out to left side | ||||
|         /* input                    */  .msbi(1'b0),   // MSB chain in | ||||
|         /* output                   */  .msbo(),       // MSB chain out | ||||
|         /* input [01:00]            */  .cei(2'b0),    // Compare equal in from prev stage | ||||
|         /* output [01:00]           */  .ceo(),        // Compare equal out to next stage | ||||
|         /* input [01:00]            */  .cli(2'b0),    // Compare less than in from prv stage | ||||
|         /* output [01:00]           */  .clo(),        // Compare less than out to next stage | ||||
|         /* input [01:00]            */  .zi(2'b0),     // Zero detect in from previous stage | ||||
|         /* output [01:00]           */  .zo(),         // Zero detect out to next stage | ||||
|         /* input [01:00]            */  .fi(2'b0),     // 0xFF detect in from previous stage | ||||
|         /* output [01:00]           */  .fo(),         // 0xFF detect out to next stage | ||||
|         /* input [01:00]            */  .capi(2'b0),   // Software capture from previous stage | ||||
|         /* output [01:00]           */  .capo(),       // Software capture to next stage | ||||
|         /* input                    */  .cfbi(1'b0),   // CRC Feedback in from previous stage | ||||
|         /* output                   */  .cfbo(),       // CRC Feedback out to next stage | ||||
|         /* input [07:00]            */  .pi(8'b0),     // Parallel data port | ||||
|         /* output [07:00]           */  .po(po)       // Parallel data port | ||||
| ); | ||||
|  | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
| endmodule | ||||
| //`#start footer` -- edit after this line, do not edit this line | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -17,14 +17,14 @@ | ||||
| #define STEP_TOWARDS0 1 | ||||
| #define STEP_AWAYFROM0 0 | ||||
|  | ||||
| static volatile uint32_t clock = 0; | ||||
| static volatile uint32_t clock = 0; /* ms */ | ||||
| static volatile bool index_irq = false; | ||||
|  | ||||
| static bool motor_on = false; | ||||
| static uint32_t motor_on_time = 0; | ||||
| static bool homed = false; | ||||
| static int current_track = 0; | ||||
| static uint8_t current_drive_flags = 0; | ||||
| static struct set_drive_frame current_drive_flags; | ||||
|  | ||||
| #define BUFFER_COUNT 16 | ||||
| #define BUFFER_SIZE 64 | ||||
| @@ -45,6 +45,21 @@ static void system_timer_cb(void) | ||||
| { | ||||
|     CyGlobalIntDisable; | ||||
|     clock++; | ||||
|      | ||||
|     static int counter300rpm = 0; | ||||
|     counter300rpm++; | ||||
|     if (counter300rpm == 200) | ||||
|         counter300rpm = 0; | ||||
|      | ||||
|     static int counter360rpm = 0; | ||||
|     counter360rpm++; | ||||
|     if (counter360rpm == 167) | ||||
|         counter360rpm = 0; | ||||
|      | ||||
|     FAKE_INDEX_GENERATOR_REG_Write( | ||||
|         ((counter300rpm == 0) ? 1 : 0) | ||||
|         | ((counter360rpm == 0) ? 2 : 0)); | ||||
|      | ||||
|     CyGlobalIntEnable; | ||||
| } | ||||
|  | ||||
| @@ -85,10 +100,22 @@ static void print(const char* msg, ...) | ||||
|     UART_PutCRLF(); | ||||
| } | ||||
|  | ||||
| static void set_drive_flags(struct set_drive_frame* flags) | ||||
| { | ||||
|     if (current_drive_flags.drive != flags->drive) | ||||
|         homed = false; | ||||
|      | ||||
|     current_drive_flags = *flags; | ||||
|     DRIVESELECT_REG_Write(flags->drive ? 2 : 1); /* select drive 1 or 0 */ | ||||
|     DENSITY_REG_Write(flags->high_density); /* density bit */ | ||||
|     INDEX_REG_Write(flags->index_mode); | ||||
| } | ||||
|  | ||||
| static void start_motor(void) | ||||
| { | ||||
|     if (!motor_on) | ||||
|     { | ||||
|         set_drive_flags(¤t_drive_flags); | ||||
|         MOTOR_REG_Write(1); | ||||
|         CyDelay(1000); | ||||
|         homed = false; | ||||
| @@ -99,6 +126,16 @@ static void start_motor(void) | ||||
|     CyWdtClear(); | ||||
| } | ||||
|  | ||||
| static void stop_motor(void) | ||||
| { | ||||
|     if (motor_on) | ||||
|     { | ||||
|         MOTOR_REG_Write(0); | ||||
|         DRIVESELECT_REG_Write(0); /* deselect all drives */ | ||||
|         motor_on = false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void wait_until_writeable(int ep) | ||||
| { | ||||
|     while (USBFS_GetEPState(ep) != USBFS_IN_BUFFER_EMPTY) | ||||
| @@ -138,25 +175,36 @@ static void cmd_get_version(struct any_frame* f) | ||||
|  | ||||
| static void step(int dir) | ||||
| { | ||||
|     STEP_REG_Write(dir); | ||||
|     CyDelayUs(1); | ||||
|     STEP_REG_Write(dir | 2); | ||||
|     CyDelayUs(1); | ||||
|     STEP_REG_Write(dir); | ||||
|     STEP_REG_Write(dir); /* step high */ | ||||
|     CyDelayUs(6); | ||||
|     STEP_REG_Write(dir | 2); /* step low */ | ||||
|     CyDelayUs(6); | ||||
|     STEP_REG_Write(dir); /* step high again, drive moves now */ | ||||
|     CyDelay(STEP_INTERVAL_TIME); | ||||
| } | ||||
|  | ||||
| static void home(void) | ||||
| { | ||||
|     for (int i=0; i<100; i++) | ||||
|     { | ||||
|         /* Don't keep stepping forever, because if a drive's | ||||
|          * not connected bad things happen. */ | ||||
|         if (TRACK0_REG_Read()) | ||||
|             break; | ||||
|         step(STEP_TOWARDS0); | ||||
|     } | ||||
|      | ||||
|     /* Step to -1, which should be a nop, to reset the disk on disk change. */ | ||||
|     step(STEP_TOWARDS0); | ||||
| } | ||||
|  | ||||
| static void seek_to(int track) | ||||
| { | ||||
|     start_motor(); | ||||
|     if (!homed) | ||||
|     if (!homed || (track == 0)) | ||||
|     { | ||||
|         print("homing"); | ||||
|         while (!TRACK0_REG_Read()) | ||||
|             step(STEP_TOWARDS0); | ||||
|              | ||||
|         /* Step to -1, which should be a nop, to reset the disk on disk change. */ | ||||
|         step(STEP_TOWARDS0); | ||||
|         home(); | ||||
|          | ||||
|         homed = true; | ||||
|         current_track = 0; | ||||
| @@ -167,11 +215,7 @@ static void seek_to(int track) | ||||
|     while (track != current_track) | ||||
|     { | ||||
|         if (TRACK0_REG_Read()) | ||||
|         { | ||||
|             if (current_track != 0) | ||||
|                 print("unexpectedly detected track 0"); | ||||
|             current_track = 0; | ||||
|         } | ||||
|          | ||||
|         if (track > current_track) | ||||
|         { | ||||
| @@ -208,17 +252,29 @@ static void cmd_measure_speed(struct any_frame* f) | ||||
| { | ||||
|     start_motor(); | ||||
|      | ||||
|     index_irq = false; | ||||
|     while (!index_irq) | ||||
|         ; | ||||
|     index_irq = false; | ||||
|     int start_clock = clock; | ||||
|     int elapsed = 0; | ||||
|     while (!index_irq) | ||||
|         ; | ||||
|     int end_clock = clock; | ||||
|     { | ||||
|         elapsed = clock - start_clock; | ||||
|         if (elapsed > 1000) | ||||
|         { | ||||
|             elapsed = 0; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (elapsed != 0) | ||||
|     { | ||||
|         index_irq = false; | ||||
|         start_clock = clock; | ||||
|         while (!index_irq) | ||||
|             elapsed = clock - start_clock; | ||||
|     } | ||||
|      | ||||
|     DECLARE_REPLY_FRAME(struct speed_frame, F_FRAME_MEASURE_SPEED_REPLY); | ||||
|     r.period_ms = end_clock - start_clock; | ||||
|     r.period_ms = elapsed; | ||||
|     send_reply((struct any_frame*) &r);     | ||||
| } | ||||
|  | ||||
| @@ -249,7 +305,7 @@ static void deinit_dma(void) | ||||
|  | ||||
| static void init_capture_dma(void) | ||||
| { | ||||
|     dma_channel = CAPTURE_DMA_DmaInitialize( | ||||
|     dma_channel = SAMPLER_DMA_DmaInitialize( | ||||
|         2 /* bytes */, | ||||
|         true /* request per burst */,  | ||||
|         HI16(CYDEV_PERIPH_BASE), | ||||
| @@ -264,8 +320,8 @@ static void init_capture_dma(void) | ||||
|             nexti = 0; | ||||
|  | ||||
|         CyDmaTdSetConfiguration(td[i], BUFFER_SIZE, td[nexti],    | ||||
|             CY_DMA_TD_INC_DST_ADR | CAPTURE_DMA__TD_TERMOUT_EN); | ||||
|         CyDmaTdSetAddress(td[i], LO16((uint32)&SAMPLER_DATAPATH_F0_REG), LO16((uint32)&dma_buffer[i])); | ||||
|             CY_DMA_TD_INC_DST_ADR | SAMPLER_DMA__TD_TERMOUT_EN); | ||||
|         CyDmaTdSetAddress(td[i], LO16((uint32)SAMPLER_FIFO_FIFO_PTR), LO16((uint32)&dma_buffer[i])); | ||||
|     }     | ||||
| } | ||||
|  | ||||
| @@ -276,26 +332,26 @@ static void cmd_read(struct read_frame* f) | ||||
|      | ||||
|     /* Do slow setup *before* we go into the real-time bit. */ | ||||
|      | ||||
|     SAMPLER_CONTROL_Write(1); /* reset */ | ||||
|      | ||||
|     { | ||||
|         uint8_t i = CyEnterCriticalSection(); | ||||
|         SAMPLER_DATAPATH_F0_SET_LEVEL_MID; | ||||
|         SAMPLER_DATAPATH_F0_CLEAR; | ||||
|         SAMPLER_DATAPATH_F0_SINGLE_BUFFER_UNSET; | ||||
|         SAMPLER_FIFO_SET_LEVEL_MID; | ||||
|         SAMPLER_FIFO_CLEAR; | ||||
|         SAMPLER_FIFO_SINGLE_BUFFER_UNSET; | ||||
|         CyExitCriticalSection(i); | ||||
|     } | ||||
|      | ||||
|     wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM); | ||||
|     init_capture_dma(); | ||||
|  | ||||
|     /* Wait for the beginning of a rotation. */ | ||||
|     /* Wait for the beginning of a rotation, if requested. */ | ||||
|          | ||||
|     print("wait"); | ||||
|     index_irq = false; | ||||
|     while (!index_irq) | ||||
|         ; | ||||
|     index_irq = false; | ||||
|     if (f->synced) | ||||
|     { | ||||
|         index_irq = false; | ||||
|         while (!index_irq) | ||||
|             ; | ||||
|         index_irq = false; | ||||
|     } | ||||
|      | ||||
|     crunch_state_t cs = {}; | ||||
|     cs.outputptr = usb_buffer; | ||||
| @@ -305,8 +361,6 @@ static void cmd_read(struct read_frame* f) | ||||
|     dma_reading_from_td = -1; | ||||
|     dma_underrun = false; | ||||
|     int count = 0; | ||||
|     SAMPLER_CONTROL_Write(0); /* !reset */ | ||||
|     CAPTURE_CONTROL_Write(1); | ||||
|     CyDmaChSetInitialTd(dma_channel, td[dma_writing_to_td]); | ||||
|     CyDmaClearPendingDrq(dma_channel); | ||||
|     CyDmaChEnable(dma_channel, 1); | ||||
| @@ -314,32 +368,27 @@ static void cmd_read(struct read_frame* f) | ||||
|     /* Wait for the first DMA transfer to complete, after which we can start the | ||||
|      * USB transfer. */ | ||||
|  | ||||
|     while ((dma_writing_to_td == 0) && !index_irq) | ||||
|     while (dma_writing_to_td == 0) | ||||
|         ; | ||||
|     dma_reading_from_td = 0; | ||||
|      | ||||
|     /* Start transferring. */ | ||||
|  | ||||
|     int revolutions = f->revolutions; | ||||
|     uint32_t start_time = clock; | ||||
|     while (!dma_underrun) | ||||
|     { | ||||
|         CyWdtClear(); | ||||
|  | ||||
|         /* Have we reached the index pulse? */ | ||||
|         if (index_irq) | ||||
|         { | ||||
|             index_irq = false; | ||||
|             revolutions--; | ||||
|             if (revolutions == 0) | ||||
|                 break; | ||||
|         } | ||||
|          | ||||
|         /* Wait for the next block to be read. */ | ||||
|         while (dma_reading_from_td == dma_writing_to_td) | ||||
|         { | ||||
|             /* On an underrun, give up immediately. */ | ||||
|             if (dma_underrun) | ||||
|                 goto abort; | ||||
|              | ||||
|             /* Also finish if the sample session is over. */ | ||||
|             if ((clock - start_time) >= f->milliseconds) | ||||
|                 goto abort; | ||||
|         } | ||||
|  | ||||
|         uint8_t dma_buffer_usage = 0; | ||||
| @@ -350,15 +399,14 @@ static void cmd_read(struct read_frame* f) | ||||
|             crunch(&cs); | ||||
|             dma_buffer_usage += BUFFER_SIZE - cs.inputlen; | ||||
|             count++; | ||||
|              | ||||
|             /* If there is no available space in the output buffer, flush the buffer via | ||||
|              * USB and go again. */ | ||||
|             if (cs.outputlen == 0) | ||||
|             { | ||||
|                 while (USBFS_GetEPState(FLUXENGINE_DATA_IN_EP_NUM) != USBFS_IN_BUFFER_EMPTY) | ||||
|                 { | ||||
|                     if (index_irq || dma_underrun) | ||||
|                         goto abort; | ||||
|                 } | ||||
|  | ||||
|                 wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM); | ||||
|                 USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE); | ||||
|                  | ||||
|                 cs.outputptr = usb_buffer; | ||||
|                 cs.outputlen = BUFFER_SIZE; | ||||
|             } | ||||
| @@ -366,22 +414,33 @@ static void cmd_read(struct read_frame* f) | ||||
|         dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td); | ||||
|     } | ||||
| abort:; | ||||
|     CAPTURE_CONTROL_Write(0); | ||||
|     bool saved_dma_underrun = dma_underrun; | ||||
|     CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN); | ||||
|     while (CyDmaChGetRequest(dma_channel)) | ||||
|         ; | ||||
|  | ||||
|     donecrunch(&cs); | ||||
|     wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM); | ||||
|     unsigned zz = cs.outputlen; | ||||
|     /* If there's a complete packet waiting, send it. */ | ||||
|     if (cs.outputlen != BUFFER_SIZE) | ||||
|     { | ||||
|         USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE); | ||||
|         wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM); | ||||
|     } | ||||
|     if ((cs.outputlen != 0) && (cs.outputlen != BUFFER_SIZE)) | ||||
|     { | ||||
|         /* If there's a partial packet waiting, send it; this will also terminate the transfer. */ | ||||
|         USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE-cs.outputlen); | ||||
|     if ((cs.outputlen == BUFFER_SIZE) || (cs.outputlen == 0)) | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         /* Otherwise just terminate the transfer. */ | ||||
|         USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, NULL, 0); | ||||
|     } | ||||
|     wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM); | ||||
|     deinit_dma(); | ||||
|  | ||||
|     if (dma_underrun) | ||||
|     if (saved_dma_underrun) | ||||
|     { | ||||
|         print("underrun after %d packets"); | ||||
|         send_error(F_ERROR_UNDERRUN); | ||||
| @@ -391,7 +450,7 @@ abort:; | ||||
|         DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_READ_REPLY); | ||||
|         send_reply(&r); | ||||
|     } | ||||
|     print("count=%d i=%d d=%d zz=%d", count, index_irq, dma_underrun, zz); | ||||
|     print("count=%d i=%d d=%d", count, index_irq, dma_underrun); | ||||
| } | ||||
|  | ||||
| static void init_replay_dma(void) | ||||
| @@ -412,25 +471,28 @@ static void init_replay_dma(void) | ||||
|  | ||||
|         CyDmaTdSetConfiguration(td[i], BUFFER_SIZE, td[nexti], | ||||
|             CY_DMA_TD_INC_SRC_ADR | SEQUENCER_DMA__TD_TERMOUT_EN); | ||||
|         CyDmaTdSetAddress(td[i], LO16((uint32)&dma_buffer[i]), LO16((uint32)&SEQUENCER_DATAPATH_F0_REG)); | ||||
|         CyDmaTdSetAddress(td[i], LO16((uint32)&dma_buffer[i]), LO16((uint32)REPLAY_FIFO_FIFO_PTR)); | ||||
|     }     | ||||
| } | ||||
|  | ||||
| static void cmd_write(struct write_frame* f) | ||||
| { | ||||
|     print("cmd_write"); | ||||
|      | ||||
|     if (f->bytes_to_write % FRAME_SIZE) | ||||
|     { | ||||
|         send_error(F_ERROR_INVALID_VALUE); | ||||
|         return; | ||||
|     } | ||||
|      | ||||
|     SEQUENCER_CONTROL_Write(1); /* put the sequencer into reset */ | ||||
|  | ||||
|     SIDE_REG_Write(f->side); | ||||
|     SEQUENCER_CONTROL_Write(1); /* reset */ | ||||
|     { | ||||
|         uint8_t i = CyEnterCriticalSection(); | ||||
|         SEQUENCER_DATAPATH_F0_SET_LEVEL_NORMAL; | ||||
|         SEQUENCER_DATAPATH_F0_CLEAR; | ||||
|         SEQUENCER_DATAPATH_F0_SINGLE_BUFFER_UNSET; | ||||
|         uint8_t i = CyEnterCriticalSection();         | ||||
|         REPLAY_FIFO_SET_LEVEL_NORMAL; | ||||
|         REPLAY_FIFO_CLEAR; | ||||
|         REPLAY_FIFO_SINGLE_BUFFER_UNSET; | ||||
|         CyExitCriticalSection(i); | ||||
|     } | ||||
|     seek_to(current_track);     | ||||
| @@ -452,6 +514,8 @@ static void cmd_write(struct write_frame* f) | ||||
|     int old_reading_from_td = -1; | ||||
|     for (;;) | ||||
|     { | ||||
|         CyWdtClear(); | ||||
|  | ||||
|         /* Read data from USB into the buffers. */ | ||||
|          | ||||
|         if (NEXT_BUFFER(dma_writing_to_td) != dma_reading_from_td) | ||||
| @@ -555,7 +619,7 @@ abort: | ||||
|         CyDmaChDisable(dma_channel); | ||||
|     } | ||||
|      | ||||
|     //debug("p=%d cr=%d cw=%d f=%d l=%d w=%d index=%d underrun=%d", packets, count_read, count_written, finished, listening, writing, index_irq, dma_underrun); | ||||
|     print("p=%d cr=%d cw=%d f=%d w=%d index=%d underrun=%d", packets, count_read, count_written, finished, writing, index_irq, dma_underrun); | ||||
|     if (!finished) | ||||
|     { | ||||
|         while (count_read < packets) | ||||
| @@ -573,6 +637,7 @@ abort: | ||||
|     } | ||||
|      | ||||
|     deinit_dma(); | ||||
|     print("write finished"); | ||||
|      | ||||
|     if (dma_underrun) | ||||
|     { | ||||
| @@ -607,17 +672,101 @@ static void cmd_erase(struct erase_frame* f) | ||||
|  | ||||
| static void cmd_set_drive(struct set_drive_frame* f) | ||||
| { | ||||
|     if (current_drive_flags != f->drive_flags) | ||||
|     { | ||||
|         current_drive_flags = f->drive_flags; | ||||
|         DRIVE_REG_Write(current_drive_flags); | ||||
|         homed = false; | ||||
|     } | ||||
|     set_drive_flags(f); | ||||
|      | ||||
|     DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_SET_DRIVE_REPLY); | ||||
|     send_reply((struct any_frame*) &r); | ||||
| } | ||||
|     | ||||
|  | ||||
| static uint16_t read_output_voltage_mv(void) | ||||
| { | ||||
|     OUTPUT_VOLTAGE_ADC_StartConvert(); | ||||
|     OUTPUT_VOLTAGE_ADC_IsEndConversion(OUTPUT_VOLTAGE_ADC_WAIT_FOR_RESULT); | ||||
|     uint16_t samples = OUTPUT_VOLTAGE_ADC_GetResult16(); | ||||
|     return OUTPUT_VOLTAGE_ADC_CountsTo_mVolts(samples); | ||||
| } | ||||
|  | ||||
| static void read_output_voltages(struct voltages* v) | ||||
| { | ||||
|     SIDE_REG_Write(1); /* set DIR to low (remember this is inverted) */ | ||||
|     CyDelay(100); | ||||
|     v->logic0_mv = read_output_voltage_mv(); | ||||
|  | ||||
|     SIDE_REG_Write(0); | ||||
|     CyDelay(100); | ||||
|     v->logic1_mv = read_output_voltage_mv(); | ||||
| } | ||||
|  | ||||
| static uint16_t read_input_voltage_mv(void) | ||||
| { | ||||
|     INPUT_VOLTAGE_ADC_StartConvert(); | ||||
|     INPUT_VOLTAGE_ADC_IsEndConversion(INPUT_VOLTAGE_ADC_WAIT_FOR_RESULT); | ||||
|     uint16_t samples = INPUT_VOLTAGE_ADC_GetResult16(); | ||||
|     return INPUT_VOLTAGE_ADC_CountsTo_mVolts(samples); | ||||
| } | ||||
|  | ||||
| static void read_input_voltages(struct voltages* v) | ||||
| { | ||||
|     home(); | ||||
|     CyDelay(50); | ||||
|     v->logic0_mv = read_input_voltage_mv(); | ||||
|      | ||||
|     step(STEP_AWAYFROM0); | ||||
|     CyDelay(50); | ||||
|     v->logic1_mv = read_input_voltage_mv(); | ||||
| } | ||||
|  | ||||
| static void cmd_measure_voltages(void) | ||||
| { | ||||
|     stop_motor(); | ||||
|     INPUT_VOLTAGE_ADC_Start(); | ||||
|     INPUT_VOLTAGE_ADC_SetPower(INPUT_VOLTAGE_ADC__HIGHPOWER); | ||||
|     OUTPUT_VOLTAGE_ADC_Start(); | ||||
|     OUTPUT_VOLTAGE_ADC_SetPower(OUTPUT_VOLTAGE_ADC__HIGHPOWER); | ||||
|      | ||||
|     DECLARE_REPLY_FRAME(struct voltages_frame, F_FRAME_MEASURE_VOLTAGES_REPLY); | ||||
|      | ||||
|     CyWdtClear(); | ||||
|     MOTOR_REG_Write(0); /* should be ignored anyway */ | ||||
|     DRIVESELECT_REG_Write(0); /* deselect both drives */ | ||||
|     CyDelay(200); /* wait for things to settle */ | ||||
|     read_output_voltages(&r.output_both_off); | ||||
|     read_input_voltages(&r.input_both_off); | ||||
|  | ||||
|     CyWdtClear(); | ||||
|     DRIVESELECT_REG_Write(1); /* select drive 0 */ | ||||
|     CyDelay(50); | ||||
|     read_output_voltages(&r.output_drive_0_selected); | ||||
|     read_input_voltages(&r.input_drive_0_selected); | ||||
|     MOTOR_REG_Write(1); | ||||
|     CyDelay(300); | ||||
|     CyWdtClear(); | ||||
|     read_output_voltages(&r.output_drive_0_running); | ||||
|     read_input_voltages(&r.input_drive_0_running); | ||||
|     MOTOR_REG_Write(0); | ||||
|     CyDelay(300); | ||||
|      | ||||
|     CyWdtClear(); | ||||
|     DRIVESELECT_REG_Write(2); /* select drive 1 */ | ||||
|     CyDelay(50); | ||||
|     read_output_voltages(&r.output_drive_1_selected); | ||||
|     read_input_voltages(&r.input_drive_1_selected); | ||||
|     MOTOR_REG_Write(1); | ||||
|     CyDelay(300); | ||||
|     CyWdtClear(); | ||||
|     read_output_voltages(&r.output_drive_1_running); | ||||
|     read_input_voltages(&r.input_drive_1_running); | ||||
|     MOTOR_REG_Write(0); | ||||
|     CyDelay(300); | ||||
|  | ||||
|     CyWdtClear(); | ||||
|     DRIVESELECT_REG_Write(0); | ||||
|     homed = false; | ||||
|     INPUT_VOLTAGE_ADC_Stop(); | ||||
|     OUTPUT_VOLTAGE_ADC_Stop(); | ||||
|     send_reply((struct any_frame*) &r); | ||||
| } | ||||
|  | ||||
| static void handle_command(void) | ||||
| { | ||||
|     static uint8_t input_buffer[FRAME_SIZE]; | ||||
| @@ -662,6 +811,10 @@ static void handle_command(void) | ||||
|         case F_FRAME_SET_DRIVE_CMD: | ||||
|             cmd_set_drive((struct set_drive_frame*) f); | ||||
|             break; | ||||
|          | ||||
|         case F_FRAME_MEASURE_VOLTAGES_CMD: | ||||
|             cmd_measure_voltages(); | ||||
|             break; | ||||
|              | ||||
|         default: | ||||
|             send_error(F_ERROR_BAD_COMMAND); | ||||
| @@ -674,9 +827,11 @@ int main(void) | ||||
|     CySysTickStart(); | ||||
|     CySysTickSetCallback(4, system_timer_cb); | ||||
|     INDEX_IRQ_StartEx(&index_irq_cb); | ||||
|     CAPTURE_DMA_FINISHED_IRQ_StartEx(&capture_dma_finished_irq_cb); | ||||
|     SAMPLER_DMA_FINISHED_IRQ_StartEx(&capture_dma_finished_irq_cb); | ||||
|     SEQUENCER_DMA_FINISHED_IRQ_StartEx(&replay_dma_finished_irq_cb); | ||||
|     DRIVE_REG_Write(0); | ||||
|     INPUT_VOLTAGE_ADC_Stop(); | ||||
|     OUTPUT_VOLTAGE_ADC_Stop(); | ||||
|     DRIVESELECT_REG_Write(0); | ||||
|     UART_Start(); | ||||
|     USBFS_Start(0, USBFS_DWR_VDDD_OPERATION); | ||||
|      | ||||
| @@ -692,10 +847,7 @@ int main(void) | ||||
|         { | ||||
|             uint32_t time_on = clock - motor_on_time; | ||||
|             if (time_on > MOTOR_ON_TIME) | ||||
|             { | ||||
|                 MOTOR_REG_Write(0); | ||||
|                 motor_on = false; | ||||
|             } | ||||
|                 stop_motor(); | ||||
|         } | ||||
|          | ||||
|         if (!USBFS_GetConfiguration() || USBFS_IsConfigurationChanged()) | ||||
| @@ -709,6 +861,7 @@ int main(void) | ||||
|          | ||||
|         if (USBFS_GetEPState(FLUXENGINE_CMD_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL) | ||||
|         { | ||||
|             set_drive_flags(¤t_drive_flags); | ||||
|             handle_command(); | ||||
|             USBFS_EnableOutEP(FLUXENGINE_CMD_OUT_EP_NUM); | ||||
|             print("idle"); | ||||
|   | ||||
							
								
								
									
										18
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,8 +1,13 @@ | ||||
| PACKAGES = zlib sqlite3 libusb-1.0 | ||||
|  | ||||
| export CFLAGS = -O3 -g --std=c++14 \ | ||||
| 	-ffunction-sections -fdata-sections | ||||
| export LDFLAGS = -O3 | ||||
| export CFLAGS = --std=c++14 -ffunction-sections -fdata-sections | ||||
| export LDFLAGS = | ||||
|  | ||||
| export COPTFLAGS = -Os | ||||
| export LDOPTFLAGS = -Os -s | ||||
|  | ||||
| export CDBGFLAGS = -O0 -g | ||||
| export LDDBGFLAGS = -O0 -g | ||||
|  | ||||
| ifeq ($(OS), Windows_NT) | ||||
| export CXX = /mingw32/bin/g++ | ||||
| @@ -13,6 +18,13 @@ export LDFLAGS += | ||||
| export LIBS = -static -lz -lsqlite3 -lusb-1.0 | ||||
| export EXTENSION = .exe | ||||
| else | ||||
|  | ||||
| packages-exist = $(shell pkg-config --exists $(PACKAGES) && echo yes) | ||||
| ifneq ($(packages-exist),yes) | ||||
| $(warning These pkg-config packages are installed: $(shell pkg-config --list-all | sort | awk '{print $$1}')) | ||||
| $(error You must have these pkg-config packages installed: $(PACKAGES)) | ||||
| endif | ||||
|  | ||||
| export CXX = g++ | ||||
| export AR = ar rcs | ||||
| export STRIP = strip | ||||
|   | ||||
| @@ -79,7 +79,7 @@ people who've had it work). | ||||
|  | ||||
| | Format                                   | Read? | Write? | Notes | | ||||
| |:-----------------------------------------|:-----:|:------:|-------| | ||||
| | IBM PC compatible                        |  🦄   |        | and compatibles (like the Atari ST) | | ||||
| | [IBM PC compatible](doc/disk-ibm.md)     |  🦄   |        | and compatibles (like the Atari ST) | | ||||
| | [Acorn ADFS](doc/disk-acornadfs.md)      |  🦄   |        | single- and double- sided           | | ||||
| | [Acorn DFS](doc/disk-acorndfs.md)        |  🦄   |        |                                     | | ||||
| | [Ampro Little Board](doc/disk-ampro.md)  |  🦖   |        |                                     | | ||||
| @@ -89,7 +89,7 @@ people who've had it work). | ||||
| | [Brother 120kB](doc/disk-brother.md)     |  🦄   |        |                                     | | ||||
| | [Brother 240kB](doc/disk-brother.md)     |  🦄   |   🦄   |                                     | | ||||
| | [Brother FB-100](doc/disk-fb100.md)      |  🦖   |        | Tandy Model 100, Husky Hunter, knitting machines | | ||||
| | [Macintosh 800kB](doc/disk-macintosh.md) |  🦄   |        | and probably the 400kB too          | | ||||
| | [Macintosh 800kB](doc/disk-macintosh.md) |  🦖   |        | and probably the 400kB too          | | ||||
| | [TRS-80](doc/disk-trs80.md)              |  🦖   |        | a minor variation of the IBM scheme | | ||||
| {: .datatable } | ||||
|  | ||||
|   | ||||
							
								
								
									
										101
									
								
								arch/amiga/amiga.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								arch/amiga/amiga.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| #include "globals.h" | ||||
| #include "record.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "amiga.h" | ||||
| #include "bytes.h" | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| uint32_t amigaChecksum(const Bytes& bytes) | ||||
| { | ||||
|     ByteReader br(bytes); | ||||
|     uint32_t checksum = 0; | ||||
|  | ||||
|     assert((bytes.size() & 3) == 0); | ||||
|     while (!br.eof()) | ||||
|         checksum ^= br.read_be32(); | ||||
|  | ||||
|     return checksum & 0x55555555; | ||||
| } | ||||
|  | ||||
| static uint8_t everyother(uint16_t x) | ||||
| { | ||||
| 	                  /* aabb ccdd eeff gghh */ | ||||
| 	x &= 0x6666;      /* 0ab0 0cd0 0ef0 0gh0 */ | ||||
| 	x >>= 1;          /* 00ab 00cd 00ef 00gh */ | ||||
| 	x |= x << 2;      /* abab cdcd efef ghgh */ | ||||
| 	x &= 0x3c3c;      /* 00ab cd00 00ef gh00 */ | ||||
| 	x >>= 2;          /* 0000 abcd 0000 efgh */ | ||||
| 	x |= x >> 4;      /* 0000 abcd abcd efgh */ | ||||
| 	return x; | ||||
| } | ||||
|  | ||||
| Bytes amigaInterleave(const Bytes& input) | ||||
| { | ||||
| 	Bytes output; | ||||
| 	ByteWriter bw(output); | ||||
|  | ||||
| 	/* Write all odd bits. (Numbering starts at 0...) */ | ||||
|  | ||||
| 	{ | ||||
| 		ByteReader br(input); | ||||
| 		while (!br.eof()) | ||||
| 		{ | ||||
| 			uint16_t x = br.read_be16(); | ||||
| 			x &= 0xaaaa;       /* a0b0 c0d0 e0f0 g0h0 */ | ||||
| 			x |= x >> 1;       /* aabb ccdd eeff gghh */ | ||||
| 			x = everyother(x); /* 0000 0000 abcd efgh */ | ||||
| 			bw.write_8(x); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Write all even bits. */ | ||||
|  | ||||
| 	{ | ||||
| 		ByteReader br(input); | ||||
| 		while (!br.eof()) | ||||
| 		{ | ||||
| 			uint16_t x = br.read_be16(); | ||||
| 			x &= 0x5555;       /* 0a0b 0c0d 0e0f 0g0h */ | ||||
| 			x |= x << 1;       /* aabb ccdd eeff gghh */ | ||||
| 			x = everyother(x); /* 0000 0000 abcd efgh */ | ||||
| 			bw.write_8(x); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return output; | ||||
| } | ||||
|  | ||||
| Bytes amigaDeinterleave(const uint8_t*& input, size_t len) | ||||
| { | ||||
|     assert(!(len & 1)); | ||||
|     const uint8_t* odds = &input[0]; | ||||
|     const uint8_t* evens = &input[len/2]; | ||||
|     Bytes output; | ||||
|     ByteWriter bw(output); | ||||
|  | ||||
|     for (size_t i=0; i<len/2; i++) | ||||
|     { | ||||
|         uint8_t o = *odds++; | ||||
|         uint8_t e = *evens++; | ||||
|  | ||||
|         /* This is the 'Interleave bits with 64-bit multiply' technique from | ||||
|          * http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN | ||||
|          */ | ||||
|         uint16_t result = | ||||
|             (((e * 0x0101010101010101ULL & 0x8040201008040201ULL) | ||||
|                 * 0x0102040810204081ULL >> 49) & 0x5555) | | ||||
|             (((o * 0x0101010101010101ULL & 0x8040201008040201ULL) | ||||
|                 * 0x0102040810204081ULL >> 48) & 0xAAAA); | ||||
|          | ||||
|         bw.write_be16(result); | ||||
|     } | ||||
|  | ||||
|     input += len; | ||||
|     return output; | ||||
| } | ||||
|  | ||||
| Bytes amigaDeinterleave(const Bytes& input) | ||||
| { | ||||
| 	const uint8_t* ptr = input.cbegin(); | ||||
| 	return amigaDeinterleave(ptr, input.size()); | ||||
| } | ||||
| @@ -1,12 +1,17 @@ | ||||
| #ifndef AMIGA_H | ||||
| #define AMIGA_H | ||||
|  | ||||
| #include "encoders/encoders.h" | ||||
|  | ||||
| #define AMIGA_SECTOR_RECORD 0xaaaa44894489LL | ||||
|  | ||||
| #define AMIGA_TRACKS_PER_DISK 80 | ||||
| #define AMIGA_SECTORS_PER_TRACK 11 | ||||
| #define AMIGA_RECORD_SIZE 0x21f | ||||
|  | ||||
| class Sector; | ||||
| class Fluxmap; | ||||
| class SectorSet; | ||||
|  | ||||
| class AmigaDecoder : public AbstractDecoder | ||||
| { | ||||
| @@ -17,4 +22,20 @@ public: | ||||
|     void decodeSectorRecord(); | ||||
| }; | ||||
|  | ||||
| class AmigaEncoder : public AbstractEncoder | ||||
| { | ||||
| public: | ||||
| 	virtual ~AmigaEncoder() {} | ||||
|  | ||||
| public: | ||||
|     std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors); | ||||
| }; | ||||
|  | ||||
| extern FlagGroup amigaEncoderFlags; | ||||
|  | ||||
| extern uint32_t amigaChecksum(const Bytes& bytes); | ||||
| extern Bytes amigaInterleave(const Bytes& input); | ||||
| extern Bytes amigaDeinterleave(const uint8_t*& input, size_t len); | ||||
| extern Bytes amigaDeinterleave(const Bytes& input); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -21,47 +21,6 @@ | ||||
|           | ||||
| static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD); | ||||
|  | ||||
| static Bytes deinterleave(const uint8_t*& input, size_t len) | ||||
| { | ||||
|     assert(!(len & 1)); | ||||
|     const uint8_t* odds = &input[0]; | ||||
|     const uint8_t* evens = &input[len/2]; | ||||
|     Bytes output; | ||||
|     ByteWriter bw(output); | ||||
|  | ||||
|     for (size_t i=0; i<len/2; i++) | ||||
|     { | ||||
|         uint8_t o = *odds++; | ||||
|         uint8_t e = *evens++; | ||||
|  | ||||
|         /* This is the 'Interleave bits with 64-bit multiply' technique from | ||||
|          * http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN | ||||
|          */ | ||||
|         uint16_t result = | ||||
|             (((e * 0x0101010101010101ULL & 0x8040201008040201ULL) | ||||
|                 * 0x0102040810204081ULL >> 49) & 0x5555) | | ||||
|             (((o * 0x0101010101010101ULL & 0x8040201008040201ULL) | ||||
|                 * 0x0102040810204081ULL >> 48) & 0xAAAA); | ||||
|          | ||||
|         bw.write_be16(result); | ||||
|     } | ||||
|  | ||||
|     input += len; | ||||
|     return output; | ||||
| } | ||||
|  | ||||
| static uint32_t checksum(const Bytes& bytes) | ||||
| { | ||||
|     ByteReader br(bytes); | ||||
|     uint32_t checksum = 0; | ||||
|  | ||||
|     assert((bytes.size() & 3) == 0); | ||||
|     while (!br.eof()) | ||||
|         checksum ^= br.read_be32(); | ||||
|  | ||||
|     return checksum & 0x55555555; | ||||
| } | ||||
|  | ||||
| AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord() | ||||
| { | ||||
|     _sector->clock = _fmr->seekToPattern(SECTOR_PATTERN); | ||||
| @@ -78,22 +37,22 @@ void AmigaDecoder::decodeSectorRecord() | ||||
|  | ||||
|     const uint8_t* ptr = bytes.begin() + 3; | ||||
|  | ||||
|     Bytes header = deinterleave(ptr, 4); | ||||
|     Bytes recoveryinfo = deinterleave(ptr, 16); | ||||
|     Bytes header = amigaDeinterleave(ptr, 4); | ||||
|     Bytes recoveryinfo = amigaDeinterleave(ptr, 16); | ||||
|  | ||||
|     _sector->logicalTrack = header[1] >> 1; | ||||
|     _sector->logicalSide = header[1] & 1; | ||||
|     _sector->logicalSector = header[2]; | ||||
|  | ||||
|     uint32_t wantedheaderchecksum = deinterleave(ptr, 4).reader().read_be32(); | ||||
|     uint32_t gotheaderchecksum = checksum(rawbytes.slice(6, 40)); | ||||
|     uint32_t wantedheaderchecksum = amigaDeinterleave(ptr, 4).reader().read_be32(); | ||||
|     uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(6, 40)); | ||||
|     if (gotheaderchecksum != wantedheaderchecksum) | ||||
|         return; | ||||
|  | ||||
|     uint32_t wanteddatachecksum = deinterleave(ptr, 4).reader().read_be32(); | ||||
|     uint32_t gotdatachecksum = checksum(rawbytes.slice(62, 1024)); | ||||
|     uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32(); | ||||
|     uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(62, 1024)); | ||||
|  | ||||
|     _sector->data.clear(); | ||||
|     _sector->data.writer().append(deinterleave(ptr, 512)).append(recoveryinfo); | ||||
|     _sector->data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo); | ||||
|     _sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
| } | ||||
|   | ||||
							
								
								
									
										126
									
								
								arch/amiga/encoder.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								arch/amiga/encoder.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
| #include "globals.h" | ||||
| #include "record.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.h" | ||||
| #include "amiga.h" | ||||
| #include "crc.h" | ||||
| #include "sectorset.h" | ||||
| #include "writer.h" | ||||
|  | ||||
| FlagGroup amigaEncoderFlags; | ||||
|  | ||||
| static DoubleFlag clockRateUs( | ||||
| 	{ "--clock-rate" }, | ||||
| 	"Encoded data clock rate (microseconds).", | ||||
| 	2.00); | ||||
|  | ||||
| static DoubleFlag postIndexGapMs( | ||||
| 	{ "--post-index-gap" }, | ||||
| 	"Post-index gap before first sector header (milliseconds).", | ||||
| 	0.5); | ||||
|  | ||||
| static int charToInt(char c) | ||||
| { | ||||
| 	if (isdigit(c)) | ||||
| 		return c - '0'; | ||||
| 	return 10 + tolower(c) - 'a'; | ||||
| } | ||||
|  | ||||
| static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src) | ||||
| { | ||||
| 	for (bool bit : src) | ||||
| 	{ | ||||
| 		if (cursor < bits.size()) | ||||
| 			bits[cursor++] = bit; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width) | ||||
| { | ||||
| 	cursor += width; | ||||
| 	for (int i=0; i<width; i++) | ||||
| 	{ | ||||
| 		unsigned pos = cursor - i - 1; | ||||
| 		if (pos < bits.size()) | ||||
| 			bits[pos] = data & 1; | ||||
| 		data >>= 1; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes) | ||||
| { | ||||
| 	assert(!(bytes.size() & 3)); | ||||
| 	Bytes interleaved = amigaInterleave(bytes); | ||||
| 	encodeMfm(bits, cursor, interleaved); | ||||
| } | ||||
|  | ||||
| static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, uint32_t data) | ||||
| { | ||||
| 	Bytes b(4); | ||||
| 	ByteWriter bw(b); | ||||
| 	bw.write_be32(data); | ||||
| 	write_interleaved_bytes(bits, cursor, b); | ||||
| } | ||||
|  | ||||
| static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector) | ||||
| { | ||||
| 	if ((sector->data.size() != 512) && (sector->data.size() != 528)) | ||||
| 		Error() << "unsupported sector size --- you must pick 512 or 528"; | ||||
|  | ||||
|     write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6*8); | ||||
|  | ||||
| 	std::vector<bool> headerBits(20*16); | ||||
| 	unsigned headerCursor = 0; | ||||
|  | ||||
| 	Bytes header =  | ||||
| 		{ | ||||
| 			0xff, /* Amiga 1.0 format byte */ | ||||
| 			(uint8_t) ((sector->logicalTrack<<1) | sector->logicalSide), | ||||
| 			(uint8_t) sector->logicalSector, | ||||
| 			(uint8_t) (AMIGA_SECTORS_PER_TRACK - sector->logicalSector) | ||||
| 		}; | ||||
| 	write_interleaved_bytes(headerBits, headerCursor, header); | ||||
| 	Bytes recoveryInfo(16); | ||||
| 	if (sector->data.size() == 528) | ||||
| 		recoveryInfo = sector->data.slice(512, 16); | ||||
| 	write_interleaved_bytes(headerBits, headerCursor, recoveryInfo); | ||||
|  | ||||
| 	std::vector<bool> dataBits(512*16); | ||||
| 	unsigned dataCursor = 0; | ||||
| 	write_interleaved_bytes(dataBits, dataCursor, sector->data); | ||||
|  | ||||
| 	write_bits(bits, cursor, headerBits); | ||||
| 	uint32_t headerChecksum = amigaChecksum(toBytes(headerBits)); | ||||
| 	write_interleaved_bytes(bits, cursor, headerChecksum); | ||||
| 	uint32_t dataChecksum = amigaChecksum(toBytes(dataBits)); | ||||
| 	write_interleaved_bytes(bits, cursor, dataChecksum); | ||||
| 	write_bits(bits, cursor, dataBits); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<Fluxmap> AmigaEncoder::encode( | ||||
| 	int physicalTrack, int physicalSide, const SectorSet& allSectors) | ||||
| { | ||||
| 	if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK)) | ||||
| 		return std::unique_ptr<Fluxmap>(); | ||||
|  | ||||
| 	int bitsPerRevolution = 200000.0 / clockRateUs; | ||||
| 	std::vector<bool> bits(bitsPerRevolution); | ||||
| 	unsigned cursor = 0; | ||||
|  | ||||
|     fillBitmapTo(bits, cursor, postIndexGapMs * 1000 / clockRateUs, { true, false }); | ||||
|  | ||||
| 	for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++) | ||||
| 	{ | ||||
| 		const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId); | ||||
| 		write_sector(bits, cursor, sectorData); | ||||
|     } | ||||
|  | ||||
| 	if (cursor >= bits.size()) | ||||
| 		Error() << "track data overrun"; | ||||
| 	fillBitmapTo(bits, cursor, bits.size(), { true, false }); | ||||
|  | ||||
| 	std::unique_ptr<Fluxmap> fluxmap(new Fluxmap); | ||||
| 	fluxmap->appendBits(bits, clockRateUs*1e3); | ||||
| 	return fluxmap; | ||||
| } | ||||
|  | ||||
| @@ -154,7 +154,7 @@ std::unique_ptr<Fluxmap> BrotherEncoder::encode( | ||||
| 		write_sector_data(bits, cursor, sectorData->data); | ||||
| 	} | ||||
|  | ||||
| 	if (cursor > bits.size()) | ||||
| 	if (cursor >= bits.size()) | ||||
| 		Error() << "track data overrun"; | ||||
| 	fillBitmapTo(bits, cursor, bits.size(), { true, false }); | ||||
|  | ||||
|   | ||||
| @@ -73,8 +73,10 @@ const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56c); | ||||
|  * encoding (you can't do 10 00). So this can't be spoofed by user data. | ||||
|  *  | ||||
|  * shifted: 10 00 10 01 00 01 00 1 | ||||
|  *  | ||||
|  * It's repeated three times. | ||||
|  */ | ||||
| const FluxPattern MFM_PATTERN(16, 0x4489); | ||||
| const FluxPattern MFM_PATTERN(48, 0x448944894489LL); | ||||
|  | ||||
| const FluxMatchers ANY_RECORD_PATTERN( | ||||
|     { | ||||
| @@ -100,7 +102,8 @@ AbstractDecoder::RecordType IbmDecoder::advanceToNextRecord() | ||||
|     if (_currentHeaderLength > 0) | ||||
|         readRawBits(_currentHeaderLength*16); | ||||
|     auto idbits = readRawBits(16); | ||||
|     uint8_t id = decodeFmMfm(idbits).slice(0, 1)[0]; | ||||
|     const Bytes idbytes = decodeFmMfm(idbits); | ||||
|     uint8_t id = idbytes.slice(0, 1)[0]; | ||||
|     seek(here); | ||||
|      | ||||
|     switch (id) | ||||
| @@ -134,6 +137,9 @@ void IbmDecoder::decodeSectorRecord() | ||||
|     uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, _currentHeaderLength + 5)); | ||||
|     if (wantCrc == gotCrc) | ||||
|         _sector->status = Sector::DATA_MISSING; /* correct but unintuitive */ | ||||
|  | ||||
|     if (_ignoreSideByte) | ||||
|         _sector->logicalSide = _sector->physicalSide; | ||||
| } | ||||
|  | ||||
| void IbmDecoder::decodeDataRecord() | ||||
|   | ||||
| @@ -31,8 +31,9 @@ struct IbmIdam | ||||
| class IbmDecoder : public AbstractDecoder | ||||
| { | ||||
| public: | ||||
|     IbmDecoder(unsigned sectorBase): | ||||
|         _sectorBase(sectorBase) | ||||
|     IbmDecoder(unsigned sectorBase, bool ignoreSideByte=false): | ||||
|         _sectorBase(sectorBase), | ||||
|         _ignoreSideByte(ignoreSideByte) | ||||
|     {} | ||||
|  | ||||
|     RecordType advanceToNextRecord(); | ||||
| @@ -41,6 +42,7 @@ public: | ||||
|  | ||||
| private: | ||||
|     unsigned _sectorBase; | ||||
|     bool _ignoreSideByte; | ||||
|     unsigned _currentSectorSize; | ||||
|     unsigned _currentHeaderLength; | ||||
| }; | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								doc/Index_sensor_mod_FDD_1.1.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								doc/Index_sensor_mod_FDD_1.1.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -103,7 +103,43 @@ the unconnected pins and solder a short piece of wire to a GND pin on the | ||||
| board. Alternatively you'll need to splice it into your drive's power supply | ||||
| cable somehow. (The black one.) | ||||
|  | ||||
| ## Building the firmware | ||||
| ## Programming the board | ||||
|  | ||||
| You've got two options here. You can either use the precompiled firmware | ||||
| supplied with the source, or else install the Cypress SDK and build it | ||||
| yourself. If you want to hack the firmware source you need the latter, but | ||||
| if you trust me to do it for you use the precompiled firmware. In either | ||||
| case you'll need Windows and have to install some Cypress stuff. | ||||
|  | ||||
| **Before you read this:** If you're on Windows, good news! You can download a | ||||
| precompiled version of the FluxEngine client and precompiled firmware [from | ||||
| the GitHub releases | ||||
| page](https://github.com/davidgiven/fluxengine/releases/latest). Simply unzip | ||||
| it somewhere and run the `.exe` files from a `cmd` window (or other shell). | ||||
| Follow the instructions below to program the board with the firmware. | ||||
|  | ||||
| ### Using the precompiled firmware | ||||
|  | ||||
| On your Windows machine, [install the PSoC | ||||
| Programmer](https://www.cypress.com/products/psoc-programming-solutions). | ||||
| **Note:** _not_ the Cypress Programmer, which is for a different board! | ||||
| Cypress will make you register. | ||||
|  | ||||
| Once done, run it. Plug the blunt end of the FluxEngine board into a USB | ||||
| port (the end which is a USB connector). The programmer should detect it | ||||
| and report it as a KitProg. You may be prompted to upgrade the programmer | ||||
| hardware; if so, follow the instructions and do it. | ||||
|  | ||||
| Now go to File -> File Load and open | ||||
| `FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex` in the | ||||
| project. If you're on Windows, the precompiled zipfile also contains a copy | ||||
| of this file. Press the Program button (the one in the toolbar marked with a | ||||
| down arrow). Stuff will happen and you should be left with three green boxes | ||||
| in the status bar and 'Programming Succeeded' at the top of the log window. | ||||
|  | ||||
| You're done. You can unplug the board and close the programmer. | ||||
|  | ||||
| ### Building the firmware yourself | ||||
|  | ||||
| On your Windows machine, [install the Cypress SDK and CY8CKIT-059 | ||||
| BSP](http://www.cypress.com/documentation/development-kitsboards/cy8ckit-059-psoc-5lp-prototyping-kit-onboard-programmer-and). | ||||
| @@ -118,7 +154,7 @@ tutorial and making the LED on your board flash. It'll tell you where all the | ||||
| controls are and how to program the board. Remember that the big end of the | ||||
| board plugs into your computer for programming. | ||||
|  | ||||
| When you're ready, open the `FluxEngine.cydsn/FluxEngine.cywrk` workspace, | ||||
| When you're ready, open the `FluxEngine.cydsn/FluxEngine.cyprj` project, | ||||
| pick 'Program' from the menu, and the firmware should compile and be | ||||
| programmed onto your board. | ||||
|  | ||||
| @@ -139,11 +175,6 @@ the port and proceed normally. | ||||
|  | ||||
| ## Building the client | ||||
|  | ||||
| **Before you read this:** If you're on Windows, good news! You can download a | ||||
| *precompiled version of the FluxEngine client [from the GitHub releases | ||||
| *page](https://github.com/davidgiven/fluxengine/releases/latest). Simply unzip | ||||
| *it somewhere and run it from a `cmd` window (or other shell). | ||||
|  | ||||
| The client software is where the intelligence, such as it is, is. It's pretty | ||||
| generic libusb stuff and should build and run on Windows, Linux and OSX as | ||||
| well, although on Windows it'll need MSYS2 and mingw32. You'll need to | ||||
| @@ -151,7 +182,7 @@ install some support packages. | ||||
|  | ||||
|   - For Linux (this is Ubuntu, but this should apply to Debian too): | ||||
|   `ninja-build`, `libusb-1.0-0-dev`, `libsqlite3-dev`. | ||||
|   - For OSX with Homebrew: `ninja`. | ||||
|   - For OSX with Homebrew: `ninja`, `libusb`, `pkg-config`, `sqlite`. | ||||
|   - For Windows with MSYS2: `make`, `ninja`, `mingw-w64-i686-libusb`, | ||||
|   `mingw-w64-i686-sqlite3`, `mingw-w64-i686-zlib`, `mingw-w64-i686-gcc`. | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,7 @@ Bizarrely, the data in each sector is stored with all the odd bits first, and | ||||
| then all the even bits. This is tied into the checksum algorithm, which is | ||||
| distinctly subpar and not particularly good at detecting errors. | ||||
|  | ||||
| Reading discs | ||||
| Reading disks | ||||
| ------------- | ||||
|  | ||||
| Just do: | ||||
| @@ -34,6 +34,28 @@ You will end up with a 929280 byte long image which you probably _can't_ use | ||||
| in an emulator; each sector will contain the 512 bytes of user payload | ||||
| followed by the 16 bytes of metadata. | ||||
|  | ||||
| Writing disks | ||||
| ------------- | ||||
|  | ||||
| Just do: | ||||
|  | ||||
| ``` | ||||
| fluxengine write amiga -i amiga.adf | ||||
| ``` | ||||
|  | ||||
| This will rake a normal 901120 byte long ADF file and write it to a DD disk. | ||||
| Note that writing to an HD disk will probably not work (this will depend on | ||||
| your drive and disk and potential FluxEngine bugs I'm still working on --- | ||||
| please [get in touch](https://github.com/davidgiven/fluxengine/issues/new) if | ||||
| you have any insight here). | ||||
|  | ||||
| If you want to write the metadata as well, specify a 528 byte sector size for | ||||
| the output image and supply a 929280 byte long file as described above. | ||||
|  | ||||
| ``` | ||||
| fluxengine write amiga -i amiga.adf:b=528 | ||||
| ``` | ||||
|  | ||||
| Useful references | ||||
| ----------------- | ||||
|  | ||||
|   | ||||
| @@ -129,8 +129,8 @@ reverse engineered it to find out. | ||||
|  | ||||
| Standard Linux mtools will access the filesystem image and allow you to move | ||||
| files in and out. However, you'll need to change the media type bytes at | ||||
| offsets 0x015 and 0x100 from 0x58 to 0xf0 before mtools will touch it. Once | ||||
| done, this will work: | ||||
| offsets 0x015 and 0x100 from 0x58 to 0xf0 before mtools will touch it. The | ||||
| supplied `brother240tool` will do this. Once done, this will work: | ||||
|  | ||||
| ``` | ||||
| mdir -i brother.img | ||||
|   | ||||
							
								
								
									
										81
									
								
								doc/disk-ibm.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								doc/disk-ibm.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| Disk: Generic IBM | ||||
| ================= | ||||
|  | ||||
| IBM scheme disks are _the_ most common disk format, ever. They're used by a | ||||
| huge variety of different systems, and they come in a huge variety of different | ||||
| forms, but they're all fundamentally the same: either FM or MFM, either single | ||||
| or double sided, with distinct sector header and data records and no sector | ||||
| metadata. Systems which use IBM scheme disks include but are not limited to: | ||||
|  | ||||
|   - IBM PCs (naturally) | ||||
|   - Atari ST | ||||
|   - late era Apple machines | ||||
|   - Acorn machines | ||||
|   - the TRS-80 | ||||
|   - late era Commodore machines (the 1571 and so on) | ||||
|   - most CP/M machines | ||||
|   - etc | ||||
|  | ||||
| FluxEngine supports reading these. However, some variants are more peculiar | ||||
| than others, and as a result there are specific decoders which set the defaults | ||||
| correctly for certain formats (for example: on PC disks the sector numbers | ||||
| start from 1, but on [Acorn](disk-acorndfs.md) disks they start from 0). The | ||||
| IBM decoder described here is the generic one, and is suited for 'conventional' | ||||
| PC disks. While you can read all the variant formats with it if you use the | ||||
| right set of arguments, it's easier to use the specific decoder. | ||||
|  | ||||
| The generic decoder is mostly self-configuring, and will detect the format of | ||||
| your disk for you. | ||||
|  | ||||
|  | ||||
| Reading disks | ||||
| ------------- | ||||
|  | ||||
| Just do: | ||||
|  | ||||
|     fluxengine read ibm | ||||
|  | ||||
| ...and you'll end up with an `ibm.img` file. This should work on most PC disks | ||||
| (including FM 360kB disks, 3.5" 1440kB disks, 5.25" 1200kB disks, etc.) The size | ||||
| of the disk image will vary depending on the format. | ||||
|  | ||||
| Configuration options you'll want include: | ||||
|  | ||||
|   - `--sector-id-base`: specifies the ID of the first sector; this defaults | ||||
|     to 1. Some formats (like the Acorn ones) start at 0. This can't be | ||||
| 	autodetected because FluxEngine can't distinguish between a disk which | ||||
| 	starts at sector 1 and a disk which starts at sector 0 but all the sector | ||||
| 	0s are missing. | ||||
|  | ||||
|   - `--ignore-side-byte`: each sector header describes the location of the | ||||
| 	sector: sector ID, track and side. Some formats use the wrong side ID, so | ||||
| 	the sectors on side 1 are labelled as belonging to side 0. This causes | ||||
| 	FluxEngine to see duplicate sectors (as it can't distinguish between the | ||||
| 	two sides). This option tells FluxEngine to ignore the side byte completely | ||||
| 	and use the physical side instead. | ||||
|  | ||||
|  | ||||
| Reading mixed-format disks | ||||
| -------------------------- | ||||
|  | ||||
| Some disks, usually those belonging to early CP/M machines, have more than one | ||||
| format on the disk at once. Typically, the first few tracks will be low-density | ||||
| FM encoded and will be read by the machine's ROM; those tracks contain new | ||||
| floppy drive handling code capable of coping with MFM data, and so the rest of | ||||
| the disk will use that, allowing them to store more data. | ||||
|  | ||||
| FluxEngine copes with these fine, but the disk images are a bit weird. If track | ||||
| 0 is FM and contains five sectors, but track 1 is MFM with nine sectors (MFM is | ||||
| more efficient and the sectors are physically smaller, allowing you to get more | ||||
| on), then the resulting image will have nine sectors per track... but track 0 | ||||
| will only contain data in the first five. | ||||
|  | ||||
| This is typically what you want as it makes locating the sectors in the image | ||||
| easier, but some emulators may require a different format. Please [get in | ||||
| touch](https://github.com/davidgiven/fluxengine/issues/new) if you have | ||||
| specific requirements (nothing's come up yet). Alternatively, you can tell | ||||
| FluxEngine to write a [`.ldbs` | ||||
| file](http://www.seasip.info/Unix/LibDsk/ldbs.html) and then use | ||||
| [libdsk](http://www.seasip.info/Unix/LibDsk/) to convert it to something | ||||
| useful. | ||||
|  | ||||
| @@ -28,8 +28,16 @@ for example the Commodore 64 1541 drive, changed bitrate this way. | ||||
| But Macintosh disks used a constant bitrate and changed the speed that the | ||||
| disk spun instead to achieve the same effect... | ||||
|  | ||||
| _Anyway_: FluxEngine will read them fine on a conventional drive. Because | ||||
| it's clever. | ||||
| _Anyway_: FluxEngine will read them fine on conventional drives. | ||||
| Because it's clever. | ||||
|  | ||||
| **Big note.** Apparently --- and I'm still getting to the bottom of this --- | ||||
| some drives work and some don't. My drives produce about 90% good reads of | ||||
| known good disks. One rumour I've heard is that drives sometimes include | ||||
| filters which damage the signals at very particular intervals which Mac disks | ||||
| use, but frankly this seems unlikely; it could be a software issue at my end | ||||
| and I'm investigating. If you have any insight, please [get in | ||||
| touch](https://github.com/davidgiven/fluxengine/issues/new). | ||||
|  | ||||
| Reading discs | ||||
| ------------- | ||||
|   | ||||
							
								
								
									
										20
									
								
								doc/faq.md
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								doc/faq.md
									
									
									
									
									
								
							| @@ -48,7 +48,7 @@ haven't had the chance to try it end-for-end. I really need a hard-sectored | ||||
|  | ||||
| **Q.** Does it work with flippy disks? | ||||
|  | ||||
| Uhhh... probably not. | ||||
| Uhhh... maybe? | ||||
|  | ||||
| So the problem with flippy disks (5.25" single-sided disks which could be | ||||
| inserted upside down to read the second side) is the index hole. Trouble is, | ||||
| @@ -79,16 +79,26 @@ the other. But a flippy disk has both sets of tracks in the same place, | ||||
| because they're both accessed using the side 0 head... | ||||
|  | ||||
| The only real way round this is to modify a 5.25" drive. That's _seriously_ | ||||
| not in FluxEngine's remit. Sorry. | ||||
| not in FluxEngine's remit, but I've had some [excellent documentation | ||||
| contributed](Index_sensor_mod_FDD_1.1.pdf) on how to do this. I've never done | ||||
| it myself; if you try this and it works/doesn't work, as always, [get in | ||||
| touch](https://github.com/davidgiven/fluxengine/issues/new). | ||||
|  | ||||
| **Q.** Is this like KryoFlux / Catweasel / DiskFerret? Do you support KryoFlux | ||||
| Another option is to fake the index signal to the drive completely. The | ||||
| FluxEngine emits suitable pulses for a 300RPM drive on pin 3[0] and the | ||||
| equivalent pulses for a 360RPM drive on pin 3[1]. Disclaimer: I have never used | ||||
| these. | ||||
|  | ||||
| **Q.** Is this like Supercard Pro / KryoFlux / Catweasel / DiskFerret? Do you | ||||
| *support KryoFlux | ||||
| stream files? | ||||
|  | ||||
| **A.** It's very like all of these; the idea's old, and lots of people have | ||||
| tried it (you can get away with any sufficiently fast microcontroller and | ||||
| enough RAM). FluxEngine can read from KryoFlux stream files natively, and | ||||
| there's a tool which will let you convert at least one kind of Catweasel file | ||||
| to FluxEngine's native flux file format. | ||||
| there's a tool which will let you convert at least one kind of Catweasel | ||||
| files and Supercard Pro files to and from FluxEngine's native flux file | ||||
| format. | ||||
|  | ||||
| **Q.** Can I use this to make exact copies of disks? | ||||
|  | ||||
|   | ||||
| @@ -59,53 +59,42 @@ Some useful and/or interesting numbers: | ||||
|  | ||||
| ## Why don't I use an Arduino / STM32 / ESP32 / Raspberry Pi / etc? | ||||
|  | ||||
| I've got a _lot_ of questions on this, and multiple Github issues of people | ||||
| -I've got a _lot_ of questions on this, and multiple Github issues of people | ||||
| debating it. It's complicated, but it's essentially a tradeoff between speed | ||||
| and complexity. | ||||
| and complexity.- | ||||
|  | ||||
| FluxEngine's read process involves generating a lot of data using a fairly | ||||
| brute force sampling approach --- about 150kB per disk revolution, and | ||||
| sometimes it needs to record multiple revolutions. Most microcontrollers | ||||
| don't have enough RAM to buffer this, so instead I have to stream it over USB | ||||
| back to the host PC in real time. The disk won't wait, so I need to stream data faster | ||||
| than the disk is producing it: the total is about 800kB/s. | ||||
| **Update as of 2020-01-08:** | ||||
|  | ||||
| Handling USB is pretty CPU-hungry, so my candidate microntroller has to be | ||||
| able to cope with the ruinously strict real-time requirements of the | ||||
| sampler's 12MHz clock as well as keeping up with 13,000 USB interrupts a | ||||
| second (one for each 64-byte frame) in order to transfer the data. | ||||
| Right. Well. | ||||
|  | ||||
| The Atmels and STM32s I found were perfectly capable of doing the real-time | ||||
| sampling, using hand-tool assembly, but I very much doubt whether they could | ||||
| do the USB streaming as well (although I want to move away from the Cypress | ||||
| onto something less proprietary and easier to source, so I'd like to be | ||||
| proven wrong here). | ||||
| This section used to have a long explanation as to why these other platforms | ||||
| were unsuitable --- essentially, they're generally missing out on either the | ||||
| realtimeness to sample the data correctly (Raspberry Pi) or enough CPU to | ||||
| stream the data over USB while also sampling it (Arduino). | ||||
|  | ||||
| The Raspberry Pi easily has enough processing power and memory, but it's also | ||||
| got terrible GPIO pin read performance --- [about | ||||
| 1kHz](https://raspberrypi.stackexchange.com/questions/9646/how-fast-is-gpiodma-multi-i2s-input/10197#10197). | ||||
| That's a long way from the 12MHz I need. | ||||
| This is correct, but it turns out that the STM32 has some built-in features | ||||
| which support the FluxEngine's use case almost exactly: you can configure the | ||||
| DMA engine to sample the interval between pulses and write them directly into | ||||
| memory, and you can configure the PWM engine the read samples from memory and | ||||
| use them to time pulses to the output. There's a bit less functionality, so you | ||||
| can't do things like measure the signal voltages, and they're less convenient | ||||
| as you need an adapter cable or board, but this will allow you to replicate the | ||||
| FluxEngine hardware on a $2 Blue Pill. | ||||
|  | ||||
| The PSoC5LP part I'm using has enough CPU to handle the USB side of things, | ||||
| and it _also_ has a whole set of FPGA-like soft programmable features, | ||||
| including 24 mini-ALU systems that are ideally suited to exactly this kind of | ||||
| sampling. I can read the disk and generate the byte stream describing the | ||||
| flux pattern entirely in 'hardware', without involving the main CPU at all. | ||||
| This is then DMAed directly into a set of ring buffers read for the USB | ||||
| system to pick up and relay back to the PC. It's incredibly simple and works | ||||
| well. (The same applies to writing flux back onto the disk.) | ||||
| I am _not_ planning on replacing the PSoC5 with a Blue Pill, because someone | ||||
| already has: [the GreaseWeazle](https://github.com/keirf/Greaseweazle/wiki) is | ||||
| a completely open source firmware package which will read and write Supercard | ||||
| Pro files via a standard Blue Pill. The GreaseWeazle's USB protocol is | ||||
| different from the FluxEngine's so they're not directly interchangeable. You | ||||
| can, however, read a Supercard Pro file with a GreaseWeazle and then use the | ||||
| FluxEngine client to decode it. It should work the other way around, too, but | ||||
| FluxEngine's SCP export [is curently | ||||
| broken](https://github.com/davidgiven/fluxengine/issues/134). | ||||
|  | ||||
| The development board I'm using, the | ||||
| [CY8CKIT-059](https://www.cypress.com/documentation/development-kitsboards/cy8ckit-059-psoc-5lp-prototyping-kit-onboard-programmer-and), | ||||
| also has another big advantage: it's the right shape. It's got 17 holes in a | ||||
| row connected to GPIO pins, and it's a native 5V part, which means I can just | ||||
| connect a floppy drive connector directly to the board without needing to | ||||
| build any hardware. No adapter board, no level shifting, no special cable, | ||||
| nothing. This makes the FluxEngine hardware incredibly easy to assemble, | ||||
| which therefore means cheap. | ||||
| I _am_ considering adding direct support for the GreaseWeazle to the FluxEngine | ||||
| client, which will let you just plug one in and make it go as a direct | ||||
| replacement to the FluxEngine hardware. | ||||
|  | ||||
| Speaking of which, the CY8CKIT-059 is $10. (Before shipping, which is | ||||
| admittedly expensive.) | ||||
|  | ||||
| ### Some useful links | ||||
|  | ||||
| @@ -123,8 +112,12 @@ admittedly expensive.) | ||||
|     sheet](http://www.bitsavers.org/pdf/mitsubishi/floppy/M4851/TJ2-G30211A_M4851_DSHH_48TPI_OEM_Manual_Nov83.pdf): | ||||
|     the equivalent data sheet for a representative 5.25" drive. | ||||
|  | ||||
|   - [The DRG Business Machines YD-174 manual](https://electrickery.hosting.philpem.me.uk/comp/divcomp/doc/YE_Data_YD-174_8inchFloppyDriveTechnicalManual.pdf): | ||||
| 	the equivalent manual (data sheets hadn't been invented then) for a | ||||
| 	representative 8" drive. | ||||
|  | ||||
|   - [KryoFlux stream file | ||||
|     documentation](https://www.kryoflux.com/download/kryoflux_stream_protocol_rev1.1.pdf): | ||||
|     the format of KryoFlux stream files (partially supported by FluxEngine) | ||||
|  | ||||
|    | ||||
|    | ||||
|   | ||||
							
								
								
									
										85
									
								
								doc/using.md
									
									
									
									
									
								
							
							
						
						
									
										85
									
								
								doc/using.md
									
									
									
									
									
								
							| @@ -51,7 +51,7 @@ In order to do anything useful, you have to plug it in to a floppy disk drive (o | ||||
|      rpm for a 3.5" disk, or 360 rpm for a 5.25" disk. If it doesn't, please | ||||
|      [get in touch](https://github.com/davidgiven/fluxengine/issues/new). | ||||
|  | ||||
|   7. Do `fluxengine testbulktransport` from the shell. It'll measure your USB | ||||
|   7. Do `fluxengine test bulktransport` from the shell. It'll measure your USB | ||||
|      bandwidth. Ideally you should be getting above 900kB/s. FluxEngine needs | ||||
|      about 850kB/s, so if you're getting less than this, try a different USB | ||||
|      port. | ||||
| @@ -64,6 +64,16 @@ In order to do anything useful, you have to plug it in to a floppy disk drive (o | ||||
|  | ||||
|   9. Profit! | ||||
|  | ||||
| ## Bonus hardware features | ||||
|  | ||||
| For advanced users, the board has a few extra signals which are useful for special purposes. | ||||
|  | ||||
|   - Pin 3[0] produces short pulses every 200ms. This is useful for spoofing | ||||
|     index signals to 300 RPM drives; for example, to read flippy disks. | ||||
|  | ||||
|   - Pin 3[1] is the same, but produces the pulses every 166ms; this works with | ||||
|     360 RPM drives. | ||||
|  | ||||
| ## The programs | ||||
|  | ||||
| I'm sorry to say that the client program is very badly documented --- it's | ||||
| @@ -176,6 +186,23 @@ case, and reading the disk label is much more reliable. | ||||
| [Lots more information on high density vs double density disks can be found | ||||
| here.](http://www.retrotechnology.com/herbs_stuff/guzis.html) | ||||
|  | ||||
| ### Other important flags | ||||
|  | ||||
| These flags apply to many operations and are useful for modifying the overall | ||||
| behaviour. | ||||
|  | ||||
|   - `--revolutions=X`: when reading, spin the disk X times. Many formats | ||||
|   require `--revolutions=2` (which should happen automatically); or you can | ||||
|   increase the number to sample more data. | ||||
|  | ||||
|   - `--index-source=X`, `--write-index-source=X`: set the source of index | ||||
|   pulses when reading or writing respectively. This is for use with drives | ||||
|   which don't produce index pulse data. Use 0 to get index pulses from the | ||||
|   drive, 1 to fake 300RPM pulses, or 2 to fake 360RPM pulses. Note this has | ||||
|   no effect on the _drive_, so it doesn't help with flippy disks, but is | ||||
|   useful for using very old drives with FluxEngine itself. If you use this | ||||
|   option, then any index marks in the sampled flux are, of course, garbage. | ||||
|  | ||||
| ### The commands | ||||
|  | ||||
| The FluxEngine client software is a largely undocumented set of small tools. | ||||
| @@ -189,12 +216,13 @@ directory. | ||||
|   - `fluxengine inspect`: dumps the raw pulsetrain / bitstream to stdout. | ||||
|   Mainly useful for debugging. | ||||
|  | ||||
|   - `fluxengine read*`: reads various formats of disk. See the per-format | ||||
|   - `fluxengine read *`: reads various formats of disk. See the per-format | ||||
|   documentation linked from the table above. These all take an optional | ||||
|   `--write-flux` option which will cause the raw flux to be written to the | ||||
|   specified file. | ||||
|   specified file. There are various `--dump` options for showing raw data | ||||
|   during the decode process. | ||||
|  | ||||
|   - `fluxengine write*`: writes various formats of disk. Again, see the | ||||
|   - `fluxengine write *`: writes various formats of disk. Again, see the | ||||
|   per-format documentation above. | ||||
|  | ||||
|   - `fluxengine writeflux`: writes raw flux files. This is much less useful | ||||
| @@ -213,18 +241,50 @@ directory. | ||||
|   - `fluxengine seek`: moves the head. Mainly useful for finding out whether | ||||
|   your drive can seek to track 82. (Mine can't.) | ||||
|  | ||||
|   - `fluxengine testbulktransport`: measures your USB throughput. You need | ||||
|   - `fluxengine test bulktransport`: measures your USB throughput. You need | ||||
|   about 600kB/s for FluxEngine to work. You don't need a disk in the drive | ||||
|   for this one. | ||||
|  | ||||
|   - `fluxengine test voltages`: measures your FDD bus signal voltages, which | ||||
|   is useful for testing for termination issues. | ||||
|  | ||||
|   - `fluxengine upgradefluxfile`: occasionally I need to upgrade the flux | ||||
|   file format in a non-backwards-compatible way; this tool will upgrade flux | ||||
|   files to the new format. | ||||
|  | ||||
|   - `fluxengine convert`: converts flux files from various formats to various | ||||
|   other formats. You can use this to convert Catweasel flux files to | ||||
|   FluxEngine's native format, FluxEngine flux files to various other formats | ||||
|   useful for debugging (including VCD which can be loaded into | ||||
|   [sigrok](http://sigrok.org)), and bidirectional conversion to and from | ||||
|   Supercard Pro `.scp` format. | ||||
|  | ||||
|   **Important SCP note:** import (`fluxengine convert scptoflux`) should be | ||||
|   fairly robust, but export (`fluxengine convert fluxtoscp`) should only be | ||||
|   done with great caution as FluxEngine files contain features which can't be | ||||
|   represented very well in `.scp` format and they're probably pretty dubious. | ||||
|   As ever, please [get in | ||||
|   touch](https://github.com/davidgiven/fluxengine/issues/new) with any reports. | ||||
|  | ||||
| Commands which normally take `--source` or `--dest` get a sensible default if | ||||
| left unspecified. `fluxengine read ibm` on its own will read drive 0 and | ||||
| write an `ibm.img` file. | ||||
|  | ||||
| ## Visualisation | ||||
|  | ||||
| When doing a read (either from a real disk or from a flux file) you can use | ||||
| `--write-svg=output.svg` to write out a graphical visualisation of where the | ||||
| sectors are on the disk. Here's a IBM PC 1232kB disk: | ||||
|  | ||||
|  | ||||
|  | ||||
| Blue represents data, light blue a header, and red is a bad sector. Side zero | ||||
| is on the left and side one is on the right. | ||||
|  | ||||
| The visualiser is extremely primitive and you have to explicitly tell it how | ||||
| big your disk is, in milliseconds. The default is 200ms (for a normal 3.5" | ||||
| disk). For a 5.25" disk, use `--visualiser-period=166`. | ||||
|  | ||||
| ## Extra programs | ||||
|  | ||||
| Supplied with FluxEngine, but not part of FluxEngine, are some little tools I | ||||
| @@ -232,25 +292,24 @@ wrote to do useful things. These are built alongside FluxEngine. | ||||
|  | ||||
|   - `brother120tool`: extracts files from a 120kB Brother filesystem image. | ||||
|  | ||||
|   - `cwftoflux`: converts (one flavour of) CatWeasel flux file into a | ||||
|     FluxEngine flux file. | ||||
|  | ||||
| ## The recommended workflow | ||||
|  | ||||
| So you've just received, say, a huge pile of old Brother word processor disks containing valuable historical data, and you want to read them. | ||||
| So you've just received, say, a huge pile of old Brother word processor disks | ||||
| containing valuable historical data, and you want to read them. | ||||
|  | ||||
| Typically I do this: | ||||
|  | ||||
| ``` | ||||
| $ fluxengine read brother -s :d=0 -o brother.img --write-flux=brother.flux | ||||
| $ fluxengine read brother -s :d=0 -o brother.img --write-flux=brother.flux --write-svg=brother.svg | ||||
| ``` | ||||
|  | ||||
| This will read the disk in drive 0 and write out a filesystem image. It'll | ||||
| also copy the flux to brother.flux. If I then need to tweak the settings, I | ||||
| can rerun the decode without having to physically touch the disk like this: | ||||
| also copy the flux to brother.flux and write out an SVG visualisation. If I | ||||
| then need to tweak the settings, I can rerun the decode without having to | ||||
| physically touch the disk like this: | ||||
|  | ||||
| ``` | ||||
| $ fluxengine read brother -s brother.flux -o brother.img | ||||
| $ fluxengine read brother -s brother.flux -o brother.img --write-svg=brother.svg | ||||
| ``` | ||||
|  | ||||
| Apart from being drastically faster, this avoids touching the (potentially | ||||
|   | ||||
							
								
								
									
										1
									
								
								doc/visualiser.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								doc/visualiser.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 394 KiB | 
| @@ -25,7 +25,7 @@ void AbstractDecoder::decodeToSectors(Track& track) | ||||
|     beginTrack(); | ||||
|     for (;;) | ||||
|     { | ||||
|         Fluxmap::Position recordStart = sector.position = fmr.tell(); | ||||
|         Fluxmap::Position recordStart = fmr.tell(); | ||||
|         sector.clock = 0; | ||||
|         sector.status = Sector::MISSING; | ||||
|         sector.data.clear(); | ||||
| @@ -41,21 +41,32 @@ void AbstractDecoder::decodeToSectors(Track& track) | ||||
|  | ||||
|         /* Read the sector record. */ | ||||
|  | ||||
|         recordStart = fmr.tell(); | ||||
|         sector.position = recordStart = fmr.tell(); | ||||
|         decodeSectorRecord(); | ||||
|         pushRecord(recordStart, fmr.tell()); | ||||
|         Fluxmap::Position recordEnd = fmr.tell(); | ||||
|         pushRecord(recordStart, recordEnd); | ||||
|         if (sector.status == Sector::DATA_MISSING) | ||||
|         { | ||||
|             /* The data is in a separate record. */ | ||||
|  | ||||
|             r = advanceToNextRecord(); | ||||
|             sector.headerStartTime = recordStart.ns(); | ||||
|             sector.headerEndTime = recordEnd.ns(); | ||||
| 			for (;;) | ||||
| 			{ | ||||
| 				r = advanceToNextRecord(); | ||||
| 				if (r != UNKNOWN_RECORD) | ||||
| 					break; | ||||
| 				if (fmr.readNextMatchingOpcode(F_OP_PULSE) == 0) | ||||
|                     break; | ||||
| 			} | ||||
|             recordStart = fmr.tell(); | ||||
|             if (r == DATA_RECORD) | ||||
|             { | ||||
|                 recordStart = fmr.tell(); | ||||
|                 decodeDataRecord(); | ||||
|                 pushRecord(recordStart, fmr.tell()); | ||||
|             } | ||||
|             recordEnd = fmr.tell(); | ||||
|             pushRecord(recordStart, recordEnd); | ||||
|         } | ||||
|         sector.dataStartTime = recordStart.ns(); | ||||
|         sector.dataEndTime = recordEnd.ns(); | ||||
|  | ||||
|         if (sector.status != Sector::MISSING) | ||||
|             track.sectors.push_back(sector); | ||||
|   | ||||
| @@ -20,6 +20,7 @@ extern void setDecoderManualClockRate(double clockrate_us); | ||||
|  | ||||
| extern Bytes decodeFmMfm(std::vector<bool>::const_iterator start, | ||||
|     std::vector<bool>::const_iterator end); | ||||
| extern void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input); | ||||
|  | ||||
| static inline Bytes decodeFmMfm(const std::vector<bool> bits) | ||||
| { return decodeFmMfm(bits.begin(), bits.end()); } | ||||
|   | ||||
| @@ -18,13 +18,18 @@ DoubleFlag pulseDebounceThreshold( | ||||
| static DoubleFlag clockDecodeThreshold( | ||||
|     { "--bit-error-threshold" }, | ||||
|     "Amount of error to tolerate in pulse timing, in fractions of a clock.", | ||||
|     0.20); | ||||
|     0.40); | ||||
|  | ||||
| static DoubleFlag clockIntervalBias( | ||||
|     { "--clock-interval-bias" }, | ||||
|     "Adjust intervals between pulses by this many clocks before decoding.", | ||||
|     -0.02); | ||||
|  | ||||
| static DoubleFlag minimumClockUs( | ||||
|     { "--minimum-clock-us" }, | ||||
|     "Refuse to detect clocks shorter than this, to avoid false positives.", | ||||
|     0.75); | ||||
|  | ||||
| int FluxmapReader::readOpcode(unsigned& ticks) | ||||
| { | ||||
|     ticks = 0; | ||||
| @@ -222,7 +227,9 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu | ||||
|             seek(positions[intervalCount-match.intervals]); | ||||
|             _pos.zeroes = match.zeroes; | ||||
|             matching = match.matcher; | ||||
|             return match.clock * NS_PER_TICK; | ||||
|             nanoseconds_t detectedClock = match.clock * NS_PER_TICK; | ||||
|             if (detectedClock > (minimumClockUs*1000)) | ||||
|                 return match.clock * NS_PER_TICK; | ||||
|         } | ||||
|  | ||||
|         for (unsigned i=0; i<intervalCount; i++) | ||||
|   | ||||
| @@ -51,3 +51,25 @@ Bytes decodeFmMfm( | ||||
|  | ||||
|     return bytes; | ||||
| } | ||||
|  | ||||
| void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input) | ||||
| { | ||||
|     bool lastBit = false; | ||||
|     unsigned len = bits.size()-1; | ||||
|  | ||||
|     for (uint8_t b : input) | ||||
|     { | ||||
|         for (int i=0; i<8; i++) | ||||
|         { | ||||
|             bool bit = b & 0x80; | ||||
|             b <<= 1; | ||||
|  | ||||
|             if (cursor >= len) | ||||
|                 return; | ||||
|              | ||||
|             bits[cursor++] = !lastBit && !bit; | ||||
|             bits[cursor++] = bit; | ||||
|             lastBit = bit; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										90
									
								
								lib/flags.cc
									
									
									
									
									
								
							
							
						
						
									
										90
									
								
								lib/flags.cc
									
									
									
									
									
								
							| @@ -29,7 +29,7 @@ void FlagGroup::addFlag(Flag* flag) | ||||
|     _flags.push_back(flag); | ||||
| } | ||||
|  | ||||
| void FlagGroup::parseFlags(int argc, const char* argv[]) | ||||
| std::vector<std::string> FlagGroup::parseFlagsWithFilenames(int argc, const char* argv[]) | ||||
| { | ||||
|     if (_initialised) | ||||
|         throw std::runtime_error("called parseFlags() twice"); | ||||
| @@ -66,6 +66,7 @@ void FlagGroup::parseFlags(int argc, const char* argv[]) | ||||
|  | ||||
|     /* Now actually parse them. */ | ||||
|  | ||||
|     std::vector<std::string> filenames; | ||||
|     int index = 1; | ||||
|     while (index < argc) | ||||
|     { | ||||
| @@ -76,52 +77,73 @@ void FlagGroup::parseFlags(int argc, const char* argv[]) | ||||
|         std::string value; | ||||
|         bool usesthat = false; | ||||
|  | ||||
|         if ((thisarg.size() == 0) || (thisarg[0] != '-')) | ||||
|             Error() << "non-option parameter " << thisarg << " seen (try --help)"; | ||||
|         if ((thisarg.size() > 1) && (thisarg[1] == '-')) | ||||
|         if (thisarg.size() == 0) | ||||
|         { | ||||
|             /* Long option. */ | ||||
|  | ||||
|             auto equals = thisarg.rfind('='); | ||||
|             if (equals != std::string::npos) | ||||
|             { | ||||
|                 key = thisarg.substr(0, equals); | ||||
|                 value = thisarg.substr(equals+1); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 key = thisarg; | ||||
|                 value = thatarg; | ||||
|                 usesthat = true; | ||||
|             } | ||||
|             /* Ignore this argument. */ | ||||
|         } | ||||
|         else if (thisarg[0] != '-') | ||||
|         { | ||||
|             /* This is a filename. */ | ||||
|             filenames.push_back(thisarg); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             /* Short option. */ | ||||
|             /* This is a flag. */ | ||||
|  | ||||
|             if (thisarg.size() > 2) | ||||
|             if ((thisarg.size() > 1) && (thisarg[1] == '-')) | ||||
|             { | ||||
|                 key = thisarg.substr(0, 2); | ||||
|                 value = thisarg.substr(2); | ||||
|                 /* Long option. */ | ||||
|  | ||||
|                 auto equals = thisarg.rfind('='); | ||||
|                 if (equals != std::string::npos) | ||||
|                 { | ||||
|                     key = thisarg.substr(0, equals); | ||||
|                     value = thisarg.substr(equals+1); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     key = thisarg; | ||||
|                     value = thatarg; | ||||
|                     usesthat = true; | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 key = thisarg; | ||||
|                 value = thatarg; | ||||
|                 usesthat = true; | ||||
|                 /* Short option. */ | ||||
|  | ||||
|                 if (thisarg.size() > 2) | ||||
|                 { | ||||
|                     key = thisarg.substr(0, 2); | ||||
|                     value = thisarg.substr(2); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     key = thisarg; | ||||
|                     value = thatarg; | ||||
|                     usesthat = true; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             auto flag = flags_by_name.find(key); | ||||
|             if (flag == flags_by_name.end()) | ||||
|                 Error() << "unknown flag '" << key << "'; try --help"; | ||||
|  | ||||
|             flag->second->set(value); | ||||
|             if (usesthat && flag->second->hasArgument()) | ||||
|                 index++; | ||||
|         } | ||||
|  | ||||
|         auto flag = flags_by_name.find(key); | ||||
|         if (flag == flags_by_name.end()) | ||||
|             Error() << "unknown flag '" << key << "'; try --help"; | ||||
|  | ||||
|         flag->second->set(value); | ||||
|  | ||||
|         index++; | ||||
|         if (usesthat && flag->second->hasArgument()) | ||||
|             index++; | ||||
|     } | ||||
|  | ||||
|     return filenames; | ||||
| } | ||||
|  | ||||
| void FlagGroup::parseFlags(int argc, const char* argv[]) | ||||
| { | ||||
|     auto filenames = parseFlagsWithFilenames(argc, argv); | ||||
|     if (!filenames.empty()) | ||||
|         Error() << "non-option parameter " << *filenames.begin() << " seen (try --help)"; | ||||
| } | ||||
|  | ||||
| void FlagGroup::checkInitialised() const | ||||
| @@ -135,6 +157,8 @@ Flag::Flag(const std::vector<std::string>& names, const std::string helptext): | ||||
|     _names(names), | ||||
|     _helptext(helptext) | ||||
| { | ||||
|     if (!currentFlagGroup) | ||||
|         Error() << "no flag group defined for " << *names.begin(); | ||||
|     _group.addFlag(this); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,7 @@ public: | ||||
|  | ||||
| public: | ||||
|     void parseFlags(int argc, const char* argv[]); | ||||
|     std::vector<std::string> parseFlagsWithFilenames(int argc, const char* argv[]); | ||||
|     void addFlag(Flag* flag); | ||||
|     void checkInitialised() const; | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,10 @@ | ||||
| #ifndef FLUXSINK_H | ||||
| #define FLUXSINK_H | ||||
|  | ||||
| #include "flags.h" | ||||
|  | ||||
| extern FlagGroup hardwareFluxSinkFlags; | ||||
|  | ||||
| class Fluxmap; | ||||
| class FluxSpec; | ||||
|  | ||||
|   | ||||
| @@ -4,8 +4,15 @@ | ||||
| #include "usb.h" | ||||
| #include "fluxsink/fluxsink.h" | ||||
|  | ||||
| FlagGroup hardwareFluxSinkFlags; | ||||
|  | ||||
| static bool high_density = false; | ||||
|  | ||||
| static IntFlag indexMode( | ||||
|     { "--write-index-mode" }, | ||||
|     "index pulse source (0=drive, 1=300 RPM fake source, 2=360 RPM fake source", | ||||
|     0); | ||||
|  | ||||
| void setHardwareFluxSinkDensity(bool high_density) | ||||
| { | ||||
| 	::high_density = high_density; | ||||
| @@ -26,7 +33,7 @@ public: | ||||
| public: | ||||
|     void writeFlux(int track, int side, Fluxmap& fluxmap) | ||||
|     { | ||||
|         usbSetDrive(_drive, high_density); | ||||
|         usbSetDrive(_drive, high_density, indexMode); | ||||
|         usbSeek(track); | ||||
|  | ||||
|         Bytes crunched = fluxmap.rawBytes().crunch(); | ||||
|   | ||||
| @@ -27,8 +27,9 @@ public: | ||||
|     virtual bool retryable() { return false; } | ||||
| }; | ||||
|  | ||||
| extern void setHardwareFluxSourceRevolutions(int revolutions); | ||||
| extern void setHardwareFluxSourceRevolutions(double revolutions); | ||||
| extern void setHardwareFluxSourceDensity(bool high_density); | ||||
| extern void setHardwareFluxSourceSynced(bool synced); | ||||
|  | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -3,13 +3,24 @@ | ||||
| #include "fluxmap.h" | ||||
| #include "usb.h" | ||||
| #include "fluxsource/fluxsource.h" | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| FlagGroup hardwareFluxSourceFlags; | ||||
|  | ||||
| static IntFlag revolutions( | ||||
| static DoubleFlag revolutions( | ||||
|     { "--revolutions" }, | ||||
|     "read this many revolutions of the disk", | ||||
|     1); | ||||
|     1.25); | ||||
|  | ||||
| static BoolFlag synced( | ||||
|     { "--sync-with-index" }, | ||||
|     "whether to wait for an index pulse before started to read", | ||||
|     false); | ||||
|  | ||||
| static IntFlag indexMode( | ||||
|     { "--index-mode" }, | ||||
|     "index pulse source (0=drive, 1=300 RPM fake source, 2=360 RPM fake source", | ||||
|     0); | ||||
|  | ||||
| static bool high_density = false; | ||||
|  | ||||
| @@ -24,6 +35,10 @@ public: | ||||
|     HardwareFluxSource(unsigned drive): | ||||
|         _drive(drive) | ||||
|     { | ||||
|         usbSetDrive(_drive, high_density, indexMode); | ||||
|         std::cerr << "Measuring rotational speed... " << std::flush; | ||||
|         _oneRevolution = usbGetRotationalPeriod(); | ||||
|         std::cerr << fmt::format("{}ms\n", _oneRevolution / 1e6); | ||||
|     } | ||||
|  | ||||
|     ~HardwareFluxSource() | ||||
| @@ -33,9 +48,9 @@ public: | ||||
| public: | ||||
|     std::unique_ptr<Fluxmap> readFlux(int track, int side) | ||||
|     { | ||||
|         usbSetDrive(_drive, high_density); | ||||
|         usbSetDrive(_drive, high_density, indexMode); | ||||
|         usbSeek(track); | ||||
|         Bytes crunched = usbRead(side, revolutions); | ||||
|         Bytes crunched = usbRead(side, synced, revolutions * _oneRevolution); | ||||
|         auto fluxmap = std::make_unique<Fluxmap>(); | ||||
|         fluxmap->appendBytes(crunched.uncrunch()); | ||||
|         return fluxmap; | ||||
| @@ -54,13 +69,19 @@ public: | ||||
| private: | ||||
|     unsigned _drive; | ||||
|     unsigned _revolutions; | ||||
|     nanoseconds_t _oneRevolution; | ||||
| }; | ||||
|  | ||||
| void setHardwareFluxSourceRevolutions(int revolutions) | ||||
| void setHardwareFluxSourceRevolutions(double revolutions) | ||||
| { | ||||
|     ::revolutions.setDefaultValue(revolutions); | ||||
| } | ||||
|  | ||||
| void setHardwareFluxSourceSynced(bool synced) | ||||
| { | ||||
|     ::synced.setDefaultValue(synced); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<FluxSource> FluxSource::createHardwareFluxSource(unsigned drive) | ||||
| { | ||||
|     return std::unique_ptr<FluxSource>(new HardwareFluxSource(drive)); | ||||
|   | ||||
| @@ -24,7 +24,7 @@ public: | ||||
|     void recalibrate() {} | ||||
|  | ||||
| private: | ||||
|     const std::string& _path; | ||||
|     const std::string _path; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<FluxSource> FluxSource::createStreamFluxSource(const std::string& path) | ||||
|   | ||||
| @@ -22,7 +22,7 @@ extern void hexdumpForSrp16(std::ostream& stream, const Bytes& bytes); | ||||
| class Error | ||||
| { | ||||
| public: | ||||
|     ~Error() | ||||
|     [[ noreturn ]] ~Error() | ||||
|     { | ||||
|         std::cerr << "Error: " << _stream.str() << std::endl; | ||||
|         exit(1); | ||||
|   | ||||
| @@ -7,6 +7,13 @@ | ||||
| #include "imagereader/imagereader.h" | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| std::map<std::string, ImageReader::Constructor> ImageReader::formats = | ||||
| { | ||||
| 	{".adf", ImageReader::createImgImageReader}, | ||||
| 	{".d81", ImageReader::createImgImageReader}, | ||||
| 	{".img", ImageReader::createImgImageReader}, | ||||
| }; | ||||
|  | ||||
| static bool ends_with(const std::string& value, const std::string& ending) | ||||
| { | ||||
|     if (ending.size() > value.size()) | ||||
| @@ -14,15 +21,29 @@ static bool ends_with(const std::string& value, const std::string& ending) | ||||
|     return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<ImageReader> ImageReader::create(const ImageSpec& spec) | ||||
| ImageReader::Constructor ImageReader::findConstructor(const ImageSpec& spec) | ||||
| { | ||||
|     const auto& filename = spec.filename; | ||||
|  | ||||
|     if (ends_with(filename, ".img") || ends_with(filename, ".adf")) | ||||
|         return createImgImageReader(spec); | ||||
| 	for (const auto& e : formats) | ||||
| 	{ | ||||
| 		if (ends_with(filename, e.first)) | ||||
| 			return e.second; | ||||
| 	} | ||||
|  | ||||
|     Error() << "unrecognised image filename extension"; | ||||
|     return std::unique_ptr<ImageReader>(); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| std::unique_ptr<ImageReader> ImageReader::create(const ImageSpec& spec) | ||||
| { | ||||
|     verifyImageSpec(spec); | ||||
|     return findConstructor(spec)(spec); | ||||
| } | ||||
|  | ||||
| void ImageReader::verifyImageSpec(const ImageSpec& spec) | ||||
| { | ||||
|     if (!findConstructor(spec)) | ||||
|         Error() << "unrecognised image filename extension"; | ||||
| } | ||||
|  | ||||
| ImageReader::ImageReader(const ImageSpec& spec): | ||||
|   | ||||
| @@ -12,10 +12,21 @@ public: | ||||
|  | ||||
| public: | ||||
|     static std::unique_ptr<ImageReader> create(const ImageSpec& spec); | ||||
| 	static void verifyImageSpec(const ImageSpec& spec); | ||||
|  | ||||
| private: | ||||
| 	typedef  | ||||
| 		std::function< | ||||
| 			std::unique_ptr<ImageReader>(const ImageSpec& spec) | ||||
| 		> | ||||
| 		Constructor; | ||||
|  | ||||
| 	static std::map<std::string, Constructor> formats; | ||||
|  | ||||
|     static std::unique_ptr<ImageReader> createImgImageReader(const ImageSpec& spec); | ||||
|  | ||||
| 	static Constructor findConstructor(const ImageSpec& spec); | ||||
|  | ||||
| public: | ||||
| 	virtual SectorSet readImage() = 0; | ||||
|  | ||||
|   | ||||
| @@ -7,6 +7,15 @@ | ||||
| #include "imagewriter/imagewriter.h" | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| std::map<std::string, ImageWriter::Constructor> ImageWriter::formats = | ||||
| { | ||||
| 	{".adf", ImageWriter::createImgImageWriter}, | ||||
| 	{".d64", ImageWriter::createD64ImageWriter}, | ||||
| 	{".d81", ImageWriter::createImgImageWriter}, | ||||
| 	{".img", ImageWriter::createImgImageWriter}, | ||||
| 	{".ldbs", ImageWriter::createLDBSImageWriter}, | ||||
| }; | ||||
|  | ||||
| static bool ends_with(const std::string& value, const std::string& ending) | ||||
| { | ||||
|     if (ending.size() > value.size()) | ||||
| @@ -14,19 +23,29 @@ static bool ends_with(const std::string& value, const std::string& ending) | ||||
|     return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<ImageWriter> ImageWriter::create(const SectorSet& sectors, const ImageSpec& spec) | ||||
| ImageWriter::Constructor ImageWriter::findConstructor(const ImageSpec& spec) | ||||
| { | ||||
|     const auto& filename = spec.filename; | ||||
|  | ||||
|     if (ends_with(filename, ".img") || ends_with(filename, ".adf")) | ||||
|         return createImgImageWriter(sectors, spec); | ||||
| 	else if (ends_with(filename, ".ldbs")) | ||||
| 		return createLDBSImageWriter(sectors, spec); | ||||
| 	else if (ends_with(filename, ".d64")) | ||||
| 		return createD64ImageWriter(sectors, spec); | ||||
| 	for (const auto& e : formats) | ||||
| 	{ | ||||
| 		if (ends_with(filename, e.first)) | ||||
| 			return e.second; | ||||
| 	} | ||||
|  | ||||
|     Error() << "unrecognised image filename extension"; | ||||
|     return std::unique_ptr<ImageWriter>(); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| std::unique_ptr<ImageWriter> ImageWriter::create(const SectorSet& sectors, const ImageSpec& spec) | ||||
| { | ||||
| 	verifyImageSpec(spec); | ||||
| 	return findConstructor(spec)(sectors, spec); | ||||
| } | ||||
|  | ||||
| void ImageWriter::verifyImageSpec(const ImageSpec& spec) | ||||
| { | ||||
| 	if (!findConstructor(spec)) | ||||
| 		Error() << "unrecognised image filename extension"; | ||||
| } | ||||
|  | ||||
| ImageWriter::ImageWriter(const SectorSet& sectors, const ImageSpec& spec): | ||||
|   | ||||
| @@ -12,8 +12,17 @@ public: | ||||
|  | ||||
| public: | ||||
|     static std::unique_ptr<ImageWriter> create(const SectorSet& sectors, const ImageSpec& spec); | ||||
| 	static void verifyImageSpec(const ImageSpec& filename); | ||||
|  | ||||
| private: | ||||
| 	typedef  | ||||
| 		std::function< | ||||
| 			std::unique_ptr<ImageWriter>(const SectorSet& sectors, const ImageSpec& spec) | ||||
| 		> | ||||
| 		Constructor; | ||||
|  | ||||
| 	static std::map<std::string, Constructor> formats; | ||||
|  | ||||
|     static std::unique_ptr<ImageWriter> createImgImageWriter( | ||||
| 		const SectorSet& sectors, const ImageSpec& spec); | ||||
|     static std::unique_ptr<ImageWriter> createLDBSImageWriter( | ||||
| @@ -21,6 +30,8 @@ private: | ||||
|     static std::unique_ptr<ImageWriter> createD64ImageWriter( | ||||
| 		const SectorSet& sectors, const ImageSpec& spec); | ||||
|  | ||||
| 	static Constructor findConstructor(const ImageSpec& spec); | ||||
|  | ||||
| public: | ||||
| 	virtual void adjustGeometry(); | ||||
| 	void printMap(); | ||||
|   | ||||
| @@ -9,14 +9,16 @@ | ||||
| #include "decoders/decoders.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "visualiser.h" | ||||
| #include "record.h" | ||||
| #include "image.h" | ||||
| #include "bytes.h" | ||||
| #include "decoders/rawbits.h" | ||||
| #include "track.h" | ||||
| #include "imagewriter/imagewriter.h" | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| FlagGroup readerFlags { &hardwareFluxSourceFlags, &fluxmapReaderFlags }; | ||||
| FlagGroup readerFlags { &hardwareFluxSourceFlags, &fluxmapReaderFlags, &visualiserFlags }; | ||||
|  | ||||
| static DataSpecFlag source( | ||||
|     { "--source", "-s" }, | ||||
| @@ -33,13 +35,22 @@ static StringFlag destination( | ||||
|     "write the raw magnetic flux to this file", | ||||
|     ""); | ||||
|  | ||||
| static StringFlag visualise( | ||||
| 	{ "--write-svg" }, | ||||
| 	"write a visualisation of the disk to this file", | ||||
| 	""); | ||||
|  | ||||
| static SettableFlag justRead( | ||||
| 	{ "--just-read" }, | ||||
| 	"just read the disk and do no further processing"); | ||||
|  | ||||
| static SettableFlag dumpRecords( | ||||
| 	{ "--dump-records" }, | ||||
| 	"Dump the parsed records."); | ||||
| 	"Dump the parsed but undecoded records."); | ||||
|  | ||||
| static SettableFlag dumpSectors( | ||||
| 	{ "--dump-sectors" }, | ||||
| 	"Dump the decoded sectors."); | ||||
|  | ||||
| static IntFlag retries( | ||||
| 	{ "--retries" }, | ||||
| @@ -142,7 +153,7 @@ static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replaceme | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!replacing || (replacing->status != Sector::OK)) | ||||
| 	if (!replacing || ((replacing->status != Sector::OK) && (replacement.status == Sector::OK))) | ||||
| 	{ | ||||
| 		if (!replacing) | ||||
| 			replacing.reset(new Sector); | ||||
| @@ -153,7 +164,8 @@ static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replaceme | ||||
| void readDiskCommand(AbstractDecoder& decoder) | ||||
| { | ||||
| 	const ImageSpec outputSpec(output); | ||||
|  | ||||
| 	ImageWriter::verifyImageSpec(outputSpec); | ||||
|          | ||||
| 	bool failures = false; | ||||
| 	SectorSet allSectors; | ||||
| 	auto tracks = readTracks(); | ||||
| @@ -211,10 +223,7 @@ void readDiskCommand(AbstractDecoder& decoder) | ||||
|                 std::cout << "giving up" << std::endl | ||||
|                           << "       "; | ||||
|             else | ||||
|             { | ||||
| 				std::cout << retry << " retries remaining" << std::endl; | ||||
|                 track->fluxsource->recalibrate(); | ||||
|             } | ||||
| 		} | ||||
|  | ||||
| 		if (dumpRecords) | ||||
| @@ -222,13 +231,27 @@ void readDiskCommand(AbstractDecoder& decoder) | ||||
| 			std::cout << "\nRaw (undecoded) records follow:\n\n"; | ||||
| 			for (auto& record : track->rawrecords) | ||||
| 			{ | ||||
| 				std::cout << fmt::format("I+{:.2f}us", record.position.ns() / 1000.0) | ||||
| 						<< std::endl; | ||||
| 				std::cout << fmt::format("I+{:.2f}us with {:.2f}us clock\n", | ||||
|                             record.position.ns() / 1000.0, record.clock / 1000.0); | ||||
| 				hexdump(std::cout, record.data); | ||||
| 				std::cout << std::endl; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|         if (dumpSectors) | ||||
|         { | ||||
|             std::cout << "\nDecoded sectors follow:\n\n"; | ||||
|             for (auto& i : readSectors) | ||||
|             { | ||||
|                 auto& sector = i.second; | ||||
| 				std::cout << fmt::format("{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock\n", | ||||
|                             sector->logicalTrack, sector->logicalSide, sector->logicalSector, | ||||
|                             sector->position.ns() / 1000.0, sector->clock / 1000.0); | ||||
| 				hexdump(std::cout, sector->data); | ||||
| 				std::cout << std::endl; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         int size = 0; | ||||
| 		bool printedTrack = false; | ||||
|         for (auto& i : readSectors) | ||||
| @@ -251,6 +274,9 @@ void readDiskCommand(AbstractDecoder& decoder) | ||||
|         std::cout << size << " bytes decoded." << std::endl; | ||||
|     } | ||||
|  | ||||
| 	if (!visualise.get().empty()) | ||||
| 		visualiseSectorsToFile(allSectors, visualise.get()); | ||||
|  | ||||
|     writeSectorsToFile(allSectors, outputSpec); | ||||
| 	if (failures) | ||||
| 		std::cerr << "Warning: some sectors could not be decoded." << std::endl; | ||||
|   | ||||
| @@ -27,6 +27,10 @@ public: | ||||
| 	Status status = Status::INTERNAL_ERROR; | ||||
|     Fluxmap::Position position; | ||||
|     nanoseconds_t clock = 0; | ||||
|     nanoseconds_t headerStartTime = 0; | ||||
|     nanoseconds_t headerEndTime = 0; | ||||
|     nanoseconds_t dataStartTime = 0; | ||||
|     nanoseconds_t dataEndTime = 0; | ||||
|     int physicalTrack = 0; | ||||
|     int physicalSide = 0; | ||||
|     int logicalTrack = 0; | ||||
|   | ||||
| @@ -17,6 +17,9 @@ public: | ||||
| 	std::unique_ptr<Sector>& get(int track, int head, int sector); | ||||
| 	Sector* get(int track, int head, int sector) const; | ||||
|  | ||||
| 	const std::map<const key_t, std::unique_ptr<Sector>>& get() const | ||||
| 	{ return _data; } | ||||
|  | ||||
| 	void calculateSize( | ||||
| 		unsigned& numTracks, unsigned& numHeads, unsigned& numSectors, | ||||
| 		unsigned& sectorSize) const; | ||||
|   | ||||
							
								
								
									
										49
									
								
								lib/usb.cc
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								lib/usb.cc
									
									
									
									
									
								
							| @@ -149,7 +149,7 @@ nanoseconds_t usbGetRotationalPeriod(void) | ||||
|     usb_cmd_send(&f, f.f.size); | ||||
|  | ||||
|     auto r = await_reply<struct speed_frame>(F_FRAME_MEASURE_SPEED_REPLY); | ||||
|     return r->period_ms * 1000; | ||||
|     return r->period_ms * 1000000; | ||||
| } | ||||
|  | ||||
| static int large_bulk_transfer(int ep, Bytes& bytes) | ||||
| @@ -202,13 +202,16 @@ void usbTestBulkTransport() | ||||
|     await_reply<struct any_frame>(F_FRAME_BULK_TEST_REPLY); | ||||
| } | ||||
|  | ||||
| Bytes usbRead(int side, int revolutions) | ||||
| Bytes usbRead(int side, bool synced, nanoseconds_t readTime) | ||||
| { | ||||
|     struct read_frame f = { | ||||
|         .f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) }, | ||||
|         .side = (uint8_t) side, | ||||
|         .revolutions = (uint8_t) revolutions | ||||
|         .synced = (uint8_t) synced | ||||
|     }; | ||||
|     uint16_t milliseconds = readTime / 1e6; | ||||
|     ((uint8_t*)&f.milliseconds)[0] = milliseconds; | ||||
|     ((uint8_t*)&f.milliseconds)[1] = milliseconds >> 8; | ||||
|     usb_cmd_send(&f, f.f.size); | ||||
|  | ||||
|     auto fluxmap = std::unique_ptr<Fluxmap>(new Fluxmap); | ||||
| @@ -253,15 +256,51 @@ void usbErase(int side) | ||||
|     await_reply<struct any_frame>(F_FRAME_ERASE_REPLY); | ||||
| } | ||||
|  | ||||
| void usbSetDrive(int drive, bool high_density) | ||||
| void usbSetDrive(int drive, bool high_density, int index_mode) | ||||
| { | ||||
|     usb_init(); | ||||
|  | ||||
|     struct set_drive_frame f = { | ||||
|         { .type = F_FRAME_SET_DRIVE_CMD, .size = sizeof(f) }, | ||||
|         .drive_flags = (uint8_t)((drive ? DRIVE_1 : DRIVE_0) | (high_density ? DRIVE_HD : DRIVE_DD)), | ||||
|         .drive = (uint8_t) drive, | ||||
|         .high_density = high_density, | ||||
|         .index_mode = (uint8_t) index_mode | ||||
|     }; | ||||
|     usb_cmd_send(&f, f.f.size); | ||||
|     await_reply<struct any_frame>(F_FRAME_SET_DRIVE_REPLY); | ||||
| } | ||||
|  | ||||
| /* Hacky: the board always operates in little-endian mode. */ | ||||
| static uint16_t read_short_from_usb(uint16_t usb) | ||||
| { | ||||
|     uint8_t* p = (uint8_t*)&usb; | ||||
|     return p[0] | (p[1] << 8); | ||||
| } | ||||
|  | ||||
| static void convert_voltages_from_usb(const struct voltages& vin, struct voltages& vout) | ||||
| { | ||||
|     vout.logic0_mv = read_short_from_usb(vin.logic0_mv); | ||||
|     vout.logic1_mv = read_short_from_usb(vin.logic1_mv); | ||||
| } | ||||
|  | ||||
| void usbMeasureVoltages(struct voltages_frame* voltages) | ||||
| { | ||||
|     usb_init(); | ||||
|  | ||||
|     struct any_frame f = { | ||||
|         { .type = F_FRAME_MEASURE_VOLTAGES_CMD, .size = sizeof(f) }, | ||||
|     }; | ||||
|     usb_cmd_send(&f, f.f.size); | ||||
|  | ||||
|     struct voltages_frame* r = await_reply<struct voltages_frame>(F_FRAME_MEASURE_VOLTAGES_REPLY); | ||||
|     convert_voltages_from_usb(r->input_both_off, voltages->input_both_off); | ||||
|     convert_voltages_from_usb(r->input_drive_0_selected, voltages->input_drive_0_selected); | ||||
|     convert_voltages_from_usb(r->input_drive_1_selected, voltages->input_drive_1_selected); | ||||
|     convert_voltages_from_usb(r->input_drive_0_running, voltages->input_drive_0_running); | ||||
|     convert_voltages_from_usb(r->input_drive_1_running, voltages->input_drive_1_running); | ||||
|     convert_voltages_from_usb(r->output_both_off, voltages->output_both_off); | ||||
|     convert_voltages_from_usb(r->output_drive_0_selected, voltages->output_drive_0_selected); | ||||
|     convert_voltages_from_usb(r->output_drive_1_selected, voltages->output_drive_1_selected); | ||||
|     convert_voltages_from_usb(r->output_drive_0_running, voltages->output_drive_0_running); | ||||
|     convert_voltages_from_usb(r->output_drive_1_running, voltages->output_drive_1_running); | ||||
| } | ||||
|   | ||||
| @@ -9,9 +9,10 @@ extern void usbRecalibrate(); | ||||
| extern void usbSeek(int track); | ||||
| extern nanoseconds_t usbGetRotationalPeriod(); | ||||
| extern void usbTestBulkTransport(); | ||||
| extern Bytes usbRead(int side, int revolutions); | ||||
| extern Bytes usbRead(int side, bool synced, nanoseconds_t readTime); | ||||
| extern void usbWrite(int side, const Bytes& bytes); | ||||
| extern void usbErase(int side); | ||||
| extern void usbSetDrive(int drive, bool high_density); | ||||
| extern void usbSetDrive(int drive, bool high_density, int index_mode); | ||||
| extern void usbMeasureVoltages(struct voltages_frame* voltages); | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										95
									
								
								lib/visualiser.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								lib/visualiser.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| #define _USE_MATH_DEFINES | ||||
| #include "globals.h" | ||||
| #include "image.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "visualiser.h" | ||||
| #include "fmt/format.h" | ||||
| #include "flags.h" | ||||
| #include <iostream> | ||||
| #include <fstream> | ||||
| #include <math.h> | ||||
|  | ||||
| FlagGroup visualiserFlags; | ||||
|  | ||||
| static IntFlag period( | ||||
|     { "--visualiser-period" }, | ||||
|     "rotational period for use by the visualiser (milliseconds)", | ||||
|     200); | ||||
|  | ||||
| static const int SIZE = 480; | ||||
| static const int BORDER = 10; | ||||
| static const int RADIUS = (SIZE/2) - (BORDER/2); | ||||
| static const int CORE = 50; | ||||
| static const int TRACKS = 83; | ||||
| static const double TRACK_SPACING = double(RADIUS-CORE) / TRACKS; | ||||
|  | ||||
| void visualiseSectorsToFile(const SectorSet& sectors, const std::string& filename) | ||||
| { | ||||
|     std::cout << "writing visualisation\n"; | ||||
|     std::ofstream f(filename, std::ios::out); | ||||
|     if (!f.is_open()) | ||||
|         Error() << "cannot open visualisation file"; | ||||
|  | ||||
|     f << fmt::format("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"{0} {1} {2} {3}\">", | ||||
|         0, 0, SIZE*2, SIZE); | ||||
|  | ||||
|     const double radians_per_ns = 2.0*M_PI / (period*1e6); | ||||
|  | ||||
|     auto drawSide = [&](int side) | ||||
|     { | ||||
|         f << fmt::format("<g transform='matrix(1 0 0 -1 {} {})'>", SIZE/2 + (side*SIZE), SIZE/2); | ||||
|         f << fmt::format("<circle cx='0' cy='0' r='{}' stroke='none' fill='#ccc'/>", RADIUS); | ||||
|  | ||||
|         for (int physicalTrack = 0; physicalTrack < TRACKS; physicalTrack++) | ||||
|         { | ||||
|             double radius = CORE + physicalTrack*TRACK_SPACING; | ||||
|             f << fmt::format("<circle cx='0' cy='0' r='{}' stroke='#888' stroke-width='0.5' fill='none'/>", radius); | ||||
|  | ||||
|             auto drawArc = [&](const std::unique_ptr<Sector>& sector, nanoseconds_t start, nanoseconds_t end, const std::string& colour) | ||||
|             { | ||||
|                 start %= period*1000000; | ||||
|                 end %= period*1000000; | ||||
|                 if (end < start) | ||||
|                     end += period*1000000; | ||||
|                  | ||||
|                 double theta1 = start * radians_per_ns; | ||||
|                 double theta2 = end * radians_per_ns; | ||||
|                 int large = (theta2 - theta1) >= M_PI; | ||||
|  | ||||
|                 f << fmt::format("\n<!-- {} {} = {} {} -->", start, end, theta1, theta2); | ||||
|                 f << fmt::format("<path fill='none' stroke='{}' stroke-width='1.5' d='", colour); | ||||
|                 f << fmt::format("M {} {} ", cos(theta1)*radius, sin(theta1)*radius); | ||||
|                 f << fmt::format("A {0} {0} 0 {3} 1 {1} {2}", radius, cos(theta2)*radius, sin(theta2)*radius, large); | ||||
|                 f << fmt::format("'><title>Track {} Head {} Sector {}; {}ms to {}ms</title></path>", | ||||
|                     sector->logicalTrack, sector->logicalSide, sector->logicalSector, | ||||
|                     start/1e6, end/1e6); | ||||
|             }; | ||||
|  | ||||
|             /* Sadly, SectorSets aren't indexable by physical track. */ | ||||
|             for (const auto& e : sectors.get()) | ||||
|             { | ||||
|                 const auto& sector = e.second; | ||||
|                 if ((sector->physicalSide == side) && (sector->physicalTrack == physicalTrack)) | ||||
|                 { | ||||
|                     const char* colour = "#f00"; | ||||
|                     if (sector->status == Sector::OK) | ||||
|                         colour = "#00f"; | ||||
|                     if (sector->headerStartTime && sector->headerEndTime) | ||||
|                         drawArc(sector, sector->headerStartTime, sector->headerEndTime, "#0ff"); | ||||
|                     if (sector->dataStartTime && sector->dataEndTime) | ||||
|                         drawArc(sector, sector->dataStartTime, sector->dataEndTime, colour); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         f << "</g>"; | ||||
|     }; | ||||
|  | ||||
|     f << fmt::format("<rect x='0' y='0' width='{}' height='{}' stroke='none' fill='#fff'/>", SIZE*2, SIZE); | ||||
|  | ||||
|     drawSide(0); | ||||
|     drawSide(1); | ||||
|  | ||||
|     f << "</svg>"; | ||||
| } | ||||
							
								
								
									
										12
									
								
								lib/visualiser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								lib/visualiser.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #ifndef VISUALISER_H | ||||
| #define VISUALISER_H | ||||
|  | ||||
| #include "flags.h" | ||||
|  | ||||
| class SectorSet; | ||||
|  | ||||
| extern FlagGroup visualiserFlags; | ||||
|  | ||||
| extern void visualiseSectorsToFile(const SectorSet& sectors, const std::string& filename); | ||||
|  | ||||
| #endif | ||||
| @@ -15,7 +15,7 @@ | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
|  | ||||
| FlagGroup writerFlags { &hardwareFluxSourceFlags }; | ||||
| FlagGroup writerFlags { &hardwareFluxSourceFlags, &hardwareFluxSinkFlags }; | ||||
|  | ||||
| static DataSpecFlag dest( | ||||
|     { "--dest", "-d" }, | ||||
|   | ||||
							
								
								
									
										53
									
								
								mkninja.sh
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								mkninja.sh
									
									
									
									
									
								
							| @@ -44,18 +44,27 @@ buildlibrary() { | ||||
|         esac | ||||
|     done | ||||
|  | ||||
|     local objs | ||||
|     objs= | ||||
|     local oobjs | ||||
| 	local dobjs | ||||
|     oobjs= | ||||
| 	dobjs= | ||||
|     for src in "$@"; do | ||||
|         local obj | ||||
|         obj="$OBJDIR/${src%%.c*}.o" | ||||
|         objs="$objs $obj" | ||||
|         obj="$OBJDIR/opt/${src%%.c*}.o" | ||||
|         oobjs="$oobjs $obj" | ||||
|  | ||||
|         echo build $obj : cxx $src | ||||
|         echo "    flags=$flags" | ||||
|         echo "    flags=$flags $COPTFLAGS" | ||||
|  | ||||
|         obj="$OBJDIR/dbg/${src%%.c*}.o" | ||||
|         dobjs="$dobjs $obj" | ||||
|  | ||||
|         echo build $obj : cxx $src | ||||
|         echo "    flags=$flags $CDBGFLAGS" | ||||
|     done | ||||
|  | ||||
|     echo build $OBJDIR/$lib : library $objs | ||||
|     echo build $OBJDIR/opt/$lib : library $oobjs | ||||
|     echo build $OBJDIR/dbg/$lib : library $dobjs | ||||
| } | ||||
|  | ||||
| buildprogram() { | ||||
| @@ -77,16 +86,21 @@ buildprogram() { | ||||
|         esac | ||||
|     done | ||||
|  | ||||
|     local objs | ||||
|     objs= | ||||
|     local oobjs | ||||
| 	local dobjs | ||||
|     oobjs= | ||||
| 	dobjs= | ||||
|     for src in "$@"; do | ||||
|         objs="$objs $OBJDIR/$src" | ||||
|         oobjs="$oobjs $OBJDIR/opt/$src" | ||||
|         dobjs="$dobjs $OBJDIR/dbg/$src" | ||||
|     done | ||||
|  | ||||
|     echo build $prog-debug$EXTENSION : link $objs | ||||
|     echo "    flags=$flags" | ||||
|     echo build $prog-debug$EXTENSION : link $dobjs | ||||
|     echo "    flags=$flags $LDDBGFLAGS" | ||||
|  | ||||
|     echo build $prog$EXTENSION : link $oobjs | ||||
|     echo "    flags=$flags $LDOPTFLAGS" | ||||
|  | ||||
|     echo build $prog$EXTENSION : strip $prog-debug$EXTENSION | ||||
| } | ||||
|  | ||||
| buildsimpleprogram() { | ||||
| @@ -145,6 +159,8 @@ buildlibrary libbackend.a \ | ||||
| 	lib/imagewriter/ldbsimagewriter.cc \ | ||||
|     arch/aeslanier/decoder.cc \ | ||||
|     arch/amiga/decoder.cc \ | ||||
|     arch/amiga/encoder.cc \ | ||||
|     arch/amiga/amiga.cc \ | ||||
|     arch/apple2/decoder.cc \ | ||||
|     arch/brother/decoder.cc \ | ||||
|     arch/brother/encoder.cc \ | ||||
| @@ -183,12 +199,14 @@ buildlibrary libbackend.a \ | ||||
|     lib/sectorset.cc \ | ||||
|     lib/sql.cc \ | ||||
|     lib/usb.cc \ | ||||
|     lib/visualiser.cc \ | ||||
|     lib/writer.cc \ | ||||
|  | ||||
| buildlibrary libfrontend.a \ | ||||
|     src/fe-cwftoflux.cc \ | ||||
|     src/fe-erase.cc \ | ||||
|     src/fe-fluxtoau.cc \ | ||||
|     src/fe-fluxtoscp.cc \ | ||||
|     src/fe-fluxtovcd.cc \ | ||||
|     src/fe-inspect.cc \ | ||||
|     src/fe-readadfs.cc \ | ||||
| @@ -207,9 +225,12 @@ buildlibrary libfrontend.a \ | ||||
|     src/fe-readvictor9k.cc \ | ||||
|     src/fe-readzilogmcz.cc \ | ||||
|     src/fe-rpm.cc \ | ||||
|     src/fe-scptoflux.cc \ | ||||
|     src/fe-seek.cc \ | ||||
|     src/fe-testbulktransport.cc \ | ||||
|     src/fe-testvoltages.cc \ | ||||
|     src/fe-upgradefluxfile.cc \ | ||||
|     src/fe-writeamiga.cc \ | ||||
|     src/fe-writebrother.cc \ | ||||
|     src/fe-writeflux.cc \ | ||||
|     src/fe-writetestpattern.cc \ | ||||
| @@ -230,6 +251,13 @@ buildsimpleprogram brother120tool \ | ||||
|     libemu.a \ | ||||
|     libfmt.a \ | ||||
|  | ||||
| buildsimpleprogram brother240tool \ | ||||
| 	-Idep/emu \ | ||||
|     tools/brother240tool.cc \ | ||||
|     libbackend.a \ | ||||
|     libemu.a \ | ||||
|     libfmt.a \ | ||||
|  | ||||
| runtest bitaccumulator-test tests/bitaccumulator.cc | ||||
| runtest bytes-test          tests/bytes.cc | ||||
| runtest compression-test    tests/compression.cc | ||||
| @@ -240,3 +268,4 @@ runtest fluxpattern-test    tests/fluxpattern.cc | ||||
| runtest fmmfm-test          tests/fmmfm.cc | ||||
| runtest kryoflux-test       tests/kryoflux.cc | ||||
| runtest ldbs-test           tests/ldbs.cc | ||||
| runtest amiga-test          tests/amiga.cc | ||||
|   | ||||
							
								
								
									
										39
									
								
								protocol.h
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								protocol.h
									
									
									
									
									
								
							| @@ -3,7 +3,7 @@ | ||||
|  | ||||
| enum  | ||||
| { | ||||
|     FLUXENGINE_VERSION = 8, | ||||
|     FLUXENGINE_VERSION = 11, | ||||
|  | ||||
|     FLUXENGINE_VID = 0x1209, | ||||
|     FLUXENGINE_PID = 0x6e00, | ||||
| @@ -62,6 +62,8 @@ enum | ||||
|     F_FRAME_RECALIBRATE_REPLY,    /* any_frame */ | ||||
|     F_FRAME_SET_DRIVE_CMD,        /* setdrive_frame */ | ||||
|     F_FRAME_SET_DRIVE_REPLY,      /* any_frame */ | ||||
|     F_FRAME_MEASURE_VOLTAGES_CMD, /* any_frame */ | ||||
|     F_FRAME_MEASURE_VOLTAGES_REPLY, /* voltages_frame */ | ||||
| }; | ||||
|  | ||||
| enum | ||||
| @@ -73,6 +75,13 @@ enum | ||||
|     F_ERROR_INTERNAL, | ||||
| }; | ||||
|  | ||||
| enum | ||||
| { | ||||
|     F_INDEX_REAL, | ||||
|     F_INDEX_300, | ||||
|     F_INDEX_360 | ||||
| }; | ||||
|  | ||||
| enum | ||||
| { | ||||
|     F_OP_PULSE = 0x80, | ||||
| @@ -124,7 +133,8 @@ struct read_frame | ||||
| { | ||||
|     struct frame_header f; | ||||
|     uint8_t side; | ||||
|     uint8_t revolutions; | ||||
|     uint8_t synced; | ||||
|     uint16_t milliseconds; | ||||
| }; | ||||
|  | ||||
| struct write_frame | ||||
| @@ -143,7 +153,30 @@ struct erase_frame | ||||
| struct set_drive_frame | ||||
| { | ||||
|     struct frame_header f; | ||||
|     uint8_t drive_flags; | ||||
|     uint8_t drive; | ||||
|     uint8_t high_density; | ||||
|     uint8_t index_mode; | ||||
| }; | ||||
|  | ||||
| struct voltages | ||||
| { | ||||
|     uint16_t logic0_mv; | ||||
|     uint16_t logic1_mv; | ||||
| }; | ||||
|  | ||||
| struct voltages_frame | ||||
| { | ||||
|     struct frame_header f; | ||||
|     struct voltages output_both_off; | ||||
|     struct voltages output_drive_0_selected; | ||||
|     struct voltages output_drive_1_selected; | ||||
|     struct voltages output_drive_0_running; | ||||
|     struct voltages output_drive_1_running; | ||||
|     struct voltages input_both_off; | ||||
|     struct voltages input_drive_0_selected; | ||||
|     struct voltages input_drive_1_selected; | ||||
|     struct voltages input_drive_0_running; | ||||
|     struct voltages input_drive_1_running; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| #include "fluxmap.h" | ||||
| #include "writer.h" | ||||
|  | ||||
| static FlagGroup flags; | ||||
| static FlagGroup flags { &writerFlags }; | ||||
|  | ||||
| int mainErase(int argc, const char* argv[]) | ||||
| { | ||||
|   | ||||
							
								
								
									
										188
									
								
								src/fe-fluxtoscp.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								src/fe-fluxtoscp.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | ||||
| #include "globals.h" | ||||
| #include "flags.h" | ||||
| #include "fluxmap.h" | ||||
| #include "sql.h" | ||||
| #include "bytes.h" | ||||
| #include "protocol.h" | ||||
| #include "dataspec.h" | ||||
| #include "fmt/format.h" | ||||
| #include "decoders/fluxmapreader.h" | ||||
| #include "scp.h" | ||||
| #include <fstream> | ||||
| #include <algorithm> | ||||
|  | ||||
| static FlagGroup flags { }; | ||||
|  | ||||
| static SettableFlag fortyTrackMode( | ||||
|     { "--48", "-4" }, | ||||
|     "set 48 tpi mode; only every other physical track is emitted" | ||||
| ); | ||||
|  | ||||
| static SettableFlag singleSided( | ||||
|     { "--single-sided", "-s" }, | ||||
|     "only emit side 0" | ||||
| ); | ||||
|  | ||||
| static IntFlag diskType( | ||||
|     { "--disk-type" }, | ||||
|     "sets the SCP disk type byte", | ||||
|     0xff | ||||
| ); | ||||
|  | ||||
| static sqlite3* inputDb; | ||||
|  | ||||
| static void syntax() | ||||
| { | ||||
|     std::cout << "Syntax: fluxengine convert fluxtoscp <fluxfile> <scpfile>\n"; | ||||
|     exit(0); | ||||
| } | ||||
|  | ||||
| static void write_le32(uint8_t dest[4], uint32_t v) | ||||
| { | ||||
|     dest[0] = v; | ||||
|     dest[1] = v >> 8; | ||||
|     dest[2] = v >> 16; | ||||
|     dest[3] = v >> 24; | ||||
| } | ||||
|  | ||||
| static int strackno(int track, int side) | ||||
| { | ||||
|     if (fortyTrackMode) | ||||
|         track /= 2; | ||||
|     if (singleSided) | ||||
|         return track; | ||||
|     else | ||||
|         return (track << 1) | side; | ||||
| } | ||||
|  | ||||
| int mainConvertFluxToScp(int argc, const char* argv[]) | ||||
| { | ||||
|     auto filenames = flags.parseFlagsWithFilenames(argc, argv); | ||||
|     if (filenames.size() != 2) | ||||
|         syntax(); | ||||
|  | ||||
|     inputDb = sqlOpen(filenames[0], SQLITE_OPEN_READONLY); | ||||
|     auto tracks = sqlFindFlux(inputDb); | ||||
|  | ||||
|     int maxTrack = 0; | ||||
|     int maxSide = 0; | ||||
|     for (auto p : tracks) | ||||
|     { | ||||
|         if (singleSided && (p.second == 1)) | ||||
|             continue; | ||||
|         maxTrack = std::max(maxTrack, (int)p.first); | ||||
|         maxSide = std::max(maxSide, (int)p.second); | ||||
|     } | ||||
|     int maxStrack = strackno(maxTrack, maxSide); | ||||
|  | ||||
|     std::cout << fmt::format("Writing {} {} SCP file containing {} SCP tracks\n", | ||||
|         fortyTrackMode ? "48 tpi" : "96 tpi", | ||||
|         singleSided ? "single sided" : "double sided", | ||||
|         maxStrack + 1 | ||||
|     ); | ||||
|  | ||||
|     ScpHeader fileheader = {0}; | ||||
|     fileheader.file_id[0] = 'S'; | ||||
|     fileheader.file_id[1] = 'C'; | ||||
|     fileheader.file_id[2] = 'P'; | ||||
|     fileheader.version = 0x18; /* Version 1.8 of the spec */ | ||||
|     fileheader.type = diskType; | ||||
|     fileheader.revolutions = 5; | ||||
|     fileheader.start_track = 0; | ||||
|     fileheader.end_track = maxStrack; | ||||
|     fileheader.flags = SCP_FLAG_INDEXED | (fortyTrackMode ? 0 : SCP_FLAG_96TPI); | ||||
|     fileheader.cell_width = 0; | ||||
|     fileheader.heads = singleSided ? 1 : 0; | ||||
|  | ||||
|     Bytes trackdata; | ||||
|     ByteWriter trackdataWriter(trackdata); | ||||
|  | ||||
|     int trackstep = 1 + fortyTrackMode; | ||||
|     int maxside = singleSided ? 0 : 1; | ||||
|     for (int track = 0; track <= maxTrack; track += trackstep) | ||||
|     { | ||||
|         for (int side = 0; side <= maxside; side++) | ||||
|         { | ||||
|             int strack = strackno(track, side); | ||||
|             std::cout << fmt::format("FE track {}.{}, SCP track {}: ", track, side, strack) << std::flush; | ||||
|  | ||||
|             auto fluxmap = sqlReadFlux(inputDb, track, side); | ||||
|             ScpTrack trackheader = {0}; | ||||
|             trackheader.track_id[0] = 'T'; | ||||
|             trackheader.track_id[1] = 'R'; | ||||
|             trackheader.track_id[2] = 'K'; | ||||
|             trackheader.strack = strack; | ||||
|  | ||||
|             FluxmapReader fmr(*fluxmap); | ||||
|             Bytes fluxdata; | ||||
|             ByteWriter fluxdataWriter(fluxdata); | ||||
|  | ||||
|             int revolution = 0; | ||||
|             unsigned revTicks = 0; | ||||
|             unsigned totalTicks = 0; | ||||
|             unsigned ticksSinceLastPulse = 0; | ||||
|             uint32_t startOffset = 0; | ||||
|             while (revolution < 5) | ||||
|             { | ||||
|                 unsigned ticks; | ||||
|                 int opcode = fmr.readOpcode(ticks); | ||||
|                 if (ticks) | ||||
|                 { | ||||
|                     ticksSinceLastPulse += ticks; | ||||
|                     totalTicks += ticks; | ||||
|                     revTicks += ticks; | ||||
|                 } | ||||
|  | ||||
|                 switch (opcode) | ||||
|                 { | ||||
|                     case -1: /* end of flux, treat like an index marker */ | ||||
|                     case F_OP_INDEX: | ||||
|                     { | ||||
|                         auto* revheader = &trackheader.revolution[revolution]; | ||||
|                         write_le32(revheader->offset, startOffset + sizeof(ScpTrack)); | ||||
|                         write_le32(revheader->length, (fluxdataWriter.pos - startOffset) / 2); | ||||
|                         write_le32(revheader->index, revTicks * NS_PER_TICK / 25); | ||||
|                         revolution++; | ||||
|                         revheader++; | ||||
|                         revTicks = 0; | ||||
|                         startOffset = fluxdataWriter.pos; | ||||
|                         break; | ||||
|                     } | ||||
|  | ||||
|                     case F_OP_PULSE: | ||||
|                     { | ||||
|                         unsigned t = ticksSinceLastPulse * NS_PER_TICK / 25; | ||||
|                         while (t >= 0x10000) | ||||
|                         { | ||||
|                             fluxdataWriter.write_be16(0); | ||||
|                             t -= 0x10000; | ||||
|                         } | ||||
|                         fluxdataWriter.write_be16(t); | ||||
|                         ticksSinceLastPulse = 0; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             write_le32(fileheader.track[strack], trackdataWriter.pos + sizeof(ScpHeader)); | ||||
|             trackdataWriter += Bytes((uint8_t*)&trackheader, sizeof(trackheader)); | ||||
|             trackdataWriter += fluxdata; | ||||
|  | ||||
|             std::cout << fmt::format("{} ms in {} bytes\n", | ||||
|                 totalTicks * MS_PER_TICK, | ||||
|                 fluxdata.size()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sqlClose(inputDb); | ||||
|      | ||||
|     std::cout << "Writing output file...\n"; | ||||
|     std::ofstream of(filenames[1], std::ios::out | std::ios::binary); | ||||
|     if (!of.is_open()) | ||||
|         Error() << "cannot open output file"; | ||||
|     of.write((const char*) &fileheader, sizeof(fileheader)); | ||||
|     of.write((const char*) trackdata.begin(), trackdata.size()); | ||||
|     of.close(); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -12,11 +12,6 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "aeslanier.img"); | ||||
|  | ||||
| int mainReadAESLanier(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79:s=0"); | ||||
|   | ||||
| @@ -17,13 +17,18 @@ static IntFlag sectorIdBase( | ||||
| 	"Sector ID of the first sector.", | ||||
| 	1); | ||||
|  | ||||
| static BoolFlag ignoreSideByte( | ||||
| 	{ "--ignore-side-byte" }, | ||||
| 	"Ignore the side byte in the sector ID, and use the physical side instead.", | ||||
| 	false); | ||||
|  | ||||
| int mainReadIBM(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79:s=0-1"); | ||||
| 	setReaderDefaultOutput("ibm.img"); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	IbmDecoder decoder(sectorIdBase); | ||||
| 	IbmDecoder decoder(sectorIdBase, ignoreSideByte); | ||||
| 	readDiskCommand(decoder); | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -8,7 +8,6 @@ | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "record.h" | ||||
| #include <fmt/format.h> | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
|   | ||||
| @@ -2,22 +2,32 @@ | ||||
| #include "flags.h" | ||||
| #include "usb.h" | ||||
| #include "dataspec.h" | ||||
| #include "protocol.h" | ||||
|  | ||||
| static FlagGroup flags; | ||||
|  | ||||
| static DataSpecFlag source( | ||||
|     { "--source", "-s" }, | ||||
|     "source for data", | ||||
|     ":d=0"); | ||||
|     ":d=0:t=0:s=0"); | ||||
|  | ||||
| int mainRpm(int argc, const char* argv[]) | ||||
| { | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
|     FluxSpec spec(source); | ||||
|     usbSetDrive(spec.drive, false); | ||||
|     usbSetDrive(spec.drive, false, F_INDEX_REAL); | ||||
|     nanoseconds_t period = usbGetRotationalPeriod(); | ||||
|     std::cout << "Rotational period is " << period/1000 << " ms (" << 60e6/period << " rpm)" << std::endl; | ||||
|     if (period != 0) | ||||
|         std::cout << "Rotational period is " << period/1000000 << " ms (" << 60e9/period << " rpm)" << std::endl; | ||||
|     else | ||||
|     { | ||||
|         std::cout << "No index pulses detected from the disk. Common causes of this are:\n" | ||||
|                      "  - no drive is connected\n" | ||||
|                      "  - the drive doesn't have an index sensor (e.g. BBC Micro drives)\n" | ||||
|                      "  - the disk has no index holes (e.g. reversed flippy disks)\n" | ||||
|                      "  - (most common) no disk is inserted in the drive!\n"; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
							
								
								
									
										142
									
								
								src/fe-scptoflux.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								src/fe-scptoflux.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| #include "globals.h" | ||||
| #include "fluxmap.h" | ||||
| #include "sql.h" | ||||
| #include "bytes.h" | ||||
| #include "protocol.h" | ||||
| #include "fmt/format.h" | ||||
| #include "scp.h" | ||||
| #include <fstream> | ||||
|  | ||||
| static std::ifstream inputFile; | ||||
| static sqlite3* outputDb; | ||||
| static ScpHeader header; | ||||
| static nanoseconds_t resolution; | ||||
| static int startSide; | ||||
| static int endSide; | ||||
|  | ||||
| static void syntax() | ||||
| { | ||||
|     std::cout << "Syntax: fluxengine convert cwftoflux <cwffile> <fluxfile>\n"; | ||||
|     exit(0); | ||||
| } | ||||
|  | ||||
| static void check_for_error() | ||||
| { | ||||
|     if (inputFile.fail()) | ||||
|         Error() << fmt::format("I/O error: {}", strerror(errno)); | ||||
| } | ||||
|  | ||||
| static int trackno(int strack) | ||||
| { | ||||
|     if (startSide == endSide) | ||||
|         return strack; | ||||
|     return strack >> 1; | ||||
| } | ||||
|  | ||||
| static int headno(int strack) | ||||
| { | ||||
|     if (startSide == endSide) | ||||
|         return startSide; | ||||
|     return strack & 1; | ||||
| } | ||||
|  | ||||
| static void read_header() | ||||
| { | ||||
|     inputFile.read((char*) &header, sizeof(header)); | ||||
|     check_for_error(); | ||||
|  | ||||
|     if ((header.file_id[0] != 'S') | ||||
|             || (header.file_id[1] != 'C') | ||||
|             || (header.file_id[2] != 'P')) | ||||
|         Error() << "input not a SCP file"; | ||||
|  | ||||
|     resolution = 25 * (header.resolution + 1); | ||||
|     startSide = (header.heads == 2) ? 1 : 0; | ||||
|     endSide = (header.heads == 1) ? 0 : 1; | ||||
|  | ||||
|     if ((header.cell_width != 0) && (header.cell_width != 16)) | ||||
|         Error() << "currently only 16-bit cells are supported"; | ||||
|  | ||||
|     std::cout << fmt::format("tracks {}-{}, heads {}-{}\n", | ||||
|         trackno(header.start_track), trackno(header.end_track), startSide, endSide); | ||||
|     std::cout << fmt::format("sample resolution: {} ns\n", resolution); | ||||
| } | ||||
|  | ||||
| static void read_track(int strack) | ||||
| { | ||||
|     uint32_t offset = Bytes(header.track[strack], 4).reader().read_le32(); | ||||
|  | ||||
|     ScpTrack trackheader; | ||||
|     inputFile.seekg(offset, std::ios::beg); | ||||
|     inputFile.read((char*) &trackheader, sizeof(trackheader)); | ||||
|     check_for_error(); | ||||
|  | ||||
|     if ((trackheader.track_id[0] != 'T') | ||||
|             || (trackheader.track_id[1] != 'R') | ||||
|             || (trackheader.track_id[2] != 'K')) | ||||
|         Error() << "corrupt SCP file"; | ||||
|  | ||||
|     std::cout << fmt::format("{}.{}: ", trackno(strack), headno(strack)) | ||||
|         << std::flush; | ||||
|  | ||||
|     Fluxmap fluxmap; | ||||
|     nanoseconds_t pending = 0; | ||||
|     unsigned inputBytes = 0; | ||||
|     for (int revolution = 0; revolution < header.revolutions; revolution++) | ||||
|     { | ||||
|         if (revolution != 0) | ||||
|             fluxmap.appendIndex(); | ||||
|  | ||||
|         uint32_t datalength = Bytes(trackheader.revolution[revolution].length, 4).reader().read_le32(); | ||||
|         uint32_t dataoffset = Bytes(trackheader.revolution[revolution].offset, 4).reader().read_le32(); | ||||
|  | ||||
|         Bytes data(datalength*2); | ||||
|         inputFile.seekg(dataoffset + offset, std::ios::beg); | ||||
|         inputFile.read((char*) data.begin(), data.size()); | ||||
|         check_for_error(); | ||||
|  | ||||
|         ByteReader br(data); | ||||
|         for (int cell = 0; cell < datalength; cell++) | ||||
|         { | ||||
|             uint16_t interval = br.read_be16(); | ||||
|             if (interval) | ||||
|             { | ||||
|                 fluxmap.appendInterval((interval + pending) * resolution / NS_PER_TICK); | ||||
|                 fluxmap.appendPulse(); | ||||
|                 pending = 0; | ||||
|             } | ||||
|             else | ||||
|                 pending += 0x10000; | ||||
|         } | ||||
|  | ||||
|         inputBytes += datalength*2; | ||||
|     } | ||||
|  | ||||
|     std::cout << fmt::format(" {} ms in {} input bytes and {} output bytes\n", | ||||
|         fluxmap.duration() / 1e6, inputBytes, fluxmap.bytes()); | ||||
|     sqlWriteFlux(outputDb, trackno(strack), headno(strack), fluxmap); | ||||
| } | ||||
|  | ||||
| int mainConvertScpToFlux(int argc, const char* argv[]) | ||||
| { | ||||
|     if (argc != 3) | ||||
|         syntax(); | ||||
|      | ||||
|     inputFile.open(argv[1], std::ios::in | std::ios::binary); | ||||
|     if (!inputFile.is_open()) | ||||
| 		Error() << fmt::format("cannot open input file '{}'", argv[1]); | ||||
|  | ||||
|     outputDb = sqlOpen(argv[2], SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); | ||||
|     sqlPrepareFlux(outputDb); | ||||
|     sqlWriteIntProperty(outputDb, "version", FLUX_VERSION_CURRENT); | ||||
|     sqlStmt(outputDb, "BEGIN;"); | ||||
|  | ||||
|     read_header(); | ||||
|     inputFile.seekg(sizeof(header), std::ios::beg); | ||||
|     for (unsigned i=header.start_track; i<=header.end_track; i++) | ||||
|         read_track(i); | ||||
|  | ||||
|     sqlStmt(outputDb, "COMMIT;"); | ||||
|     sqlClose(outputDb); | ||||
|     return 0; | ||||
| } | ||||
| @@ -1,6 +1,7 @@ | ||||
| #include "globals.h" | ||||
| #include "flags.h" | ||||
| #include "usb.h" | ||||
| #include "protocol.h" | ||||
|  | ||||
| static FlagGroup flags; | ||||
|  | ||||
| @@ -18,7 +19,7 @@ int mainSeek(int argc, const char* argv[]) | ||||
| { | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
|     usbSetDrive(drive, false); | ||||
|     usbSetDrive(drive, false, F_INDEX_REAL); | ||||
|     usbSeek(track); | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
							
								
								
									
										47
									
								
								src/fe-testvoltages.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/fe-testvoltages.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| #include "globals.h" | ||||
| #include "flags.h" | ||||
| #include "usb.h" | ||||
| #include "protocol.h" | ||||
| #include <fmt/format.h> | ||||
|  | ||||
| static FlagGroup flags; | ||||
|  | ||||
| static std::string display_voltages(struct voltages& v) | ||||
| { | ||||
|     return fmt::format( | ||||
|         "      Logic 1 / 0:  {:.2f}V / {:.2f}V\n", | ||||
|         v.logic0_mv / 1000.0, | ||||
|         v.logic1_mv / 1000.0); | ||||
| } | ||||
|  | ||||
| int mainTestVoltages(int argc, const char* argv[]) | ||||
| { | ||||
|     flags.parseFlags(argc, argv); | ||||
|     struct voltages_frame f; | ||||
|     usbMeasureVoltages(&f); | ||||
|  | ||||
|     std::cout << "Output voltages:\n" | ||||
|               << "  Both drives deselected\n" | ||||
|               << display_voltages(f.output_both_off) | ||||
|               << "  Drive 0 selected\n" | ||||
|               << display_voltages(f.output_drive_0_selected) | ||||
|               << "  Drive 1 selected\n" | ||||
|               << display_voltages(f.output_drive_1_selected) | ||||
|               << "  Drive 0 running\n" | ||||
|               << display_voltages(f.output_drive_0_running) | ||||
|               << "  Drive 1 running\n" | ||||
|               << display_voltages(f.output_drive_1_running) | ||||
|               << "Input voltages:\n" | ||||
|               << "  Both drives deselected\n" | ||||
|               << display_voltages(f.input_both_off) | ||||
|               << "  Drive 0 selected\n" | ||||
|               << display_voltages(f.input_drive_0_selected) | ||||
|               << "  Drive 1 selected\n" | ||||
|               << display_voltages(f.input_drive_1_selected) | ||||
|               << "  Drive 0 running\n" | ||||
|               << display_voltages(f.input_drive_0_running) | ||||
|               << "  Drive 1 running\n" | ||||
|               << display_voltages(f.input_drive_1_running); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										23
									
								
								src/fe-writeamiga.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/fe-writeamiga.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| #include "globals.h" | ||||
| #include "flags.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "amiga/amiga.h" | ||||
| #include "writer.h" | ||||
| #include "fmt/format.h" | ||||
| #include "image.h" | ||||
| #include <fstream> | ||||
|  | ||||
| static FlagGroup flags { &writerFlags, &amigaEncoderFlags }; | ||||
|  | ||||
| int mainWriteAmiga(int argc, const char* argv[]) | ||||
| { | ||||
|     setWriterDefaultInput(":c=80:h=2:s=11:b=512"); | ||||
|     setWriterDefaultDest(":d=0:t=0-79:s=0-1"); | ||||
|     flags.parseFlags(argc, argv); | ||||
|  | ||||
| 	AmigaEncoder encoder; | ||||
| 	writeDiskCommand(encoder); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -5,7 +5,9 @@ typedef int command_cb(int agrc, const char* argv[]); | ||||
| extern command_cb mainErase; | ||||
| extern command_cb mainConvertCwfToFlux; | ||||
| extern command_cb mainConvertFluxToAu; | ||||
| extern command_cb mainConvertFluxToScp; | ||||
| extern command_cb mainConvertFluxToVcd; | ||||
| extern command_cb mainConvertScpToFlux; | ||||
| extern command_cb mainInspect; | ||||
| extern command_cb mainReadADFS; | ||||
| extern command_cb mainReadAESLanier; | ||||
| @@ -25,7 +27,9 @@ extern command_cb mainReadZilogMCZ; | ||||
| extern command_cb mainRpm; | ||||
| extern command_cb mainSeek; | ||||
| extern command_cb mainTestBulkTransport; | ||||
| extern command_cb mainTestVoltages; | ||||
| extern command_cb mainUpgradeFluxFile; | ||||
| extern command_cb mainWriteAmiga; | ||||
| extern command_cb mainWriteBrother; | ||||
| extern command_cb mainWriteFlux; | ||||
| extern command_cb mainWriteTestPattern; | ||||
| @@ -40,6 +44,7 @@ struct Command | ||||
| static command_cb mainRead; | ||||
| static command_cb mainWrite; | ||||
| static command_cb mainConvert; | ||||
| static command_cb mainTest; | ||||
|  | ||||
| static std::vector<Command> commands = | ||||
| { | ||||
| @@ -49,7 +54,7 @@ static std::vector<Command> commands = | ||||
|     { "read",              mainRead,              "Reads a disk, producing a sector image.", }, | ||||
|     { "rpm",               mainRpm,               "Measures the disk rotational speed.", }, | ||||
|     { "seek",              mainSeek,              "Moves the disk head.", }, | ||||
|     { "testbulktransport", mainTestBulkTransport, "Measures your USB bandwidth.", }, | ||||
|     { "test",              mainTest,              "Various testing commands.", }, | ||||
|     { "upgradefluxfile",   mainUpgradeFluxFile,   "Upgrades a flux file from a previous version of this software.", }, | ||||
|     { "write",             mainWrite,             "Writes a sector image to a disk.", }, | ||||
|     { "writeflux",         mainWriteFlux,         "Writes a raw flux file. Warning: you can't use this to copy disks.", }, | ||||
| @@ -77,16 +82,25 @@ static std::vector<Command> readables = | ||||
|  | ||||
| static std::vector<Command> writeables = | ||||
| { | ||||
|     { "amiga",         mainWriteAmiga,    "Writes Amiga disks.", }, | ||||
|     { "brother",       mainWriteBrother,  "Writes 120kB and 240kB Brother word processor disks.", }, | ||||
| }; | ||||
|  | ||||
| static std::vector<Command> convertables = | ||||
| { | ||||
|     { "cwftoflux",     mainConvertCwfToFlux, "Converts CatWeasel stream files to flux.", }, | ||||
|     { "scptoflux",     mainConvertScpToFlux, "Converts Supercard Pro stream files to flux.", }, | ||||
|     { "fluxtoau",      mainConvertFluxToAu,  "Converts (one track of a) flux file to an .au audio file.", }, | ||||
|     { "fluxtoscp",     mainConvertFluxToScp, "Converrt a flux file to a Supercard Pro file.", }, | ||||
|     { "fluxtovcd",     mainConvertFluxToVcd, "Converts (one track of a) flux file to a VCD file.", }, | ||||
| }; | ||||
|  | ||||
| static std::vector<Command> testables = | ||||
| { | ||||
|     { "bulktransport", mainTestBulkTransport, "Measures your USB bandwidth.", }, | ||||
|     { "voltages",      mainTestVoltages,      "Measures the FDD bus voltages.", }, | ||||
| }; | ||||
|  | ||||
| static void extendedHelp(std::vector<Command>& subcommands, const std::string& command) | ||||
| { | ||||
|     std::cout << "fluxengine: syntax: fluxengine " << command << " <format> [<flags>...]\n" | ||||
| @@ -127,6 +141,9 @@ static int mainWrite(int argc, const char* argv[]) | ||||
| static int mainConvert(int argc, const char* argv[]) | ||||
| { return mainExtended(convertables, "convert", argc, argv); } | ||||
|  | ||||
| static int mainTest(int argc, const char* argv[]) | ||||
| { return mainExtended(testables, "test", argc, argv); } | ||||
|  | ||||
| static void help() | ||||
| { | ||||
|     std::cout << "fluxengine: syntax: fluxengine <command> [<flags>...]\n" | ||||
|   | ||||
							
								
								
									
										43
									
								
								src/scp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/scp.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| #ifndef SCP_H | ||||
| #define SCP_H | ||||
|  | ||||
| struct ScpHeader | ||||
| { | ||||
|     char file_id[3];       // file ID - 'SCP' | ||||
|     uint8_t version;       // major/minor in nibbles | ||||
|     uint8_t type;          // disk type - subclass/class in nibbles | ||||
|     uint8_t revolutions;   // up to 5 | ||||
|     uint8_t start_track;   // 0..165 | ||||
|     uint8_t end_track;     // 0..165 | ||||
|     uint8_t flags;         // see below | ||||
|     uint8_t cell_width;    // in bits, 0 meaning 16 | ||||
|     uint8_t heads;         // 0 = both, 1 = side 0 only, 2 = side 1 only | ||||
|     uint8_t resolution;    // 25ns * (resolution+1) | ||||
|     uint8_t checksum[4];   // of data after this point | ||||
|     uint8_t track[165][4]; // track offsets, not necessarily 165 | ||||
| }; | ||||
|  | ||||
| enum | ||||
| { | ||||
|     SCP_FLAG_INDEXED    = (1<<0), | ||||
|     SCP_FLAG_96TPI      = (1<<1), | ||||
|     SCP_FLAG_360RPM     = (1<<2), | ||||
|     SCP_FLAG_NORMALIZED = (1<<3), | ||||
|     SCP_FLAG_READWRITE  = (1<<4), | ||||
|     SCP_FLAG_FOOTER     = (1<<5) | ||||
| }; | ||||
|  | ||||
| struct ScpTrack | ||||
| { | ||||
|     char track_id[3];      // 'TRK' | ||||
|     uint8_t strack;        // SCP track number | ||||
|     struct | ||||
|     { | ||||
|         uint8_t index[4];  // time for one revolution | ||||
|         uint8_t length[4]; // number of bitcells | ||||
|         uint8_t offset[4]; // offset to bitcell data, relative to track header | ||||
|     } | ||||
|     revolution[5]; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										38
									
								
								tests/amiga.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								tests/amiga.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| #include "globals.h" | ||||
| #include "bytes.h" | ||||
| #include "record.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "arch/amiga/amiga.h" | ||||
| #include <assert.h> | ||||
|  | ||||
| static const Bytes testData = { | ||||
|     0x52, /* 0101 0010 */ | ||||
|     0xff, /* 1111 1111 */ | ||||
|     0x4a, /* 0100 1010 */ | ||||
|     0x22, /* 0010 0010 */ | ||||
| }; | ||||
| static const Bytes testDataInterleaved = { | ||||
|     0x1f, /* 0001 1111 */ | ||||
|     0x35, /* 0011 0101 */ | ||||
|     0xcf, /* 1100 1111 */ | ||||
|     0x80, /* 1000 0000 */ | ||||
| }; | ||||
|  | ||||
| static void testInterleave(void) | ||||
| { | ||||
|     Bytes interleaved = amigaInterleave(testData); | ||||
|     assert(interleaved == testDataInterleaved); | ||||
| } | ||||
|  | ||||
| static void testDeinterleave(void) | ||||
| { | ||||
|     Bytes deinterleaved = amigaDeinterleave(testDataInterleaved); | ||||
|     assert(deinterleaved == testData); | ||||
| } | ||||
|  | ||||
| int main(int argc, const char* argv[]) | ||||
| { | ||||
|     testDeinterleave(); | ||||
|     testInterleave(); | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										56
									
								
								tools/brother240tool.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								tools/brother240tool.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| #include "globals.h" | ||||
| #include "fmt/format.h" | ||||
| #include <fstream> | ||||
|  | ||||
| static std::fstream inputFile; | ||||
|  | ||||
| void syntax() | ||||
| { | ||||
|     std::cout << "Syntax: brother240tool <image>\n" | ||||
|                  "The disk image will be flipped from Brother to DOS format and back\n" | ||||
|                  "again.\n"; | ||||
|     exit(0); | ||||
| } | ||||
|  | ||||
| uint8_t getbyte(uint32_t offset) | ||||
| { | ||||
|     inputFile.seekg(offset, std::ifstream::beg); | ||||
|     return inputFile.get(); | ||||
| } | ||||
|  | ||||
| void putbyte(uint32_t offset, uint8_t value) | ||||
| { | ||||
|     inputFile.seekp(offset, std::ifstream::beg); | ||||
|     inputFile.put(value); | ||||
| } | ||||
|  | ||||
| int main(int argc, const char* argv[]) | ||||
| { | ||||
|     if (argc < 2) | ||||
|         syntax(); | ||||
|      | ||||
|     inputFile.open(argv[1], std::ios::in | std::ios::out | std::ios::binary); | ||||
|     if (!inputFile.is_open()) | ||||
| 		Error() << fmt::format("cannot open input file '{}'", argv[1]); | ||||
|  | ||||
|     uint8_t b1 = getbyte(0x015); | ||||
|     uint8_t b2 = getbyte(0x100); | ||||
|     if ((b1 == 0x58) && (b2 == 0x58)) | ||||
|     { | ||||
|         std::cerr << "Flipping from Brother to DOS.\n"; | ||||
|         putbyte(0x015, 0xf0); | ||||
|         putbyte(0x100, 0xf0); | ||||
|     } | ||||
|     else if ((b1 == 0xf0) && (b2 == 0xf0)) | ||||
|     { | ||||
|         std::cerr << "Flipping from DOS to Brother.\n"; | ||||
|         putbyte(0x015, 0x58); | ||||
|         putbyte(0x100, 0x58); | ||||
|     } | ||||
|     else | ||||
|         Error() << "Unknown image format."; | ||||
|  | ||||
|     inputFile.close(); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user