mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	Compare commits
	
		
			169 Commits
		
	
	
		
			FluxEngine
			...
			FluxEngine
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | dce0a26820 | ||
|  | 14e0a67e7d | ||
|  | 1656947764 | ||
|  | 647862cdbd | ||
|  | 4a8d83838c | ||
|  | 8acf8e181d | ||
|  | 2df9920209 | ||
|  | 1a6c6b5420 | ||
|  | edc56d44d6 | ||
|  | ef4eff0195 | ||
|  | df8d45bf66 | ||
|  | 89a27619ff | ||
|  | 387a86969a | ||
|  | acb5059d17 | ||
|  | a4002d2617 | ||
|  | a63a90bbd0 | ||
|  | d25f96dd24 | ||
|  | e8febe6508 | ||
|  | ad3a930c6a | ||
|  | be41c1de76 | ||
|  | 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 | ||
|  | d6ae373fa8 | ||
|  | a626d5f9a0 | ||
|  | 29db67528d | ||
|  | 31d7477c6a | ||
|  | 56af9eaf18 | ||
|  | 5de0636fe7 | ||
|  | f9117b8d11 | ||
|  | 10d385375f | ||
|  | 2f72c3f8f0 | ||
|  | 54edff9b94 | ||
|  | 112377f885 | ||
|  | 87e29fc386 | ||
|  | b1db5c48b1 | ||
|  | 38fab7edcb | ||
|  | d8172154c3 | ||
|  | eb924780ab | ||
|  | 28e0ef0463 | ||
|  | 4b07c38782 | 
| @@ -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 | ||||
|  | ||||
| @@ -3,6 +3,10 @@ streams | ||||
| .*\.flux | ||||
| .*\.img | ||||
| .*\.raw | ||||
| .*\.orig | ||||
| .vscode | ||||
| remote | ||||
| FluxEngine.cydsn/CortexM3 | ||||
| FluxEngine.cydsn/Generated_Source | ||||
| FluxEngine.cydsn/codegentemp | ||||
|  | ||||
|   | ||||
							
								
								
									
										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.
										
									
								
							
							
								
								
									
										169
									
								
								FluxEngine.cydsn/FIFOout/FIFOout.v
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								FluxEngine.cydsn/FIFOout/FIFOout.v
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,169 @@ | ||||
|  | ||||
| //`#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_WAITFORREQ = 0; | ||||
| localparam STATE_READFROMFIFO = 1; | ||||
| localparam STATE_WAITFORNREQ = 2; | ||||
|  | ||||
| reg [1:0] state; | ||||
| wire readfromfifo; | ||||
|  | ||||
| assign ack = (state == STATE_WAITFORNREQ); | ||||
| assign readfromfifo = (state == STATE_READFROMFIFO); | ||||
|  | ||||
| always @(posedge clk) | ||||
| begin | ||||
|     case (state) | ||||
|         /* opcode is not valid; req is low; wait for req to go high. */ | ||||
|         STATE_WAITFORREQ: | ||||
|         begin | ||||
|             if (!empty && req) | ||||
|                 state <= STATE_READFROMFIFO; | ||||
|         end | ||||
|          | ||||
|         /* Fetch a single value from the FIFO. */ | ||||
|         STATE_READFROMFIFO: | ||||
|             state <= STATE_WAITFORNREQ; | ||||
|              | ||||
|         /* opcode is valid; ack is high. Wait for req to go low. */ | ||||
|         STATE_WAITFORNREQ: | ||||
|             if (!req) | ||||
|                 state <= STATE_WAITFORREQ; | ||||
|     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: idle */ | ||||
|     `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: read from fifo */ | ||||
|     `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, readfromfifo}), | ||||
|         /*  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> | ||||
| @@ -628,6 +851,7 @@ | ||||
|       <Data key="4a398466-709f-4228-9500-96178658e13e" value="RDATA" /> | ||||
|       <Data key="5a3407c1-b434-4438-a7b4-b9dfd2280495" value="MOTEA" /> | ||||
|       <Data key="8d318d8b-cf7b-4b6b-b02c-ab1c5c49d0ba" value="SW1" /> | ||||
|       <Data key="8fc20a4f-e4d1-44b3-a5d4-546e8628d61e" value="LED" /> | ||||
|       <Data key="12e00eac-69b5-4717-85c8-25ef6b224d4c" value="DEBUG_PINS" /> | ||||
|       <Data key="41e2d8ed-5494-4d8c-8ff7-f4f789cece51" value="REDWC" /> | ||||
|       <Data key="264be2d3-9481-494b-8d9c-c1905a45e9cc" value="FDD" /> | ||||
| @@ -638,16 +862,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" /> | ||||
| @@ -3752,6 +3978,11 @@ | ||||
|         <Data key="Port Format" value="2,2" /> | ||||
|       </Group> | ||||
|     </Group> | ||||
|     <Group key="8fc20a4f-e4d1-44b3-a5d4-546e8628d61e"> | ||||
|       <Group key="0"> | ||||
|         <Data key="Port Format" value="2,1" /> | ||||
|       </Group> | ||||
|     </Group> | ||||
|     <Group key="12e00eac-69b5-4717-85c8-25ef6b224d4c"> | ||||
|       <Group key="0"> | ||||
|         <Data key="Port Format" value="2,2" /> | ||||
| @@ -3859,6 +4090,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 +4151,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 +4169,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.
										
									
								
							
							
								
								
									
										111
									
								
								FluxEngine.cydsn/Sampler/Sampler.v
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								FluxEngine.cydsn/Sampler/Sampler.v
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
|  | ||||
| //`#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_OPCODE = 2; | ||||
|  | ||||
| reg [1:0] state; | ||||
| reg [6:0] 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_OPCODE); | ||||
|  | ||||
| always @(posedge clock) | ||||
| begin | ||||
|     if (reset) | ||||
|     begin | ||||
|         state <= STATE_RESET; | ||||
|         opcode <= 0; | ||||
|         oldsampleclock <= 0; | ||||
|         oldindex <= 0; | ||||
|         oldrdata <= 0; | ||||
|         counter <= 0; | ||||
|     end | ||||
|     else | ||||
|         case (state) | ||||
|             STATE_RESET: | ||||
|                 state <= STATE_WAITING; | ||||
|              | ||||
|             STATE_WAITING: | ||||
|             begin | ||||
|                 /* If something has happened, emit any necessary interval byte. */ | ||||
|                 if ((rdataed || indexed) && (counter != 0)) | ||||
|                 begin | ||||
|                     opcode <= {0, counter}; | ||||
|                     state <= STATE_OPCODE; | ||||
|                 end | ||||
|                 else if (indexed) | ||||
|                 begin | ||||
|                     oldindex <= 1; | ||||
|                     opcode <= 8'h81; | ||||
|                     state <= STATE_OPCODE; | ||||
|                 end | ||||
|                 else if (rdataed) | ||||
|                 begin | ||||
|                     oldrdata <= 1; | ||||
|                     opcode <= 8'h80; | ||||
|                     state <= STATE_OPCODE; | ||||
|                 end | ||||
|                 else if (sampleclocked) | ||||
|                 begin | ||||
|                     oldsampleclock <= 1; | ||||
|                     if (counter == 7'h7f) | ||||
|                     begin | ||||
|                         opcode <= {0, counter}; | ||||
|                         state <= STATE_OPCODE; | ||||
|                     end | ||||
|                     counter <= counter + 1; | ||||
|                 end | ||||
|                  | ||||
|                 /* Reset state once we've done the thing. */ | ||||
|                  | ||||
|                 if (oldrdata && !rdata) | ||||
|                     oldrdata <= 0; | ||||
|                 if (oldindex && !index) | ||||
|                     oldindex <= 0; | ||||
|                 if (oldsampleclock && !sampleclock) | ||||
|                     oldsampleclock <= 0; | ||||
|             end | ||||
|              | ||||
|             STATE_OPCODE: /* opcode or interval byte sent here */ | ||||
|             begin | ||||
|                 state <= STATE_WAITING; | ||||
|                 counter <= 0; | ||||
|             end | ||||
|         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.
										
									
								
							
							
								
								
									
										105
									
								
								FluxEngine.cydsn/Sequencer/Sequencer.v
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								FluxEngine.cydsn/Sequencer/Sequencer.v
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
|  | ||||
| //`#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_LOAD = 0; | ||||
| localparam STATE_WAITING = 1; | ||||
| localparam STATE_PULSING = 2; | ||||
| localparam STATE_INDEXING = 3; | ||||
|  | ||||
| localparam OPCODE_PULSE = 8'h80; | ||||
| localparam OPCODE_INDEX = 8'h81; | ||||
|  | ||||
| reg [1:0] state; | ||||
| reg [6:0] countdown; | ||||
|  | ||||
| assign req = (!reset && (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; | ||||
| reg sampleclocked; | ||||
|  | ||||
| reg oldindex; | ||||
| wire indexed; | ||||
| always @(posedge clock) oldindex <= index; | ||||
| assign indexed = !oldindex && index; | ||||
|  | ||||
| always @(posedge clock) | ||||
| begin | ||||
|     if (reset) | ||||
|     begin | ||||
|         state <= STATE_LOAD; | ||||
|         countdown <= 0; | ||||
|     end | ||||
|     else | ||||
|     begin | ||||
|         if (!oldsampleclock && sampleclock) | ||||
|             sampleclocked <= 1; | ||||
|         oldsampleclock <= sampleclock; | ||||
|          | ||||
|         case (state) | ||||
|             STATE_LOAD: | ||||
|                 /* Wait for a posedge on dataclocked, indicating an opcode has | ||||
|                  * arrived. */ | ||||
|                 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 | ||||
|                     sampleclocked <= 0; | ||||
|                     countdown <= countdown - 1; | ||||
|                     /* Nasty fudge factor here to account for one to two | ||||
|                      * sample ticks lost per pulse. */ | ||||
|                     if (countdown <= 2) | ||||
|                         state <= STATE_LOAD; | ||||
|                 end | ||||
|              | ||||
|             STATE_PULSING: | ||||
|                 state <= STATE_LOAD; | ||||
|              | ||||
|             STATE_INDEXING: | ||||
|                 if (indexed) | ||||
|                     state <= STATE_LOAD; | ||||
|         endcase | ||||
|     end | ||||
| 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,20 +17,21 @@ | ||||
| #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_COUNT 64 /* the maximum */ | ||||
| #define BUFFER_SIZE 64 | ||||
| static uint8_t td[BUFFER_COUNT]; | ||||
| static uint8_t dma_buffer[BUFFER_COUNT][BUFFER_SIZE] __attribute__((aligned())); | ||||
| static uint8_t usb_buffer[BUFFER_SIZE] __attribute__((aligned())); | ||||
| static uint8_t xfer_buffer[BUFFER_SIZE] __attribute__((aligned())); | ||||
| static uint8_t dma_channel; | ||||
| #define NEXT_BUFFER(b) (((b)+1) % BUFFER_COUNT) | ||||
|  | ||||
| @@ -45,6 +46,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 +101,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 +127,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 +176,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 +216,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,26 +253,40 @@ 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);     | ||||
| } | ||||
|  | ||||
| static void cmd_bulk_test(struct any_frame* f) | ||||
| static void cmd_bulk_write_test(struct any_frame* f) | ||||
| { | ||||
|     uint8_t buffer[64]; | ||||
|      | ||||
|     wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM); | ||||
|     for (int x=0; x<64; x++) | ||||
|     { | ||||
|         CyWdtClear(); | ||||
|         for (int y=0; y<256; y++) | ||||
|         { | ||||
|             for (unsigned z=0; z<sizeof(buffer); z++) | ||||
| @@ -236,11 +295,50 @@ static void cmd_bulk_test(struct any_frame* f) | ||||
|             wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM); | ||||
|             USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, buffer, sizeof(buffer)); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_TEST_REPLY); | ||||
|     DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_WRITE_TEST_REPLY); | ||||
|     send_reply(&r); | ||||
| } | ||||
|  | ||||
| static void cmd_bulk_read_test(struct any_frame* f) | ||||
| { | ||||
|     uint8_t buffer[64]; | ||||
|      | ||||
|     bool passed = true; | ||||
|     for (int x=0; x<64; x++) | ||||
|     { | ||||
|         CyWdtClear(); | ||||
|         for (int y=0; y<256; y++) | ||||
|         { | ||||
|             USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM); | ||||
|             while (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) != USBFS_OUT_BUFFER_FULL) | ||||
|                 ; | ||||
|             USBFS_ReadOutEP(FLUXENGINE_DATA_OUT_EP_NUM, buffer, sizeof(buffer)); | ||||
|             while (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) != USBFS_OUT_BUFFER_EMPTY) | ||||
|                 ; | ||||
|              | ||||
|             for (unsigned z=0; z<sizeof(buffer); z++) | ||||
|             { | ||||
|                 if (buffer[z] != (uint8)(x+y+z)) | ||||
|                 { | ||||
|                     print("fail %d+%d+%d == %d, not %d", x, y, z, buffer[z], (uint8)(x+y+z)); | ||||
|                     passed = false; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     print("passed=%d", passed); | ||||
|     if (passed) | ||||
|     { | ||||
|         DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_READ_TEST_REPLY); | ||||
|         send_reply(&r); | ||||
|     } | ||||
|     else | ||||
|         send_error(F_ERROR_INVALID_VALUE); | ||||
| } | ||||
|  | ||||
| static void deinit_dma(void) | ||||
| { | ||||
|     for (int i=0; i<BUFFER_COUNT; i++) | ||||
| @@ -249,7 +347,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 +362,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,37 +374,35 @@ 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_NORMAL; | ||||
|         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; | ||||
|     cs.outputptr = xfer_buffer; | ||||
|     cs.outputlen = BUFFER_SIZE; | ||||
|      | ||||
|     dma_writing_to_td = 0; | ||||
|     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,74 +410,100 @@ 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; | ||||
|     bool dma_running = true; | ||||
|      | ||||
|     /* Start transferring. */ | ||||
|  | ||||
|     int revolutions = f->revolutions; | ||||
|     while (!dma_underrun) | ||||
|     uint32_t start_time = clock; | ||||
|     for (;;) | ||||
|     { | ||||
|         CyWdtClear(); | ||||
|  | ||||
|         /* Have we reached the index pulse? */ | ||||
|         if (index_irq) | ||||
|         { | ||||
|             index_irq = false; | ||||
|             revolutions--; | ||||
|             if (revolutions == 0) | ||||
|                 break; | ||||
|         } | ||||
|         /* If the sample session is over, stop reading but continue processing until | ||||
|          * the DMA chain is empty. */ | ||||
|          | ||||
|         /* Wait for the next block to be read. */ | ||||
|         while (dma_reading_from_td == dma_writing_to_td) | ||||
|         if ((clock - start_time) >= f->milliseconds) | ||||
|         { | ||||
|             /* On an underrun, give up immediately. */ | ||||
|             if (dma_underrun) | ||||
|                 goto abort; | ||||
|         } | ||||
|  | ||||
|         uint8_t dma_buffer_usage = 0; | ||||
|         while (dma_buffer_usage < BUFFER_SIZE) | ||||
|         { | ||||
|             cs.inputptr = dma_buffer[dma_reading_from_td] + dma_buffer_usage; | ||||
|             cs.inputlen = BUFFER_SIZE - dma_buffer_usage; | ||||
|             crunch(&cs); | ||||
|             dma_buffer_usage += BUFFER_SIZE - cs.inputlen; | ||||
|             count++; | ||||
|             if (cs.outputlen == 0) | ||||
|             if (dma_running) | ||||
|             { | ||||
|                 while (USBFS_GetEPState(FLUXENGINE_DATA_IN_EP_NUM) != USBFS_IN_BUFFER_EMPTY) | ||||
|                 { | ||||
|                     if (index_irq || dma_underrun) | ||||
|                         goto abort; | ||||
|                 } | ||||
|  | ||||
|                 USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE); | ||||
|                 cs.outputptr = usb_buffer; | ||||
|                 cs.outputlen = BUFFER_SIZE; | ||||
|                 CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN); | ||||
|                 while (CyDmaChGetRequest(dma_channel)) | ||||
|                     ; | ||||
|                 dma_running = false; | ||||
|                 dma_underrun = false; | ||||
|             } | ||||
|         } | ||||
|         dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td); | ||||
|          | ||||
|         /* If there's an underrun event, stop immediately. */ | ||||
|          | ||||
|         if (dma_underrun) | ||||
|             goto abort; | ||||
|          | ||||
|         /* If there are no more blocks to be read, check to see if we've finished. */ | ||||
|          | ||||
|         if (dma_reading_from_td == dma_writing_to_td) | ||||
|         { | ||||
|             /* Also if we've run out of blocks to send. */ | ||||
|              | ||||
|             if (!dma_running) | ||||
|                 goto abort; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             /* Otherwise, there's a block waiting, so attempt to send it. */ | ||||
|              | ||||
|             uint8_t dma_buffer_usage = 0; | ||||
|             while (dma_buffer_usage < BUFFER_SIZE) | ||||
|             { | ||||
|                 cs.inputptr = dma_buffer[dma_reading_from_td] + dma_buffer_usage; | ||||
|                 cs.inputlen = BUFFER_SIZE - dma_buffer_usage; | ||||
|                 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) | ||||
|                 { | ||||
|                     wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM); | ||||
|                     memcpy(usb_buffer, xfer_buffer, FRAME_SIZE); | ||||
|                     USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE); | ||||
|                      | ||||
|                     cs.outputptr = xfer_buffer; | ||||
|                     cs.outputlen = BUFFER_SIZE; | ||||
|                 } | ||||
|             } | ||||
|             dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td); | ||||
|         } | ||||
|     } | ||||
| abort:; | ||||
|     CAPTURE_CONTROL_Write(0); | ||||
|     CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN); | ||||
|     while (CyDmaChGetRequest(dma_channel)) | ||||
|         ; | ||||
|     bool saved_dma_underrun = dma_underrun; | ||||
|  | ||||
|     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 +513,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 +534,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_MID; | ||||
|         REPLAY_FIFO_CLEAR; | ||||
|         REPLAY_FIFO_SINGLE_BUFFER_UNSET; | ||||
|         CyExitCriticalSection(i); | ||||
|     } | ||||
|     seek_to(current_track);     | ||||
| @@ -441,6 +566,7 @@ static void cmd_write(struct write_frame* f) | ||||
|     int packets = f->bytes_to_write / FRAME_SIZE; | ||||
|     int count_written = 0; | ||||
|     int count_read = 0; | ||||
|     int packetwaiting = 0; | ||||
|     dma_writing_to_td = 0; | ||||
|     dma_reading_from_td = -1; | ||||
|     dma_underrun = false; | ||||
| @@ -452,6 +578,22 @@ static void cmd_write(struct write_frame* f) | ||||
|     int old_reading_from_td = -1; | ||||
|     for (;;) | ||||
|     { | ||||
|         CyWdtClear(); | ||||
|  | ||||
|         /* Make sure that we always have a USB read in progress whenever possible. */ | ||||
|          | ||||
|         if (!finished && !packetwaiting) | ||||
|         { | ||||
|             /* There is no read in progress; has data arrived in the external USB buffer? */ | ||||
|              | ||||
|             if (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL) | ||||
|             { | ||||
|                 /* Yes, data has arrived, so initiate the copy. */ | ||||
|                  | ||||
|                 packetwaiting = USBFS_ReadOutEP(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer, FRAME_SIZE); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /* Read data from USB into the buffers. */ | ||||
|          | ||||
|         if (NEXT_BUFFER(dma_writing_to_td) != dma_reading_from_td) | ||||
| @@ -468,26 +610,33 @@ static void cmd_write(struct write_frame* f) | ||||
|                     /* There's no more data to read, so fake some. */ | ||||
|                      | ||||
|                     for (int i=0; i<BUFFER_SIZE; i++) | ||||
|                         usb_buffer[i+0] = 0x7f; | ||||
|                     cs.inputptr = usb_buffer; | ||||
|                         xfer_buffer[i+0] = 0x7f; | ||||
|                     cs.inputptr = xfer_buffer; | ||||
|                     cs.inputlen = BUFFER_SIZE; | ||||
|                 } | ||||
|                 else | ||||
|                 else if (packetwaiting) | ||||
|                 { | ||||
|                     while (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) != USBFS_OUT_BUFFER_FULL) | ||||
|                     /* There's a USB read into usb_buffer in progress, so check if it's finished. */ | ||||
|                      | ||||
|                     if (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) == USBFS_OUT_BUFFER_EMPTY) | ||||
|                     { | ||||
|                         if (writing && (dma_underrun || index_irq)) | ||||
|                             goto abort; | ||||
|                         /* It's done, so copy out the data. */ | ||||
|                          | ||||
|                         memcpy(xfer_buffer, usb_buffer, FRAME_SIZE); | ||||
|                         cs.inputptr = xfer_buffer; | ||||
|                         cs.inputlen = packetwaiting; | ||||
|  | ||||
|                         count_read++; | ||||
|                         if ((packetwaiting < FRAME_SIZE) || (count_read == packets)) | ||||
|                             finished = true; | ||||
|                         else | ||||
|                         { | ||||
|                             /* Wait for more USB data to show up. */ | ||||
|                              | ||||
|                             packetwaiting = 0; | ||||
|                             USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     int length = usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer); | ||||
|                     cs.inputptr = usb_buffer; | ||||
|                     cs.inputlen = length; | ||||
|                     USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM); | ||||
|  | ||||
|                     count_read++; | ||||
|                     if ((length < FRAME_SIZE) || (count_read == packets)) | ||||
|                         finished = true; | ||||
|                 } | ||||
|             } | ||||
|              | ||||
| @@ -506,7 +655,8 @@ static void cmd_write(struct write_frame* f) | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             /* If we have a full buffer, start writing. */ | ||||
|             /* Once all the buffers are full, start writing. */ | ||||
|              | ||||
|             if ((dma_reading_from_td == -1) && (dma_writing_to_td == BUFFER_COUNT-1)) | ||||
|             { | ||||
|                 dma_reading_from_td = old_reading_from_td = 0; | ||||
| @@ -521,7 +671,7 @@ static void cmd_write(struct write_frame* f) | ||||
|  | ||||
|                 /* Wait for the index marker. While this happens, the DMA engine | ||||
|                  * will prime the FIFO. */ | ||||
|                  | ||||
|  | ||||
|                 index_irq = false; | ||||
|                 while (!index_irq) | ||||
|                     ; | ||||
| @@ -532,7 +682,7 @@ static void cmd_write(struct write_frame* f) | ||||
|                 SEQUENCER_CONTROL_Write(0); /* start writing! */ | ||||
|             } | ||||
|         } | ||||
|          | ||||
|  | ||||
|         if (writing && (dma_underrun || index_irq)) | ||||
|             goto abort; | ||||
|  | ||||
| @@ -543,6 +693,7 @@ static void cmd_write(struct write_frame* f) | ||||
|         } | ||||
|     } | ||||
| abort: | ||||
|     print("done %d %d", dma_reading_from_td, dma_writing_to_td); | ||||
|     SEQUENCER_DMA_FINISHED_IRQ_Disable(); | ||||
|  | ||||
|     SEQUENCER_CONTROL_Write(1); /* reset */ | ||||
| @@ -555,10 +706,12 @@ 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) | ||||
|         /* There's still some data to read, so just read and blackhole it --- | ||||
|          * easier than trying to terminate the connection. */ | ||||
|         while (count_read != packets) | ||||
|         { | ||||
|             if (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL) | ||||
|             { | ||||
| @@ -573,6 +726,7 @@ abort: | ||||
|     } | ||||
|      | ||||
|     deinit_dma(); | ||||
|     print("write finished"); | ||||
|      | ||||
|     if (dma_underrun) | ||||
|     { | ||||
| @@ -607,17 +761,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]; | ||||
| @@ -639,8 +877,12 @@ static void handle_command(void) | ||||
|             cmd_measure_speed(f); | ||||
|             break; | ||||
|              | ||||
|         case F_FRAME_BULK_TEST_CMD: | ||||
|             cmd_bulk_test(f); | ||||
|         case F_FRAME_BULK_WRITE_TEST_CMD: | ||||
|             cmd_bulk_write_test(f); | ||||
|             break; | ||||
|              | ||||
|         case F_FRAME_BULK_READ_TEST_CMD: | ||||
|             cmd_bulk_read_test(f); | ||||
|             break; | ||||
|              | ||||
|         case F_FRAME_READ_CMD: | ||||
| @@ -662,6 +904,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 +920,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 +940,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 +954,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"); | ||||
|   | ||||
							
								
								
									
										20
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								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 | ||||
| @@ -22,7 +34,7 @@ export LIBS = $(shell pkg-config --libs $(PACKAGES)) | ||||
| export EXTENSION = | ||||
| endif | ||||
|  | ||||
| CFLAGS += -Ilib -Idep/fmt | ||||
| CFLAGS += -Ilib -Idep/fmt -Iarch | ||||
|  | ||||
| export OBJDIR = .obj | ||||
|  | ||||
|   | ||||
							
								
								
									
										16
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								README.md
									
									
									
									
									
								
							| @@ -79,20 +79,24 @@ people who've had it work). | ||||
|  | ||||
| | Format                                   | Read? | Write? | Notes | | ||||
| |:-----------------------------------------|:-----:|:------:|-------| | ||||
| | IBM PC compatible                        |  🦄   |        | 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)  |  🦖   |        |                                     | | ||||
| | [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)  |  🦖   |   🦖*   |                                     | | ||||
| | [Apple II DOS 3.3](doc/disk-apple2.md)   |  🦄   |        | doesn't do logical sector remapping | | ||||
| | [Amiga](doc/disk-amiga.md)               |  🦄   |        |                                     | | ||||
| | [Commodore 64 1541](doc/disk-c64.md)     |  🦖   |        | and probably the other GCR formats  | | ||||
| | [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          | | ||||
| | [TRS-80](doc/disk-trs80.md)              |  🦖   |        | a minor variation of the IBM scheme | | ||||
| | [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 } | ||||
|  | ||||
| `*`: these formats are variations of the generic IBM format, and since the | ||||
| IBM writer is completely generic, it should be configurable for these | ||||
| formats... theoretically. I don't have the hardware to try it. | ||||
|  | ||||
| ### Even older disk formats | ||||
|  | ||||
| These formats are for particularly old, weird architectures, even by the | ||||
|   | ||||
							
								
								
									
										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()); | ||||
| } | ||||
							
								
								
									
										41
									
								
								arch/amiga/amiga.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								arch/amiga/amiga.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| #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 | ||||
| { | ||||
| public: | ||||
|     virtual ~AmigaDecoder() {} | ||||
|  | ||||
|     RecordType advanceToNextRecord(); | ||||
|     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 | ||||
							
								
								
									
										58
									
								
								arch/amiga/decoder.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								arch/amiga/decoder.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| #include "globals.h" | ||||
| #include "fluxmap.h" | ||||
| #include "decoders/fluxmapreader.h" | ||||
| #include "protocol.h" | ||||
| #include "record.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "sector.h" | ||||
| #include "amiga.h" | ||||
| #include "bytes.h" | ||||
| #include "fmt/format.h" | ||||
| #include <string.h> | ||||
| #include <algorithm> | ||||
|  | ||||
| /*  | ||||
|  * Amiga disks use MFM but it's not quite the same as IBM MFM. They only use | ||||
|  * a single type of record with a different marker byte. | ||||
|  *  | ||||
|  * See the big comment in the IBM MFM decoder for the gruesome details of how | ||||
|  * MFM works. | ||||
|  */ | ||||
|           | ||||
| static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD); | ||||
|  | ||||
| AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord() | ||||
| { | ||||
|     _sector->clock = _fmr->seekToPattern(SECTOR_PATTERN); | ||||
|     if (_fmr->eof() || !_sector->clock) | ||||
|         return UNKNOWN_RECORD; | ||||
|     return SECTOR_RECORD; | ||||
| } | ||||
|  | ||||
| void AmigaDecoder::decodeSectorRecord() | ||||
| { | ||||
|     const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16); | ||||
|     const auto& rawbytes = toBytes(rawbits).slice(0, AMIGA_RECORD_SIZE*2); | ||||
|     const auto& bytes = decodeFmMfm(rawbits).slice(0, AMIGA_RECORD_SIZE); | ||||
|  | ||||
|     const uint8_t* ptr = bytes.begin() + 3; | ||||
|  | ||||
|     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 = amigaDeinterleave(ptr, 4).reader().read_be32(); | ||||
|     uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(6, 40)); | ||||
|     if (gotheaderchecksum != wantedheaderchecksum) | ||||
|         return; | ||||
|  | ||||
|     uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32(); | ||||
|     uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(62, 1024)); | ||||
|  | ||||
|     _sector->data.clear(); | ||||
|     _sector->data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo); | ||||
|     _sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
| } | ||||
							
								
								
									
										129
									
								
								arch/amiga/encoder.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								arch/amiga/encoder.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| #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 bool lastBit; | ||||
|  | ||||
| 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, lastBit); | ||||
| } | ||||
|  | ||||
| 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 }); | ||||
| 	lastBit = 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() | ||||
							
								
								
									
										235
									
								
								arch/ibm/encoder.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								arch/ibm/encoder.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,235 @@ | ||||
| #include "globals.h" | ||||
| #include "record.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.h" | ||||
| #include "ibm.h" | ||||
| #include "crc.h" | ||||
| #include "sectorset.h" | ||||
| #include "writer.h" | ||||
| #include "fmt/format.h" | ||||
| #include <ctype.h> | ||||
|  | ||||
| /* IAM record separator: | ||||
|  * 0xC2 is: | ||||
|  * data:    1  1  0  0  0  0  1  0  = 0xc2 | ||||
|  * mfm:     01 01 00 10 10 10 01 00 = 0x5254 | ||||
|  * special: 01 01 00 10 00 10 01 00 = 0x5224 | ||||
|  */ | ||||
| #define MFM_IAM_SEPARATOR 0x5224 | ||||
|  | ||||
| /* FM IAM record: | ||||
|  * flux:   XXXX-XXX-XXXX-X- = 0xf77a | ||||
|  * clock:  X X - X - X X X  = 0xd7 | ||||
|  * data:    X X X X X X - - = 0xfc | ||||
|  */ | ||||
| #define FM_IAM_RECORD 0xf77a | ||||
|  | ||||
| /* MFM IAM record: | ||||
|  * data:   1  1  1  1  1  1  0  0  = 0xfc | ||||
|  * flux:   01 01 01 01 01 01 00 10 = 0x5552 | ||||
|  */ | ||||
| #define MFM_IAM_RECORD 0x5552 | ||||
|  | ||||
| /* MFM record separator: | ||||
|  * 0xA1 is: | ||||
|  * data:    1  0  1  0  0  0  0  1  = 0xa1 | ||||
|  * mfm:     01 00 01 00 10 10 10 01 = 0x44a9 | ||||
|  * special: 01 00 01 00 10 00 10 01 = 0x4489 | ||||
|  *                       ^^^^^ | ||||
|  * When shifted out of phase, the special 0xa1 byte becomes an illegal | ||||
|  * 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. | ||||
|  */ | ||||
| #define MFM_RECORD_SEPARATOR 0x4489 | ||||
| #define MFM_RECORD_SEPARATOR_BYTE 0xa1 | ||||
|  | ||||
| /* MFM IDAM byte: | ||||
|  * data:    1  1  1  1  1  1  1  0  = 0xfe | ||||
|  * mfm:     01 01 01 01 01 01 01 00 = 0x5554 | ||||
|  */ | ||||
|  | ||||
| /* MFM DAM byte: | ||||
|  * data:    1  1  1  1  1  0  1  1  = 0xfb | ||||
|  * mfm:     01 01 01 01 01 00 01 01 = 0x5545 | ||||
|  */ | ||||
|  | ||||
| static int charToInt(char c) | ||||
| { | ||||
| 	if (isdigit(c)) | ||||
| 		return c - '0'; | ||||
| 	return 10 + tolower(c) - 'a'; | ||||
| } | ||||
|  | ||||
| void IbmEncoder::writeRawBits(uint32_t data, int width) | ||||
| { | ||||
| 	_cursor += width; | ||||
| 	_lastBit = data & 1; | ||||
| 	for (int i=0; i<width; i++) | ||||
| 	{ | ||||
| 		unsigned pos = _cursor - i - 1; | ||||
| 		if (pos < _bits.size()) | ||||
| 			_bits[pos] = data & 1; | ||||
| 		data >>= 1; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void IbmEncoder::writeBytes(const Bytes& bytes) | ||||
| { | ||||
| 	if (_parameters.useFm) | ||||
| 		encodeFm(_bits, _cursor, bytes); | ||||
| 	else | ||||
| 		encodeMfm(_bits, _cursor, bytes, _lastBit); | ||||
| } | ||||
|  | ||||
| void IbmEncoder::writeBytes(int count, uint8_t byte) | ||||
| { | ||||
| 	Bytes bytes = { byte }; | ||||
| 	for (int i=0; i<count; i++) | ||||
| 		writeBytes(bytes); | ||||
| } | ||||
|  | ||||
| static uint8_t decodeUint16(uint16_t raw) | ||||
| { | ||||
| 	Bytes b; | ||||
| 	ByteWriter bw(b); | ||||
| 	bw.write_be16(raw); | ||||
| 	return decodeFmMfm(b.toBits())[0]; | ||||
| } | ||||
|  | ||||
| std::unique_ptr<Fluxmap> IbmEncoder::encode( | ||||
| 	int physicalTrack, int physicalSide, const SectorSet& allSectors) | ||||
| { | ||||
| 	double clockRateUs = 1e3 / _parameters.clockRateKhz; | ||||
| 	if (!_parameters.useFm) | ||||
| 		clockRateUs /= 2.0; | ||||
| 	int bitsPerRevolution = (_parameters.trackLengthMs * 1000.0) / clockRateUs; | ||||
| 	_bits.resize(bitsPerRevolution); | ||||
| 	_cursor = 0; | ||||
|  | ||||
| 	uint8_t idamUnencoded = decodeUint16(_parameters.idamByte); | ||||
| 	uint8_t damUnencoded = decodeUint16(_parameters.damByte); | ||||
|  | ||||
| 	uint8_t sectorSize = 0; | ||||
| 	{ | ||||
| 		int s = _parameters.sectorSize >> 7; | ||||
| 		while (s > 1) | ||||
| 		{ | ||||
| 			s >>= 1; | ||||
| 			sectorSize += 1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	uint8_t gapFill = _parameters.useFm ? 0x00 : 0x4e; | ||||
|  | ||||
| 	writeBytes(_parameters.gap0, gapFill); | ||||
| 	if (_parameters.emitIam) | ||||
| 	{ | ||||
| 		writeBytes(_parameters.useFm ? 6 : 12, 0x00); | ||||
| 		if (!_parameters.useFm) | ||||
| 		{ | ||||
| 			for (int i=0; i<3; i++) | ||||
| 				writeRawBits(MFM_IAM_SEPARATOR, 16); | ||||
| 		} | ||||
| 		writeRawBits(_parameters.useFm ? FM_IAM_RECORD : MFM_IAM_RECORD, 16); | ||||
| 		writeBytes(_parameters.gap1, gapFill); | ||||
| 	} | ||||
|  | ||||
| 	bool first = true; | ||||
| 	for (char sectorChar : _parameters.sectorSkew) | ||||
| 	{ | ||||
| 		int sectorId = charToInt(sectorChar); | ||||
| 		if (!first) | ||||
| 			writeBytes(_parameters.gap3, gapFill); | ||||
| 		first = false; | ||||
|  | ||||
| 		const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId); | ||||
| 		if (!sectorData) | ||||
| 			Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId); | ||||
|  | ||||
| 		/* Writing the sector and data records are fantastically annoying. | ||||
| 		 * The CRC is calculated from the *very start* of the record, and | ||||
| 		 * include the malformed marker bytes. Our encoder doesn't know | ||||
| 		 * about this, of course, with the result that we have to construct | ||||
| 		 * the unencoded header, calculate the checksum, and then use the | ||||
| 		 * same logic to emit the bytes which require special encoding | ||||
| 		 * before encoding the rest of the header normally. */ | ||||
|  | ||||
| 		{ | ||||
| 			Bytes header; | ||||
| 			ByteWriter bw(header); | ||||
|  | ||||
| 			writeBytes(_parameters.useFm ? 6 : 12, 0x00); | ||||
| 			if (!_parameters.useFm) | ||||
| 			{ | ||||
| 				for (int i=0; i<3; i++) | ||||
| 					bw.write_8(MFM_RECORD_SEPARATOR_BYTE); | ||||
| 			} | ||||
| 			bw.write_8(idamUnencoded); | ||||
| 			bw.write_8(sectorData->logicalTrack); | ||||
| 			bw.write_8(sectorData->logicalSide); | ||||
| 			bw.write_8(sectorData->logicalSector + _parameters.startSectorId); | ||||
| 			bw.write_8(sectorSize); | ||||
| 			uint16_t crc = crc16(CCITT_POLY, header); | ||||
| 			bw.write_be16(crc); | ||||
|  | ||||
| 			int conventionalHeaderStart = 0; | ||||
| 			if (!_parameters.useFm) | ||||
| 			{ | ||||
| 				for (int i=0; i<3; i++) | ||||
| 					writeRawBits(MFM_RECORD_SEPARATOR, 16); | ||||
| 				conventionalHeaderStart += 3; | ||||
|  | ||||
| 			} | ||||
| 			writeRawBits(_parameters.idamByte, 16); | ||||
| 			conventionalHeaderStart += 1; | ||||
|  | ||||
| 			writeBytes(header.slice(conventionalHeaderStart)); | ||||
| 		} | ||||
|  | ||||
| 		writeBytes(_parameters.gap2, gapFill); | ||||
|  | ||||
| 		{ | ||||
| 			Bytes data; | ||||
| 			ByteWriter bw(data); | ||||
|  | ||||
| 			writeBytes(_parameters.useFm ? 6 : 12, 0x00); | ||||
| 			if (!_parameters.useFm) | ||||
| 			{ | ||||
| 				for (int i=0; i<3; i++) | ||||
| 					bw.write_8(MFM_RECORD_SEPARATOR_BYTE); | ||||
| 			} | ||||
| 			bw.write_8(damUnencoded); | ||||
|  | ||||
| 			Bytes truncatedData = sectorData->data.slice(0, _parameters.sectorSize); | ||||
| 			bw += truncatedData; | ||||
| 			uint16_t crc = crc16(CCITT_POLY, data); | ||||
| 			bw.write_be16(crc); | ||||
|  | ||||
| 			int conventionalHeaderStart = 0; | ||||
| 			if (!_parameters.useFm) | ||||
| 			{ | ||||
| 				for (int i=0; i<3; i++) | ||||
| 					writeRawBits(MFM_RECORD_SEPARATOR, 16); | ||||
| 				conventionalHeaderStart += 3; | ||||
|  | ||||
| 			} | ||||
| 			writeRawBits(_parameters.damByte, 16); | ||||
| 			conventionalHeaderStart += 1; | ||||
|  | ||||
| 			writeBytes(data.slice(conventionalHeaderStart)); | ||||
| 		} | ||||
|     } | ||||
|  | ||||
| 	if (_cursor >= _bits.size()) | ||||
| 		Error() << "track data overrun"; | ||||
| 	while (_cursor < _bits.size()) | ||||
| 		writeBytes(1, gapFill); | ||||
|  | ||||
| 	std::unique_ptr<Fluxmap> fluxmap(new Fluxmap); | ||||
| 	fluxmap->appendBits(_bits, clockRateUs*1e3); | ||||
| 	return fluxmap; | ||||
| } | ||||
|  | ||||
| @@ -2,6 +2,7 @@ | ||||
| #define IBM_H | ||||
| 
 | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.h" | ||||
| 
 | ||||
| /* IBM format (i.e. ordinary PC floppies). */ | ||||
| 
 | ||||
| @@ -31,8 +32,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,56 +43,51 @@ public: | ||||
| 
 | ||||
| private: | ||||
|     unsigned _sectorBase; | ||||
|     bool _ignoreSideByte; | ||||
|     unsigned _currentSectorSize; | ||||
|     unsigned _currentHeaderLength; | ||||
| }; | ||||
| 
 | ||||
| #if 0 | ||||
| class AbstractIbmDecoder : public AbstractSoftSectorDecoder | ||||
| struct IbmParameters | ||||
| { | ||||
| 	int trackLengthMs; | ||||
| 	int sectorSize; | ||||
| 	bool emitIam; | ||||
| 	int startSectorId; | ||||
| 	int clockRateKhz; | ||||
| 	bool useFm; | ||||
| 	uint16_t idamByte; | ||||
| 	uint16_t damByte; | ||||
| 	int gap0; | ||||
| 	int gap1; | ||||
| 	int gap2; | ||||
| 	int gap3; | ||||
| 	std::string sectorSkew; | ||||
| }; | ||||
| 
 | ||||
| class IbmEncoder : public AbstractEncoder | ||||
| { | ||||
| public: | ||||
|     AbstractIbmDecoder(unsigned sectorIdBase): | ||||
|         _sectorIdBase(sectorIdBase) | ||||
|     {} | ||||
|     virtual ~AbstractIbmDecoder() {} | ||||
| 	IbmEncoder(const IbmParameters& parameters): | ||||
| 		_parameters(parameters) | ||||
| 	{} | ||||
| 
 | ||||
|     SectorVector decodeToSectors(const RawRecordVector& rawRecords, unsigned physicalTrack, unsigned physicalSide); | ||||
| 	virtual ~IbmEncoder() {} | ||||
| 
 | ||||
| protected: | ||||
|     virtual int skipHeaderBytes() const = 0; | ||||
| public: | ||||
|     std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors); | ||||
| 
 | ||||
| private: | ||||
|     unsigned _sectorIdBase; | ||||
| 	void writeRawBits(uint32_t data, int width); | ||||
| 	void writeBytes(const Bytes& bytes); | ||||
| 	void writeBytes(int count, uint8_t value); | ||||
| 	void writeSync(); | ||||
| 	 | ||||
| private: | ||||
| 	IbmParameters _parameters; | ||||
| 	std::vector<bool> _bits; | ||||
| 	unsigned _cursor; | ||||
| 	bool _lastBit; | ||||
| }; | ||||
| 
 | ||||
| class IbmFmDecoder : public AbstractIbmDecoder | ||||
| { | ||||
| public: | ||||
|     IbmFmDecoder(unsigned sectorIdBase): | ||||
|         AbstractIbmDecoder(sectorIdBase) | ||||
|     {} | ||||
| 
 | ||||
|     int recordMatcher(uint64_t fifo) const; | ||||
| 
 | ||||
| protected: | ||||
|     int skipHeaderBytes() const | ||||
|     { return 0; } | ||||
| }; | ||||
| 
 | ||||
| class IbmMfmDecoder : public AbstractIbmDecoder | ||||
| { | ||||
| public: | ||||
|     IbmMfmDecoder(unsigned sectorIdBase): | ||||
|         AbstractIbmDecoder(sectorIdBase) | ||||
|     {} | ||||
| 
 | ||||
|     nanoseconds_t guessClock(Fluxmap& fluxmap) const; | ||||
|     int recordMatcher(uint64_t fifo) const; | ||||
| 
 | ||||
| protected: | ||||
|     int skipHeaderBytes() const | ||||
|     { return 3; } | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
| @@ -180,5 +180,7 @@ void MacintoshDecoder::decodeDataRecord() | ||||
|         inputbuffer[i] = decode_data_gcr(inputbuffer[i]); | ||||
|          | ||||
|     _sector->status = Sector::BAD_CHECKSUM; | ||||
|     _sector->data = decode_crazy_data(inputbuffer, _sector->status); | ||||
|     Bytes userData = decode_crazy_data(inputbuffer, _sector->status); | ||||
|     _sector->data.clear(); | ||||
|     _sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12)); | ||||
| } | ||||
							
								
								
									
										
											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: | ||||
| @@ -23,6 +23,39 @@ You should end up with an `amiga.adf` which is 901120 bytes long (for a | ||||
| normal DD disk) --- it ought to be a perfectly normal ADF file which you can | ||||
| use in an emulator. | ||||
|  | ||||
| If you want the metadata as well, specify a 528 byte sector size for the | ||||
| output image: | ||||
|  | ||||
| ``` | ||||
| fluxengine read amiga -o amiga.adf:b=528 | ||||
| ``` | ||||
|  | ||||
| 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 | ||||
|   | ||||
| @@ -23,7 +23,7 @@ computer](https://ilesj.wordpress.com/2014/05/14/1541-why-so-complicated/) of | ||||
| 300 bytes per second (!). (The drive itself could transfer data reasonably | ||||
| quickly.) | ||||
|  | ||||
| A standard 1541 disk has 35 tracks of 17 to 20 sectors, each 256 bytes long. | ||||
| A standard 1541 disk has 35 tracks of 17 to 21 sectors, each 256 bytes long. | ||||
|  | ||||
| Reading discs | ||||
| ------------- | ||||
| @@ -34,15 +34,14 @@ Just do: | ||||
| fluxengine read c64 | ||||
| ``` | ||||
|  | ||||
| You should end up with an `c64.img` which is 187136 bytes long (for a normal | ||||
| 1541 disk). | ||||
| You should end up with an `c64.d64` file which is 174848 bytes long. You can | ||||
| load this straight into a Commodore 64 emulator such as | ||||
| [VICE](http://vice-emu.sourceforge.net/). | ||||
|  | ||||
| **Big warning!** The image may not work in an emulator. Commodore 64 disk images are | ||||
| **Big warning!** Commodore 64 disk images are | ||||
| complicated due to the way the tracks are different sizes and the odd sector | ||||
| size. FluxEngine chooses to store them in a simple 256 x 20 x 35 layout, | ||||
| with holes where missing sectors should be. This was easiest. If anyone can | ||||
| suggest a better way, please [get in | ||||
| touch](https://github.com/davidgiven/fluxengine/issues/new). | ||||
| size, so you need the special D64 or LDBS output formats to represent them | ||||
| sensibly. Don't use IMG unless you know what you're doing. | ||||
|  | ||||
| Useful references | ||||
| ----------------- | ||||
|   | ||||
							
								
								
									
										143
									
								
								doc/disk-ibm.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								doc/disk-ibm.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| 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. | ||||
|  | ||||
|  | ||||
| Writing disks | ||||
| ------------- | ||||
|  | ||||
| FluxEngine can also write IBM scheme disks. Unfortunately the format is | ||||
| incredibly flexible and you need to specify every single parameter, which | ||||
| makes things slightly awkward. | ||||
|  | ||||
| The syntax is: | ||||
|  | ||||
|     fluxengine write ibm -i input.img <options> | ||||
|  | ||||
| The format of `input.img` will vary depending on the kind of disk you're | ||||
| writing, which is configured by the options. There are some presets, which | ||||
| you will almost certainly want to use if possible: | ||||
|  | ||||
|   - `--ibm-preset-720`: a standard 720kB DS DD 3.5" disk, with 80 cylinders, | ||||
|   2 sides, and 9 sectors per track. | ||||
|   - `--ibm-preset-1440`: a standard 1440kB DS HD 3.5" disk, with 80 | ||||
|   cylinders, 2 sides, and 18 sectors per track. | ||||
|  | ||||
| These options simply preset the following, lower-level options. Note that | ||||
| options are processed left to right, so it's possible to use a preset and | ||||
| then change some settings. To see the values for a preset, simply append | ||||
| `--help`. | ||||
|  | ||||
|   - `--ibm-track-length-ms=N`: one disk rotation, in milliseconds. This is used | ||||
|   to determine whether all the data will fit on a track or not. `fluxengine | ||||
|   rpm` will tell you this; it'll be 200 for a normal 3.5" drive and 166 for a | ||||
|   normal 5.25" drive. | ||||
|   - `--ibm-sector-size=N`: the size of a sector, in bytes. Must be a power of | ||||
|   two. | ||||
|   - `--ibm-emit-iam=true|false`: whether to emit the IAM record at the top of | ||||
|   the track. The standard format requires it, but it's ignored by absolutely | ||||
|   everyone and you can fit a bit more data on the disk without it. | ||||
|   - `--ibm-start-sector-id=N`: the sector ID of the first sector. Normally 1, | ||||
|   except for non-standard formats like Acorn's, which use 0. | ||||
|   - `--ibm-use-fm=true|false`: uses FM rather than MFM. | ||||
|   - `--ibm-idam-byte=N`: the sixteen-bit raw bit pattern used for the IDAM ID | ||||
|   byte. Big-endian, clock bit first. | ||||
|   - `--ibm-dam-byte-N`: the sixteen-bit raw bit pattern used for the DAM ID | ||||
|   byte. Big-endian, clock bit first. | ||||
|   - `--ibm-gap0-bytes=N`: the size of gap 0 in bytes (between the start of | ||||
|   the track and the IAM record). | ||||
|   - `--ibm-gap1-bytes=N`: the size of gap 1 in bytes (between the IAM record | ||||
|   and the first sector record). | ||||
|   - `--ibm-gap2-bytes=N`: the size of gap 2 in bytes (between each sector | ||||
|   record and the data record). | ||||
|   - `--ibm-gap3-bytes=N`: the size of gap 3 in bytes (between the data record | ||||
|   and the next sector record). | ||||
|   - `--ibm-sector-skew=0123...`: a string representing the order in which to | ||||
|   write sectors: each character represents on sector, with `0` being the | ||||
|   first (always, regardless of `--ibm-start-sector-id` above). Sectors 10 and | ||||
|   above are represented as latters from `A` up. | ||||
|  | ||||
| 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 emulators will typically 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. | ||||
|  | ||||
| One easy option when reading these is to simply read the two sections of the | ||||
| disk into two different image files. | ||||
|  | ||||
| FluxEngine can write these too, but in two different passes with different | ||||
| options. It's possible to assemble a flux file by judicious use of `-D | ||||
| something.flux --merge`, which can then be written in a single pass with | ||||
| `fluxengine writeflux`, but it's usually not worth the bother: just write the | ||||
| boot tracks, then write the data tracks, possibly with a script for automation. | ||||
| @@ -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 | ||||
| ------------- | ||||
| @@ -50,6 +58,10 @@ with holes where missing sectors should be. This was easiest. If anyone can | ||||
| suggest a better way, please [get in | ||||
| touch](https://github.com/davidgiven/fluxengine/issues/new). | ||||
|  | ||||
| The 12 bytes of metadata _follow_ the 512 bytes of user payload in the sector | ||||
| image. If you don't want it, specify a geometry in the output file with a | ||||
| 512-byte sectore size like `-o mac.img:c=80:h=1:s=12:b=512`. | ||||
|  | ||||
| Useful references | ||||
| ----------------- | ||||
|  | ||||
|   | ||||
							
								
								
									
										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) | ||||
|  | ||||
|    | ||||
|    | ||||
|   | ||||
							
								
								
									
										136
									
								
								doc/using.md
									
									
									
									
									
								
							
							
						
						
									
										136
									
								
								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 bandwidth` 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 | ||||
| @@ -112,6 +122,51 @@ sensible for the command you're using. | ||||
| **Important note:** FluxEngine _always_ uses zero-based units (even if the | ||||
| *disk format says otherwise). | ||||
|  | ||||
| ### Input and output specifiers | ||||
|  | ||||
| These use a very similar syntax to the source and destination specifiers | ||||
| (because they're based on the same microformat library!) but are used for | ||||
| input and output _images_: i.e. nicely lined up arrays of sectors which you | ||||
| can actually do something with. | ||||
|  | ||||
| Use `--input` (`-i`) or `--output` (`-o`) as appropriate to tell FluxEngine | ||||
| where you want to read from or write to. The actual format is autodetected | ||||
| based on the extension: | ||||
|  | ||||
|   - `.img` or `.adf`: raw sector images in CHS order. Append | ||||
|     `:c=80:h=2:s=9:b=512` to set the geometry; that specifies 80 cylinders, 2 | ||||
|     heads, 9 sectors, 512 bytes per sector. For output files (`--output`) the | ||||
|     geometry will be autodetected if left unspecified. For input files you | ||||
|     normally have to specify it. | ||||
|  | ||||
|   - `.ldbs`: John Elliott's [LDBS disk image | ||||
|     format](http://www.seasip.info/Unix/LibDsk/ldbs.html), which is | ||||
|     consumable by the [libdsk](http://www.seasip.info/Unix/LibDsk/) suite of | ||||
|     tools. This allows things like variable numbers of sectors per track | ||||
|     (e.g. Macintosh or Commodore 64) and also provides information about | ||||
|     whether sectors were read correctly. You can use libdsk to convert this | ||||
|     to other formats, using a command like this: | ||||
|  | ||||
|     ``` | ||||
|     $ dsktrans out.ldbs -otype tele out.td0 | ||||
|     ``` | ||||
|  | ||||
|     ...to convert to TeleDisk format. (Note you have to use dsktrans rather | ||||
|     than dskconv due to a minor bug in the geometry hadnling.) | ||||
|  | ||||
|     FluxEngine's LDBS support is currently limited to write only, and | ||||
|     it doesn't store a lot of the more esoteric LDBS features like format | ||||
|     types, timings, and data rates. | ||||
|  | ||||
|   - `.d64`: the venerable Commodore 64 disk image format as used by the 1540, | ||||
|     1541, etc. This is a special-purpose format due to the weird layout of | ||||
|     1540 disks and while you can use this for non-Commodore disks the result | ||||
|     will be gibberish. Use this to image Commodore 64 disks and load the | ||||
|     result into an emulator. | ||||
|  | ||||
|     FluxEngine's D64 support is currently limited to write only. It will work | ||||
|     with up to 40 logical tracks. | ||||
|  | ||||
| ### High density disks | ||||
|  | ||||
| High density disks use a different magnetic medium to low and double density | ||||
| @@ -131,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. | ||||
| @@ -144,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 | ||||
| @@ -168,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 | ||||
|   about 600kB/s for FluxEngine to work. You don't need a disk in the drive | ||||
|   for this one. | ||||
|   - `fluxengine test bandwidth`: measures your USB throughput. You need about | ||||
|   600kB/s for FluxEngine to work for DD disks, and 900 to 950kB/s for HD | ||||
|   disks. 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 | ||||
| @@ -187,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 --overwrite --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: | ||||
| This will read the disk in drive 0 and write out a filesystem image. It'll also | ||||
| copy the flux to `brother.flux` (replacing any old one) 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 | 
| @@ -1,20 +0,0 @@ | ||||
| #ifndef AMIGA_H | ||||
| #define AMIGA_H | ||||
|  | ||||
| #define AMIGA_SECTOR_RECORD 0xaaaa44894489LL | ||||
|  | ||||
| #define AMIGA_RECORD_SIZE 0x21f | ||||
|  | ||||
| class Sector; | ||||
| class Fluxmap; | ||||
|  | ||||
| class AmigaDecoder : public AbstractDecoder | ||||
| { | ||||
| public: | ||||
|     virtual ~AmigaDecoder() {} | ||||
|  | ||||
|     RecordType advanceToNextRecord(); | ||||
|     void decodeSectorRecord(); | ||||
| }; | ||||
|  | ||||
| #endif | ||||
| @@ -1,97 +0,0 @@ | ||||
| #include "globals.h" | ||||
| #include "fluxmap.h" | ||||
| #include "decoders/fluxmapreader.h" | ||||
| #include "protocol.h" | ||||
| #include "record.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "sector.h" | ||||
| #include "amiga.h" | ||||
| #include "bytes.h" | ||||
| #include "fmt/format.h" | ||||
| #include <string.h> | ||||
| #include <algorithm> | ||||
|  | ||||
| /*  | ||||
|  * Amiga disks use MFM but it's not quite the same as IBM MFM. They only use | ||||
|  * a single type of record with a different marker byte. | ||||
|  *  | ||||
|  * See the big comment in the IBM MFM decoder for the gruesome details of how | ||||
|  * MFM works. | ||||
|  */ | ||||
|           | ||||
| 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); | ||||
|     if (_fmr->eof() || !_sector->clock) | ||||
|         return UNKNOWN_RECORD; | ||||
|     return SECTOR_RECORD; | ||||
| } | ||||
|  | ||||
| void AmigaDecoder::decodeSectorRecord() | ||||
| { | ||||
|     const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16); | ||||
|     const auto& rawbytes = toBytes(rawbits).slice(0, AMIGA_RECORD_SIZE*2); | ||||
|     const auto& bytes = decodeFmMfm(rawbits).slice(0, AMIGA_RECORD_SIZE); | ||||
|  | ||||
|     const uint8_t* ptr = bytes.begin() + 3; | ||||
|  | ||||
|     Bytes header = deinterleave(ptr, 4); | ||||
|     Bytes recoveryinfo = deinterleave(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)); | ||||
|     if (gotheaderchecksum != wantedheaderchecksum) | ||||
|         return; | ||||
|  | ||||
|     uint32_t wanteddatachecksum = deinterleave(ptr, 4).reader().read_be32(); | ||||
|     uint32_t gotdatachecksum = checksum(rawbytes.slice(62, 1024)); | ||||
|     _sector->data = deinterleave(ptr, 512); | ||||
|     _sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
| } | ||||
							
								
								
									
										33
									
								
								lib/bytes.cc
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								lib/bytes.cc
									
									
									
									
									
								
							| @@ -2,6 +2,7 @@ | ||||
| #include "bytes.h" | ||||
| #include "fmt/format.h" | ||||
| #include "common/crunch.h" | ||||
| #include <fstream> | ||||
| #include <zlib.h> | ||||
|  | ||||
| static std::shared_ptr<std::vector<uint8_t>> createVector(unsigned size) | ||||
| @@ -146,6 +147,28 @@ Bytes Bytes::slice(unsigned start, unsigned len) const | ||||
|     } | ||||
| } | ||||
|  | ||||
| Bytes Bytes::slice(unsigned start) const | ||||
| { | ||||
| 	int len = 0; | ||||
| 	if (start < size()) | ||||
| 		len = size() - start; | ||||
| 	return slice(start, len); | ||||
| } | ||||
|  | ||||
| std::vector<bool> Bytes::toBits() const | ||||
| { | ||||
| 	std::vector<bool> bits; | ||||
| 	for (uint8_t byte : *this) | ||||
| 	{ | ||||
| 		for (int i=0; i<8; i++) | ||||
| 		{ | ||||
| 			bits.push_back(byte & 0x80); | ||||
| 			byte <<= 1; | ||||
| 		} | ||||
| 	} | ||||
| 	return bits; | ||||
| } | ||||
|  | ||||
| uint8_t toByte( | ||||
|     std::vector<bool>::const_iterator start, | ||||
|     std::vector<bool>::const_iterator end) | ||||
| @@ -280,6 +303,16 @@ Bytes Bytes::uncrunch() const | ||||
|     return output; | ||||
| } | ||||
|  | ||||
| void Bytes::writeToFile(const std::string& filename) const | ||||
| { | ||||
|     std::ofstream f(filename, std::ios::out | std::ios::binary); | ||||
|     if (!f.is_open()) | ||||
|         Error() << fmt::format("cannot open output file '{}'", filename); | ||||
|  | ||||
|     f.write((const char*) cbegin(), size()); | ||||
|     f.close(); | ||||
| } | ||||
|  | ||||
| ByteReader Bytes::reader() const | ||||
| { | ||||
|     return ByteReader(*this); | ||||
|   | ||||
| @@ -47,15 +47,19 @@ public: | ||||
|     { resize(0); return *this; } | ||||
|  | ||||
|     Bytes slice(unsigned start, unsigned len) const; | ||||
|     Bytes slice(unsigned start) const; | ||||
|     Bytes swab() const; | ||||
|     Bytes compress() const; | ||||
|     Bytes decompress() const; | ||||
|     Bytes crunch() const; | ||||
|     Bytes uncrunch() const; | ||||
| 	std::vector<bool> toBits() const; | ||||
|  | ||||
|     ByteReader reader() const; | ||||
|     ByteWriter writer(); | ||||
|  | ||||
|     void writeToFile(const std::string& filename) const; | ||||
|  | ||||
| private: | ||||
|     std::shared_ptr<std::vector<uint8_t>> _data; | ||||
|     unsigned _low; | ||||
|   | ||||
| @@ -9,14 +9,37 @@ void crunch(crunch_state_t* state) | ||||
|         uint8_t data = *state->inputptr++; | ||||
|         state->inputlen--; | ||||
|  | ||||
|         if (data & 0x80) | ||||
| 		if (data == 0x80) | ||||
| 		{ | ||||
| 			/* Multiple 0x80s in a row get swallowed as they're | ||||
| 			 * meaningless. */ | ||||
| 			state->haspending = true; | ||||
| 		} | ||||
|         else if (data & 0x80) | ||||
|         { | ||||
|             state->fifo = (state->fifo << 2) | 2 | (data & 1); | ||||
|             state->fifolen += 2; | ||||
| 			if (state->haspending) | ||||
| 			{ | ||||
| 				state->fifo = (state->fifo << 3) | 4; | ||||
| 				state->fifolen += 3; | ||||
| 				state->haspending = false; | ||||
| 			} | ||||
|  | ||||
|             state->fifo = (state->fifo << 3) | 4 | (data & 1); | ||||
|             state->fifolen += 3; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             state->fifo = (state->fifo << 8) | data; | ||||
| 			if (state->haspending && (data >= 0x40)) | ||||
| 			{ | ||||
| 				state->fifo = (state->fifo << 3) | 4; | ||||
| 				state->fifolen += 3; | ||||
| 				state->haspending = false; | ||||
| 			} | ||||
| 				 | ||||
| 			state->fifo = (state->fifo << 8) | data; | ||||
| 			if (state->haspending) | ||||
| 				state->fifo |= 0xc0; | ||||
| 			state->haspending = false; | ||||
|             state->fifolen += 8; | ||||
|         } | ||||
|  | ||||
| @@ -45,6 +68,14 @@ void uncrunch(crunch_state_t* state) | ||||
| { | ||||
|     while (state->inputlen && state->outputlen) | ||||
|     { | ||||
| 		if (state->haspending) | ||||
| 		{ | ||||
| 			*state->outputptr++ = state->pendingbyte; | ||||
| 			state->outputlen--; | ||||
| 			state->haspending = false; | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
|         if (state->fifolen < 8) | ||||
|         { | ||||
|             if (state->inputlen) | ||||
| @@ -58,13 +89,21 @@ void uncrunch(crunch_state_t* state) | ||||
|         } | ||||
|  | ||||
|         uint8_t data = state->fifo >> (state->fifolen - 8); | ||||
|         if (data & 0x80) | ||||
|         { | ||||
|             data = ((data >> 6) & 0x01) | 0x80; | ||||
|             state->fifolen -= 2; | ||||
|         } | ||||
|         else | ||||
|             state->fifolen -= 8; | ||||
| 		switch (data & 0xc0) | ||||
| 		{ | ||||
| 			case 0x80: | ||||
| 				data = ((data >> 5) & 0x01) | 0x80; | ||||
| 				state->fifolen -= 3; | ||||
| 				break; | ||||
|  | ||||
| 			case 0xc0: | ||||
| 				state->haspending = true; | ||||
| 				state->pendingbyte = data & 0x3f; | ||||
| 				data = 0x80; | ||||
| 				/* fall through */ | ||||
| 			default: | ||||
| 				state->fifolen -= 8; | ||||
| 		} | ||||
|  | ||||
|         if (data) | ||||
|         { | ||||
|   | ||||
| @@ -11,7 +11,8 @@ extern "C" { | ||||
|  * sending it over USB. The encoding used is: | ||||
|  *  | ||||
|  * 0nnn.nnnn: value 0x00..0x7f | ||||
|  * 1n       : value 0x80|n | ||||
|  * 11nn.nnnn: value 0x80 then 0x00..0x3f | ||||
|  * 10n      : value 0x80|n | ||||
|  * | ||||
|  * The end of the buffer is terminated with zeroes, which are ignored | ||||
|  * (not written to the output). | ||||
| @@ -25,8 +26,10 @@ typedef struct crunch_state_t | ||||
|     uint32_t inputlen; | ||||
|     uint8_t* outputptr; | ||||
|     uint32_t outputlen; | ||||
|     uint16_t fifo; | ||||
|     uint32_t fifo; | ||||
|     uint8_t fifolen; | ||||
| 	bool haspending; | ||||
| 	uint8_t pendingbyte; | ||||
| } | ||||
| crunch_state_t; | ||||
|  | ||||
|   | ||||
| @@ -5,6 +5,11 @@ | ||||
| #include <regex> | ||||
| #include <sstream> | ||||
|  | ||||
| MissingModifierException::MissingModifierException(const std::string& mod): | ||||
|     mod(mod), | ||||
|     std::runtime_error(fmt::format("missing mandatory modifier '{}'", mod)) | ||||
| {} | ||||
|  | ||||
| std::vector<std::string> DataSpec::split( | ||||
|         const std::string& s, const std::string& delimiter) | ||||
| { | ||||
| @@ -74,31 +79,108 @@ void DataSpec::set(const std::string& spec) | ||||
|     filename = words[0]; | ||||
|     if (words.size() > 1) | ||||
|     { | ||||
|         locations.clear(); | ||||
|  | ||||
|         for (size_t i = 1; i < words.size(); i++) | ||||
|         { | ||||
|             auto mod = parseMod(words[i]); | ||||
|             if ((mod.name != "t") && (mod.name != "s") && (mod.name != "d")) | ||||
|                 Error() << fmt::format("unknown data modifier '{}'", mod.name); | ||||
|             modifiers[mod.name] = mod; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|         const auto& drives = modifiers["d"].data; | ||||
| const DataSpec::Modifier& DataSpec::at(const std::string& mod) const | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         return modifiers.at(mod); | ||||
|     } | ||||
|     catch (const std::out_of_range& e) | ||||
|     { | ||||
|         throw MissingModifierException(mod); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool DataSpec::has(const std::string& mod) const | ||||
| { | ||||
|     return modifiers.find(mod) != modifiers.end(); | ||||
| } | ||||
|  | ||||
| FluxSpec::FluxSpec(const DataSpec& spec) | ||||
| { | ||||
|     try  | ||||
|     { | ||||
|         filename = spec.filename; | ||||
|  | ||||
|         locations.clear(); | ||||
|  | ||||
|         const auto& drives = spec.at("d").data; | ||||
|         if (drives.size() != 1) | ||||
|             Error() << "you must specify exactly one drive"; | ||||
|         drive = *drives.begin(); | ||||
|  | ||||
|         const auto& tracks = modifiers["t"].data; | ||||
|         const auto& sides = modifiers["s"].data; | ||||
|         const auto& tracks = spec.at("t").data; | ||||
|         const auto& sides = spec.at("s").data; | ||||
|         for (auto track : tracks) | ||||
|         { | ||||
|             for (auto side : sides) | ||||
|                 locations.push_back({ drive, track, side }); | ||||
|         } | ||||
|  | ||||
|         for (const auto& e : spec.modifiers) | ||||
|         { | ||||
|             const auto name = e.second.name; | ||||
|             if ((name != "t") && (name != "s") && (name != "d")) | ||||
|                 Error() << fmt::format("unknown fluxspec modifier '{}'", name); | ||||
|         } | ||||
|     } | ||||
|     catch (const MissingModifierException& e) | ||||
|     { | ||||
|         Error() << e.what() << " in fluxspec '" << spec << "'"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| ImageSpec::ImageSpec(const DataSpec& spec) | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         filename = spec.filename; | ||||
|  | ||||
|         if (!spec.has("c") && !spec.has("h") && !spec.has("s") && !spec.has("b")) | ||||
|         { | ||||
|             cylinders = heads = sectors = bytes = 0; | ||||
|             initialised = false; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             cylinders = spec.at("c").only(); | ||||
|             heads = spec.at("h").only(); | ||||
|             sectors = spec.at("s").only(); | ||||
|             bytes = spec.at("b").only(); | ||||
|             initialised = true; | ||||
|         } | ||||
|     } | ||||
|     catch (const MissingModifierException& e) | ||||
|     { | ||||
|         Error() << e.what() << " in imagespec '" << spec << "'"; | ||||
|     } | ||||
|  | ||||
|     for (const auto& e : spec.modifiers) | ||||
|     { | ||||
|         const auto name = e.second.name; | ||||
|         if ((name != "c") && (name != "h") && (name != "s") && (name != "b")) | ||||
|             Error() << fmt::format("unknown fluxspec modifier '{}'", name); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ImageSpec::ImageSpec(const std::string filename, | ||||
|         unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes): | ||||
|     filename(filename), | ||||
|     cylinders(cylinders), | ||||
|     heads(heads), | ||||
|     sectors(sectors), | ||||
|     bytes(bytes), | ||||
|     initialised(true) | ||||
| {} | ||||
|  | ||||
| DataSpec::operator std::string(void) const | ||||
| { | ||||
|     std::stringstream ss; | ||||
|   | ||||
| @@ -1,8 +1,57 @@ | ||||
| #ifndef DATASPEC_H | ||||
| #define DATASPEC_H | ||||
|  | ||||
| class MissingModifierException : public std::runtime_error | ||||
| { | ||||
| public: | ||||
|     MissingModifierException(const std::string& mod); | ||||
|     const std::string mod; | ||||
| }; | ||||
|  | ||||
| class DataSpec | ||||
| { | ||||
| public: | ||||
|     struct Modifier | ||||
|     { | ||||
|         std::string name; | ||||
|         std::set<unsigned> data; | ||||
|         std::string source; | ||||
|  | ||||
|         bool operator == (const Modifier& other) const | ||||
|         { return (name == other.name) && (data == other.data); } | ||||
|  | ||||
|         bool operator != (const Modifier& other) const | ||||
|         { return (name != other.name) || (data != other.data); } | ||||
|  | ||||
|         unsigned only() const | ||||
|         { | ||||
|             if (data.size() != 1) | ||||
|                 Error() << "modifier " << name << " can only have one value"; | ||||
|             return *(data.begin()); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| public: | ||||
|     static std::vector<std::string> split( | ||||
|         const std::string& s, const std::string& delimiter); | ||||
|     static Modifier parseMod(const std::string& spec); | ||||
|  | ||||
| public: | ||||
|     DataSpec(const std::string& spec) | ||||
|     { set(spec); } | ||||
|  | ||||
|     void set(const std::string& spec); | ||||
|     operator std::string () const; | ||||
|  | ||||
|     const Modifier& at(const std::string& mod) const; | ||||
|     bool has(const std::string& mod) const; | ||||
|  | ||||
|     std::string filename; | ||||
|     std::map<std::string, Modifier> modifiers; | ||||
| }; | ||||
|  | ||||
| class FluxSpec | ||||
| { | ||||
| public: | ||||
|     struct Location | ||||
|     { | ||||
| @@ -17,36 +66,29 @@ public: | ||||
|         { return (drive != other.drive) || (track != other.track) || (side != other.side); } | ||||
|     }; | ||||
|  | ||||
|     struct Modifier | ||||
|     { | ||||
|         std::string name; | ||||
|         std::set<unsigned> data; | ||||
|         std::string source; | ||||
|  | ||||
|         bool operator == (const Modifier& other) const | ||||
|         { return (name == other.name) && (data == other.data); } | ||||
|  | ||||
|         bool operator != (const Modifier& other) const | ||||
|         { return (name != other.name) || (data != other.data); } | ||||
|     }; | ||||
| public: | ||||
|     FluxSpec(const DataSpec& dataspec); | ||||
|  | ||||
| public: | ||||
|     static std::vector<std::string> split( | ||||
|         const std::string& s, const std::string& delimiter); | ||||
|     static Modifier parseMod(const std::string& spec); | ||||
|  | ||||
| public: | ||||
|     DataSpec(const std::string& spec) | ||||
|     { set(spec); } | ||||
|  | ||||
|     void set(const std::string& spec); | ||||
|     operator std::string () const; | ||||
|  | ||||
|     std::string filename; | ||||
|     std::map<std::string, Modifier> modifiers; | ||||
|     std::vector<Location> locations; | ||||
|     unsigned drive; | ||||
|     unsigned revolutions; | ||||
| }; | ||||
|  | ||||
| class ImageSpec | ||||
| { | ||||
| public: | ||||
|     ImageSpec(const DataSpec& dataspec); | ||||
|     ImageSpec(const std::string filename, | ||||
|         unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes); | ||||
|  | ||||
| public: | ||||
|     std::string filename; | ||||
|     unsigned cylinders; | ||||
|     unsigned heads; | ||||
|     unsigned sectors; | ||||
|     unsigned bytes; | ||||
|     bool initialised : 1; | ||||
| }; | ||||
|  | ||||
| static inline std::ostream& operator << (std::ostream& os, const DataSpec& dataSpec) | ||||
|   | ||||
| @@ -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,8 @@ 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, bool& lastBit); | ||||
| extern void encodeFm(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,48 @@ Bytes decodeFmMfm( | ||||
|  | ||||
|     return bytes; | ||||
| } | ||||
|  | ||||
| void encodeFm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input) | ||||
| { | ||||
| 	if (bits.size() == 0) | ||||
| 		return; | ||||
|     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++] = true; | ||||
|             bits[cursor++] = bit; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input, bool& lastBit) | ||||
| { | ||||
| 	if (bits.size() == 0) | ||||
| 		return; | ||||
|     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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										96
									
								
								lib/flags.cc
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								lib/flags.cc
									
									
									
									
									
								
							| @@ -1,5 +1,6 @@ | ||||
| #include "globals.h" | ||||
| #include "flags.h" | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| static FlagGroup* currentFlagGroup; | ||||
| static std::vector<Flag*> all_flags; | ||||
| @@ -29,7 +30,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 +67,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 +78,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 +158,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); | ||||
| } | ||||
|  | ||||
| @@ -148,6 +173,11 @@ void BoolFlag::set(const std::string& value) | ||||
| 		Error() << "can't parse '" << value << "'; try 'true' or 'false'"; | ||||
| } | ||||
|  | ||||
| const std::string HexIntFlag::defaultValueAsString() const | ||||
| { | ||||
| 	return fmt::format("0x{:x}", _defaultValue); | ||||
| } | ||||
|  | ||||
| static void doHelp() | ||||
| { | ||||
|     std::cout << "FluxEngine options:" << std::endl; | ||||
|   | ||||
							
								
								
									
										14
									
								
								lib/flags.h
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								lib/flags.h
									
									
									
									
									
								
							| @@ -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; | ||||
|  | ||||
| @@ -134,6 +135,17 @@ public: | ||||
|     void set(const std::string& value) { _value = std::stoi(value); } | ||||
| }; | ||||
|  | ||||
| class HexIntFlag : public IntFlag | ||||
| { | ||||
| public: | ||||
|     HexIntFlag(const std::vector<std::string>& names, const std::string helptext, | ||||
|             int defaultValue = 0): | ||||
|         IntFlag(names, helptext, defaultValue) | ||||
|     {} | ||||
|  | ||||
|     const std::string defaultValueAsString() const; | ||||
| }; | ||||
|  | ||||
| class DoubleFlag : public ValueFlag<double> | ||||
| { | ||||
| public: | ||||
| @@ -146,7 +158,7 @@ public: | ||||
|     void set(const std::string& value) { _value = std::stod(value); } | ||||
| }; | ||||
|  | ||||
| class BoolFlag : public ValueFlag<double> | ||||
| class BoolFlag : public ValueFlag<bool> | ||||
| { | ||||
| public: | ||||
|     BoolFlag(const std::vector<std::string>& names, const std::string helptext, | ||||
|   | ||||
| @@ -10,7 +10,7 @@ static bool ends_with(const std::string& value, const std::string& ending) | ||||
|     return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<FluxSink> FluxSink::create(const DataSpec& spec) | ||||
| std::unique_ptr<FluxSink> FluxSink::create(const FluxSpec& spec) | ||||
| { | ||||
|     const auto& filename = spec.filename; | ||||
|  | ||||
|   | ||||
| @@ -1,20 +1,23 @@ | ||||
| #ifndef FLUXSINK_H | ||||
| #define FLUXSINK_H | ||||
|  | ||||
| #include "flags.h" | ||||
|  | ||||
| extern FlagGroup hardwareFluxSinkFlags; | ||||
| extern FlagGroup sqliteFluxSinkFlags; | ||||
|  | ||||
| class Fluxmap; | ||||
| class DataSpec; | ||||
| class FluxSpec; | ||||
|  | ||||
| class FluxSink | ||||
| { | ||||
| public: | ||||
|     virtual ~FluxSink() {} | ||||
|  | ||||
| private: | ||||
|     static std::unique_ptr<FluxSink> createSqliteFluxSink(const std::string& filename); | ||||
|     static std::unique_ptr<FluxSink> createHardwareFluxSink(unsigned drive); | ||||
|  | ||||
| public: | ||||
|     static std::unique_ptr<FluxSink> create(const DataSpec& spec); | ||||
|     static std::unique_ptr<FluxSink> create(const FluxSpec& spec); | ||||
|  | ||||
| public: | ||||
|     virtual void writeFlux(int track, int side, Fluxmap& fluxmap) = 0; | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -2,14 +2,37 @@ | ||||
| #include "fluxmap.h" | ||||
| #include "sql.h" | ||||
| #include "fluxsink/fluxsink.h" | ||||
| #include "flags.h" | ||||
| #include "fmt/format.h" | ||||
| #include <unistd.h> | ||||
|  | ||||
| FlagGroup sqliteFluxSinkFlags; | ||||
|  | ||||
| static SettableFlag mergeFlag( | ||||
| 	{ "--merge" }, | ||||
| 	"merge new data into existing flux file"); | ||||
|  | ||||
| static SettableFlag overwriteFlag( | ||||
| 	{ "--overwrite" }, | ||||
| 	"overwrite existing flux file"); | ||||
|  | ||||
| class SqliteFluxSink : public FluxSink | ||||
| { | ||||
| public: | ||||
|     SqliteFluxSink(const std::string& filename) | ||||
|     { | ||||
| 		if (mergeFlag && overwriteFlag) | ||||
| 			Error() << "you can't specify --merge and --overwrite"; | ||||
|  | ||||
| 		if (!mergeFlag) | ||||
| 		{ | ||||
| 			if (!overwriteFlag && (access(filename.c_str(), F_OK) == 0)) | ||||
| 				Error() << "cowardly refusing to overwrite flux file without --merge or --overwrite specified"; | ||||
| 			if ((access(filename.c_str(), F_OK) == 0) && (remove(filename.c_str()) != 0)) | ||||
| 				Error() << fmt::format("failed to overwrite flux file"); | ||||
| 		} | ||||
| 		_outdb = sqlOpen(filename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); | ||||
|  | ||||
| 		int oldVersion = sqlReadIntProperty(_outdb, "version"); | ||||
| 		if ((oldVersion != 0) && (oldVersion != FLUX_VERSION_CURRENT)) | ||||
|             Error() << fmt::format("that flux file is version {}, but this client is for version {}", | ||||
|   | ||||
| @@ -10,7 +10,7 @@ static bool ends_with(const std::string& value, const std::string& ending) | ||||
|     return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<FluxSource> FluxSource::create(const DataSpec& spec) | ||||
| std::unique_ptr<FluxSource> FluxSource::create(const FluxSpec& spec) | ||||
| { | ||||
|     const auto& filename = spec.filename; | ||||
|  | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| extern FlagGroup hardwareFluxSourceFlags; | ||||
|  | ||||
| class Fluxmap; | ||||
| class DataSpec; | ||||
| class FluxSpec; | ||||
|  | ||||
| class FluxSource | ||||
| { | ||||
| @@ -19,7 +19,7 @@ private: | ||||
|     static std::unique_ptr<FluxSource> createStreamFluxSource(const std::string& path); | ||||
|  | ||||
| public: | ||||
|     static std::unique_ptr<FluxSource> create(const DataSpec& spec); | ||||
|     static std::unique_ptr<FluxSource> create(const FluxSpec& spec); | ||||
|  | ||||
| public: | ||||
|     virtual std::unique_ptr<Fluxmap> readFlux(int track, int side) = 0; | ||||
| @@ -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); | ||||
|   | ||||
							
								
								
									
										147
									
								
								lib/image.cc
									
									
									
									
									
								
							
							
						
						
									
										147
									
								
								lib/image.cc
									
									
									
									
									
								
							| @@ -1,150 +1,25 @@ | ||||
| #include "globals.h" | ||||
| #include "image.h" | ||||
| #include "flags.h" | ||||
| #include "dataspec.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "imagereader/imagereader.h" | ||||
| #include "imagewriter/imagewriter.h" | ||||
| #include "fmt/format.h" | ||||
| #include <algorithm> | ||||
| #include <iostream> | ||||
| #include <fstream> | ||||
|  | ||||
| Geometry guessGeometry(const SectorSet& sectors) | ||||
| SectorSet readSectorsFromFile(const ImageSpec& spec) | ||||
| { | ||||
| 	Geometry g; | ||||
| 	sectors.calculateSize(g.tracks, g.heads, g.sectors, g.sectorSize); | ||||
| 	return g; | ||||
| 	return ImageReader::create(spec)->readImage(); | ||||
| } | ||||
|  | ||||
| void readSectorsFromFile(SectorSet& sectors, const Geometry& geometry, | ||||
| 		const std::string& filename) | ||||
| void writeSectorsToFile(const SectorSet& sectors, const ImageSpec& spec) | ||||
| { | ||||
|     std::ifstream inputFile(filename, std::ios::in | std::ios::binary); | ||||
|     if (!inputFile.is_open()) | ||||
| 		Error() << "cannot open input file"; | ||||
|  | ||||
|     size_t headSize = geometry.sectors * geometry.sectorSize; | ||||
|     size_t trackSize = headSize * geometry.heads; | ||||
|  | ||||
|     std::cout << fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total", | ||||
| 					geometry.tracks, geometry.heads, | ||||
| 					geometry.sectors, geometry.sectorSize, | ||||
| 					geometry.tracks * trackSize / 1024) | ||||
| 			  << std::endl; | ||||
|  | ||||
| 	for (int track = 0; track < geometry.tracks; track++) | ||||
| 	{ | ||||
| 		for (int head = 0; head < geometry.heads; head++) | ||||
| 		{ | ||||
| 			for (int sectorId = 0; sectorId < geometry.sectors; sectorId++) | ||||
| 			{ | ||||
| 				inputFile.seekg(track*trackSize + head*headSize + sectorId*geometry.sectorSize, std::ios::beg); | ||||
|  | ||||
| 				Bytes data(geometry.sectorSize); | ||||
| 				inputFile.read((char*) data.begin(), geometry.sectorSize); | ||||
|  | ||||
| 				std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId); | ||||
| 				sector.reset(new Sector); | ||||
| 				sector->status = Sector::OK; | ||||
| 				sector->logicalTrack = sector->physicalTrack = track; | ||||
| 				sector->logicalSide = sector->physicalSide = head; | ||||
| 				sector->logicalSector = sectorId; | ||||
| 				sector->data = data; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void writeSectorsToFile(const SectorSet& sectors, const Geometry& geometry, | ||||
| 		const std::string& filename) | ||||
| { | ||||
| 	/* Emit the map. */ | ||||
|  | ||||
| 	int badSectors = 0; | ||||
| 	int missingSectors = 0; | ||||
| 	int totalSectors = 0; | ||||
| 	std::cout << "H.SS Tracks --->" << std::endl; | ||||
| 	for (int head = 0; head < geometry.heads; head++) | ||||
| 	{ | ||||
| 		for (int sectorId = 0; sectorId < geometry.sectors; sectorId++) | ||||
| 		{ | ||||
| 			std::cout << fmt::format("{}.{:2} ", head, sectorId); | ||||
| 			for (int track = 0; track < geometry.tracks; track++) | ||||
| 			{ | ||||
| 				Sector* sector = sectors.get(track, head, sectorId); | ||||
| 				if (!sector) | ||||
| 				{ | ||||
| 					std::cout << 'X'; | ||||
| 					missingSectors++; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					switch (sector->status) | ||||
| 					{ | ||||
| 						case Sector::OK: | ||||
|                             std::cout << '.'; | ||||
|                             break; | ||||
|  | ||||
|                         case Sector::BAD_CHECKSUM: | ||||
|                             std::cout << 'B'; | ||||
|                             badSectors++; | ||||
|                             break; | ||||
|  | ||||
|                         case Sector::CONFLICT: | ||||
|                             std::cout << 'C'; | ||||
|                             badSectors++; | ||||
|                             break; | ||||
|  | ||||
|                         default: | ||||
|                             std::cout << '?'; | ||||
|                             break; | ||||
|                     } | ||||
| 				} | ||||
| 				totalSectors++; | ||||
| 			} | ||||
| 			std::cout << std::endl; | ||||
| 		} | ||||
| 	} | ||||
| 	int goodSectors = totalSectors - missingSectors - badSectors; | ||||
| 	if (totalSectors == 0) | ||||
| 		std::cout << "No sectors in output; skipping analysis" << std::endl; | ||||
| 	else | ||||
| 	{ | ||||
| 		std::cout << "Good sectors: " << goodSectors << "/" << totalSectors | ||||
| 				  << " (" << (100*goodSectors/totalSectors) << "%)" | ||||
| 				  << std::endl; | ||||
| 		std::cout << "Missing sectors: " << missingSectors << "/" << totalSectors | ||||
| 				  << " (" << (100*missingSectors/totalSectors) << "%)" | ||||
| 				  << std::endl; | ||||
| 		std::cout << "Bad sectors: " << badSectors << "/" << totalSectors | ||||
| 				  << " (" << (100*badSectors/totalSectors) << "%)" | ||||
| 				  << std::endl; | ||||
|     } | ||||
|  | ||||
|     size_t headSize = geometry.sectors * geometry.sectorSize; | ||||
|     size_t trackSize = headSize * geometry.heads; | ||||
|  | ||||
|     std::cout << fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total", | ||||
| 					geometry.tracks, geometry.heads, | ||||
| 					geometry.sectors, geometry.sectorSize, | ||||
| 					geometry.tracks * trackSize / 1024) | ||||
| 			  << std::endl; | ||||
|  | ||||
|     std::ofstream outputFile(filename, std::ios::out | std::ios::binary); | ||||
|     if (!outputFile.is_open()) | ||||
| 		Error() << "cannot open output file"; | ||||
|  | ||||
| 	for (int track = 0; track < geometry.tracks; track++) | ||||
| 	{ | ||||
| 		for (int head = 0; head < geometry.heads; head++) | ||||
| 		{ | ||||
| 			for (int sectorId = 0; sectorId < geometry.sectors; sectorId++) | ||||
| 			{ | ||||
| 				auto sector = sectors.get(track, head, sectorId); | ||||
| 				if (sector) | ||||
| 				{ | ||||
| 					outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*geometry.sectorSize, std::ios::beg); | ||||
| 					outputFile.write((const char*) sector->data.cbegin(), sector->data.size()); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	std::unique_ptr<ImageWriter> writer(ImageWriter::create(sectors, spec)); | ||||
| 	writer->adjustGeometry(); | ||||
| 	writer->printMap(); | ||||
| 	writer->writeImage(); | ||||
| } | ||||
|   | ||||
							
								
								
									
										21
									
								
								lib/image.h
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								lib/image.h
									
									
									
									
									
								
							| @@ -2,26 +2,13 @@ | ||||
| #define IMAGE_H | ||||
|  | ||||
| class SectorSet; | ||||
| class ImageSpec; | ||||
|  | ||||
| class Geometry | ||||
| { | ||||
| public: | ||||
| 	int tracks; | ||||
| 	int heads; | ||||
| 	int sectors; | ||||
| 	int sectorSize; | ||||
| }; | ||||
|  | ||||
| extern Geometry guessGeometry(const SectorSet& sectors); | ||||
|  | ||||
| extern void readSectorsFromFile( | ||||
| 	SectorSet& sectors, | ||||
| 	const Geometry& geometry, | ||||
| 	const std::string& filename); | ||||
| extern SectorSet readSectorsFromFile( | ||||
| 	const ImageSpec& filename); | ||||
|  | ||||
| extern void writeSectorsToFile( | ||||
| 	const SectorSet& sectors, | ||||
| 	const Geometry& geometry, | ||||
| 	const std::string& filename); | ||||
| 	const ImageSpec& filename); | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										52
									
								
								lib/imagereader/imagereader.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								lib/imagereader/imagereader.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| #include "globals.h" | ||||
| #include "image.h" | ||||
| #include "flags.h" | ||||
| #include "dataspec.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #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()) | ||||
|         return false; | ||||
|     return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); | ||||
| } | ||||
|  | ||||
| ImageReader::Constructor ImageReader::findConstructor(const ImageSpec& spec) | ||||
| { | ||||
|     const auto& filename = spec.filename; | ||||
|  | ||||
| 	for (const auto& e : formats) | ||||
| 	{ | ||||
| 		if (ends_with(filename, e.first)) | ||||
| 			return e.second; | ||||
| 	} | ||||
|  | ||||
| 	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): | ||||
|     spec(spec) | ||||
| {} | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user