mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	Compare commits
	
		
			217 Commits
		
	
	
		
			qd
			...
			FluxEngine
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | a03283ce64 | ||
|  | 984cdaeb03 | ||
|  | a1ed4a9171 | ||
|  | 93caf8e549 | ||
|  | 3841942153 | ||
|  | 5706877b67 | ||
|  | d60900262b | ||
|  | 54ea34400b | ||
|  | db2ab8841a | ||
|  | adba93ae0a | ||
|  | 98587d04a7 | ||
|  | 0051b64648 | ||
|  | 603009ba15 | ||
|  | adb9809692 | ||
|  | 06eb10d2a0 | ||
|  | 2244299bd9 | ||
|  | 6ca06ecafb | ||
|  | 9a5958f80b | ||
|  | 2b53ac057c | ||
|  | 5deba8af41 | ||
|  | 3c54a663b8 | ||
|  | 1fd65452c4 | ||
|  | 30646ccb07 | ||
|  | 5be7249a30 | ||
|  | 067af18103 | ||
|  | 8dbd2a72a7 | ||
|  | c29e131a3b | ||
|  | a9e30c1e49 | ||
|  | 972c8c6b61 | ||
|  | 2007ff7546 | ||
|  | 64694580cd | ||
|  | deaab94494 | ||
|  | 1509e1f89d | ||
|  | 29e1ddc2ff | ||
|  | 1fe6434563 | ||
|  | 0367b7e77d | ||
|  | e6da85bf64 | ||
|  | cd19fcdadd | ||
|  | 1954f02cfb | ||
|  | 39b23200b0 | ||
|  | 0644d6d965 | ||
|  | a075694d8e | ||
|  | b1ea5a9a35 | ||
|  | 00087cbb6b | ||
|  | 1b48ea20c4 | ||
|  | 3d0f019fc4 | ||
|  | a08bfc183f | ||
|  | c5aef9b051 | ||
|  | fc2655ecd6 | ||
|  | a737c723d3 | ||
|  | 37aa8b62b0 | ||
|  | a401173f6d | ||
|  | ce76dc4279 | ||
|  | 1025bd857b | ||
|  | 025802b2d0 | ||
|  | adbcb2cd31 | ||
|  | c47a563790 | ||
|  | 04c09d1a5b | ||
|  | 323da8272a | ||
|  | 38700c79fc | ||
|  | d504d1890a | ||
|  | d53e757cfb | ||
|  | 4983239458 | ||
|  | 376985828a | ||
|  | 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 | 
| @@ -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 update && sudo apt install libusb-1.0-0-dev libsqlite3-dev ninja-build | ||||
|     - name: make | ||||
|       run: make | ||||
|  | ||||
|   build-macos: | ||||
|     runs-on: macos-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v1 | ||||
|       with: | ||||
|         fetch-depth: 1 | ||||
|     - name: brew | ||||
|       run: brew install sqlite pkg-config libusb ninja | ||||
|     - name: make | ||||
|       run: make | ||||
|  | ||||
| #  build-windows: | ||||
| #    runs-on: windows-latest | ||||
| #    steps: | ||||
| #    - uses: numworks/setup-msys2@v1 | ||||
| #      with: | ||||
| #        path-type: inherit | ||||
| #    - uses: actions/checkout@v1 | ||||
| #    - name: pacman | ||||
| #      run: | | ||||
| #        msys2do pacman -S --noconfirm --needed make ninja mingw-w64-i686-libusb mingw-w64-i686-sqlite3 mingw-w64-i686-zlib mingw-w64-i686-gcc zip | ||||
| #    - name: build | ||||
| #      run: | | ||||
| #        msys2do make | ||||
|  | ||||
							
								
								
									
										39
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,39 +0,0 @@ | ||||
| language: shell | ||||
| git: | ||||
|     depth: 1 | ||||
|  | ||||
| matrix: | ||||
|     include: | ||||
|         - | ||||
|             os: linux | ||||
|             sudo: false | ||||
|             dist: xenial | ||||
|             compiler: gcc | ||||
|             env: CXX=g++-8 | ||||
|             script: | ||||
|             - make | ||||
|         - | ||||
|             os: osx | ||||
|             osx_image: xcode10.2 | ||||
|             compiler: clang | ||||
|             env: | ||||
|             - HOMEBREW_NO_INSTALL_CLEANUP=1 | ||||
|  | ||||
| addons: | ||||
|     apt: | ||||
|         sources: | ||||
|         - llvm-toolchain-precise-3.8 | ||||
|         - ubuntu-toolchain-r-test | ||||
|         packages: | ||||
|         - ninja-build | ||||
|         - libusb-1.0-0-dev | ||||
|         - libsqlite3-dev | ||||
|         - g++-8 | ||||
|     homebrew: | ||||
|         packages: | ||||
|         - ninja | ||||
|  | ||||
| script: | ||||
| - make | ||||
|  | ||||
|  | ||||
							
								
								
									
										4626
									
								
								FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4626
									
								
								FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										27
									
								
								FluxEngine.cydsn/FIFOin/API/c.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								FluxEngine.cydsn/FIFOin/API/c.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #include "cyfitter_cfg.h" | ||||
| #include "cydevice_trm.h" | ||||
| #include "cyfitter.h" | ||||
| #include "`$INSTANCE_NAME`_h.h" | ||||
|  | ||||
| void `$INSTANCE_NAME`_Start() | ||||
| { | ||||
|    `$INSTANCE_NAME`_Init(); | ||||
| }     | ||||
|  | ||||
| void `$INSTANCE_NAME`_Stop() | ||||
| { | ||||
|     `$INSTANCE_NAME`_Disable(); | ||||
| } | ||||
|  | ||||
| void `$INSTANCE_NAME`_Init() | ||||
| {     | ||||
|     `$INSTANCE_NAME`_Enable(); | ||||
|      | ||||
| } | ||||
| void `$INSTANCE_NAME`_Enable() | ||||
| { | ||||
| } | ||||
|  | ||||
| void `$INSTANCE_NAME`_Disable() | ||||
| { | ||||
| } | ||||
							
								
								
									
										50
									
								
								FluxEngine.cydsn/FIFOin/API/h.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								FluxEngine.cydsn/FIFOin/API/h.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| #if !defined(`$INSTANCE_NAME`_H) | ||||
| #define `$INSTANCE_NAME`_H | ||||
|  | ||||
| #include "cytypes.h" | ||||
| #include "cyfitter.h" | ||||
| #include "CyLib.h"  | ||||
|  | ||||
| #define `$INSTANCE_NAME`_FIFO_PTR	         ((reg8 *) `$INSTANCE_NAME`_dp__F0_REG) | ||||
|  | ||||
|     /* Macros to clear DP FIFOs.*/ | ||||
| #define `$INSTANCE_NAME`_CLEAR do { \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x01u | \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)));\ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfeu & \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)));\ | ||||
|     } while(0) | ||||
|  | ||||
| /* Macros to set FIFO level mode. See the TRM for details */ | ||||
| #define `$INSTANCE_NAME`_SET_LEVEL_NORMAL \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfbu & \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
| #define `$INSTANCE_NAME`_SET_LEVEL_MID \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x04u | \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
|  | ||||
| /* Macros to set FIFO to single-buffer mode. */ | ||||
| #define `$INSTANCE_NAME`_SINGLE_BUFFER_SET \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x01u | \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
|  | ||||
| /* Macros to return the FIFO to normal mode. */ | ||||
| #define `$INSTANCE_NAME`_SINGLE_BUFFER_UNSET \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfeu & \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
|      | ||||
| void `$INSTANCE_NAME`_Enable(); | ||||
| void `$INSTANCE_NAME`_Disable(); | ||||
| void `$INSTANCE_NAME`_Start(); | ||||
| void `$INSTANCE_NAME`_Stop(); | ||||
| void `$INSTANCE_NAME`_Init(); | ||||
|  | ||||
| #endif | ||||
|  | ||||
| /* [] END OF FILE */ | ||||
							
								
								
									
										
											BIN
										
									
								
								FluxEngine.cydsn/FIFOin/FIFOin.cysym
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								FluxEngine.cydsn/FIFOin/FIFOin.cysym
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										128
									
								
								FluxEngine.cydsn/FIFOin/FIFOin.v
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								FluxEngine.cydsn/FIFOin/FIFOin.v
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
|  | ||||
| //`#start header` -- edit after this line, do not edit this line | ||||
| `include "cypress.v" | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
|  | ||||
| /* Ultra-simple FIFO in component: a byte is shifted in every clock when req | ||||
|  * is high. */ | ||||
|   | ||||
| module FIFOin (drq, clk, d, req); | ||||
| 	output  drq; | ||||
| 	input   clk; | ||||
| 	input  [7:0] d; | ||||
| 	input  req; | ||||
|  | ||||
| //`#start body` -- edit after this line, do not edit this line | ||||
|  | ||||
| wire [7:0] pi; | ||||
| assign pi = d; | ||||
|  | ||||
| wire load; | ||||
| assign load = req; | ||||
|  | ||||
| cy_psoc3_dp #(.cy_dpconfig( | ||||
| { | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM0:    */ | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM1:     */ | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM2:     */ | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM3:     */ | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM4:     */ | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM5:     */ | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM6:     */ | ||||
| 	`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, | ||||
| 	`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, | ||||
| 	`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, | ||||
| 	`CS_CMP_SEL_CFGA, /*CFGRAM7:     */ | ||||
| 	8'hFF, 8'h00,	/*CFG9:     */ | ||||
| 	8'hFF, 8'hFF,	/*CFG11-10:     */ | ||||
| 	`SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH, | ||||
| 	`SC_CI_A_ARITH, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL, | ||||
| 	`SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI, | ||||
| 	`SC_SI_A_DEFSI, /*CFG13-12:     */ | ||||
| 	`SC_A0_SRC_PIN, `SC_SHIFT_SL, 1'h0, | ||||
| 	1'h0, `SC_FIFO1_BUS, `SC_FIFO0_ALU, | ||||
| 	`SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN, | ||||
| 	`SC_FB_NOCHN, `SC_CMP1_NOCHN, | ||||
| 	`SC_CMP0_NOCHN, /*CFG15-14:     */ | ||||
| 	10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX, | ||||
| 	`SC_FIFO_LEVEL,`SC_FIFO__SYNC,`SC_EXTCRC_DSBL, | ||||
| 	`SC_WRK16CAT_DSBL /*CFG17-16:     */ | ||||
| } | ||||
| )) dp( | ||||
| 	/* input          */ .clk(clk), | ||||
| 	/* input [02:00]  */ .cs_addr(3'b0),    // Program counter | ||||
| 	/* input          */ .route_si(1'b0),   // Shift in | ||||
| 	/* input          */ .route_ci(1'b0),   // Carry in | ||||
| 	/* input          */ .f0_load(load),    // Load FIFO 0 | ||||
| 	/* input          */ .f1_load(1'b0), 	// Load FIFO 1 | ||||
| 	/* input          */ .d0_load(1'b0), 	// Load Data Register 0 | ||||
| 	/* input          */ .d1_load(1'b0), 	// Load Data Register 1 | ||||
| 	/* output         */ .ce0(), 			// Accumulator 0 = Data register 0 | ||||
| 	/* output         */ .cl0(), 			// Accumulator 0 < Data register 0 | ||||
| 	/* output         */ .z0(), 			// Accumulator 0 = 0 | ||||
| 	/* output         */ .ff0(), 			// Accumulator 0 = FF | ||||
| 	/* output         */ .ce1(), 			// Accumulator [0|1] = Data register 1 | ||||
| 	/* output         */ .cl1(), 			// Accumulator [0|1] < Data register 1 | ||||
| 	/* output         */ .z1(), 			// Accumulator 1 = 0 | ||||
| 	/* output         */ .ff1(), 			// Accumulator 1 = FF | ||||
| 	/* output         */ .ov_msb(), 		// Operation over flow | ||||
| 	/* output         */ .co_msb(), 		// Carry out | ||||
| 	/* output         */ .cmsb(), 			// Carry out | ||||
| 	/* output         */ .so(), 			// Shift out | ||||
|     /* output         */ .f0_bus_stat(drq), // not empty | ||||
| 	/* output         */ .f0_blk_stat(full),// full | ||||
| 	/* output         */ .f1_bus_stat(), 	// FIFO 1 status to uP | ||||
| 	/* output         */ .f1_blk_stat(), 	// FIFO 1 status to DP | ||||
| 	/* input          */ .ci(1'b0), 		// Carry in from previous stage | ||||
| 	/* output         */ .co(), 			// Carry out to next stage | ||||
| 	/* input          */ .sir(1'b0), 		// Shift in from right side | ||||
| 	/* output         */ .sor(), 			// Shift out to right side | ||||
| 	/* input          */ .sil(1'b0), 		// Shift in from left side | ||||
| 	/* output         */ .sol(), 			// Shift out to left side | ||||
| 	/* input          */ .msbi(1'b0), 		// MSB chain in | ||||
| 	/* output         */ .msbo(), 			// MSB chain out | ||||
| 	/* input [01:00]  */ .cei(2'b0),        // Compare equal in from prev stage | ||||
| 	/* output [01:00] */ .ceo(),            // Compare equal out to next stage | ||||
| 	/* input [01:00]  */ .cli(2'b0), 	    // Compare less than in from prv stage | ||||
| 	/* output [01:00] */ .clo(),            // Compare less than out to next stage | ||||
| 	/* input [01:00]  */ .zi(2'b0),         // Zero detect in from previous stage | ||||
| 	/* output [01:00] */ .zo(),             // Zero detect out to next stage | ||||
| 	/* input [01:00]  */ .fi(2'b0), 		// 0xFF detect in from previous stage | ||||
| 	/* output [01:00] */ .fo(), 	        // 0xFF detect out to next stage | ||||
| 	/* input [01:00]  */ .capi(2'b0),	    // Capture in from previous stage | ||||
| 	/* output [01:00] */ .capo(),		    // Capture out to next stage | ||||
| 	/* input          */ .cfbi(1'b0), 		// CRC Feedback in from previous stage | ||||
| 	/* output         */ .cfbo(), 			// CRC Feedback out to next stage | ||||
| 	/* input [07:00]  */ .pi(pi), 		    // Parallel data port | ||||
| 	/* output [07:00] */ .po()              // Parallel data port | ||||
| ); | ||||
|  | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
| endmodule | ||||
| //`#start footer` -- edit after this line, do not edit this line | ||||
| //`#end` -- edit above this line, do not edit this line | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										27
									
								
								FluxEngine.cydsn/FIFOout/API/c.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								FluxEngine.cydsn/FIFOout/API/c.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #include "cyfitter_cfg.h" | ||||
| #include "cydevice_trm.h" | ||||
| #include "cyfitter.h" | ||||
| #include "`$INSTANCE_NAME`_h.h" | ||||
|  | ||||
| void `$INSTANCE_NAME`_Start() | ||||
| { | ||||
|    `$INSTANCE_NAME`_Init(); | ||||
| }     | ||||
|  | ||||
| void `$INSTANCE_NAME`_Stop() | ||||
| { | ||||
|     `$INSTANCE_NAME`_Disable(); | ||||
| } | ||||
|  | ||||
| void `$INSTANCE_NAME`_Init() | ||||
| {     | ||||
|     `$INSTANCE_NAME`_Enable(); | ||||
|      | ||||
| } | ||||
| void `$INSTANCE_NAME`_Enable() | ||||
| { | ||||
| } | ||||
|  | ||||
| void `$INSTANCE_NAME`_Disable() | ||||
| { | ||||
| } | ||||
							
								
								
									
										50
									
								
								FluxEngine.cydsn/FIFOout/API/h.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								FluxEngine.cydsn/FIFOout/API/h.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| #if !defined(`$INSTANCE_NAME`_H) | ||||
| #define `$INSTANCE_NAME`_H | ||||
|  | ||||
| #include "cytypes.h" | ||||
| #include "cyfitter.h" | ||||
| #include "CyLib.h"  | ||||
|  | ||||
| #define `$INSTANCE_NAME`_FIFO_PTR	         ((reg8 *) `$INSTANCE_NAME`_dp__F0_REG) | ||||
|  | ||||
|     /* Macros to clear DP FIFOs.*/ | ||||
| #define `$INSTANCE_NAME`_CLEAR do { \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x01u | \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)));\ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfeu & \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)));\ | ||||
|     } while(0) | ||||
|  | ||||
| /* Macros to set FIFO level mode. See the TRM for details */ | ||||
| #define `$INSTANCE_NAME`_SET_LEVEL_NORMAL \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfbu & \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
| #define `$INSTANCE_NAME`_SET_LEVEL_MID \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x04u | \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
|  | ||||
| /* Macros to set FIFO to single-buffer mode. */ | ||||
| #define `$INSTANCE_NAME`_SINGLE_BUFFER_SET \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x01u | \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
|  | ||||
| /* Macros to return the FIFO to normal mode. */ | ||||
| #define `$INSTANCE_NAME`_SINGLE_BUFFER_UNSET \ | ||||
|     CY_SET_XTND_REG8(\ | ||||
|         ((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfeu & \ | ||||
|         CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG))) | ||||
|      | ||||
| void `$INSTANCE_NAME`_Enable(); | ||||
| void `$INSTANCE_NAME`_Disable(); | ||||
| void `$INSTANCE_NAME`_Start(); | ||||
| void `$INSTANCE_NAME`_Stop(); | ||||
| void `$INSTANCE_NAME`_Init(); | ||||
|  | ||||
| #endif | ||||
|  | ||||
| /* [] END OF FILE */ | ||||
							
								
								
									
										
											BIN
										
									
								
								FluxEngine.cydsn/FIFOout/FIFOout.cysym
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								FluxEngine.cydsn/FIFOout/FIFOout.cysym
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										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,45 +823,59 @@ | ||||
|   <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> | ||||
|     <Group key="Pin"> | ||||
|       <Data key="3e1862bb-be82-47b0-9549-7ebfe76b6f7b" value="Pin_2" /> | ||||
|       <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" /> | ||||
|       <Data key="472f8fdb-f772-44fb-8897-cc690694237b" value="WDATA" /> | ||||
|       <Data key="736cb12b-c863-43d4-a8f0-42f06023f8b5" value="SIDE1" /> | ||||
|       <Data key="4249c923-fcff-453b-8629-bec6fddd00c1" value="STEP" /> | ||||
|       <Data key="27315b0e-6a8c-4b7f-be77-73ab434fa803" value="Pin_1" /> | ||||
|       <Data key="1425177d-0d0e-4468-8bcc-e638e5509a9b" value="UartRx" /> | ||||
|       <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" /> | ||||
| @@ -3737,6 +3965,11 @@ | ||||
|     </Group> | ||||
|   </Group> | ||||
|   <Group key="Pin2"> | ||||
|     <Group key="3e1862bb-be82-47b0-9549-7ebfe76b6f7b"> | ||||
|       <Group key="0"> | ||||
|         <Data key="Port Format" value="0,6" /> | ||||
|       </Group> | ||||
|     </Group> | ||||
|     <Group key="4a398466-709f-4228-9500-96178658e13e"> | ||||
|       <Group key="0"> | ||||
|         <Data key="Port Format" value="1,5" /> | ||||
| @@ -3752,6 +3985,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" /> | ||||
| @@ -3833,6 +4071,11 @@ | ||||
|         <Data key="Port Format" value="1,0" /> | ||||
|       </Group> | ||||
|     </Group> | ||||
|     <Group key="27315b0e-6a8c-4b7f-be77-73ab434fa803"> | ||||
|       <Group key="0"> | ||||
|         <Data key="Port Format" value="0,7" /> | ||||
|       </Group> | ||||
|     </Group> | ||||
|     <Group key="1425177d-0d0e-4468-8bcc-e638e5509a9b"> | ||||
|       <Group key="0"> | ||||
|         <Data key="Port Format" value="12,6" /> | ||||
| @@ -3859,6 +4102,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 +4163,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 +4181,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.
										
									
								
							
							
								
								
									
										83
									
								
								FluxEngine.cydsn/Sampler/Sampler.v
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								FluxEngine.cydsn/Sampler/Sampler.v
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
|  | ||||
| //`#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 12/11/2019 at 21:18 | ||||
| // Component: Sampler | ||||
| module Sampler ( | ||||
| 	output [2:0] debug_state, | ||||
| 	output reg [7:0] opcode, | ||||
| 	output  reg       req, | ||||
| 	input   clock, | ||||
| 	input   index, | ||||
| 	input   rdata, | ||||
| 	input   reset, | ||||
| 	input   sampleclock | ||||
| ); | ||||
|  | ||||
| //`#start body` -- edit after this line, do not edit this line | ||||
|  | ||||
| // NOTE: Reset pulse is used in both clock domains, and must be long enough | ||||
| // to be detected in both. | ||||
|  | ||||
| reg [5:0] counter; | ||||
|  | ||||
| reg index_q; | ||||
| reg rdata_q; | ||||
|  | ||||
| reg index_edge; | ||||
| reg rdata_edge; | ||||
|  | ||||
| reg req_toggle; | ||||
|  | ||||
| always @(posedge sampleclock) | ||||
| begin | ||||
|     if (reset) | ||||
|     begin | ||||
|         index_edge <= 0; | ||||
|         rdata_edge <= 0; | ||||
|         index_q <= 0; | ||||
|         rdata_q <= 0; | ||||
|         counter <= 0; | ||||
|         req_toggle <= 0; | ||||
|     end | ||||
|     else | ||||
|     begin | ||||
|         /* Both index and rdata are active high -- positive-going edges | ||||
|          * indicate the start of an index pulse and read pulse, respectively. | ||||
|          */ | ||||
|           | ||||
|         index_edge <= index && !index_q; | ||||
|         index_q <= index; | ||||
|          | ||||
|         rdata_edge <= rdata && !rdata_q; | ||||
|         rdata_q <= rdata; | ||||
|          | ||||
|         if (rdata_edge || index_edge || (counter == 6'h3f)) begin | ||||
|             opcode <= { rdata_edge, index_edge, counter }; | ||||
|             req_toggle <= ~req_toggle; | ||||
|             counter <= 1;   /* remember to count this tick */ | ||||
|         end else begin | ||||
|             counter <= counter + 1; | ||||
|         end | ||||
|     end | ||||
| end | ||||
|  | ||||
| reg req_toggle_q; | ||||
|  | ||||
| always @(posedge clock) | ||||
| begin | ||||
|     if (reset) begin | ||||
|         req_toggle_q <= 0; | ||||
|         req <= 0; | ||||
|     end else begin | ||||
|         req_toggle_q <= req_toggle; | ||||
|         req <= (req_toggle != req_toggle_q); | ||||
|     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 | ||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										91
									
								
								FluxEngine.cydsn/Sequencer/Sequencer.v
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								FluxEngine.cydsn/Sequencer/Sequencer.v
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
|  | ||||
| //`#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_WRITING = 1; | ||||
|  | ||||
| reg state; | ||||
| reg [5:0] countdown; | ||||
| reg pulsepending; | ||||
|  | ||||
| assign req = (!reset && (state == STATE_LOAD)); | ||||
| assign wdata = (!reset && (state == STATE_WRITING) && (countdown == 0) && pulsepending); | ||||
| assign debug_state = 0; | ||||
|  | ||||
| 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; | ||||
|         pulsepending <= 0; | ||||
|         oldsampleclock <= 0; | ||||
|     end | ||||
|     else | ||||
|     begin | ||||
|         if (!oldsampleclock && sampleclock) | ||||
|             sampleclocked <= 1; | ||||
|         oldsampleclock <= sampleclock; | ||||
|              | ||||
|         case (state) | ||||
|             STATE_LOAD: | ||||
|             begin | ||||
|                 /* A posedge on dataclocked indicates that another opcode has | ||||
|                  * arrived. */ | ||||
|                 if (dataclocked) | ||||
|                 begin | ||||
|                     pulsepending <= opcode[7]; | ||||
|                     countdown <= opcode[5:0] - 1; /* compensate for delay in last tick */ | ||||
|                     state <= STATE_WRITING; | ||||
|                 end | ||||
|             end | ||||
|              | ||||
|             STATE_WRITING: | ||||
|             begin | ||||
|                 if (sampleclocked) | ||||
|                 begin | ||||
|                     if (countdown == 0) | ||||
|                         state <= STATE_LOAD; | ||||
|                     else | ||||
|                         countdown <= countdown - 1; | ||||
|                     sampleclocked <= 0; | ||||
|                 end | ||||
|             end | ||||
|         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.
										
									
								
							| @@ -5,7 +5,6 @@ | ||||
| #include <setjmp.h> | ||||
| #include "project.h" | ||||
| #include "../protocol.h" | ||||
| #include "../lib/common/crunch.h" | ||||
|  | ||||
| #define MOTOR_ON_TIME 5000 /* milliseconds */ | ||||
| #define STEP_INTERVAL_TIME 6 /* ms */ | ||||
| @@ -17,16 +16,19 @@ | ||||
| #define STEP_TOWARDS0 1 | ||||
| #define STEP_AWAYFROM0 0 | ||||
|  | ||||
| static volatile uint32_t clock = 0; | ||||
| static bool drive0_present; | ||||
| static bool drive1_present; | ||||
|  | ||||
| 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())); | ||||
| @@ -41,10 +43,27 @@ static volatile bool dma_underrun = false; | ||||
| #define DECLARE_REPLY_FRAME(STRUCT, TYPE) \ | ||||
|     STRUCT r = {.f = { .type = TYPE, .size = sizeof(STRUCT) }} | ||||
|  | ||||
| static void stop_motor(void); | ||||
|  | ||||
| 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 +104,25 @@ 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) | ||||
|     { | ||||
|         stop_motor(); | ||||
|         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,12 +133,28 @@ 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) | ||||
|         ; | ||||
| } | ||||
|  | ||||
| static void wait_until_readable(int ep) | ||||
| { | ||||
|     while (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_FULL) | ||||
|         ; | ||||
| } | ||||
|  | ||||
| static void send_reply(struct any_frame* f) | ||||
| { | ||||
|     print("reply 0x%02x", f->f.type); | ||||
| @@ -122,9 +172,15 @@ static void send_error(int code) | ||||
| /* buffer must be big enough for a frame */ | ||||
| static int usb_read(int ep, uint8_t buffer[FRAME_SIZE]) | ||||
| { | ||||
|     if (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_FULL) | ||||
|     { | ||||
|         USBFS_EnableOutEP(ep); | ||||
|         wait_until_readable(ep); | ||||
|     } | ||||
|  | ||||
|     int length = USBFS_GetEPCount(ep); | ||||
|     USBFS_ReadOutEP(ep, buffer, length); | ||||
|     while (USBFS_GetEPState(ep) == USBFS_OUT_BUFFER_FULL) | ||||
|     while (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_EMPTY) | ||||
|         ; | ||||
|     return length; | ||||
| } | ||||
| @@ -138,25 +194,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); | ||||
| } | ||||
|  | ||||
| /* returns true if it looks like a drive is attached */ | ||||
| static bool 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()) | ||||
|             return true; | ||||
|         step(STEP_TOWARDS0); | ||||
|     } | ||||
|      | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| 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 +234,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 +271,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 +313,44 @@ 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++) | ||||
|         { | ||||
|             usb_read(FLUXENGINE_DATA_OUT_EP_NUM, buffer); | ||||
|             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,8 +359,8 @@ static void deinit_dma(void) | ||||
|  | ||||
| static void init_capture_dma(void) | ||||
| { | ||||
|     dma_channel = CAPTURE_DMA_DmaInitialize( | ||||
|         2 /* bytes */, | ||||
|     dma_channel = SAMPLER_DMA_DmaInitialize( | ||||
|         1 /* bytes */, | ||||
|         true /* request per burst */,  | ||||
|         HI16(CYDEV_PERIPH_BASE), | ||||
|         HI16(CYDEV_SRAM_BASE)); | ||||
| @@ -264,8 +374,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 +386,31 @@ 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; | ||||
|      | ||||
|     crunch_state_t cs = {}; | ||||
|     cs.outputptr = usb_buffer; | ||||
|     cs.outputlen = BUFFER_SIZE; | ||||
|     if (f->synced) | ||||
|     { | ||||
|         index_irq = false; | ||||
|         while (!index_irq) | ||||
|             ; | ||||
|         index_irq = false; | ||||
|     } | ||||
|      | ||||
|     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 +418,67 @@ 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. */ | ||||
|              | ||||
|             wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM); | ||||
|             USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, dma_buffer[dma_reading_from_td], BUFFER_SIZE); | ||||
|             count++; | ||||
|             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); | ||||
|     /* Terminate the transfer (all transfers are an exact number of fragments). */ | ||||
|     wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM); | ||||
|     unsigned zz = cs.outputlen; | ||||
|     if (cs.outputlen != BUFFER_SIZE) | ||||
|         USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE-cs.outputlen); | ||||
|     if ((cs.outputlen == BUFFER_SIZE) || (cs.outputlen == 0)) | ||||
|         USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, NULL, 0); | ||||
|     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 +488,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,46 +509,47 @@ 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);     | ||||
|  | ||||
|     init_replay_dma(); | ||||
|     bool writing = false; /* to the disk */ | ||||
|     bool finished = false; | ||||
|     int packets = f->bytes_to_write / FRAME_SIZE; | ||||
|     bool finished = (packets == 0); | ||||
|     int count_written = 0; | ||||
|     int count_read = 0; | ||||
|     dma_writing_to_td = 0; | ||||
|     dma_reading_from_td = -1; | ||||
|     dma_underrun = false; | ||||
|  | ||||
|     crunch_state_t cs = {}; | ||||
|     cs.outputlen = BUFFER_SIZE; | ||||
|     USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM); | ||||
|      | ||||
|     int old_reading_from_td = -1; | ||||
|     for (;;) | ||||
|     { | ||||
|         //CyWdtClear(); | ||||
|  | ||||
|         /* Read data from USB into the buffers. */ | ||||
|          | ||||
|         if (NEXT_BUFFER(dma_writing_to_td) != dma_reading_from_td) | ||||
| @@ -459,54 +557,25 @@ static void cmd_write(struct write_frame* f) | ||||
|             if (writing && (dma_underrun || index_irq)) | ||||
|                 goto abort; | ||||
|              | ||||
|             /* Read crunched data, if necessary. */ | ||||
|              | ||||
|             if (cs.inputlen == 0) | ||||
|             uint8_t* buffer = dma_buffer[dma_writing_to_td]; | ||||
|             if (finished) | ||||
|             { | ||||
|                 if (finished) | ||||
|                 { | ||||
|                     /* 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; | ||||
|                     cs.inputlen = BUFFER_SIZE; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     while (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) != USBFS_OUT_BUFFER_FULL) | ||||
|                     { | ||||
|                         if (writing && (dma_underrun || index_irq)) | ||||
|                             goto abort; | ||||
|                     } | ||||
|  | ||||
|                     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; | ||||
|                 } | ||||
|                 /* There's no more data to read, so fake some. */ | ||||
|                  | ||||
|                 memset(buffer, 0x3f, BUFFER_SIZE); | ||||
|             } | ||||
|              | ||||
|             /* If there *is* data waiting in the buffer, uncrunch it. */ | ||||
|              | ||||
|             if (cs.inputlen != 0) | ||||
|             else | ||||
|             { | ||||
|                 cs.outputptr = dma_buffer[dma_writing_to_td] + BUFFER_SIZE - cs.outputlen; | ||||
|                 uncrunch(&cs); | ||||
|                 if (cs.outputlen == 0) | ||||
|                 { | ||||
|                     /* Completed a DMA buffer; queue it for writing. */ | ||||
|                      | ||||
|                     dma_writing_to_td = NEXT_BUFFER(dma_writing_to_td); | ||||
|                     cs.outputlen = BUFFER_SIZE; | ||||
|                 } | ||||
|                 (void) usb_read(FLUXENGINE_DATA_OUT_EP_NUM, buffer); | ||||
|                 count_read++; | ||||
|                  | ||||
|                 if (count_read == packets) | ||||
|                     finished = true; | ||||
|             } | ||||
|             dma_writing_to_td = NEXT_BUFFER(dma_writing_to_td); | ||||
|              | ||||
|             /* Once all the buffers are full, start writing. */ | ||||
|              | ||||
|             /* If we have a full buffer, 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 +590,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 +601,7 @@ static void cmd_write(struct write_frame* f) | ||||
|                 SEQUENCER_CONTROL_Write(0); /* start writing! */ | ||||
|             } | ||||
|         } | ||||
|          | ||||
|  | ||||
|         if (writing && (dma_underrun || index_irq)) | ||||
|             goto abort; | ||||
|  | ||||
| @@ -555,31 +624,28 @@ 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) | ||||
|             { | ||||
|                 int length = usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer); | ||||
|                 if (length < FRAME_SIZE) | ||||
|                     break; | ||||
|                 USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM); | ||||
|                 count_read++; | ||||
|             } | ||||
|             (void) usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer); | ||||
|             count_read++; | ||||
|         } | ||||
|         USBFS_DisableOutEP(FLUXENGINE_DATA_OUT_EP_NUM); | ||||
|     } | ||||
|      | ||||
|     deinit_dma(); | ||||
|      | ||||
|     if (dma_underrun) | ||||
|     { | ||||
|         print("underrun!"); | ||||
|         send_error(F_ERROR_UNDERRUN); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     print("success"); | ||||
|     DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_WRITE_REPLY); | ||||
|     send_reply((struct any_frame*) &r); | ||||
| } | ||||
| @@ -607,17 +673,105 @@ 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; | ||||
|     } | ||||
|     if (drive0_present && !drive1_present) | ||||
|         f->drive = 0; | ||||
|     if (drive1_present && !drive0_present) | ||||
|         f->drive = 1; | ||||
|     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 +793,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,28 +820,49 @@ 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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void detect_drives(void) | ||||
| { | ||||
|     current_drive_flags.drive = 0; | ||||
|     start_motor(); | ||||
|     drive0_present = home(); | ||||
|     stop_motor(); | ||||
|      | ||||
|     current_drive_flags.drive = 1; | ||||
|     start_motor(); | ||||
|     drive1_present = home(); | ||||
|     stop_motor(); | ||||
|      | ||||
|     print("drive 0: %s drive 1: %s", drive0_present ? "yes" : "no", drive1_present ? "yes" : "no"); | ||||
| } | ||||
|  | ||||
| int main(void) | ||||
| { | ||||
|     CyGlobalIntEnable; | ||||
|     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); | ||||
|     USBFS_DisableOutEP(FLUXENGINE_DATA_OUT_EP_NUM); | ||||
|      | ||||
|     detect_drives(); | ||||
|     CyWdtStart(CYWDT_1024_TICKS, CYWDT_LPMODE_DISABLED); | ||||
|      | ||||
|     /* UART_PutString("GO\r"); */ | ||||
|  | ||||
|     for (;;) | ||||
|     { | ||||
|         CyWdtClear(); | ||||
| @@ -692,23 +871,21 @@ 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()) | ||||
|         { | ||||
|             print("Waiting for USB..."); | ||||
|             while (!USBFS_GetConfiguration()) | ||||
|                 ; | ||||
|                 CyWdtClear(); | ||||
|             print("USB ready"); | ||||
|             USBFS_EnableOutEP(FLUXENGINE_CMD_OUT_EP_NUM); | ||||
|         } | ||||
|          | ||||
|         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 | ||||
| @@ -27,7 +39,7 @@ CFLAGS += -Ilib -Idep/fmt -Iarch | ||||
| export OBJDIR = .obj | ||||
|  | ||||
| all: .obj/build.ninja | ||||
| 	@ninja -f .obj/build.ninja -v | ||||
| 	@ninja -f .obj/build.ninja | ||||
|  | ||||
| clean: | ||||
| 	@echo CLEAN | ||||
|   | ||||
							
								
								
									
										31
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								README.md
									
									
									
									
									
								
							| @@ -24,17 +24,10 @@ Don't believe me? Watch the demo reel! | ||||
| <iframe width="373" height="210" src="https://www.youtube.com/embed/m_s1iw8eW7o" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> | ||||
| </div> | ||||
|  | ||||
| **Important note.** On 2019-02-09 I did a hardware redesign and moved the pins on | ||||
| the board. Sorry for the inconvenience, but it means you don't have to modify | ||||
| the board any more to make it work. If you built the hardware prior to then, | ||||
| you'll need to adjust it. | ||||
|  | ||||
| **Another important note.** On 2019-07-03 I've revamped the build process and | ||||
| the (command line) user interface. It should be much nicer now, not least in | ||||
| that there's a single client binary with all the functionality in it. The | ||||
| interface is a little different, but not much. The build process is now | ||||
| better (simpler). See [the building](doc/building.md) and | ||||
| [using](doc/using.md) pages for more details. | ||||
| **Important note.** On 2020-04-02 I changed the bytecode format (and firmware). | ||||
| Flux files will need to be upgraded with `fluxengine upgradefluxfile`. The new | ||||
| format should be more reliable and use way, way less bandwidth. Sorry for the | ||||
| inconvenience. | ||||
|  | ||||
| Where? | ||||
| ------ | ||||
| @@ -79,20 +72,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()); | ||||
| } | ||||
| @@ -1,12 +1,17 @@ | ||||
| #ifndef AMIGA_H | ||||
| #define AMIGA_H | ||||
|  | ||||
| #include "encoders/encoders.h" | ||||
|  | ||||
| #define AMIGA_SECTOR_RECORD 0xaaaa44894489LL | ||||
|  | ||||
| #define AMIGA_TRACKS_PER_DISK 80 | ||||
| #define AMIGA_SECTORS_PER_TRACK 11 | ||||
| #define AMIGA_RECORD_SIZE 0x21f | ||||
|  | ||||
| class Sector; | ||||
| class Fluxmap; | ||||
| class SectorSet; | ||||
|  | ||||
| class AmigaDecoder : public AbstractDecoder | ||||
| { | ||||
| @@ -15,6 +20,24 @@ public: | ||||
|  | ||||
|     RecordType advanceToNextRecord(); | ||||
|     void decodeSectorRecord(); | ||||
|  | ||||
| 	std::set<unsigned> requiredSectors(Track& track) const; | ||||
| }; | ||||
|  | ||||
| class AmigaEncoder : public AbstractEncoder | ||||
| { | ||||
| public: | ||||
| 	virtual ~AmigaEncoder() {} | ||||
|  | ||||
| public: | ||||
|     std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors); | ||||
| }; | ||||
|  | ||||
| extern FlagGroup amigaEncoderFlags; | ||||
|  | ||||
| extern uint32_t amigaChecksum(const Bytes& bytes); | ||||
| extern Bytes amigaInterleave(const Bytes& input); | ||||
| extern Bytes amigaDeinterleave(const uint8_t*& input, size_t len); | ||||
| extern Bytes amigaDeinterleave(const Bytes& input); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -21,47 +21,6 @@ | ||||
|           | ||||
| static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD); | ||||
|  | ||||
| static Bytes deinterleave(const uint8_t*& input, size_t len) | ||||
| { | ||||
|     assert(!(len & 1)); | ||||
|     const uint8_t* odds = &input[0]; | ||||
|     const uint8_t* evens = &input[len/2]; | ||||
|     Bytes output; | ||||
|     ByteWriter bw(output); | ||||
|  | ||||
|     for (size_t i=0; i<len/2; i++) | ||||
|     { | ||||
|         uint8_t o = *odds++; | ||||
|         uint8_t e = *evens++; | ||||
|  | ||||
|         /* This is the 'Interleave bits with 64-bit multiply' technique from | ||||
|          * http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN | ||||
|          */ | ||||
|         uint16_t result = | ||||
|             (((e * 0x0101010101010101ULL & 0x8040201008040201ULL) | ||||
|                 * 0x0102040810204081ULL >> 49) & 0x5555) | | ||||
|             (((o * 0x0101010101010101ULL & 0x8040201008040201ULL) | ||||
|                 * 0x0102040810204081ULL >> 48) & 0xAAAA); | ||||
|          | ||||
|         bw.write_be16(result); | ||||
|     } | ||||
|  | ||||
|     input += len; | ||||
|     return output; | ||||
| } | ||||
|  | ||||
| static uint32_t checksum(const Bytes& bytes) | ||||
| { | ||||
|     ByteReader br(bytes); | ||||
|     uint32_t checksum = 0; | ||||
|  | ||||
|     assert((bytes.size() & 3) == 0); | ||||
|     while (!br.eof()) | ||||
|         checksum ^= br.read_be32(); | ||||
|  | ||||
|     return checksum & 0x55555555; | ||||
| } | ||||
|  | ||||
| AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord() | ||||
| { | ||||
|     _sector->clock = _fmr->seekToPattern(SECTOR_PATTERN); | ||||
| @@ -73,27 +32,36 @@ AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord() | ||||
| void AmigaDecoder::decodeSectorRecord() | ||||
| { | ||||
|     const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16); | ||||
| 	if (rawbits.size() < (AMIGA_RECORD_SIZE*16)) | ||||
| 		return; | ||||
|     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); | ||||
|     Bytes header = amigaDeinterleave(ptr, 4); | ||||
|     Bytes recoveryinfo = amigaDeinterleave(ptr, 16); | ||||
|  | ||||
|     _sector->logicalTrack = header[1] >> 1; | ||||
|     _sector->logicalSide = header[1] & 1; | ||||
|     _sector->logicalSector = header[2]; | ||||
|  | ||||
|     uint32_t wantedheaderchecksum = deinterleave(ptr, 4).reader().read_be32(); | ||||
|     uint32_t gotheaderchecksum = checksum(rawbytes.slice(6, 40)); | ||||
|     uint32_t wantedheaderchecksum = amigaDeinterleave(ptr, 4).reader().read_be32(); | ||||
|     uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(6, 40)); | ||||
|     if (gotheaderchecksum != wantedheaderchecksum) | ||||
|         return; | ||||
|  | ||||
|     uint32_t wanteddatachecksum = deinterleave(ptr, 4).reader().read_be32(); | ||||
|     uint32_t gotdatachecksum = checksum(rawbytes.slice(62, 1024)); | ||||
|     uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32(); | ||||
|     uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(62, 1024)); | ||||
|  | ||||
|     _sector->data.clear(); | ||||
|     _sector->data.writer().append(deinterleave(ptr, 512)).append(recoveryinfo); | ||||
|     _sector->data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo); | ||||
|     _sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
| } | ||||
|  | ||||
| std::set<unsigned> AmigaDecoder::requiredSectors(Track& track) const | ||||
| { | ||||
| 	static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | ||||
| 	return sectors; | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										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; | ||||
| } | ||||
|  | ||||
| @@ -9,7 +9,8 @@ | ||||
| #define BROTHER_DATA_RECORD_CHECKSUM     3 | ||||
| #define BROTHER_DATA_RECORD_ENCODED_SIZE 415 | ||||
|  | ||||
| #define BROTHER_TRACKS_PER_DISK          78 | ||||
| #define BROTHER_TRACKS_PER_240KB_DISK    78 | ||||
| #define BROTHER_TRACKS_PER_120KB_DISK    39 | ||||
| #define BROTHER_SECTORS_PER_TRACK        12 | ||||
|  | ||||
| class Sector; | ||||
| @@ -28,8 +29,16 @@ public: | ||||
| class BrotherEncoder : public AbstractEncoder | ||||
| { | ||||
| public: | ||||
| 	BrotherEncoder(int format, int bias): | ||||
| 		_format(format), | ||||
| 		_bias(bias) | ||||
| 	{} | ||||
|  | ||||
| 	virtual ~BrotherEncoder() {} | ||||
|  | ||||
| private: | ||||
| 	int _format; | ||||
| 	int _bias; | ||||
| public: | ||||
|     std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors); | ||||
| }; | ||||
|   | ||||
| @@ -129,9 +129,25 @@ static int charToInt(char c) | ||||
| std::unique_ptr<Fluxmap> BrotherEncoder::encode( | ||||
| 	int physicalTrack, int physicalSide, const SectorSet& allSectors) | ||||
| { | ||||
| 	if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_DISK) | ||||
|         || (physicalSide != 0)) | ||||
| 	int logicalTrack; | ||||
| 	if (physicalSide != 0) | ||||
| 		return std::unique_ptr<Fluxmap>(); | ||||
| 	physicalTrack -= _bias; | ||||
| 	switch (_format) | ||||
| 	{ | ||||
| 		case 120: | ||||
| 			if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2)) | ||||
| 					|| (physicalTrack & 1)) | ||||
| 				return std::unique_ptr<Fluxmap>(); | ||||
| 			logicalTrack = physicalTrack/2; | ||||
| 			break; | ||||
|  | ||||
| 		case 240: | ||||
| 			if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK)) | ||||
| 				return std::unique_ptr<Fluxmap>(); | ||||
| 			logicalTrack = physicalTrack; | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| 	int bitsPerRevolution = 200000.0 / clockRateUs; | ||||
| 	const std::string& skew = sectorSkew.get(); | ||||
| @@ -146,15 +162,15 @@ std::unique_ptr<Fluxmap> BrotherEncoder::encode( | ||||
| 		double dataMs = headerMs + postHeaderSpacingMs; | ||||
| 		unsigned dataCursor = dataMs*1e3 / clockRateUs; | ||||
|  | ||||
| 		const auto& sectorData = allSectors.get(physicalTrack, 0, sectorId); | ||||
| 		const auto& sectorData = allSectors.get(logicalTrack, 0, sectorId); | ||||
|  | ||||
| 		fillBitmapTo(bits, cursor, headerCursor, { true, false }); | ||||
| 		write_sector_header(bits, cursor, physicalTrack, sectorId); | ||||
| 		write_sector_header(bits, cursor, logicalTrack, sectorId); | ||||
| 		fillBitmapTo(bits, cursor, dataCursor, { true, false }); | ||||
| 		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 }); | ||||
|  | ||||
|   | ||||
| @@ -57,11 +57,11 @@ const FluxPattern FM_TRS80DAM1_PATTERN(16, 0xf56b); | ||||
|  | ||||
| /*  | ||||
|  * TRS80DAM2 record: | ||||
|  * flux:   XXXX-X-X-XX-XXX- = 0xf56c | ||||
|  * flux:   XXXX-X-X-XX-XXX- = 0xf56e | ||||
|  * clock:  X X - - - X X X  = 0xc7 | ||||
|  * data:    X X X X X - X - = 0xfa | ||||
|  */ | ||||
| const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56c); | ||||
| const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56e); | ||||
|  | ||||
| /* MFM record separator: | ||||
|  * 0xA1 is: | ||||
| @@ -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() | ||||
|   | ||||
							
								
								
									
										236
									
								
								arch/ibm/encoder.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								arch/ibm/encoder.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,236 @@ | ||||
| #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; | ||||
| 			hexdump(std::cout, data.slice(0, 64)); | ||||
| 			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,66 +32,68 @@ struct IbmIdam | ||||
| class IbmDecoder : public AbstractDecoder | ||||
| { | ||||
| public: | ||||
|     IbmDecoder(unsigned sectorBase): | ||||
|         _sectorBase(sectorBase) | ||||
|     IbmDecoder(unsigned sectorBase, bool ignoreSideByte=false, | ||||
| 			const std::set<unsigned> requiredSectors=std::set<unsigned>()): | ||||
|         _sectorBase(sectorBase), | ||||
|         _ignoreSideByte(ignoreSideByte), | ||||
| 		_requiredSectors(requiredSectors) | ||||
|     {} | ||||
|  | ||||
|     RecordType advanceToNextRecord(); | ||||
|     void decodeSectorRecord(); | ||||
|     void decodeDataRecord(); | ||||
|  | ||||
| 	std::set<unsigned> requiredSectors(Track& track) const | ||||
| 	{ return _requiredSectors; } | ||||
|  | ||||
| private: | ||||
|     unsigned _sectorBase; | ||||
|     bool _ignoreSideByte; | ||||
| 	std::set<unsigned> _requiredSectors; | ||||
|     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 | ||||
|   | ||||
| @@ -184,3 +184,25 @@ void MacintoshDecoder::decodeDataRecord() | ||||
|     _sector->data.clear(); | ||||
|     _sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12)); | ||||
| } | ||||
|  | ||||
| std::set<unsigned> MacintoshDecoder::requiredSectors(Track& track) const | ||||
| { | ||||
| 	int count; | ||||
| 	if (track.physicalTrack < 16) | ||||
| 		count = 12; | ||||
| 	else if (track.physicalTrack < 32) | ||||
| 		count = 11; | ||||
| 	else if (track.physicalTrack < 48) | ||||
| 		count = 10; | ||||
| 	else if (track.physicalTrack < 64) | ||||
| 		count = 9; | ||||
| 	else | ||||
| 		count = 8; | ||||
|  | ||||
| 	std::set<unsigned> sectors; | ||||
| 	while (count--) | ||||
| 		sectors.insert(count); | ||||
| 	return sectors; | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -18,6 +18,8 @@ public: | ||||
|     RecordType advanceToNextRecord(); | ||||
|     void decodeSectorRecord(); | ||||
|     void decodeDataRecord(); | ||||
|  | ||||
| 	std::set<unsigned> requiredSectors(Track& track) const; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -37,7 +37,7 @@ AbstractDecoder::RecordType MxDecoder::advanceToNextRecord() | ||||
|         const FluxMatcher* matcher = nullptr; | ||||
|         _sector->clock = _clock = _fmr->seekToPattern(ID_PATTERN, matcher); | ||||
|         readRawBits(32); /* skip the ID mark */ | ||||
|         _logicalTrack = decodeFmMfm(readRawBits(32)).reader().read_be16(); | ||||
|         _logicalTrack = decodeFmMfm(readRawBits(32)).slice(0, 32).reader().read_be16(); | ||||
|     } | ||||
|     else if (_currentSector == 10) | ||||
|     { | ||||
|   | ||||
							
								
								
									
										
											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.
										
									
								
							
							
								
								
									
										229
									
								
								doc/building.md
									
									
									
									
									
								
							
							
						
						
									
										229
									
								
								doc/building.md
									
									
									
									
									
								
							| @@ -25,6 +25,8 @@ This is the physical stuff you'll need. | ||||
|     connector](https://eu.mouser.com/ProductDetail/Amphenol-FCI/86130342114345E1LF?qs=%2Fha2pyFadug%252BpMTyxmFhglPPVKuWXYuFpPNgq%252BsrzhDnXxo8B28k7UCGc7F%2FXjsi) | ||||
|     (or one of the other myriad compatible connectors; there's a billion). | ||||
|  | ||||
|   - A floppy drive cable, preferably one with two connectors and a twist. | ||||
|  | ||||
|   - A suitable power supply. 3.5" floppy drives use 5V at about an amp | ||||
|     (usually less) --- sadly, too much to power from USB. 5.25" floppy drives | ||||
|     also require 12V. An old but decent quality PC power supply is ideal, as | ||||
| @@ -48,7 +50,7 @@ All you need to do is attach your chosen connector to the board. You'll need | ||||
| to make sure that pin 2 on the cable is connected to pin 2.7 on the board, | ||||
| and pin 34 to pin 1.7 on the board (and of course all the ones in between). | ||||
| Apart from grounding the board (see below), this is literally all there is to | ||||
| it. | ||||
| it. The actual pinout is described in detail below. | ||||
|  | ||||
| The pads are small, but soldering them isn't too bad with a needle-nosed | ||||
| soldering iron tip. | ||||
| @@ -103,7 +105,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 +156,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. | ||||
|  | ||||
| @@ -137,12 +175,8 @@ pattern. Press and hold the little button near the light for five seconds | ||||
| until the light stays solidly on. Now you should be able to acquire | ||||
| 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). | ||||
| ## Building the client | ||||
|  | ||||
| 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 | ||||
| @@ -150,12 +184,12 @@ well, although on Windows it'll need MSYS2 and mingw32. You'll need to | ||||
| 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`. | ||||
| 	`ninja-build`, `libusb-1.0-0-dev`, `libsqlite3-dev`. | ||||
|   - 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`. | ||||
| 	`mingw-w64-i686-sqlite3`, `mingw-w64-i686-zlib`, `mingw-w64-i686-gcc`. | ||||
|  | ||||
| These lists are not necessarily exhaustive --- plaese [get in | ||||
| These lists are not necessarily exhaustive --- please [get in | ||||
| touch](https://github.com/davidgiven/fluxengine/issues/new) if I've missed | ||||
| anything. | ||||
|  | ||||
| @@ -166,11 +200,176 @@ dependencies and you should be able to put it anywhere. | ||||
| If it doesn't build, please [get in | ||||
| touch](https://github.com/davidgiven/fluxengine/issues/new). | ||||
|  | ||||
|  | ||||
| ## Connecting it up | ||||
|  | ||||
| You should now have a working board, so it's time to test it. | ||||
|  | ||||
|   1. Plug the motherboard end of your floppy disk cable into the FluxEngine. | ||||
|       | ||||
|      The **red stripe goes on the right**. The **lower set of | ||||
|      holes connect to the board**. See the pinout below. | ||||
|  | ||||
|      If you're using header pins, the upper row of holes in the connector | ||||
|      should overhang the edge of the board. If you're using a floppy drive | ||||
|      motherboard connector, you're golden, of course (unless you have one of | ||||
|      those annoying unkeyed cables, or have accidentally soldered the | ||||
|      connector on in the wrong place --- don't laugh, I've done it.) | ||||
|  | ||||
|   2. Plug the drive end of your floppy disk cable into the drive (or drives). | ||||
|  | ||||
|      Floppy disk cables typically have [two pairs of floppy disk drive | ||||
|      connectors with a twist between | ||||
|      them](http://www.nullmodem.com/Floppy.htm). (Each pair has one connector | ||||
|      for a 3.5" drive and a different one for a 5.25" drive.) (Some cables | ||||
|      are cheap and just have the 3.5" connectors. Some are _very_ cheap and | ||||
|      have a single 3.5" connector, after the twist.) | ||||
|       | ||||
|      If you have **two** drives, plug them into both connectors. FluxEngine, | ||||
|      sadly, non-standard disk numbering (there are reasons). Drive 0 is the | ||||
|      one nearest the motherboard; that is, before the twist. Drive 1 is the | ||||
|      one at the end of the cable; that is, after the twist. Drive 0 is the | ||||
|      default. You can tell the client to select drive 1 by using `-s :d=1`. | ||||
|  | ||||
|      If you have **one** drive, you may plug it into _either_ connector. | ||||
|      FluxEngine will autodetect it and treat it as drive 0. However, you'll | ||||
|      get the most reliable electrical signal if you plug it in at the end of | ||||
|      the cable. | ||||
|  | ||||
|      **A note on termination:** some 5.25" drives require jumper configuration | ||||
|      to tell them whether they're at the end of the cable or in the middle of | ||||
|      the cable. 3.5" drives don't, and my 5.25" drives don't, so I can't | ||||
|      advise there. Consult your drive datasheet for details. | ||||
|  | ||||
|   3. **Important.** Make sure that no disk you care about is in the drive. | ||||
| 	 (Because if your wiring is wrong and a disk is inserted, you'll corrupt | ||||
| 	 it.) | ||||
|  | ||||
|   4. Connect the floppy drive to power. Nothing should happen. If you've | ||||
| 	 connected something in backwards, you'll see the drive light up, the motor | ||||
| 	 start, and if you didn't take the disk out, one track has just been wiped. | ||||
| 	 If this happens, check your wiring. | ||||
|  | ||||
|   5. Strip off the little piece of protective plastic on the USB socket on the | ||||
| 	 board --- the little socket at the end, not the big programmer plug. | ||||
|  | ||||
|   6. Connect the FluxEngine to your PC via USB. | ||||
|  | ||||
|   7. Insert a scratch disk and do `fluxengine rpm` from the shell. The motor | ||||
|      should work and it'll tell you that the disk is spinning at about 300 | ||||
|      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). | ||||
|  | ||||
|   8. Do `fluxengine test bandwidth` from the shell. It'll measure your USB | ||||
| 	 bandwidth. Ideally you should be getting above 800kB/s in both directions. | ||||
| 	 FluxEngine needs about 300kB/s for a DD disk and about 600kB/s for a HD | ||||
| 	 disk, so if you're getting less than this, try a different USB port. | ||||
|  | ||||
|   9. Insert a standard PC formatted floppy disk into the drive (probably a good | ||||
|      idea to remove the old disk first). Then do `fluxengine read ibm`. It | ||||
|      should read the disk, emitting copious diagnostics, and spit out an | ||||
|      `ibm.img` file containing the decoded disk image (either 1440kB or 720kB | ||||
|      depending). | ||||
|  | ||||
|  10. Profit! | ||||
|  | ||||
| ## Technical details | ||||
|  | ||||
| The board pinout and the way it's connected to the floppy bus is described | ||||
| below. | ||||
|  | ||||
| ```ditaa | ||||
| :-E -s 0.75 | ||||
|                  +-----+ | ||||
|                  ||||||| | ||||
|             +----+-----+----+ | ||||
|             +cAAA           + | ||||
|             +  Debug board  + | ||||
|             +----+-----+----+ | ||||
|             + GND|cDDD | VDD+   | ||||
|             +----+     +----+ | ||||
| INDEX300 ---+ 3.0|     | GND+--------------------------+ | ||||
|             +----+     +----+                 +--+--+  | | ||||
| INDEX360 ---+ 3.1|     | 1.7+------ DISKCHG --+34+33+--+ | ||||
|             +----+     +----+                 +--+--+ | ||||
|             + 3.2|     | 1.6+------- SIDE1 ---+32+31+ | ||||
|             +----+     +----+                 +--+--+ | ||||
|             + 3.3|     | 1.5+------- RDATA ---+30+29+ | ||||
|             +----+     +----+                 +--+--+ | ||||
|             + 3.4|     | 1.4+-------- WPT ----+28+27+ | ||||
|             +----+     +----+                 +--+--+ | ||||
|             + 3.5|     | 1.3+------- TRK00 ---+26+25+ | ||||
|             +----+     +----+                 +--+--+ | ||||
|             + 3.6|     | 1.2+------- WGATE ---+24+23+ | ||||
|             +----+     +----+                 +--+--+ | ||||
|             + 3.7|     | 1.1+------- WDATA ---+22+21+ | ||||
|             +----+     +----+                 +--+--+ | ||||
|             +15.0|     | 1.0+------- STEP ----+20+19+ | ||||
|             +----+     +----+                 +--+--+ | ||||
|             +15.1|     |12.0+-------- DIR ----+18+17+ | ||||
|             +----+     +----+                 +--+--+ | ||||
|             +15.2|     |12.1+------- MOTEB ---+16+15+ | ||||
|             +----+     +----+                 +--+--+ | ||||
|             +15.3|     |12.2+------- DRVSA ---+14+13+ | ||||
|             +----+     +----+                 +--+--+ | ||||
|             +15.4|     |12.3+------- DRVSB ---+12+11+ | ||||
|             +----+     +----+                 +--+--+ | ||||
|             +15.5|     |12.4+------- MOTEA ---+10+9 + | ||||
|             +----+     +----+                 +--+--+ | ||||
|             + 0.0|     |12.5+------- INDEX ---+8 +7 + | ||||
|             +----+     +----+                 +--+--+ | ||||
|             + 0.1|     |12.6+-------- n/c ----+6 +5 + | ||||
|             +----+     +----+                 +--+--+ | ||||
|             + 0.2|     |12.7+- TX --- n/c ----+4 +3 + | ||||
|             +----+     +----+                 +--+--+ | ||||
|             + 0.3|     | 2.7+------- REDWC ---+2 +1 + | ||||
|             +----+     +----+                 +--+--+ | ||||
|             + 0.4|     | 2.6+   | ||||
|             +----+     +----+                FDD socket | ||||
|             + 0.5|     | 2.5+   | ||||
|             +----+     +----+ | ||||
|             + 0.6|     | 2.4+    TX: debug UART from board | ||||
|             +----+     +----+ | ||||
|             + 0.7|     | 2.3+ | ||||
|             +----+     +----+ | ||||
|             + RST|     | 2.2+   | ||||
|             +----+     +----+ | ||||
|             + GND|     | 2.1+   | ||||
|             +----+ USB +----+ | ||||
|             + VDD+-----+ 2.0+   | ||||
|             +----+-----+----+ | ||||
|                PSoC5 board | ||||
| ``` | ||||
|  | ||||
| Notes: | ||||
|  | ||||
|   - `TX` is the debug UART port. It's on pin 12.7 because the board routes it | ||||
|   to the USB serial port on the programmer, so you can get debug information | ||||
|   from the FluxEngine by just plugging the programming end into a USB port | ||||
|   and using a serial terminal at 115200 baud. If you solder a floppy drive | ||||
|   connector on, then it'll end up connected to pin 4 of the floppy drive bus, | ||||
|   which is usually not connected. It's possible that some floppy drives do, | ||||
|   in fact, use this pin. You may wish to remove pin 4 from the floppy drive | ||||
|   socket before attaching it to the FluxEngine to make sure that this pin is | ||||
|   not connected; however, so far I have not found any drives for which this | ||||
|   is necessary. If you do find one, _please_ [get in | ||||
|   touch](https://github.com/davidgiven/fluxengine/issues/new) so I can | ||||
|   document it. | ||||
|  | ||||
|   - The `GND` pin only really needs to be connected to one of the floppy bus | ||||
|   ground pins; pin 33 is the closest. For extra safety, you can bridge all | ||||
|   the odd numbered pins together and ground them all if you like. | ||||
|  | ||||
|   - `INDEX300` and `INDEX360` are optional output pins which generate fake | ||||
|   timing pulses for 300 and 360 RPM drives. These are useful for certain | ||||
|   rather exotic things. See the section on flippy disks [in the FAQ](faq.md) | ||||
|   for more details; you can normally ignore these. | ||||
|  | ||||
| ## Next steps | ||||
|  | ||||
| The board's now assembled and programmed. Plug it into your drive, strip the | ||||
| plastic off the little USB connector and plug that into your computer, and | ||||
| you're ready to start using it. | ||||
| You should now be ready to go. You'll want to read [the client | ||||
| documentation](using.md) for information about how to actually do interesting | ||||
| things. | ||||
|  | ||||
| I _do_ make updates to the firmware whenever necessary, so you may need to | ||||
| reprogram it at intervals; you may want to take this into account if you | ||||
|   | ||||
| @@ -10,7 +10,7 @@ Bizarrely, the data in each sector is stored with all the odd bits first, and | ||||
| then all the even bits. This is tied into the checksum algorithm, which is | ||||
| distinctly subpar and not particularly good at detecting errors. | ||||
|  | ||||
| Reading discs | ||||
| Reading disks | ||||
| ------------- | ||||
|  | ||||
| Just do: | ||||
| @@ -34,6 +34,28 @@ You will end up with a 929280 byte long image which you probably _can't_ use | ||||
| in an emulator; each sector will contain the 512 bytes of user payload | ||||
| followed by the 16 bytes of metadata. | ||||
|  | ||||
| Writing disks | ||||
| ------------- | ||||
|  | ||||
| Just do: | ||||
|  | ||||
| ``` | ||||
| fluxengine write amiga -i amiga.adf | ||||
| ``` | ||||
|  | ||||
| This will rake a normal 901120 byte long ADF file and write it to a DD disk. | ||||
| Note that writing to an HD disk will probably not work (this will depend on | ||||
| your drive and disk and potential FluxEngine bugs I'm still working on --- | ||||
| please [get in touch](https://github.com/davidgiven/fluxengine/issues/new) if | ||||
| you have any insight here). | ||||
|  | ||||
| If you want to write the metadata as well, specify a 528 byte sector size for | ||||
| the output image and supply a 929280 byte long file as described above. | ||||
|  | ||||
| ``` | ||||
| fluxengine write amiga -i amiga.adf:b=528 | ||||
| ``` | ||||
|  | ||||
| Useful references | ||||
| ----------------- | ||||
|  | ||||
|   | ||||
| @@ -14,10 +14,12 @@ Apparently about 20% of Brother word processors have alignment issues which | ||||
| means that the disks can't be read by FluxEngine (because the tracks on the | ||||
| disk don't line up with the position of the head in a PC drive). The word | ||||
| processors themselves solved this by microstepping until they found where the | ||||
| real track is, but normal PC drives aren't capable of doing this. | ||||
| Particularly with the 120kB disks, you might want to fiddle with the start | ||||
| track (e.g. `:t=0-79x2`) to get a clean read. Keep an eye on the bad sector | ||||
| map that's dumped at the end of a read. | ||||
| real track is, but normal PC drives aren't capable of doing this.  Particularly | ||||
| with the 120kB disks, you might want to fiddle with the start track (e.g. | ||||
| `:t=0-79x2`) to get a clean read. Keep an eye on the bad sector map that's | ||||
| dumped at the end of a read. My word processor likes to put logical track 0 on | ||||
| physical track 3, which means that logical track 77 is on physical track 80; | ||||
| luckily my PC drive can access track 80. | ||||
|  | ||||
| Using FluxEngine to *write* disks isn't a problem, so the | ||||
| simplest solution is to use FluxEngine to create a new disk, with the tracks | ||||
| @@ -30,7 +32,7 @@ If you find one of these misaligned disks then *please* [get in | ||||
| touch](https://github.com/davidgiven/fluxengine/issues/new); I want to | ||||
| investigate. | ||||
|  | ||||
| Reading discs | ||||
| Reading disks | ||||
| ------------- | ||||
|  | ||||
| Just do: | ||||
| @@ -41,7 +43,7 @@ fluxengine read brother | ||||
|  | ||||
| You should end up with a `brother.img` which is 239616 bytes long. | ||||
|  | ||||
| Writing discs | ||||
| Writing disks | ||||
| ------------- | ||||
|  | ||||
| Just do: | ||||
| @@ -53,6 +55,27 @@ fluxengine write brother | ||||
| ...and it'll write a `brother.img` file which is 239616 bytes long to the | ||||
| disk. (Use `-i` to specify a different input filename.) | ||||
|  | ||||
| Dealing with misaligned disks | ||||
| ----------------------------- | ||||
|  | ||||
| While FluxEngine can't read misaligned disks directly, Brother word processors | ||||
| _can_. If you have access to a compatible word processor, there's a fairly | ||||
| simple workaround to allow you to extract the data: | ||||
|  | ||||
|   1. Format a disk using FluxEngine (by simply writing a blank filesystem image | ||||
| 	 to a disk). This will have the correct alignment to work on a PC drive. | ||||
|  | ||||
|   2. Use a word processor to copy the misaligned disk to the newly formatted | ||||
| 	 disk. The machine will happily adjust itself to both sets of alignments. | ||||
|  | ||||
|   3. Use FluxEngine to read the data off the correctly aligned disk. | ||||
|  | ||||
| I realise this is rather unsatisfactory, as the Brother hardware is becoming | ||||
| rarer and they cope rather badly with damaged disks, but this is a limitation | ||||
| of the hardware of normal PC drives. (It _is_ possible to deliberately misalign | ||||
| a drive to make it match up with a bad disk, but this is for experts only --- I | ||||
| wouldn't dare.) | ||||
|  | ||||
| Low level format | ||||
| ---------------- | ||||
|  | ||||
| @@ -60,14 +83,6 @@ The drive is a single-sided 3.5" drive spinning at not 300 rpm (I don't know | ||||
| the precise speed yet but FluxEngine doesn't care). The 240kB disks have 78 | ||||
| tracks and the 120kB disks have 39. | ||||
|  | ||||
| The Brother drive alignment is kinda variable; when you put the disk in the | ||||
| drive it seeks all the way to physical track 0 and then starts searching for | ||||
| something which looks like data. My machine likes to put logical track 0 on | ||||
| physical track 3. FluxEngine puts logical track 0 on physical track 0 for | ||||
| simplicity, which works fine (at least on my machine). If this doesn't work | ||||
| for you, [get in touch](https://github.com/davidgiven/fluxengine/issues/new); | ||||
| there are potential workarounds. | ||||
|  | ||||
| Each track has 12 256-byte sectors. The drive ignores the index hole so they're | ||||
| lined up all anyhow. As FluxEngine can only read from index to index, it | ||||
| actually reads two complete revolutions and reassembles the sectors from that. | ||||
| @@ -129,8 +144,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 | ||||
| @@ -138,7 +153,8 @@ mcopy -i brother.img ::brother.doc linux.doc | ||||
| ``` | ||||
|  | ||||
| The word processor checks the media byte, unfortunately, so you'll need to | ||||
| change it back to 0x58 before writing an image to disk. | ||||
| change it back to 0x58 before writing an image to disk. Just run | ||||
| `brother240tool` on the image again and it will flip it back. | ||||
|  | ||||
| The file format is not WP-1, and currently remains completely unknown, | ||||
| although it's probably related. If anyone knows anything about this, please | ||||
|   | ||||
| @@ -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 | ||||
| ----------------- | ||||
|   | ||||
							
								
								
									
										152
									
								
								doc/disk-ibm.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								doc/disk-ibm.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| 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: | ||||
|  | ||||
|   - `--ibm-sector-id-base=N`: 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. | ||||
|  | ||||
|   - `--ibm-ignore-side-byte=true|false`: 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. | ||||
|  | ||||
|   - `--ibm-required-sectors=range`: if you know how many sectors to expect per | ||||
| 	track, you can improve reads by telling FluxEngine what to expect here. If | ||||
| 	a track is read and a sector on this list is _not_ present, then FluxEngine | ||||
| 	assumes the read failed and will retry. This avoids the situation where | ||||
| 	FluxEngine can't tell the difference between a sector missing because it's | ||||
| 	bad or a sector missing because it was never written in the first place. If | ||||
| 	sectors are seen outside the range here, it will still be read. You can use | ||||
| 	the same syntax as for track specifiers: e.g. `0-9`, `0,1,2,3`, etc. | ||||
|  | ||||
|  | ||||
| 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 | ||||
| ------------- | ||||
|   | ||||
							
								
								
									
										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? | ||||
|  | ||||
|   | ||||
| @@ -22,19 +22,17 @@ FluxEngine, where a different datapath state machine thingy (the PSoC5LP has | ||||
| 24, all independently programmable) to interpret the bytecodes, generate a | ||||
| stream of pulses to the disk. | ||||
|  | ||||
| The bytecode format represents an interval between pulses as a byte, a pulse | ||||
| as a byte, and the index hole as a byte. Timer overflows are handled by | ||||
| sending multiple intervals in a row. However, the USB transport applies a | ||||
| simple compression system to this in order to get the USB bandwidth down to | ||||
| something manageable. | ||||
| The bytecode format is very simple with a six-bit interval since the previous | ||||
| event in the lower six bits and the top two bits are set of a pulse or an index | ||||
| hole (or both, or neither). | ||||
|  | ||||
| An HD floppy has a nominal pulse frequency of 500kHz, and we use a sample | ||||
| clock of 12MHz (every 83ns). This means that our 500kHz pulses will have an | ||||
| average interval of 24. This gives us more than enough resolution. At this | ||||
| speed, in the 200ms that a 3.5" disk takes to rotate, we will see about | ||||
| 100,000 pulses. Each one is encoded as two bytes, one for the interval and | ||||
| one to generate the pulse; so that revolution generates 200kB of data. | ||||
| (Extremely approximately. The actual figure is less.) | ||||
| 100,000 pulses. Each one is encoded as a single byte; so that revolution | ||||
| generates 100kB of data.  (Extremely approximately. The actual figure varies | ||||
| depending on what data is stored on the disk.) | ||||
|  | ||||
| (The clock needs to be absolutely rock solid or we get jitter which makes the | ||||
| data difficult to analyse, so 12 was chosen to be derivable from the | ||||
| @@ -59,53 +57,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 +110,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) | ||||
|  | ||||
|    | ||||
|    | ||||
|   | ||||
							
								
								
									
										242
									
								
								doc/using.md
									
									
									
									
									
								
							
							
						
						
									
										242
									
								
								doc/using.md
									
									
									
									
									
								
							| @@ -1,68 +1,8 @@ | ||||
| Using a FluxEngine | ||||
| ================== | ||||
|  | ||||
| So you've [built the hardware](building.md)! What now? | ||||
|  | ||||
| ## Connecting it up | ||||
|  | ||||
| In order to do anything useful, you have to plug it in to a floppy disk drive (or two). | ||||
|  | ||||
|   1. Plug the motherboard end of your floppy disk cable into the FluxEngine. | ||||
|       | ||||
|      The **red stripe goes on the right**. The **lower set of | ||||
|      holes connect to the board**. (Pin 2 of the connector needs to connect | ||||
|      to pin 2.7 on the board.) | ||||
|  | ||||
|      If you're using header pins, the upper row of holes in the connector | ||||
|      should overhang the edge of the board. If you're using a floppy drive | ||||
|      motherboard connector, you're golden, of course (unless you have one of | ||||
|      those annoying unkeyed cables, or have accidentally soldered the | ||||
|      connector on in the wrong place --- don't laugh, I've done it.) | ||||
|  | ||||
|   2. Plug the drive end of your floppy disk cable into the drive (or drives). | ||||
|  | ||||
|      Floppy disk cables typically have [two pairs of floppy disk drive | ||||
|      connectors with a twist between | ||||
|      them](http://www.nullmodem.com/Floppy.htm). (Each pair has one connector | ||||
|      for a 3.5" drive and a different one for a 5.25" drive.) (Some cables | ||||
|      are cheap and just have the 3.5" connectors. Some are _very_ cheap and | ||||
|      have a single 3.5" connector, after the twist.) | ||||
|       | ||||
|      FluxEngine uses, sadly, non-standard disk numbering (there are reasons). | ||||
|      Drive 0 is the one nearest the motherboard; that is, before the twist. | ||||
|      Drive 1 is the one at the end of the cable; that is, after the twist. | ||||
|      Drive 0 is the default. If you only have one drive, remember to plug the | ||||
|      drive into the connector _before_ the twist. (Or use `-s :d=1` to select | ||||
|      drive 1 when working with disks.) | ||||
|  | ||||
|   3. **Important.** Make sure that no disk you care about is in the drive. | ||||
|      (Because if your wiring is wrong and a disk is inserted, you'll corrupt it.) | ||||
|  | ||||
|   4. Connect the floppy drive to power. Nothing should happen. If you've | ||||
|      connected something in backwards, you'll see the drive light up, the | ||||
|      motor start, and if you didn't take the disk out, one track has just | ||||
|      been wiped. If this happens, check your wiring. | ||||
|  | ||||
|   5. Connect the FluxEngine to your PC via USB --- using the little socket on | ||||
|      the board, not the big programmer plug. | ||||
|  | ||||
|   6. Insert a scratch disk and do `fluxengine rpm` from the shell. The motor | ||||
|      should work and it'll tell you that the disk is spinning at about 300 | ||||
|      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 | ||||
|      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. | ||||
|  | ||||
|   8. Insert a standard PC formatted floppy disk into the drive (probably a good | ||||
|      idea to remove the old disk first). Then do `fluxengine read ibm`. It | ||||
|      should read the disk, emitting copious diagnostics, and spit out an | ||||
|      `ibm.img` file containing the decoded disk image (either 1440kB or 720kB | ||||
|      depending). | ||||
|  | ||||
|   9. Profit! | ||||
| So you've [built the hardware](building.md), programmed and tested it! What | ||||
| now? | ||||
|  | ||||
| ## The programs | ||||
|  | ||||
| @@ -71,10 +11,33 @@ moving too quickly for the documentation to keep up. It does respond to | ||||
| `--help` or `help` depending on context. There are some common properties, | ||||
| described below. | ||||
|  | ||||
| ### Core concepts | ||||
|  | ||||
| FluxEngine fundamentally takes file system images and puts them on disk; or | ||||
| reads the disk and produces a file system image. | ||||
|  | ||||
| A file system image typically has the extension `.img`. It contains a | ||||
| sector-by-sector record of the _decoded_ data on the disk. For example, on a | ||||
| disk with 512 byte sectors, one sector will occupy 512 bytes. These are | ||||
| typically what you want in everyday life. | ||||
|  | ||||
| FluxEngine can also record the raw magnetic data on the disk into a file, which | ||||
| we call a _flux file_. This contains all the low-level data which the drive | ||||
| produced as the disk rotated. These are continuous streams of samples from the | ||||
| disk and are completely useless in day-to-day life. FluxEngine uses its own | ||||
| format for this, `.flux`, although it's capable of limited interchange with | ||||
| Kryoflux, Supercard Pro and Catweasel files. A flux file will typically contain | ||||
| from 80 to 150 kilobytes of data per track. | ||||
|  | ||||
| In general, FluxEngine can use either a real disk or a flux file | ||||
| interchangeably: you can specify either at (very nearly) any time. A very | ||||
| common workflow is to read a disk to a flux file, and then reread from the flux | ||||
| file while changing the decoder options, to save disk wear. It's also much faster. | ||||
|  | ||||
| ### Source and destination specifiers | ||||
|  | ||||
| When reading from or writing to _a disk_ (or a file pretending to be a disk), | ||||
| use the `--source` (`-s`) and `--dest` (`-d`) options to tell FluxEngine | ||||
| When reading from or writing _flux_ (either from or to a real disk, or a flux | ||||
| file), use the `--source` (`-s`) and `--dest` (`-d`) options to tell FluxEngine | ||||
| which bits of the disk you want to access. These use a common syntax: | ||||
|  | ||||
| ``` | ||||
| @@ -84,7 +47,7 @@ fluxengine read ibm -s fakedisk.flux:t=0-79:s=0 | ||||
|   - To access a real disk, leave out the filename (so `:t=0-79:s=0`). | ||||
|  | ||||
|   - To access only some tracks, use the `t=` modifier. To access only some | ||||
|     sides, use the `s=` modifier. To change drives, use `d=`. | ||||
|     sides, use the `s=` modifier. | ||||
|  | ||||
|   - Inside a modifier, you can use a comma separated list of ranges. So | ||||
|     `:t=0-3` and `:t=0,1,2,3` are equivalent. | ||||
| @@ -110,18 +73,15 @@ If you _don't_ specify a modifier, you'll get the default, which should be | ||||
| sensible for the command you're using. | ||||
|  | ||||
| **Important note:** FluxEngine _always_ uses zero-based units (even if the | ||||
| *disk format says otherwise). | ||||
| 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: | ||||
| When reading or writing _file system images_, use the `--input` (`-i`) and | ||||
| `--output` (`-o`) options to specify the file and file format. These use a very | ||||
| similar syntax to the source and destination specifiers (because they're based | ||||
| on the same microformat library!) but with different specifiers. Also, the | ||||
| exact format varies according to 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 | ||||
| @@ -148,6 +108,15 @@ based on the extension: | ||||
|     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 | ||||
| @@ -167,6 +136,29 @@ 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. X can be a floating | ||||
| 	point number. The default is usually 1.25. Some formats default to 1. | ||||
| 	Increasing the number will sample more data, and can be useful on dubious | ||||
| 	disks to try and get a better read. | ||||
|  | ||||
|   - `--sync-with-index=true|false`: wait for an index pulse before starting to | ||||
| 	read the disk. (Ignored for write operations.) By default FluxEngine | ||||
| 	doesn't, as it makes reads faster, but when diagnosing disk problems it's | ||||
| 	helpful to have all your data start at the same place each time. | ||||
|  | ||||
|   - `--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. | ||||
| @@ -175,73 +167,109 @@ installed anywhere and after building you'll find them in the `.obj` | ||||
| directory. | ||||
|  | ||||
|   - `fluxengine erase`: wipes (all or part of) a disk --- erases it without | ||||
|   writing a pulsetrain. | ||||
| 	writing a pulsetrain. | ||||
|  | ||||
|   - `fluxengine inspect`: dumps the raw pulsetrain / bitstream to stdout. | ||||
|   Mainly useful for debugging. | ||||
| 	Mainly useful for debugging. | ||||
|  | ||||
|   - `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. | ||||
|   - `fluxengine read *`: reads various formats of disk. See the per-format | ||||
| 	documentation linked from the table [in the index page](../README.md). | ||||
| 	These all take an optional `--write-flux` option which will cause the raw | ||||
| 	flux to be written to the specified file as well as the normal decode. | ||||
| 	There are various `--dump` options for showing raw data during the decode | ||||
| 	process, and `--write-csv` will write a copious CSV report of the state of | ||||
| 	every sector in the file in machine-readable format. | ||||
|  | ||||
|   - `fluxengine write*`: writes various formats of disk. Again, see the | ||||
|   per-format documentation above. | ||||
|   - `fluxengine write *`: writes various formats of disk. Again, see the | ||||
| 	per-format documentation [in the index page](../README.md). | ||||
|  | ||||
|   - `fluxengine writeflux`: writes raw flux files. This is much less useful | ||||
|   than you might think: you can't write flux files read from a disk to | ||||
|   another disk. (See the [FAQ](faq.md) for more information.) It's mainly | ||||
|   useful for flux files synthesised by the other `fluxengine write` commands. | ||||
| 	than you might think: you can't reliably write flux files read from a disk | ||||
| 	to another disk. (See the [FAQ](faq.md) for more information.) It's mainly | ||||
| 	useful for flux files synthesised by the other `fluxengine write` commands. | ||||
|  | ||||
|   - `fluxengine writetestpattern`: writes regular pulses (at a configurable | ||||
|   interval) to the disk. Useful for testing drive jitter, erasing disks in a | ||||
|   more secure fashion, or simply debugging. Goes well with `fluxengine | ||||
|   inspect`. | ||||
| 	interval) to the disk. Useful for testing drive jitter, erasing disks in a | ||||
| 	more secure fashion, or simply debugging. Goes well with `fluxengine | ||||
| 	inspect`. | ||||
|  | ||||
|   - `fluxengine rpm`: measures the RPM of the drive (requires a disk in the | ||||
|   drive). Mainly useful for testing. | ||||
| 	drive). Mainly useful for testing. | ||||
|  | ||||
|   - `fluxengine seek`: moves the head. Mainly useful for finding out whether | ||||
|   your drive can seek to track 82. (Mine can't.) | ||||
| 	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 don't need | ||||
| 	a disk in the drive for this one. | ||||
|  | ||||
|   - `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 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 | ||||
| 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. | ||||
|  | ||||
|   - `brother120tool`, `brother240tool`: does things to Brother word processor | ||||
| 	disks. These are [documented on the Brother disk format | ||||
| 	page](disk-brother.md). | ||||
|    | ||||
| ## 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 | 
							
								
								
									
										82
									
								
								lib/bytes.cc
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								lib/bytes.cc
									
									
									
									
									
								
							| @@ -1,7 +1,6 @@ | ||||
| #include "globals.h" | ||||
| #include "bytes.h" | ||||
| #include "fmt/format.h" | ||||
| #include "common/crunch.h" | ||||
| #include <fstream> | ||||
| #include <zlib.h> | ||||
|  | ||||
| @@ -147,6 +146,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) | ||||
| @@ -227,60 +248,6 @@ Bytes Bytes::decompress() const | ||||
|     return output; | ||||
| } | ||||
|  | ||||
| Bytes Bytes::crunch() const | ||||
| { | ||||
|     Bytes output; | ||||
|     ByteWriter bw(output); | ||||
|     Bytes outputBuffer(1024*1024); | ||||
|  | ||||
|     crunch_state_t cs = {}; | ||||
|     cs.inputptr = begin(); | ||||
|     cs.inputlen = size(); | ||||
|  | ||||
|     do | ||||
|     { | ||||
|         cs.outputptr = outputBuffer.begin(); | ||||
|         cs.outputlen = outputBuffer.size(); | ||||
|  | ||||
|         ::crunch(&cs); | ||||
|         bw += outputBuffer.slice(0, outputBuffer.size() - cs.outputlen); | ||||
|     } | ||||
|     while (cs.inputlen != 0); | ||||
|     cs.outputptr = outputBuffer.begin(); | ||||
|     cs.outputlen = outputBuffer.size(); | ||||
|     donecrunch(&cs); | ||||
|     bw += outputBuffer.slice(0, outputBuffer.size() - cs.outputlen); | ||||
|  | ||||
|     return output; | ||||
| } | ||||
|  | ||||
| Bytes Bytes::uncrunch() const | ||||
| { | ||||
|     Bytes output; | ||||
|     ByteWriter bw(output); | ||||
|     Bytes outputBuffer(1024*1024); | ||||
|  | ||||
|     crunch_state_t cs = {}; | ||||
|     cs.inputptr = begin(); | ||||
|     cs.inputlen = size(); | ||||
|  | ||||
|     do | ||||
|     { | ||||
|         cs.outputptr = outputBuffer.begin(); | ||||
|         cs.outputlen = outputBuffer.size(); | ||||
|  | ||||
|         ::uncrunch(&cs); | ||||
|         bw += outputBuffer.slice(0, outputBuffer.size() - cs.outputlen); | ||||
|     } | ||||
|     while (cs.inputlen != 0); | ||||
|     cs.outputptr = outputBuffer.begin(); | ||||
|     cs.outputlen = outputBuffer.size(); | ||||
|     doneuncrunch(&cs); | ||||
|     bw += outputBuffer.slice(0, outputBuffer.size() - cs.outputlen); | ||||
|  | ||||
|     return output; | ||||
| } | ||||
|  | ||||
| void Bytes::writeToFile(const std::string& filename) const | ||||
| { | ||||
|     std::ofstream f(filename, std::ios::out | std::ios::binary); | ||||
| @@ -291,6 +258,11 @@ void Bytes::writeToFile(const std::string& filename) const | ||||
|     f.close(); | ||||
| } | ||||
|  | ||||
| void Bytes::writeTo(std::ostream& stream) const | ||||
| { | ||||
| 	stream.write((const char*) cbegin(), size()); | ||||
| } | ||||
|  | ||||
| ByteReader Bytes::reader() const | ||||
| { | ||||
|     return ByteReader(*this); | ||||
|   | ||||
| @@ -47,16 +47,17 @@ 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; | ||||
| 	void writeTo(std::ostream& stream) const; | ||||
|  | ||||
| private: | ||||
|     std::shared_ptr<std::vector<uint8_t>> _data; | ||||
|   | ||||
| @@ -1,86 +0,0 @@ | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include "crunch.h" | ||||
|  | ||||
| void crunch(crunch_state_t* state) | ||||
| { | ||||
|     while (state->inputlen && state->outputlen) | ||||
|     { | ||||
|         uint8_t data = *state->inputptr++; | ||||
|         state->inputlen--; | ||||
|  | ||||
|         if (data & 0x80) | ||||
|         { | ||||
|             state->fifo = (state->fifo << 2) | 2 | (data & 1); | ||||
|             state->fifolen += 2; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             state->fifo = (state->fifo << 8) | data; | ||||
|             state->fifolen += 8; | ||||
|         } | ||||
|  | ||||
|         if (state->fifolen >= 8) | ||||
|         { | ||||
|             data = state->fifo >> (state->fifolen - 8); | ||||
|             *state->outputptr++ = data; | ||||
|             state->outputlen--; | ||||
|             state->fifolen -= 8; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void donecrunch(crunch_state_t* state) | ||||
| { | ||||
|     if (state->fifolen > 0) | ||||
|     { | ||||
|         uint8_t b = 0; | ||||
|         state->inputptr = &b; | ||||
|         state->inputlen = 1; | ||||
|         crunch(state); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void uncrunch(crunch_state_t* state) | ||||
| { | ||||
|     while (state->inputlen && state->outputlen) | ||||
|     { | ||||
|         if (state->fifolen < 8) | ||||
|         { | ||||
|             if (state->inputlen) | ||||
|             { | ||||
|                 state->fifo = (state->fifo << 8) | *state->inputptr++; | ||||
|                 state->inputlen--; | ||||
|                 state->fifolen += 8; | ||||
|             } | ||||
|             else | ||||
|                 state->fifo <<= 8; | ||||
|         } | ||||
|  | ||||
|         uint8_t data = state->fifo >> (state->fifolen - 8); | ||||
|         if (data & 0x80) | ||||
|         { | ||||
|             data = ((data >> 6) & 0x01) | 0x80; | ||||
|             state->fifolen -= 2; | ||||
|         } | ||||
|         else | ||||
|             state->fifolen -= 8; | ||||
|  | ||||
|         if (data) | ||||
|         { | ||||
|             *state->outputptr++ = data; | ||||
|             state->outputlen--; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void doneuncrunch(crunch_state_t* state) | ||||
| { | ||||
|     if (state->fifolen > 0) | ||||
|     { | ||||
|         uint8_t b = 0; | ||||
|         state->inputptr = &b; | ||||
|         state->inputlen = 1; | ||||
|         uncrunch(state); | ||||
|     } | ||||
| } | ||||
| @@ -1,45 +0,0 @@ | ||||
| #ifndef CRUNCH_H | ||||
| #define CRUNCH_H | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| #include <stdint.h> | ||||
|  | ||||
| /* To save bandwidth, we compress the byte stream from the sampler when | ||||
|  * sending it over USB. The encoding used is: | ||||
|  *  | ||||
|  * 0nnn.nnnn: value 0x00..0x7f | ||||
|  * 1n       : value 0x80|n | ||||
|  * | ||||
|  * The end of the buffer is terminated with zeroes, which are ignored | ||||
|  * (not written to the output). | ||||
|  *  | ||||
|  * This saves ~40%, which gets us in under the bandwidth cap. | ||||
|  */ | ||||
|      | ||||
| typedef struct crunch_state_t | ||||
| { | ||||
|     const uint8_t* inputptr; | ||||
|     uint32_t inputlen; | ||||
|     uint8_t* outputptr; | ||||
|     uint32_t outputlen; | ||||
|     uint16_t fifo; | ||||
|     uint8_t fifolen; | ||||
| } | ||||
| crunch_state_t; | ||||
|  | ||||
| /* Crunches as much as possible and then stops. */ | ||||
| extern void crunch(crunch_state_t* state); | ||||
| extern void donecrunch(crunch_state_t* state); | ||||
|  | ||||
| /* Uncrunches as much as possible and then stops. */ | ||||
| extern void uncrunch(crunch_state_t* state); | ||||
| extern void doneuncrunch(crunch_state_t* state); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
| @@ -15,34 +15,31 @@ std::vector<std::string> DataSpec::split( | ||||
| { | ||||
|     std::vector<std::string> ret; | ||||
|  | ||||
|     size_t start = 0; | ||||
|     size_t end = 0; | ||||
|     size_t len = 0; | ||||
|     do | ||||
|     { | ||||
|         end = s.find(delimiter,start);  | ||||
|         len = end - start; | ||||
|         std::string token = s.substr(start, len); | ||||
|         ret.emplace_back( token ); | ||||
|         start += len + delimiter.length(); | ||||
|     } | ||||
|     while (end != std::string::npos); | ||||
| 	if (!s.empty()) | ||||
| 	{ | ||||
| 		size_t start = 0; | ||||
| 		size_t end = 0; | ||||
| 		size_t len = 0; | ||||
| 		do | ||||
| 		{ | ||||
| 			end = s.find(delimiter,start);  | ||||
| 			len = end - start; | ||||
| 			std::string token = s.substr(start, len); | ||||
| 			ret.emplace_back( token ); | ||||
| 			start += len + delimiter.length(); | ||||
| 		} | ||||
| 		while (end != std::string::npos); | ||||
| 	} | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| DataSpec::Modifier DataSpec::parseMod(const std::string& spec) | ||||
| std::set<unsigned> DataSpec::parseRange(const std::string& data) | ||||
| { | ||||
|     static const std::regex MOD_REGEX("([a-z]*)=([-x+0-9,]*)"); | ||||
|     static const std::regex DATA_REGEX("([0-9]+)(?:(?:-([0-9]+))|(?:\\+([0-9]+)))?(?:x([0-9]+))?"); | ||||
| 	static const std::regex DATA_REGEX("([0-9]+)(?:(?:-([0-9]+))|(?:\\+([0-9]+)))?(?:x([0-9]+))?"); | ||||
|  | ||||
|     std::smatch match; | ||||
|     if (!std::regex_match(spec, match, MOD_REGEX)) | ||||
|         Error() << "invalid data modifier syntax '" << spec << "'"; | ||||
|      | ||||
|     Modifier m; | ||||
|     m.name = match[1]; | ||||
|     m.source = spec; | ||||
|     for (auto& data : split(match[2], ",")) | ||||
| 	std::set<unsigned> result; | ||||
|     for (auto& data : split(data, ",")) | ||||
|     { | ||||
|         int start = 0; | ||||
|         int count = 1; | ||||
| @@ -64,9 +61,24 @@ DataSpec::Modifier DataSpec::parseMod(const std::string& spec) | ||||
|             Error() << "mod '" << data << "' specifies an illegal quantity"; | ||||
|  | ||||
|         for (int i = start; i < (start+count); i += step) | ||||
|             m.data.insert(i); | ||||
|             result.insert(i); | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| DataSpec::Modifier DataSpec::parseMod(const std::string& spec) | ||||
| { | ||||
| 	static const std::regex MOD_REGEX("([a-z]*)=([-x+0-9,]*)"); | ||||
|  | ||||
|     std::smatch match; | ||||
|     if (!std::regex_match(spec, match, MOD_REGEX)) | ||||
|         Error() << "invalid data modifier syntax '" << spec << "'"; | ||||
|      | ||||
|     Modifier m; | ||||
|     m.name = match[1]; | ||||
|     m.source = spec; | ||||
| 	m.data = parseRange(match[2]); | ||||
|     return m; | ||||
| } | ||||
|  | ||||
| @@ -74,7 +86,7 @@ void DataSpec::set(const std::string& spec) | ||||
| { | ||||
|     std::vector<std::string> words = split(spec, ":"); | ||||
|     if (words.size() == 0) | ||||
|         Error() << "empty data specification (you have to specify *something*)"; | ||||
| 		return; | ||||
|  | ||||
|     filename = words[0]; | ||||
|     if (words.size() > 1) | ||||
|   | ||||
| @@ -34,6 +34,8 @@ public: | ||||
| public: | ||||
|     static std::vector<std::string> split( | ||||
|         const std::string& s, const std::string& delimiter); | ||||
| 	static std::set<unsigned> parseRange(const std::string& spec); | ||||
|  | ||||
|     static Modifier parseMod(const std::string& spec); | ||||
|  | ||||
| public: | ||||
| @@ -117,4 +119,34 @@ private: | ||||
|     DataSpec _value; | ||||
| }; | ||||
|  | ||||
| class RangeFlag : public Flag | ||||
| { | ||||
| public: | ||||
|     RangeFlag(const std::vector<std::string>& names, const std::string helptext, | ||||
|             const std::string& defaultValue): | ||||
|         Flag(names, helptext), | ||||
| 		_stringValue(defaultValue), | ||||
|         _value(DataSpec::parseRange(defaultValue)) | ||||
|     {} | ||||
|  | ||||
|     const std::set<unsigned>& get() const | ||||
|     { checkInitialised(); return _value; } | ||||
|  | ||||
|     operator const std::set<unsigned>& () const | ||||
|     { return get(); } | ||||
|  | ||||
|     bool hasArgument() const { return true; } | ||||
|     const std::string defaultValueAsString() const { return _stringValue; } | ||||
|  | ||||
|     void set(const std::string& value) | ||||
| 	{ | ||||
| 		_stringValue = value; | ||||
| 		_value = DataSpec::parseRange(value); | ||||
| 	} | ||||
|  | ||||
| private: | ||||
| 	std::string _stringValue; | ||||
|     std::set<unsigned> _value; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -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(); | ||||
| @@ -35,27 +35,38 @@ void AbstractDecoder::decodeToSectors(Track& track) | ||||
|             return; | ||||
|         if ((r == UNKNOWN_RECORD) || (r == DATA_RECORD)) | ||||
|         { | ||||
|             fmr.readNextMatchingOpcode(F_OP_PULSE); | ||||
|             fmr.findEvent(F_BIT_PULSE); | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         /* 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.findEvent(F_BIT_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); | ||||
| @@ -77,3 +88,10 @@ void AbstractDecoder::pushRecord(const Fluxmap::Position& start, const Fluxmap:: | ||||
|     _track->rawrecords.push_back(record); | ||||
|     _fmr->seek(here); | ||||
| } | ||||
|  | ||||
| std::set<unsigned> AbstractDecoder::requiredSectors(Track& track) const | ||||
| { | ||||
| 	static std::set<unsigned> empty; | ||||
| 	return empty; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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()); } | ||||
| @@ -50,6 +52,11 @@ public: | ||||
|     void seek(const Fluxmap::Position& pos) | ||||
|     { return _fmr->seek(pos); }  | ||||
|  | ||||
| 	/* Returns a set of sectors required to exist on this track. If the reader | ||||
| 	 * sees any missing, it will consider this to be an error and will retry | ||||
| 	 * the read. */ | ||||
| 	virtual std::set<unsigned> requiredSectors(Track& track) const; | ||||
|  | ||||
| protected: | ||||
|     virtual void beginTrack() {}; | ||||
|     virtual RecordType advanceToNextRecord() = 0; | ||||
|   | ||||
| @@ -18,23 +18,27 @@ 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); | ||||
|  | ||||
| int FluxmapReader::readOpcode(unsigned& ticks) | ||||
| static DoubleFlag minimumClockUs( | ||||
|     { "--minimum-clock-us" }, | ||||
|     "Refuse to detect clocks shorter than this, to avoid false positives.", | ||||
|     0.75); | ||||
|  | ||||
| uint8_t FluxmapReader::getNextEvent(unsigned& ticks) | ||||
| { | ||||
|     ticks = 0; | ||||
|  | ||||
|     while (!eof()) | ||||
|     { | ||||
|         uint8_t b = _bytes[_pos.bytes++]; | ||||
|         if (b < 0x80) | ||||
|             ticks += b; | ||||
|         else | ||||
| 		ticks += b & 0x3f; | ||||
| 		if (b & (F_BIT_PULSE|F_BIT_INDEX)) | ||||
|         { | ||||
|             _pos.ticks += ticks; | ||||
|             return b; | ||||
| @@ -42,21 +46,21 @@ int FluxmapReader::readOpcode(unsigned& ticks) | ||||
|     } | ||||
|  | ||||
|     _pos.ticks += ticks; | ||||
|     return -1; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| unsigned FluxmapReader::readNextMatchingOpcode(uint8_t opcode) | ||||
| unsigned FluxmapReader::findEvent(uint8_t target) | ||||
| { | ||||
|     unsigned ticks = 0; | ||||
|  | ||||
|     for (;;) | ||||
|     { | ||||
|         unsigned thisTicks; | ||||
|         int op = readOpcode(thisTicks); | ||||
|         uint8_t bits = getNextEvent(thisTicks); | ||||
|         ticks += thisTicks; | ||||
|         if (op == -1) | ||||
|         if (eof()) | ||||
|             return 0; | ||||
|         if (op == opcode) | ||||
|         if (bits & target) | ||||
|             return ticks; | ||||
|     } | ||||
| } | ||||
| @@ -68,7 +72,7 @@ unsigned FluxmapReader::readInterval(nanoseconds_t clock) | ||||
|  | ||||
|     while (ticks < thresholdTicks) | ||||
|     { | ||||
|         unsigned thisTicks = readNextMatchingOpcode(F_OP_PULSE); | ||||
|         unsigned thisTicks = findEvent(F_BIT_PULSE); | ||||
|         if (!thisTicks) | ||||
|             break; | ||||
|         ticks += thisTicks; | ||||
| @@ -191,7 +195,7 @@ void FluxmapReader::seek(nanoseconds_t ns) | ||||
|     while (!eof() && (_pos.ticks < ticks)) | ||||
|     { | ||||
|         unsigned t; | ||||
|         readOpcode(t); | ||||
|         getNextEvent(t); | ||||
|     } | ||||
|     _pos.zeroes = 0; | ||||
| } | ||||
| @@ -222,7 +226,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++) | ||||
| @@ -230,7 +236,7 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu | ||||
|             positions[i] = positions[i+1]; | ||||
|             candidates[i] = candidates[i+1]; | ||||
|         } | ||||
|         candidates[intervalCount] = readNextMatchingOpcode(F_OP_PULSE); | ||||
|         candidates[intervalCount] = findEvent(F_BIT_PULSE); | ||||
|         positions[intervalCount] = tell(); | ||||
|  | ||||
|     } | ||||
| @@ -241,7 +247,7 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu | ||||
|  | ||||
| void FluxmapReader::seekToIndexMark() | ||||
| { | ||||
|     readNextMatchingOpcode(F_OP_INDEX); | ||||
|     findEvent(F_BIT_INDEX); | ||||
|     _pos.zeroes = 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -96,8 +96,8 @@ public: | ||||
|         _pos = pos; | ||||
|     } | ||||
|  | ||||
|     int readOpcode(unsigned& ticks); | ||||
|     unsigned readNextMatchingOpcode(uint8_t opcode); | ||||
|     uint8_t getNextEvent(unsigned& ticks); | ||||
|     unsigned findEvent(uint8_t bits); | ||||
|     unsigned readInterval(nanoseconds_t clock); /* with debounce support */ | ||||
|  | ||||
|     /* Important! You can only reliably seek to 1 bits. */ | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -4,6 +4,9 @@ | ||||
|  | ||||
| Fluxmap& Fluxmap::appendBytes(const Bytes& bytes) | ||||
| { | ||||
| 	if (bytes.size() == 0) | ||||
| 		return *this; | ||||
|  | ||||
|     return appendBytes(&bytes[0], bytes.size()); | ||||
| } | ||||
|  | ||||
| @@ -15,8 +18,7 @@ Fluxmap& Fluxmap::appendBytes(const uint8_t* ptr, size_t len) | ||||
|     while (len--) | ||||
|     { | ||||
|         uint8_t byte = *ptr++; | ||||
|         if (byte < 0x80) | ||||
|             _ticks += byte; | ||||
| 		_ticks += byte & 0x3f; | ||||
|         bw.write_8(byte); | ||||
|     } | ||||
|  | ||||
| @@ -24,12 +26,19 @@ Fluxmap& Fluxmap::appendBytes(const uint8_t* ptr, size_t len) | ||||
|     return *this; | ||||
| } | ||||
|  | ||||
| uint8_t& Fluxmap::findLastByte() | ||||
| { | ||||
| 	if (_bytes.empty()) | ||||
| 		appendByte(0x00); | ||||
| 	return *(_bytes.end() - 1); | ||||
| } | ||||
|  | ||||
| Fluxmap& Fluxmap::appendInterval(uint32_t ticks) | ||||
| { | ||||
|     while (ticks >= 0x7f) | ||||
|     while (ticks >= 0x3f) | ||||
|     { | ||||
|         appendByte(0x7f); | ||||
|         ticks -= 0x7f; | ||||
|         appendByte(0x3f); | ||||
|         ticks -= 0x3f; | ||||
|     } | ||||
|     appendByte((uint8_t)ticks); | ||||
|     return *this; | ||||
| @@ -37,13 +46,13 @@ Fluxmap& Fluxmap::appendInterval(uint32_t ticks) | ||||
|  | ||||
| Fluxmap& Fluxmap::appendPulse() | ||||
| { | ||||
|     appendByte(0x80); | ||||
| 	findLastByte() |= 0x80; | ||||
|     return *this; | ||||
| } | ||||
|  | ||||
| Fluxmap& Fluxmap::appendIndex() | ||||
| { | ||||
|     appendByte(0x81); | ||||
| 	findLastByte() |= 0x40; | ||||
|     return *this; | ||||
| } | ||||
|  | ||||
| @@ -54,25 +63,26 @@ void Fluxmap::precompensate(int threshold_ticks, int amount_ticks) | ||||
|     for (unsigned i=0; i<_bytes.size(); i++) | ||||
|     { | ||||
|         uint8_t& prev = (i == 0) ? junk : _bytes[i-1]; | ||||
|         uint8_t curr = _bytes[i]; | ||||
| 		uint8_t prevticks = prev & 0x3f; | ||||
|         uint8_t currticks = _bytes[i] & 0x3f; | ||||
|  | ||||
|         if (curr < (3*threshold_ticks)) | ||||
|         if (currticks < (3*threshold_ticks)) | ||||
|         { | ||||
|             if ((prev <= threshold_ticks) && (curr > threshold_ticks)) | ||||
|             if ((prevticks <= threshold_ticks) && (currticks > threshold_ticks)) | ||||
|             { | ||||
|                 /* 01001; move the previous bit backwards. */ | ||||
|                 if (prev >= (1+amount_ticks)) | ||||
|                 if (prevticks >= (1+amount_ticks)) | ||||
|                     prev -= amount_ticks; | ||||
|                 if (curr <= (0x7f-amount_ticks)) | ||||
|                     curr += amount_ticks; | ||||
|                 if (currticks <= (0x7f-amount_ticks)) | ||||
|                     currticks += amount_ticks; | ||||
|             } | ||||
|             else if ((prev > threshold_ticks) && (curr <= threshold_ticks)) | ||||
|             else if ((prevticks > threshold_ticks) && (currticks <= threshold_ticks)) | ||||
|             { | ||||
|                 /* 00101; move the current bit forwards. */ | ||||
|                 if (prev <= (0x7f-amount_ticks)) | ||||
|                 if (prevticks <= (0x7f-amount_ticks)) | ||||
|                     prev += amount_ticks; | ||||
|                 if (curr >= (1+amount_ticks)) | ||||
|                     curr -= amount_ticks; | ||||
|                 if (currticks >= (1+amount_ticks)) | ||||
|                     currticks -= amount_ticks; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -48,6 +48,9 @@ public: | ||||
|  | ||||
| 	void precompensate(int threshold_ticks, int amount_ticks); | ||||
|  | ||||
| private: | ||||
| 	uint8_t& findLastByte(); | ||||
|  | ||||
| private: | ||||
|     nanoseconds_t _duration = 0; | ||||
|     int _ticks = 0; | ||||
|   | ||||
| @@ -1,6 +1,11 @@ | ||||
| #ifndef FLUXSINK_H | ||||
| #define FLUXSINK_H | ||||
|  | ||||
| #include "flags.h" | ||||
|  | ||||
| extern FlagGroup hardwareFluxSinkFlags; | ||||
| extern FlagGroup sqliteFluxSinkFlags; | ||||
|  | ||||
| class Fluxmap; | ||||
| class FluxSpec; | ||||
|  | ||||
| @@ -9,11 +14,9 @@ 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 FluxSpec& spec); | ||||
|  | ||||
| public: | ||||
|   | ||||
| @@ -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,11 +33,10 @@ 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(); | ||||
|         return usbWrite(side, crunched); | ||||
|         return usbWrite(side, fluxmap.rawBytes()); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|   | ||||
| @@ -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 {}", | ||||
|   | ||||
| @@ -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,11 +48,11 @@ 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 data = usbRead(side, synced, revolutions * _oneRevolution); | ||||
|         auto fluxmap = std::make_unique<Fluxmap>(); | ||||
|         fluxmap->appendBytes(crunched.uncrunch()); | ||||
|         fluxmap->appendBytes(data); | ||||
|         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) | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
| #include <set> | ||||
| #include <cassert> | ||||
|  | ||||
| typedef int nanoseconds_t; | ||||
| typedef double nanoseconds_t; | ||||
| class Bytes; | ||||
|  | ||||
| extern double getCurrentTime(); | ||||
| @@ -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); | ||||
|   | ||||
							
								
								
									
										25
									
								
								lib/image.cc
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								lib/image.cc
									
									
									
									
									
								
							| @@ -1,25 +0,0 @@ | ||||
| #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> | ||||
|  | ||||
| SectorSet readSectorsFromFile(const ImageSpec& spec) | ||||
| { | ||||
| 	return ImageReader::create(spec)->readImage(); | ||||
| } | ||||
|  | ||||
| void writeSectorsToFile(const SectorSet& sectors, const ImageSpec& spec) | ||||
| { | ||||
| 	std::unique_ptr<ImageWriter> writer(ImageWriter::create(sectors, spec)); | ||||
| 	writer->adjustGeometry(); | ||||
| 	writer->printMap(); | ||||
| 	writer->writeImage(); | ||||
| } | ||||
							
								
								
									
										14
									
								
								lib/image.h
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								lib/image.h
									
									
									
									
									
								
							| @@ -1,14 +0,0 @@ | ||||
| #ifndef IMAGE_H | ||||
| #define IMAGE_H | ||||
|  | ||||
| class SectorSet; | ||||
| class ImageSpec; | ||||
|  | ||||
| extern SectorSet readSectorsFromFile( | ||||
| 	const ImageSpec& filename); | ||||
|  | ||||
| extern void writeSectorsToFile( | ||||
| 	const SectorSet& sectors, | ||||
| 	const ImageSpec& filename); | ||||
|  | ||||
| #endif | ||||
| @@ -1,11 +1,20 @@ | ||||
| #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" | ||||
| #include <algorithm> | ||||
| #include <ctype.h> | ||||
|  | ||||
| std::map<std::string, ImageReader::Constructor> ImageReader::formats = | ||||
| { | ||||
| 	{".adf", ImageReader::createImgImageReader}, | ||||
| 	{".d81", ImageReader::createImgImageReader}, | ||||
| 	{".img", ImageReader::createImgImageReader}, | ||||
| 	{".ima", ImageReader::createImgImageReader}, | ||||
| }; | ||||
|  | ||||
| static bool ends_with(const std::string& value, const std::string& ending) | ||||
| { | ||||
| @@ -14,15 +23,29 @@ static bool ends_with(const std::string& value, const std::string& ending) | ||||
|     return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<ImageReader> ImageReader::create(const ImageSpec& spec) | ||||
| ImageReader::Constructor ImageReader::findConstructor(const ImageSpec& spec) | ||||
| { | ||||
|     const auto& filename = spec.filename; | ||||
|  | ||||
|     if (ends_with(filename, ".img") || ends_with(filename, ".adf")) | ||||
|         return createImgImageReader(spec); | ||||
| 	for (const auto& e : formats) | ||||
| 	{ | ||||
| 		if (ends_with(filename, e.first)) | ||||
| 			return e.second; | ||||
| 	} | ||||
|  | ||||
|     Error() << "unrecognised image filename extension"; | ||||
|     return std::unique_ptr<ImageReader>(); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| std::unique_ptr<ImageReader> ImageReader::create(const ImageSpec& spec) | ||||
| { | ||||
|     verifyImageSpec(spec); | ||||
|     return findConstructor(spec)(spec); | ||||
| } | ||||
|  | ||||
| void ImageReader::verifyImageSpec(const ImageSpec& spec) | ||||
| { | ||||
|     if (!findConstructor(spec)) | ||||
|         Error() << "unrecognised image filename extension"; | ||||
| } | ||||
|  | ||||
| ImageReader::ImageReader(const ImageSpec& spec): | ||||
|   | ||||
| @@ -12,10 +12,21 @@ public: | ||||
|  | ||||
| public: | ||||
|     static std::unique_ptr<ImageReader> create(const ImageSpec& spec); | ||||
| 	static void verifyImageSpec(const ImageSpec& spec); | ||||
|  | ||||
| private: | ||||
| 	typedef  | ||||
| 		std::function< | ||||
| 			std::unique_ptr<ImageReader>(const ImageSpec& spec) | ||||
| 		> | ||||
| 		Constructor; | ||||
|  | ||||
| 	static std::map<std::string, Constructor> formats; | ||||
|  | ||||
|     static std::unique_ptr<ImageReader> createImgImageReader(const ImageSpec& spec); | ||||
|  | ||||
| 	static Constructor findConstructor(const ImageSpec& spec); | ||||
|  | ||||
| public: | ||||
| 	virtual SectorSet readImage() = 0; | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| #include "globals.h" | ||||
| #include "image.h" | ||||
| #include "flags.h" | ||||
| #include "dataspec.h" | ||||
| #include "sector.h" | ||||
|   | ||||
							
								
								
									
										63
									
								
								lib/imagewriter/d64imagewriter.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								lib/imagewriter/d64imagewriter.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| #include "globals.h" | ||||
| #include "flags.h" | ||||
| #include "dataspec.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "imagewriter/imagewriter.h" | ||||
| #include "fmt/format.h" | ||||
| #include "ldbs.h" | ||||
| #include <algorithm> | ||||
| #include <iostream> | ||||
| #include <fstream> | ||||
|  | ||||
| static int sectors_per_track(int track) | ||||
| { | ||||
|     if (track < 17) | ||||
|         return 21; | ||||
|     if (track < 24) | ||||
|         return 19; | ||||
|     if (track < 30) | ||||
|         return 18; | ||||
|     return 17; | ||||
| } | ||||
|  | ||||
| class D64ImageWriter : public ImageWriter | ||||
| { | ||||
| public: | ||||
| 	D64ImageWriter(const SectorSet& sectors, const ImageSpec& spec): | ||||
| 		ImageWriter(sectors, spec) | ||||
| 	{} | ||||
|  | ||||
| 	void writeImage() | ||||
| 	{ | ||||
| 		std::cout << "writing D64 triangular image\n"; | ||||
|  | ||||
| 		std::ofstream outputFile(spec.filename, std::ios::out | std::ios::binary); | ||||
| 		if (!outputFile.is_open()) | ||||
| 			Error() << "cannot open output file"; | ||||
|  | ||||
|         uint32_t offset = 0; | ||||
| 		for (int track = 0; track < 40; track++) | ||||
| 		{ | ||||
|             int sectorCount = sectors_per_track(track); | ||||
|             for (int sectorId = 0; sectorId < sectorCount; sectorId++) | ||||
|             { | ||||
|                 const auto& sector = sectors.get(track, 0, sectorId); | ||||
|                 if (sector) | ||||
|                 { | ||||
|                     outputFile.seekp(offset); | ||||
|                     outputFile.write((const char*) sector->data.cbegin(), 256); | ||||
|                 } | ||||
|  | ||||
|                 offset += 256; | ||||
|             } | ||||
| 		} | ||||
|     } | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<ImageWriter> ImageWriter::createD64ImageWriter( | ||||
| 	const SectorSet& sectors, const ImageSpec& spec) | ||||
| { | ||||
|     return std::unique_ptr<ImageWriter>(new D64ImageWriter(sectors, spec)); | ||||
| } | ||||
|  | ||||
| @@ -1,11 +1,21 @@ | ||||
| #include "globals.h" | ||||
| #include "image.h" | ||||
| #include "flags.h" | ||||
| #include "dataspec.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "imagewriter/imagewriter.h" | ||||
| #include "fmt/format.h" | ||||
| #include <iostream> | ||||
| #include <fstream> | ||||
|  | ||||
| std::map<std::string, ImageWriter::Constructor> ImageWriter::formats = | ||||
| { | ||||
| 	{".adf", ImageWriter::createImgImageWriter}, | ||||
| 	{".d64", ImageWriter::createD64ImageWriter}, | ||||
| 	{".d81", ImageWriter::createImgImageWriter}, | ||||
| 	{".img", ImageWriter::createImgImageWriter}, | ||||
| 	{".ldbs", ImageWriter::createLDBSImageWriter}, | ||||
| }; | ||||
|  | ||||
| static bool ends_with(const std::string& value, const std::string& ending) | ||||
| { | ||||
| @@ -14,17 +24,29 @@ static bool ends_with(const std::string& value, const std::string& ending) | ||||
|     return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<ImageWriter> ImageWriter::create(const SectorSet& sectors, const ImageSpec& spec) | ||||
| ImageWriter::Constructor ImageWriter::findConstructor(const ImageSpec& spec) | ||||
| { | ||||
|     const auto& filename = spec.filename; | ||||
|  | ||||
|     if (ends_with(filename, ".img") || ends_with(filename, ".adf")) | ||||
|         return createImgImageWriter(sectors, spec); | ||||
| 	else if (ends_with(filename, ".ldbs")) | ||||
| 		return createLDBSImageWriter(sectors, spec); | ||||
| 	for (const auto& e : formats) | ||||
| 	{ | ||||
| 		if (ends_with(filename, e.first)) | ||||
| 			return e.second; | ||||
| 	} | ||||
|  | ||||
|     Error() << "unrecognised image filename extension"; | ||||
|     return std::unique_ptr<ImageWriter>(); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| std::unique_ptr<ImageWriter> ImageWriter::create(const SectorSet& sectors, const ImageSpec& spec) | ||||
| { | ||||
| 	verifyImageSpec(spec); | ||||
| 	return findConstructor(spec)(sectors, spec); | ||||
| } | ||||
|  | ||||
| void ImageWriter::verifyImageSpec(const ImageSpec& spec) | ||||
| { | ||||
| 	if (!findConstructor(spec)) | ||||
| 		Error() << "unrecognised image filename extension"; | ||||
| } | ||||
|  | ||||
| ImageWriter::ImageWriter(const SectorSet& sectors, const ImageSpec& spec): | ||||
| @@ -42,6 +64,56 @@ void ImageWriter::adjustGeometry() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void ImageWriter::writeCsv(const std::string& filename) | ||||
| { | ||||
| 	std::ofstream f(filename, std::ios::out); | ||||
| 	if (!f.is_open()) | ||||
| 		Error() << "cannot open CSV report file"; | ||||
|  | ||||
| 	f << "\"Physical track\"," | ||||
| 		"\"Physical side\"," | ||||
| 		"\"Logical track\"," | ||||
| 		"\"Logical side\"," | ||||
| 		"\"Logical sector\"," | ||||
| 		"\"Clock (ns)\"," | ||||
| 		"\"Header start (ns)\"," | ||||
| 		"\"Header end (ns)\"," | ||||
| 		"\"Data start (ns)\"," | ||||
| 		"\"Data end (ns)\"," | ||||
| 		"\"Raw data address (bytes)\"," | ||||
| 		"\"User payload length (bytes)\"," | ||||
| 		"\"Status\"" | ||||
| 		"\n"; | ||||
|  | ||||
| 	for (int track = 0; track < spec.cylinders; track++) | ||||
| 	{ | ||||
| 		for (int head = 0; head < spec.heads; head++) | ||||
| 		{ | ||||
| 			for (int sectorId = 0; sectorId < spec.sectors; sectorId++) | ||||
| 			{ | ||||
| 				f << fmt::format("{},{},", track, head); | ||||
| 				const auto& sector = sectors.get(track, head, sectorId); | ||||
| 				if (!sector) | ||||
| 					f << fmt::format(",,{},,,,,,,,MISSING\n", sectorId); | ||||
| 				else | ||||
| 					f << fmt::format("{},{},{},{},{},{},{},{},{},{},{}\n", | ||||
| 						sector->logicalTrack, | ||||
| 						sector->logicalSide, | ||||
| 						sector->logicalSector, | ||||
| 						sector->clock, | ||||
| 						sector->headerStartTime, | ||||
| 						sector->headerEndTime, | ||||
| 						sector->dataStartTime, | ||||
| 						sector->dataEndTime, | ||||
| 						sector->position.bytes, | ||||
| 						sector->data.size(), | ||||
| 						Sector::statusToString(sector->status) | ||||
| 					); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void ImageWriter::printMap() | ||||
| { | ||||
| 	int badSectors = 0; | ||||
|   | ||||
| @@ -12,16 +12,30 @@ public: | ||||
|  | ||||
| public: | ||||
|     static std::unique_ptr<ImageWriter> create(const SectorSet& sectors, const ImageSpec& spec); | ||||
| 	static void verifyImageSpec(const ImageSpec& filename); | ||||
|  | ||||
| private: | ||||
| 	typedef  | ||||
| 		std::function< | ||||
| 			std::unique_ptr<ImageWriter>(const SectorSet& sectors, const ImageSpec& spec) | ||||
| 		> | ||||
| 		Constructor; | ||||
|  | ||||
| 	static std::map<std::string, Constructor> formats; | ||||
|  | ||||
|     static std::unique_ptr<ImageWriter> createImgImageWriter( | ||||
| 		const SectorSet& sectors, const ImageSpec& spec); | ||||
|     static std::unique_ptr<ImageWriter> createLDBSImageWriter( | ||||
| 		const SectorSet& sectors, const ImageSpec& spec); | ||||
|     static std::unique_ptr<ImageWriter> createD64ImageWriter( | ||||
| 		const SectorSet& sectors, const ImageSpec& spec); | ||||
|  | ||||
| 	static Constructor findConstructor(const ImageSpec& spec); | ||||
|  | ||||
| public: | ||||
| 	virtual void adjustGeometry(); | ||||
| 	void printMap(); | ||||
| 	void writeCsv(const std::string& filename); | ||||
| 	virtual void writeImage() = 0; | ||||
|  | ||||
| protected: | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| #include "globals.h" | ||||
| #include "image.h" | ||||
| #include "flags.h" | ||||
| #include "dataspec.h" | ||||
| #include "sector.h" | ||||
| @@ -47,7 +46,7 @@ public: | ||||
| 					if (sector) | ||||
| 					{ | ||||
| 						outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*numBytes, std::ios::beg); | ||||
| 						outputFile.write((const char*) sector->data.cbegin(), sector->data.size()); | ||||
| 						sector->data.slice(0, numBytes).writeTo(outputFile); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| #include "globals.h" | ||||
| #include "image.h" | ||||
| #include "flags.h" | ||||
| #include "dataspec.h" | ||||
| #include "sector.h" | ||||
|   | ||||
							
								
								
									
										100
									
								
								lib/reader.cc
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								lib/reader.cc
									
									
									
									
									
								
							| @@ -2,6 +2,7 @@ | ||||
| #include "flags.h" | ||||
| #include "usb.h" | ||||
| #include "fluxsource/fluxsource.h" | ||||
| #include "fluxsink/fluxsink.h" | ||||
| #include "reader.h" | ||||
| #include "fluxmap.h" | ||||
| #include "sql.h" | ||||
| @@ -9,14 +10,23 @@ | ||||
| #include "decoders/decoders.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "visualiser.h" | ||||
| #include "record.h" | ||||
| #include "image.h" | ||||
| #include "bytes.h" | ||||
| #include "decoders/rawbits.h" | ||||
| #include "track.h" | ||||
| #include "imagewriter/imagewriter.h" | ||||
| #include "fmt/format.h" | ||||
| #include <iostream> | ||||
| #include <fstream> | ||||
|  | ||||
| FlagGroup readerFlags { &hardwareFluxSourceFlags, &fluxmapReaderFlags }; | ||||
| FlagGroup readerFlags | ||||
| { | ||||
| 	&hardwareFluxSourceFlags, | ||||
| 	&sqliteFluxSinkFlags, | ||||
| 	&fluxmapReaderFlags, | ||||
| 	&visualiserFlags | ||||
| }; | ||||
|  | ||||
| static DataSpecFlag source( | ||||
|     { "--source", "-s" }, | ||||
| @@ -33,13 +43,22 @@ static StringFlag destination( | ||||
|     "write the raw magnetic flux to this file", | ||||
|     ""); | ||||
|  | ||||
| static StringFlag visualise( | ||||
| 	{ "--write-svg" }, | ||||
| 	"write a visualisation of the disk to this file", | ||||
| 	""); | ||||
|  | ||||
| static SettableFlag justRead( | ||||
| 	{ "--just-read" }, | ||||
| 	"just read the disk and do no further processing"); | ||||
|  | ||||
| static SettableFlag dumpRecords( | ||||
| 	{ "--dump-records" }, | ||||
| 	"Dump the parsed records."); | ||||
| 	"Dump the parsed but undecoded records."); | ||||
|  | ||||
| static SettableFlag dumpSectors( | ||||
| 	{ "--dump-sectors" }, | ||||
| 	"Dump the decoded sectors."); | ||||
|  | ||||
| static IntFlag retries( | ||||
| 	{ "--retries" }, | ||||
| @@ -50,7 +69,12 @@ static SettableFlag highDensityFlag( | ||||
| 	{ "--high-density", "--hd" }, | ||||
| 	"set the drive to high density mode"); | ||||
|  | ||||
| static sqlite3* outdb; | ||||
| static StringFlag csvFile( | ||||
| 	{ "--write-csv" }, | ||||
| 	"write a CSV report of the disk state", | ||||
| 	""); | ||||
|  | ||||
| static std::unique_ptr<FluxSink> outputFluxSink; | ||||
|  | ||||
| void setReaderDefaultSource(const std::string& source) | ||||
| { | ||||
| @@ -67,16 +91,26 @@ void setReaderRevolutions(int revolutions) | ||||
| 	setHardwareFluxSourceRevolutions(revolutions); | ||||
| } | ||||
|  | ||||
| static void writeSectorsToFile(const SectorSet& sectors, const ImageSpec& spec) | ||||
| { | ||||
| 	std::unique_ptr<ImageWriter> writer(ImageWriter::create(sectors, spec)); | ||||
| 	writer->adjustGeometry(); | ||||
| 	writer->printMap(); | ||||
| 	if (!csvFile.get().empty()) | ||||
| 		writer->writeCsv(csvFile.get()); | ||||
| 	writer->writeImage(); | ||||
| } | ||||
|  | ||||
| void Track::readFluxmap() | ||||
| { | ||||
| 	std::cout << fmt::format("{0:>3}.{1}: ", physicalTrack, physicalSide) << std::flush; | ||||
| 	fluxmap = fluxsource->readFlux(physicalTrack, physicalSide); | ||||
| 	std::cout << fmt::format( | ||||
| 		"{0} ms in {1} bytes\n", | ||||
|             int(fluxmap->duration()/1e6), | ||||
|             fluxmap->duration()/1e6, | ||||
|             fluxmap->bytes()); | ||||
| 	if (outdb) | ||||
| 		sqlWriteFlux(outdb, physicalTrack, physicalSide, *fluxmap); | ||||
| 	if (outputFluxSink) | ||||
| 		outputFluxSink->writeFlux(physicalTrack, physicalSide, *fluxmap); | ||||
| } | ||||
|  | ||||
| std::vector<std::unique_ptr<Track>> readTracks() | ||||
| @@ -89,17 +123,8 @@ std::vector<std::unique_ptr<Track>> readTracks() | ||||
|  | ||||
| 	if (!destination.get().empty()) | ||||
| 	{ | ||||
| 		outdb = sqlOpen(destination, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); | ||||
| 		std::cout << "Writing a copy of the flux to " << destination.get() << std::endl; | ||||
| 		sqlPrepareFlux(outdb); | ||||
| 		sqlStmt(outdb, "BEGIN;"); | ||||
|         sqlWriteIntProperty(outdb, "version", FLUX_VERSION_CURRENT); | ||||
| 		atexit([]() | ||||
| 			{ | ||||
| 				sqlStmt(outdb, "COMMIT;"); | ||||
| 				sqlClose(outdb); | ||||
| 			} | ||||
| 		); | ||||
| 		outputFluxSink = FluxSink::createSqliteFluxSink(destination.get()); | ||||
| 	} | ||||
|  | ||||
| 	std::shared_ptr<FluxSource> fluxSource = FluxSource::create(spec); | ||||
| @@ -142,7 +167,7 @@ static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replaceme | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!replacing || (replacing->status != Sector::OK)) | ||||
| 	if (!replacing || ((replacing->status != Sector::OK) && (replacement.status == Sector::OK))) | ||||
| 	{ | ||||
| 		if (!replacing) | ||||
| 			replacing.reset(new Sector); | ||||
| @@ -153,7 +178,8 @@ static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replaceme | ||||
| void readDiskCommand(AbstractDecoder& decoder) | ||||
| { | ||||
| 	const ImageSpec outputSpec(output); | ||||
|  | ||||
| 	ImageWriter::verifyImageSpec(outputSpec); | ||||
|          | ||||
| 	bool failures = false; | ||||
| 	SectorSet allSectors; | ||||
| 	auto tracks = readTracks(); | ||||
| @@ -166,8 +192,6 @@ void readDiskCommand(AbstractDecoder& decoder) | ||||
| 			decoder.decodeToSectors(*track); | ||||
|  | ||||
| 			std::cout << "       "; | ||||
| 			if (!track->sectors.empty()) | ||||
| 			{ | ||||
| 				std::cout << fmt::format("{} records, {} sectors; ", | ||||
| 					track->rawrecords.size(), | ||||
| 					track->sectors.size()); | ||||
| @@ -183,9 +207,12 @@ void readDiskCommand(AbstractDecoder& decoder) | ||||
| 				} | ||||
|  | ||||
| 				bool hasBadSectors = false; | ||||
| 				std::set<unsigned> requiredSectors = decoder.requiredSectors(*track); | ||||
| 				for (const auto& i : readSectors) | ||||
| 				{ | ||||
| 					const auto& sector = i.second; | ||||
| 					requiredSectors.erase(sector->logicalSector); | ||||
|  | ||||
| 					if (sector->status != Sector::OK) | ||||
| 					{ | ||||
| 						std::cout << std::endl | ||||
| @@ -194,6 +221,12 @@ void readDiskCommand(AbstractDecoder& decoder) | ||||
| 						hasBadSectors = true; | ||||
| 					} | ||||
| 				} | ||||
| 				for (unsigned logicalSector : requiredSectors) | ||||
| 				{ | ||||
| 					std::cout << "\n" | ||||
| 					          << "       Required sector " << logicalSector << " missing; "; | ||||
| 					hasBadSectors = true; | ||||
| 				} | ||||
|  | ||||
| 				if (hasBadSectors) | ||||
| 					failures = false; | ||||
| @@ -203,7 +236,6 @@ void readDiskCommand(AbstractDecoder& decoder) | ||||
|  | ||||
| 				if (!hasBadSectors) | ||||
| 					break; | ||||
| 			} | ||||
|  | ||||
| 			if (!track->fluxsource->retryable()) | ||||
| 				break; | ||||
| @@ -211,10 +243,7 @@ void readDiskCommand(AbstractDecoder& decoder) | ||||
|                 std::cout << "giving up" << std::endl | ||||
|                           << "       "; | ||||
|             else | ||||
|             { | ||||
| 				std::cout << retry << " retries remaining" << std::endl; | ||||
|                 track->fluxsource->recalibrate(); | ||||
|             } | ||||
| 		} | ||||
|  | ||||
| 		if (dumpRecords) | ||||
| @@ -222,13 +251,27 @@ void readDiskCommand(AbstractDecoder& decoder) | ||||
| 			std::cout << "\nRaw (undecoded) records follow:\n\n"; | ||||
| 			for (auto& record : track->rawrecords) | ||||
| 			{ | ||||
| 				std::cout << fmt::format("I+{:.2f}us", record.position.ns() / 1000.0) | ||||
| 						<< std::endl; | ||||
| 				std::cout << fmt::format("I+{:.2f}us with {:.2f}us clock\n", | ||||
|                             record.position.ns() / 1000.0, record.clock / 1000.0); | ||||
| 				hexdump(std::cout, record.data); | ||||
| 				std::cout << std::endl; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|         if (dumpSectors) | ||||
|         { | ||||
|             std::cout << "\nDecoded sectors follow:\n\n"; | ||||
|             for (auto& sector : track->sectors) | ||||
|             { | ||||
| 				std::cout << fmt::format("{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock: status {}\n", | ||||
|                             sector.logicalTrack, sector.logicalSide, sector.logicalSector, | ||||
|                             sector.position.ns() / 1000.0, sector.clock / 1000.0, | ||||
| 							sector.status); | ||||
| 				hexdump(std::cout, sector.data); | ||||
| 				std::cout << std::endl; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         int size = 0; | ||||
| 		bool printedTrack = false; | ||||
|         for (auto& i : readSectors) | ||||
| @@ -251,6 +294,9 @@ void readDiskCommand(AbstractDecoder& decoder) | ||||
|         std::cout << size << " bytes decoded." << std::endl; | ||||
|     } | ||||
|  | ||||
| 	if (!visualise.get().empty()) | ||||
| 		visualiseSectorsToFile(allSectors, visualise.get()); | ||||
| 	 | ||||
|     writeSectorsToFile(allSectors, outputSpec); | ||||
| 	if (failures) | ||||
| 		std::cerr << "Warning: some sectors could not be decoded." << std::endl; | ||||
|   | ||||
| @@ -27,6 +27,10 @@ public: | ||||
| 	Status status = Status::INTERNAL_ERROR; | ||||
|     Fluxmap::Position position; | ||||
|     nanoseconds_t clock = 0; | ||||
|     nanoseconds_t headerStartTime = 0; | ||||
|     nanoseconds_t headerEndTime = 0; | ||||
|     nanoseconds_t dataStartTime = 0; | ||||
|     nanoseconds_t dataEndTime = 0; | ||||
|     int physicalTrack = 0; | ||||
|     int physicalSide = 0; | ||||
|     int logicalTrack = 0; | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| #include "globals.h" | ||||
| #include "image.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
|  | ||||
|   | ||||
| @@ -17,6 +17,9 @@ public: | ||||
| 	std::unique_ptr<Sector>& get(int track, int head, int sector); | ||||
| 	Sector* get(int track, int head, int sector) const; | ||||
|  | ||||
| 	const std::map<const key_t, std::unique_ptr<Sector>>& get() const | ||||
| 	{ return _data; } | ||||
|  | ||||
| 	void calculateSize( | ||||
| 		unsigned& numTracks, unsigned& numHeads, unsigned& numSectors, | ||||
| 		unsigned& sectorSize) const; | ||||
|   | ||||
| @@ -10,8 +10,9 @@ enum | ||||
|     FLUX_VERSION_0, /* without properties table */ | ||||
|     FLUX_VERSION_1, | ||||
|     FLUX_VERSION_2, /* new bytecode with index marks */ | ||||
| 	FLUX_VERSION_3, /* simplified bytecode with six-bit timer */ | ||||
|  | ||||
|     FLUX_VERSION_CURRENT = 2, | ||||
|     FLUX_VERSION_CURRENT = FLUX_VERSION_3, | ||||
| }; | ||||
|  | ||||
| extern void sqlCheck(sqlite3* db, int i); | ||||
|   | ||||
							
								
								
									
										103
									
								
								lib/usb.cc
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								lib/usb.cc
									
									
									
									
									
								
							| @@ -3,7 +3,6 @@ | ||||
| #include "protocol.h" | ||||
| #include "fluxmap.h" | ||||
| #include "bytes.h" | ||||
| #include "common/crunch.h" | ||||
| #include <libusb.h> | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| @@ -149,23 +148,26 @@ nanoseconds_t usbGetRotationalPeriod(void) | ||||
|     usb_cmd_send(&f, f.f.size); | ||||
|  | ||||
|     auto r = await_reply<struct speed_frame>(F_FRAME_MEASURE_SPEED_REPLY); | ||||
|     return r->period_ms * 1000; | ||||
|     return r->period_ms * 1000000; | ||||
| } | ||||
|  | ||||
| static int large_bulk_transfer(int ep, Bytes& bytes) | ||||
| { | ||||
|     if (bytes.size() == 0) | ||||
|         return 0; | ||||
|  | ||||
|     int len; | ||||
|     int i = libusb_bulk_transfer(device, ep, bytes.begin(), bytes.size(), &len, TIMEOUT); | ||||
|     if (i < 0) | ||||
|         Error() << "data transfer failed: " << usberror(i); | ||||
|         Error() << fmt::format("data transfer failed at {} bytes: {}", len, usberror(i)); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| void usbTestBulkTransport() | ||||
| void usbTestBulkWrite() | ||||
| { | ||||
|     usb_init(); | ||||
|  | ||||
|     struct any_frame f = { .f = {.type = F_FRAME_BULK_TEST_CMD, .size = sizeof(f)} }; | ||||
|     struct any_frame f = { .f = {.type = F_FRAME_BULK_WRITE_TEST_CMD, .size = sizeof(f)} }; | ||||
|     usb_cmd_send(&f, f.f.size); | ||||
|  | ||||
|     /* These must match the device. */ | ||||
| @@ -180,9 +182,9 @@ void usbTestBulkTransport() | ||||
|  | ||||
|     std::cout << "Transferred " | ||||
|               << bulk_buffer.size() | ||||
|               << " bytes in " | ||||
|               << " bytes from FluxEngine -> PC in " | ||||
|               << int(elapsed_time * 1000.0) | ||||
|               << " (" | ||||
|               << " ms (" | ||||
|               << int((bulk_buffer.size() / 1024.0) / elapsed_time) | ||||
|               << " kB/s)" | ||||
|               << std::endl; | ||||
| @@ -199,16 +201,56 @@ void usbTestBulkTransport() | ||||
|                             << x << '.' << y << '.' << z << '.'; | ||||
|             } | ||||
|  | ||||
|     await_reply<struct any_frame>(F_FRAME_BULK_TEST_REPLY); | ||||
|     await_reply<struct any_frame>(F_FRAME_BULK_WRITE_TEST_REPLY); | ||||
| } | ||||
|  | ||||
| Bytes usbRead(int side, int revolutions) | ||||
| void usbTestBulkRead() | ||||
| { | ||||
|     usb_init(); | ||||
|  | ||||
|     struct any_frame f = { .f = {.type = F_FRAME_BULK_READ_TEST_CMD, .size = sizeof(f)} }; | ||||
|     usb_cmd_send(&f, f.f.size); | ||||
|  | ||||
|     /* These must match the device. */ | ||||
|     const int XSIZE = 64; | ||||
|     const int YSIZE = 256; | ||||
|     const int ZSIZE = 64; | ||||
|  | ||||
|     Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE); | ||||
|     for (int x=0; x<XSIZE; x++) | ||||
|         for (int y=0; y<YSIZE; y++) | ||||
|             for (int z=0; z<ZSIZE; z++) | ||||
|             { | ||||
|                 int offset = x*XSIZE*YSIZE + y*ZSIZE + z; | ||||
|                 bulk_buffer[offset] = uint8_t(x+y+z); | ||||
|             } | ||||
|  | ||||
|     double start_time = getCurrentTime(); | ||||
|     large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, bulk_buffer); | ||||
|     double elapsed_time = getCurrentTime() - start_time; | ||||
|  | ||||
|     std::cout << "Transferred " | ||||
|               << bulk_buffer.size() | ||||
|               << " bytes from PC -> FluxEngine in " | ||||
|               << int(elapsed_time * 1000.0) | ||||
|               << " ms (" | ||||
|               << int((bulk_buffer.size() / 1024.0) / elapsed_time) | ||||
|               << " kB/s)" | ||||
|               << std::endl; | ||||
|  | ||||
|     await_reply<struct any_frame>(F_FRAME_BULK_READ_TEST_REPLY); | ||||
| } | ||||
|  | ||||
| Bytes usbRead(int side, bool synced, nanoseconds_t readTime) | ||||
| { | ||||
|     struct read_frame f = { | ||||
|         .f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) }, | ||||
|         .side = (uint8_t) side, | ||||
|         .revolutions = (uint8_t) revolutions | ||||
|         .synced = (uint8_t) synced | ||||
|     }; | ||||
|     uint16_t milliseconds = readTime / 1e6; | ||||
|     ((uint8_t*)&f.milliseconds)[0] = milliseconds; | ||||
|     ((uint8_t*)&f.milliseconds)[1] = milliseconds >> 8; | ||||
|     usb_cmd_send(&f, f.f.size); | ||||
|  | ||||
|     auto fluxmap = std::unique_ptr<Fluxmap>(new Fluxmap); | ||||
| @@ -236,7 +278,6 @@ void usbWrite(int side, const Bytes& bytes) | ||||
|     ((uint8_t*)&f.bytes_to_write)[3] = safelen >> 24; | ||||
|  | ||||
|     usb_cmd_send(&f, f.f.size); | ||||
|  | ||||
|     large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, safeBytes); | ||||
|      | ||||
|     await_reply<struct any_frame>(F_FRAME_WRITE_REPLY); | ||||
| @@ -253,15 +294,51 @@ void usbErase(int side) | ||||
|     await_reply<struct any_frame>(F_FRAME_ERASE_REPLY); | ||||
| } | ||||
|  | ||||
| void usbSetDrive(int drive, bool high_density) | ||||
| void usbSetDrive(int drive, bool high_density, int index_mode) | ||||
| { | ||||
|     usb_init(); | ||||
|  | ||||
|     struct set_drive_frame f = { | ||||
|         { .type = F_FRAME_SET_DRIVE_CMD, .size = sizeof(f) }, | ||||
|         .drive_flags = (uint8_t)((drive ? DRIVE_1 : DRIVE_0) | (high_density ? DRIVE_HD : DRIVE_DD)), | ||||
|         .drive = (uint8_t) drive, | ||||
|         .high_density = high_density, | ||||
|         .index_mode = (uint8_t) index_mode | ||||
|     }; | ||||
|     usb_cmd_send(&f, f.f.size); | ||||
|     await_reply<struct any_frame>(F_FRAME_SET_DRIVE_REPLY); | ||||
| } | ||||
|  | ||||
| /* Hacky: the board always operates in little-endian mode. */ | ||||
| static uint16_t read_short_from_usb(uint16_t usb) | ||||
| { | ||||
|     uint8_t* p = (uint8_t*)&usb; | ||||
|     return p[0] | (p[1] << 8); | ||||
| } | ||||
|  | ||||
| static void convert_voltages_from_usb(const struct voltages& vin, struct voltages& vout) | ||||
| { | ||||
|     vout.logic0_mv = read_short_from_usb(vin.logic0_mv); | ||||
|     vout.logic1_mv = read_short_from_usb(vin.logic1_mv); | ||||
| } | ||||
|  | ||||
| void usbMeasureVoltages(struct voltages_frame* voltages) | ||||
| { | ||||
|     usb_init(); | ||||
|  | ||||
|     struct any_frame f = { | ||||
|         { .type = F_FRAME_MEASURE_VOLTAGES_CMD, .size = sizeof(f) }, | ||||
|     }; | ||||
|     usb_cmd_send(&f, f.f.size); | ||||
|  | ||||
|     struct voltages_frame* r = await_reply<struct voltages_frame>(F_FRAME_MEASURE_VOLTAGES_REPLY); | ||||
|     convert_voltages_from_usb(r->input_both_off, voltages->input_both_off); | ||||
|     convert_voltages_from_usb(r->input_drive_0_selected, voltages->input_drive_0_selected); | ||||
|     convert_voltages_from_usb(r->input_drive_1_selected, voltages->input_drive_1_selected); | ||||
|     convert_voltages_from_usb(r->input_drive_0_running, voltages->input_drive_0_running); | ||||
|     convert_voltages_from_usb(r->input_drive_1_running, voltages->input_drive_1_running); | ||||
|     convert_voltages_from_usb(r->output_both_off, voltages->output_both_off); | ||||
|     convert_voltages_from_usb(r->output_drive_0_selected, voltages->output_drive_0_selected); | ||||
|     convert_voltages_from_usb(r->output_drive_1_selected, voltages->output_drive_1_selected); | ||||
|     convert_voltages_from_usb(r->output_drive_0_running, voltages->output_drive_0_running); | ||||
|     convert_voltages_from_usb(r->output_drive_1_running, voltages->output_drive_1_running); | ||||
| } | ||||
|   | ||||
| @@ -8,10 +8,12 @@ extern int usbGetVersion(); | ||||
| extern void usbRecalibrate(); | ||||
| extern void usbSeek(int track); | ||||
| extern nanoseconds_t usbGetRotationalPeriod(); | ||||
| extern void usbTestBulkTransport(); | ||||
| extern Bytes usbRead(int side, int revolutions); | ||||
| extern void usbTestBulkWrite(); | ||||
| extern void usbTestBulkRead(); | ||||
| extern Bytes usbRead(int side, bool synced, nanoseconds_t readTime); | ||||
| extern void usbWrite(int side, const Bytes& bytes); | ||||
| extern void usbErase(int side); | ||||
| extern void usbSetDrive(int drive, bool high_density); | ||||
| extern void usbSetDrive(int drive, bool high_density, int index_mode); | ||||
| extern void usbMeasureVoltages(struct voltages_frame* voltages); | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										94
									
								
								lib/visualiser.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								lib/visualiser.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| #define _USE_MATH_DEFINES | ||||
| #include "globals.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "visualiser.h" | ||||
| #include "fmt/format.h" | ||||
| #include "flags.h" | ||||
| #include <iostream> | ||||
| #include <fstream> | ||||
| #include <math.h> | ||||
|  | ||||
| FlagGroup visualiserFlags; | ||||
|  | ||||
| static IntFlag period( | ||||
|     { "--visualiser-period" }, | ||||
|     "rotational period for use by the visualiser (milliseconds)", | ||||
|     200); | ||||
|  | ||||
| static const int SIZE = 480; | ||||
| static const int BORDER = 10; | ||||
| static const int RADIUS = (SIZE/2) - (BORDER/2); | ||||
| static const int CORE = 50; | ||||
| static const int TRACKS = 83; | ||||
| static const double TRACK_SPACING = double(RADIUS-CORE) / TRACKS; | ||||
|  | ||||
| void visualiseSectorsToFile(const SectorSet& sectors, const std::string& filename) | ||||
| { | ||||
|     std::cout << "writing visualisation\n"; | ||||
|     std::ofstream f(filename, std::ios::out); | ||||
|     if (!f.is_open()) | ||||
|         Error() << "cannot open visualisation file"; | ||||
|  | ||||
|     f << fmt::format("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"{0} {1} {2} {3}\">", | ||||
|         0, 0, SIZE*2, SIZE); | ||||
|  | ||||
|     const double radians_per_ns = 2.0*M_PI / (period*1e6); | ||||
|  | ||||
|     auto drawSide = [&](int side) | ||||
|     { | ||||
|         f << fmt::format("<g transform='matrix(1 0 0 -1 {} {})'>", SIZE/2 + (side*SIZE), SIZE/2); | ||||
|         f << fmt::format("<circle cx='0' cy='0' r='{}' stroke='none' fill='#ccc'/>", RADIUS); | ||||
|  | ||||
|         for (int physicalTrack = 0; physicalTrack < TRACKS; physicalTrack++) | ||||
|         { | ||||
|             double radius = CORE + physicalTrack*TRACK_SPACING; | ||||
|             f << fmt::format("<circle cx='0' cy='0' r='{}' stroke='#888' stroke-width='0.5' fill='none'/>", radius); | ||||
|  | ||||
|             auto drawArc = [&](const std::unique_ptr<Sector>& sector, nanoseconds_t start, nanoseconds_t end, const std::string& colour) | ||||
|             { | ||||
|                 start = fmod(start, period*1000000.0); | ||||
|                 end = fmod(end, period*1000000.0); | ||||
|                 if (end < start) | ||||
|                     end += period*1000000; | ||||
|                  | ||||
|                 double theta1 = start * radians_per_ns; | ||||
|                 double theta2 = end * radians_per_ns; | ||||
|                 int large = (theta2 - theta1) >= M_PI; | ||||
|  | ||||
|                 f << fmt::format("\n<!-- {} {} = {} {} -->", start, end, theta1, theta2); | ||||
|                 f << fmt::format("<path fill='none' stroke='{}' stroke-width='1.5' d='", colour); | ||||
|                 f << fmt::format("M {} {} ", cos(theta1)*radius, sin(theta1)*radius); | ||||
|                 f << fmt::format("A {0} {0} 0 {3} 1 {1} {2}", radius, cos(theta2)*radius, sin(theta2)*radius, large); | ||||
|                 f << fmt::format("'><title>Track {} Head {} Sector {}; {}ms to {}ms</title></path>", | ||||
|                     sector->logicalTrack, sector->logicalSide, sector->logicalSector, | ||||
|                     start/1e6, end/1e6); | ||||
|             }; | ||||
|  | ||||
|             /* Sadly, SectorSets aren't indexable by physical track. */ | ||||
|             for (const auto& e : sectors.get()) | ||||
|             { | ||||
|                 const auto& sector = e.second; | ||||
|                 if ((sector->physicalSide == side) && (sector->physicalTrack == physicalTrack)) | ||||
|                 { | ||||
|                     const char* colour = "#f00"; | ||||
|                     if (sector->status == Sector::OK) | ||||
|                         colour = "#00f"; | ||||
|                     if (sector->headerStartTime && sector->headerEndTime) | ||||
|                         drawArc(sector, sector->headerStartTime, sector->headerEndTime, "#0ff"); | ||||
|                     if (sector->dataStartTime && sector->dataEndTime) | ||||
|                         drawArc(sector, sector->dataStartTime, sector->dataEndTime, colour); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         f << "</g>"; | ||||
|     }; | ||||
|  | ||||
|     f << fmt::format("<rect x='0' y='0' width='{}' height='{}' stroke='none' fill='#fff'/>", SIZE*2, SIZE); | ||||
|  | ||||
|     drawSide(0); | ||||
|     drawSide(1); | ||||
|  | ||||
|     f << "</svg>"; | ||||
| } | ||||
							
								
								
									
										12
									
								
								lib/visualiser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								lib/visualiser.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #ifndef VISUALISER_H | ||||
| #define VISUALISER_H | ||||
|  | ||||
| #include "flags.h" | ||||
|  | ||||
| class SectorSet; | ||||
|  | ||||
| extern FlagGroup visualiserFlags; | ||||
|  | ||||
| extern void visualiseSectorsToFile(const SectorSet& sectors, const std::string& filename); | ||||
|  | ||||
| #endif | ||||
| @@ -9,13 +9,13 @@ | ||||
| #include "encoders/encoders.h" | ||||
| #include "fluxsource/fluxsource.h" | ||||
| #include "fluxsink/fluxsink.h" | ||||
| #include "imagereader/imagereader.h" | ||||
| #include "fmt/format.h" | ||||
| #include "record.h" | ||||
| #include "image.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
|  | ||||
| FlagGroup writerFlags { &hardwareFluxSourceFlags }; | ||||
| FlagGroup writerFlags { &hardwareFluxSourceFlags, &sqliteFluxSinkFlags, &hardwareFluxSinkFlags }; | ||||
|  | ||||
| static DataSpecFlag dest( | ||||
|     { "--dest", "-d" }, | ||||
| @@ -43,6 +43,11 @@ void setWriterDefaultInput(const std::string& input) | ||||
|     ::input.set(input); | ||||
| } | ||||
|  | ||||
| static SectorSet readSectorsFromFile(const ImageSpec& spec) | ||||
| { | ||||
| 	return ImageReader::create(spec)->readImage(); | ||||
| } | ||||
|  | ||||
| void writeTracks( | ||||
| 	const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer) | ||||
| { | ||||
| @@ -53,19 +58,7 @@ void writeTracks( | ||||
| 	setHardwareFluxSourceDensity(highDensityFlag); | ||||
| 	setHardwareFluxSinkDensity(highDensityFlag); | ||||
|  | ||||
| 	if (!spec.filename.empty()) | ||||
| 	{ | ||||
| 		outdb = sqlOpen(spec.filename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); | ||||
| 		sqlPrepareFlux(outdb); | ||||
| 		sqlStmt(outdb, "BEGIN;"); | ||||
|         sqlWriteIntProperty(outdb, "version", FLUX_VERSION_CURRENT); | ||||
| 		atexit([]() | ||||
| 			{ | ||||
| 				sqlStmt(outdb, "COMMIT;"); | ||||
| 				sqlClose(outdb); | ||||
| 			} | ||||
| 		); | ||||
| 	} | ||||
| 	std::shared_ptr<FluxSink> fluxSink = FluxSink::create(spec); | ||||
|  | ||||
|     for (const auto& location : spec.locations) | ||||
|     { | ||||
| @@ -73,29 +66,16 @@ void writeTracks( | ||||
|         std::unique_ptr<Fluxmap> fluxmap = producer(location.track, location.side); | ||||
|         if (!fluxmap) | ||||
|         { | ||||
|             if (!outdb) | ||||
|             { | ||||
|                 std::cout << "erasing\n"; | ||||
|                 usbSeek(location.track); | ||||
|                 usbErase(location.side); | ||||
|             } | ||||
|             else | ||||
|                 std::cout << "skipping\n"; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS, 2); | ||||
|             if (outdb) | ||||
|                 sqlWriteFlux(outdb, location.track, location.side, *fluxmap); | ||||
|             else | ||||
|             { | ||||
|                 Bytes crunched = fluxmap->rawBytes().crunch(); | ||||
|                 usbSeek(location.track); | ||||
|                 usbWrite(location.side, crunched); | ||||
|             } | ||||
|             std::cout << fmt::format( | ||||
|                 "{0} ms in {1} bytes", int(fluxmap->duration()/1e6), fluxmap->bytes()) << std::endl; | ||||
|             /* Create an empty fluxmap for writing. */ | ||||
|             fluxmap.reset(new Fluxmap()); | ||||
|         } | ||||
|  | ||||
|         /* Precompensation actually seems to make things worse, so let's leave | ||||
|             * it disabled for now. */ | ||||
|         //fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS, 2); | ||||
|         fluxSink->writeFlux(location.track, location.side, *fluxmap); | ||||
|         std::cout << fmt::format( | ||||
|             "{0} ms in {1} bytes", int(fluxmap->duration()/1e6), fluxmap->bytes()) << std::endl; | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										61
									
								
								mkninja.sh
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								mkninja.sh
									
									
									
									
									
								
							| @@ -44,18 +44,27 @@ buildlibrary() { | ||||
|         esac | ||||
|     done | ||||
|  | ||||
|     local objs | ||||
|     objs= | ||||
|     local oobjs | ||||
| 	local dobjs | ||||
|     oobjs= | ||||
| 	dobjs= | ||||
|     for src in "$@"; do | ||||
|         local obj | ||||
|         obj="$OBJDIR/${src%%.c*}.o" | ||||
|         objs="$objs $obj" | ||||
|         obj="$OBJDIR/opt/${src%%.c*}.o" | ||||
|         oobjs="$oobjs $obj" | ||||
|  | ||||
|         echo build $obj : cxx $src | ||||
|         echo "    flags=$flags" | ||||
|         echo "    flags=$flags $COPTFLAGS" | ||||
|  | ||||
|         obj="$OBJDIR/dbg/${src%%.c*}.o" | ||||
|         dobjs="$dobjs $obj" | ||||
|  | ||||
|         echo build $obj : cxx $src | ||||
|         echo "    flags=$flags $CDBGFLAGS" | ||||
|     done | ||||
|  | ||||
|     echo build $OBJDIR/$lib : library $objs | ||||
|     echo build $OBJDIR/opt/$lib : library $oobjs | ||||
|     echo build $OBJDIR/dbg/$lib : library $dobjs | ||||
| } | ||||
|  | ||||
| buildprogram() { | ||||
| @@ -77,16 +86,21 @@ buildprogram() { | ||||
|         esac | ||||
|     done | ||||
|  | ||||
|     local objs | ||||
|     objs= | ||||
|     local oobjs | ||||
| 	local dobjs | ||||
|     oobjs= | ||||
| 	dobjs= | ||||
|     for src in "$@"; do | ||||
|         objs="$objs $OBJDIR/$src" | ||||
|         oobjs="$oobjs $OBJDIR/opt/$src" | ||||
|         dobjs="$dobjs $OBJDIR/dbg/$src" | ||||
|     done | ||||
|  | ||||
|     echo build $prog-debug$EXTENSION : link $objs | ||||
|     echo "    flags=$flags" | ||||
|     echo build $prog-debug$EXTENSION : link $dobjs | ||||
|     echo "    flags=$flags $LDDBGFLAGS" | ||||
|  | ||||
|     echo build $prog$EXTENSION : link $oobjs | ||||
|     echo "    flags=$flags $LDOPTFLAGS" | ||||
|  | ||||
|     echo build $prog$EXTENSION : strip $prog-debug$EXTENSION | ||||
| } | ||||
|  | ||||
| buildsimpleprogram() { | ||||
| @@ -139,11 +153,14 @@ buildlibrary libfmt.a \ | ||||
| buildlibrary libbackend.a \ | ||||
| 	lib/imagereader/imagereader.cc \ | ||||
| 	lib/imagereader/imgimagereader.cc \ | ||||
| 	lib/imagewriter/d64imagewriter.cc \ | ||||
| 	lib/imagewriter/imagewriter.cc \ | ||||
| 	lib/imagewriter/imgimagewriter.cc \ | ||||
| 	lib/imagewriter/ldbsimagewriter.cc \ | ||||
|     arch/aeslanier/decoder.cc \ | ||||
|     arch/amiga/decoder.cc \ | ||||
|     arch/amiga/encoder.cc \ | ||||
|     arch/amiga/amiga.cc \ | ||||
|     arch/apple2/decoder.cc \ | ||||
|     arch/brother/decoder.cc \ | ||||
|     arch/brother/encoder.cc \ | ||||
| @@ -151,12 +168,12 @@ buildlibrary libbackend.a \ | ||||
|     arch/f85/decoder.cc \ | ||||
|     arch/fb100/decoder.cc \ | ||||
|     arch/ibm/decoder.cc \ | ||||
|     arch/ibm/encoder.cc \ | ||||
|     arch/macintosh/decoder.cc \ | ||||
|     arch/mx/decoder.cc \ | ||||
|     arch/victor9k/decoder.cc \ | ||||
|     arch/zilogmcz/decoder.cc \ | ||||
|     lib/bytes.cc \ | ||||
|     lib/common/crunch.c \ | ||||
|     lib/crc.cc \ | ||||
|     lib/dataspec.cc \ | ||||
|     lib/decoders/decoders.cc \ | ||||
| @@ -175,19 +192,20 @@ buildlibrary libbackend.a \ | ||||
|     lib/fluxsource/streamfluxsource.cc \ | ||||
|     lib/globals.cc \ | ||||
|     lib/hexdump.cc \ | ||||
|     lib/image.cc \ | ||||
|     lib/ldbs.cc \ | ||||
|     lib/reader.cc \ | ||||
|     lib/sector.cc \ | ||||
|     lib/sectorset.cc \ | ||||
|     lib/sql.cc \ | ||||
|     lib/usb.cc \ | ||||
|     lib/visualiser.cc \ | ||||
|     lib/writer.cc \ | ||||
|  | ||||
| buildlibrary libfrontend.a \ | ||||
|     src/fe-cwftoflux.cc \ | ||||
|     src/fe-erase.cc \ | ||||
|     src/fe-fluxtoau.cc \ | ||||
|     src/fe-fluxtoscp.cc \ | ||||
|     src/fe-fluxtovcd.cc \ | ||||
|     src/fe-inspect.cc \ | ||||
|     src/fe-readadfs.cc \ | ||||
| @@ -206,10 +224,14 @@ buildlibrary libfrontend.a \ | ||||
|     src/fe-readvictor9k.cc \ | ||||
|     src/fe-readzilogmcz.cc \ | ||||
|     src/fe-rpm.cc \ | ||||
|     src/fe-scptoflux.cc \ | ||||
|     src/fe-seek.cc \ | ||||
|     src/fe-testbulktransport.cc \ | ||||
|     src/fe-testbandwidth.cc \ | ||||
|     src/fe-testvoltages.cc \ | ||||
|     src/fe-upgradefluxfile.cc \ | ||||
|     src/fe-writeamiga.cc \ | ||||
|     src/fe-writebrother.cc \ | ||||
|     src/fe-writeibm.cc \ | ||||
|     src/fe-writeflux.cc \ | ||||
|     src/fe-writetestpattern.cc \ | ||||
|     src/fluxengine.cc \ | ||||
| @@ -229,13 +251,20 @@ buildsimpleprogram brother120tool \ | ||||
|     libemu.a \ | ||||
|     libfmt.a \ | ||||
|  | ||||
| buildsimpleprogram brother240tool \ | ||||
| 	-Idep/emu \ | ||||
|     tools/brother240tool.cc \ | ||||
|     libbackend.a \ | ||||
|     libemu.a \ | ||||
|     libfmt.a \ | ||||
|  | ||||
| runtest bitaccumulator-test tests/bitaccumulator.cc | ||||
| runtest bytes-test          tests/bytes.cc | ||||
| runtest compression-test    tests/compression.cc | ||||
| runtest crunch-test         tests/crunch.cc | ||||
| runtest dataspec-test       tests/dataspec.cc | ||||
| runtest flags-test          tests/flags.cc | ||||
| runtest fluxpattern-test    tests/fluxpattern.cc | ||||
| runtest fmmfm-test          tests/fmmfm.cc | ||||
| runtest kryoflux-test       tests/kryoflux.cc | ||||
| runtest ldbs-test           tests/ldbs.cc | ||||
| runtest amiga-test          tests/amiga.cc | ||||
|   | ||||
							
								
								
									
										49
									
								
								protocol.h
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								protocol.h
									
									
									
									
									
								
							| @@ -3,7 +3,7 @@ | ||||
|  | ||||
| enum  | ||||
| { | ||||
|     FLUXENGINE_VERSION = 8, | ||||
|     FLUXENGINE_VERSION = 14, | ||||
|  | ||||
|     FLUXENGINE_VID = 0x1209, | ||||
|     FLUXENGINE_PID = 0x6e00, | ||||
| @@ -50,8 +50,10 @@ enum | ||||
|     F_FRAME_SEEK_REPLY,           /* any_frame */ | ||||
|     F_FRAME_MEASURE_SPEED_CMD,    /* any_frame */ | ||||
|     F_FRAME_MEASURE_SPEED_REPLY,  /* speed_frame */ | ||||
|     F_FRAME_BULK_TEST_CMD,        /* any_frame */ | ||||
|     F_FRAME_BULK_TEST_REPLY,      /* any_frame */ | ||||
|     F_FRAME_BULK_WRITE_TEST_CMD,   /* any_frame */ | ||||
|     F_FRAME_BULK_WRITE_TEST_REPLY, /* any_frame */ | ||||
|     F_FRAME_BULK_READ_TEST_CMD,   /* any_frame */ | ||||
|     F_FRAME_BULK_READ_TEST_REPLY, /* any_frame */ | ||||
|     F_FRAME_READ_CMD,             /* read_frame */ | ||||
|     F_FRAME_READ_REPLY,           /* any_frame */ | ||||
|     F_FRAME_WRITE_CMD,            /* write_frame */ | ||||
| @@ -62,6 +64,8 @@ enum | ||||
|     F_FRAME_RECALIBRATE_REPLY,    /* any_frame */ | ||||
|     F_FRAME_SET_DRIVE_CMD,        /* setdrive_frame */ | ||||
|     F_FRAME_SET_DRIVE_REPLY,      /* any_frame */ | ||||
|     F_FRAME_MEASURE_VOLTAGES_CMD, /* any_frame */ | ||||
|     F_FRAME_MEASURE_VOLTAGES_REPLY, /* voltages_frame */ | ||||
| }; | ||||
|  | ||||
| enum | ||||
| @@ -75,8 +79,15 @@ enum | ||||
|  | ||||
| enum | ||||
| { | ||||
|     F_OP_PULSE = 0x80, | ||||
|     F_OP_INDEX = 0x81 | ||||
|     F_INDEX_REAL, | ||||
|     F_INDEX_300, | ||||
|     F_INDEX_360 | ||||
| }; | ||||
|  | ||||
| enum | ||||
| { | ||||
|     F_BIT_PULSE = 0x80, | ||||
|     F_BIT_INDEX = 0x40 | ||||
| }; | ||||
|  | ||||
| struct frame_header | ||||
| @@ -124,7 +135,8 @@ struct read_frame | ||||
| { | ||||
|     struct frame_header f; | ||||
|     uint8_t side; | ||||
|     uint8_t revolutions; | ||||
|     uint8_t synced; | ||||
|     uint16_t milliseconds; | ||||
| }; | ||||
|  | ||||
| struct write_frame | ||||
| @@ -143,7 +155,30 @@ struct erase_frame | ||||
| struct set_drive_frame | ||||
| { | ||||
|     struct frame_header f; | ||||
|     uint8_t drive_flags; | ||||
|     uint8_t drive; | ||||
|     uint8_t high_density; | ||||
|     uint8_t index_mode; | ||||
| }; | ||||
|  | ||||
| struct voltages | ||||
| { | ||||
|     uint16_t logic0_mv; | ||||
|     uint16_t logic1_mv; | ||||
| }; | ||||
|  | ||||
| struct voltages_frame | ||||
| { | ||||
|     struct frame_header f; | ||||
|     struct voltages output_both_off; | ||||
|     struct voltages output_drive_0_selected; | ||||
|     struct voltages output_drive_1_selected; | ||||
|     struct voltages output_drive_0_running; | ||||
|     struct voltages output_drive_1_running; | ||||
|     struct voltages input_both_off; | ||||
|     struct voltages input_drive_0_selected; | ||||
|     struct voltages input_drive_1_selected; | ||||
|     struct voltages input_drive_0_running; | ||||
|     struct voltages input_drive_1_running; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| #include "fluxmap.h" | ||||
| #include "writer.h" | ||||
|  | ||||
| static FlagGroup flags; | ||||
| static FlagGroup flags { &writerFlags }; | ||||
|  | ||||
| int mainErase(int argc, const char* argv[]) | ||||
| { | ||||
|   | ||||
| @@ -81,14 +81,14 @@ int mainConvertFluxToAu(int argc, const char* argv[]) | ||||
|         while (!fmr.eof()) | ||||
|         { | ||||
|             unsigned ticks; | ||||
|             int op = fmr.readOpcode(ticks); | ||||
|             if (op == -1) | ||||
|             uint8_t bits = fmr.getNextEvent(ticks); | ||||
|             if (fmr.eof()) | ||||
|                 break; | ||||
|             timestamp += ticks; | ||||
|  | ||||
|             if (op == F_OP_PULSE) | ||||
|             if (bits & F_BIT_PULSE) | ||||
|                 data[timestamp*channels + 0] = 0x7f; | ||||
|             if (withIndex && (op == F_OP_INDEX)) | ||||
|             if (withIndex && (bits & F_BIT_INDEX)) | ||||
|                 data[timestamp*channels + 1] = 0x7f; | ||||
|         } | ||||
|  | ||||
|   | ||||
							
								
								
									
										202
									
								
								src/fe-fluxtoscp.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								src/fe-fluxtoscp.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | ||||
| #include "globals.h" | ||||
| #include "flags.h" | ||||
| #include "fluxmap.h" | ||||
| #include "sql.h" | ||||
| #include "bytes.h" | ||||
| #include "protocol.h" | ||||
| #include "dataspec.h" | ||||
| #include "fmt/format.h" | ||||
| #include "decoders/fluxmapreader.h" | ||||
| #include "scp.h" | ||||
| #include <fstream> | ||||
| #include <algorithm> | ||||
|  | ||||
| static FlagGroup flags { }; | ||||
|  | ||||
| static SettableFlag fortyTrackMode( | ||||
|     { "--48", "-4" }, | ||||
|     "set 48 tpi mode; only every other physical track is emitted" | ||||
| ); | ||||
|  | ||||
| static SettableFlag indexedMode( | ||||
| 	{ "--indexed", "-i" }, | ||||
| 	"align data to track boundaries" | ||||
| ); | ||||
|  | ||||
| static SettableFlag singleSided( | ||||
|     { "--single-sided", "-s" }, | ||||
|     "only emit side 0" | ||||
| ); | ||||
|  | ||||
| static IntFlag diskType( | ||||
|     { "--disk-type" }, | ||||
|     "sets the SCP disk type byte", | ||||
|     0xff | ||||
| ); | ||||
|  | ||||
| static sqlite3* inputDb; | ||||
|  | ||||
| static void syntax() | ||||
| { | ||||
|     std::cout << "Syntax: fluxengine convert fluxtoscp <fluxfile> <scpfile>\n"; | ||||
|     exit(0); | ||||
| } | ||||
|  | ||||
| static void write_le32(uint8_t dest[4], uint32_t v) | ||||
| { | ||||
|     dest[0] = v; | ||||
|     dest[1] = v >> 8; | ||||
|     dest[2] = v >> 16; | ||||
|     dest[3] = v >> 24; | ||||
| } | ||||
|  | ||||
| static void appendChecksum(uint32_t& checksum, const Bytes& bytes) | ||||
| { | ||||
| 	ByteReader br(bytes); | ||||
| 	while (!br.eof()) | ||||
| 		checksum += br.read_8(); | ||||
| } | ||||
|  | ||||
| static int strackno(int track, int side) | ||||
| { | ||||
|     if (fortyTrackMode) | ||||
|         track /= 2; | ||||
|     if (singleSided) | ||||
|         return track; | ||||
|     else | ||||
|         return (track << 1) | side; | ||||
| } | ||||
|  | ||||
| int mainConvertFluxToScp(int argc, const char* argv[]) | ||||
| { | ||||
|     auto filenames = flags.parseFlagsWithFilenames(argc, argv); | ||||
|     if (filenames.size() != 2) | ||||
|         syntax(); | ||||
|  | ||||
|     inputDb = sqlOpen(filenames[0], SQLITE_OPEN_READONLY); | ||||
|     auto tracks = sqlFindFlux(inputDb); | ||||
|  | ||||
|     int maxTrack = 0; | ||||
|     int maxSide = 0; | ||||
|     for (auto p : tracks) | ||||
|     { | ||||
|         if (singleSided && (p.second == 1)) | ||||
|             continue; | ||||
|         maxTrack = std::max(maxTrack, (int)p.first); | ||||
|         maxSide = std::max(maxSide, (int)p.second); | ||||
|     } | ||||
|     int maxStrack = strackno(maxTrack, maxSide); | ||||
|  | ||||
|     std::cout << fmt::format("Writing {} {} SCP file containing {} SCP tracks\n", | ||||
|         fortyTrackMode ? "48 tpi" : "96 tpi", | ||||
|         singleSided ? "single sided" : "double sided", | ||||
|         maxStrack + 1 | ||||
|     ); | ||||
|  | ||||
|     ScpHeader fileheader = {0}; | ||||
|     fileheader.file_id[0] = 'S'; | ||||
|     fileheader.file_id[1] = 'C'; | ||||
|     fileheader.file_id[2] = 'P'; | ||||
|     fileheader.version = 0x18; /* Version 1.8 of the spec */ | ||||
|     fileheader.type = diskType; | ||||
|     fileheader.revolutions = 5; | ||||
|     fileheader.start_track = 0; | ||||
|     fileheader.end_track = maxStrack; | ||||
|     fileheader.flags = (indexedMode ? SCP_FLAG_INDEXED : 0) | ||||
| 			| (fortyTrackMode ? 0 : SCP_FLAG_96TPI); | ||||
|     fileheader.cell_width = 0; | ||||
|     fileheader.heads = singleSided ? 1 : 0; | ||||
|  | ||||
|     Bytes trackdata; | ||||
|     ByteWriter trackdataWriter(trackdata); | ||||
|  | ||||
|     int trackstep = 1 + fortyTrackMode; | ||||
|     int maxside = singleSided ? 0 : 1; | ||||
|     for (int track = 0; track <= maxTrack; track += trackstep) | ||||
|     { | ||||
|         for (int side = 0; side <= maxside; side++) | ||||
|         { | ||||
|             int strack = strackno(track, side); | ||||
|             std::cout << fmt::format("FE track {}.{}, SCP track {}: ", track, side, strack) << std::flush; | ||||
|  | ||||
|             auto fluxmap = sqlReadFlux(inputDb, track, side); | ||||
|             ScpTrack trackheader = {0}; | ||||
|             trackheader.track_id[0] = 'T'; | ||||
|             trackheader.track_id[1] = 'R'; | ||||
|             trackheader.track_id[2] = 'K'; | ||||
|             trackheader.strack = strack; | ||||
|  | ||||
|             FluxmapReader fmr(*fluxmap); | ||||
|             Bytes fluxdata; | ||||
|             ByteWriter fluxdataWriter(fluxdata); | ||||
|  | ||||
| 			if (indexedMode) | ||||
| 				fmr.findEvent(F_BIT_INDEX); | ||||
|  | ||||
|             int revolution = 0; | ||||
|             unsigned revTicks = 0; | ||||
|             unsigned totalTicks = 0; | ||||
|             unsigned ticksSinceLastPulse = 0; | ||||
|             uint32_t startOffset = 0; | ||||
|             while (revolution < 5) | ||||
|             { | ||||
|                 unsigned ticks; | ||||
|                 uint8_t bits = fmr.getNextEvent(ticks); | ||||
| 				ticksSinceLastPulse += ticks; | ||||
| 				totalTicks += ticks; | ||||
| 				revTicks += ticks; | ||||
|  | ||||
| 				if (fmr.eof() || (bits & F_BIT_INDEX)) | ||||
| 				{ | ||||
| 					auto* revheader = &trackheader.revolution[revolution]; | ||||
| 					write_le32(revheader->offset, startOffset + sizeof(ScpTrack)); | ||||
| 					write_le32(revheader->length, (fluxdataWriter.pos - startOffset) / 2); | ||||
| 					write_le32(revheader->index, revTicks * NS_PER_TICK / 25); | ||||
| 					revolution++; | ||||
| 					revheader++; | ||||
| 					revTicks = 0; | ||||
| 					startOffset = fluxdataWriter.pos; | ||||
| 				} | ||||
|  | ||||
| 				if (bits & F_BIT_PULSE) | ||||
| 				{ | ||||
| 					unsigned t = ticksSinceLastPulse * NS_PER_TICK / 25; | ||||
| 					while (t >= 0x10000) | ||||
| 					{ | ||||
| 						fluxdataWriter.write_be16(0); | ||||
| 						t -= 0x10000; | ||||
| 					} | ||||
| 					fluxdataWriter.write_be16(t); | ||||
| 					ticksSinceLastPulse = 0; | ||||
| 				} | ||||
|             } | ||||
|  | ||||
|             write_le32(fileheader.track[strack], trackdataWriter.pos + sizeof(ScpHeader)); | ||||
|             trackdataWriter += Bytes((uint8_t*)&trackheader, sizeof(trackheader)); | ||||
|             trackdataWriter += fluxdata; | ||||
|  | ||||
|             std::cout << fmt::format("{} ms in {} bytes\n", | ||||
|                 totalTicks * MS_PER_TICK, | ||||
|                 fluxdata.size()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sqlClose(inputDb); | ||||
|      | ||||
| 	uint32_t checksum = 0; | ||||
| 	appendChecksum(checksum, | ||||
| 		Bytes((const uint8_t*) &fileheader, sizeof(fileheader)) | ||||
| 			.slice(0x10)); | ||||
| 	appendChecksum(checksum, trackdata); | ||||
| 	write_le32(fileheader.checksum, checksum); | ||||
|  | ||||
|     std::cout << "Writing output file...\n"; | ||||
|     std::ofstream of(filenames[1], std::ios::out | std::ios::binary); | ||||
|     if (!of.is_open()) | ||||
|         Error() << "cannot open output file"; | ||||
|     of.write((const char*) &fileheader, sizeof(fileheader)); | ||||
|     of.write((const char*) trackdata.begin(), trackdata.size()); | ||||
|     of.close(); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -59,8 +59,8 @@ int mainConvertFluxToVcd(int argc, const char* argv[]) | ||||
|     while (!fmr.eof()) | ||||
|     { | ||||
|         unsigned ticks; | ||||
|         int op = fmr.readOpcode(ticks); | ||||
|         if (op == -1) | ||||
|         uint8_t bits = fmr.getNextEvent(ticks); | ||||
|         if (fmr.eof()) | ||||
|             break; | ||||
|  | ||||
|         unsigned newtimestamp = timestamp + ticks; | ||||
| @@ -71,9 +71,9 @@ int mainConvertFluxToVcd(int argc, const char* argv[]) | ||||
|             of << fmt::format("#{} ", (uint64_t)(timestamp * NS_PER_TICK)); | ||||
|         } | ||||
|  | ||||
|         if (op == F_OP_PULSE) | ||||
|         if (bits & F_BIT_PULSE) | ||||
|             of << "1p "; | ||||
|         if (op == F_OP_INDEX) | ||||
|         if (bits & F_BIT_INDEX) | ||||
|             of << "1i "; | ||||
|  | ||||
|         lasttimestamp = timestamp; | ||||
|   | ||||
| @@ -4,7 +4,6 @@ | ||||
| #include "fluxmap.h" | ||||
| #include "decoders/fluxmapreader.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "image.h" | ||||
| #include "protocol.h" | ||||
| #include "decoders/rawbits.h" | ||||
| #include "record.h" | ||||
| @@ -73,7 +72,7 @@ static nanoseconds_t guessClock(const Fluxmap& fluxmap) | ||||
|  | ||||
|     while (!fr.eof()) | ||||
|     { | ||||
|         unsigned interval = fr.readNextMatchingOpcode(F_OP_PULSE); | ||||
|         unsigned interval = fr.findEvent(F_BIT_PULSE); | ||||
|         if (interval > 0xff) | ||||
|             continue; | ||||
|         buckets[interval]++; | ||||
| @@ -156,7 +155,11 @@ static nanoseconds_t guessClock(const Fluxmap& fluxmap) | ||||
| 				s += BLOCK_ELEMENTS[8]; | ||||
| 			s += BLOCK_ELEMENTS[bar & 7]; | ||||
|  | ||||
| 			std::cout << fmt::format("{:.2f} {:6} {}", (double)i * US_PER_TICK, value, s); | ||||
| 			std::cout << fmt::format("{: 3} {:.2f} {:6} {}", | ||||
| 					i, | ||||
| 					(double)i * US_PER_TICK, | ||||
| 					value, | ||||
| 					s); | ||||
| 			std::cout << std::endl; | ||||
| 		} | ||||
| 	} | ||||
| @@ -186,6 +189,12 @@ int mainInspect(int argc, const char* argv[]) | ||||
| 	auto& track = *tracks.begin(); | ||||
| 	track->readFluxmap(); | ||||
|  | ||||
| 	std::cout << fmt::format("0x{:x} bytes of data in {:.3f}ms\n", | ||||
| 			track->fluxmap->bytes(), | ||||
| 			track->fluxmap->duration() / 1e6); | ||||
| 	std::cout << fmt::format("Required USB bandwidth: {}kB/s\n", | ||||
| 			track->fluxmap->bytes()/1024.0 / (track->fluxmap->duration() / 1e9)); | ||||
|  | ||||
| 	nanoseconds_t clockPeriod = guessClock(*track->fluxmap); | ||||
| 	std::cout << fmt::format("{:.2f} us clock detected.", (double)clockPeriod/1000.0) << std::flush; | ||||
|  | ||||
| @@ -209,7 +218,7 @@ int mainInspect(int argc, const char* argv[]) | ||||
| 		nanoseconds_t lasttransition = 0; | ||||
| 		while (!fmr.eof()) | ||||
| 		{ | ||||
| 			ticks += fmr.readNextMatchingOpcode(F_OP_PULSE); | ||||
| 			ticks += fmr.findEvent(F_BIT_PULSE); | ||||
|  | ||||
| 			nanoseconds_t transition = ticks*NS_PER_TICK; | ||||
| 			nanoseconds_t next; | ||||
|   | ||||
| @@ -3,7 +3,6 @@ | ||||
| #include "reader.h" | ||||
| #include "fluxmap.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "image.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "record.h" | ||||
|   | ||||
| @@ -3,7 +3,6 @@ | ||||
| #include "reader.h" | ||||
| #include "fluxmap.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "image.h" | ||||
| #include "sector.h" | ||||
| #include "sectorset.h" | ||||
| #include "record.h" | ||||
| @@ -12,11 +11,6 @@ | ||||
|  | ||||
| static FlagGroup flags { &readerFlags }; | ||||
|  | ||||
| static StringFlag outputFilename( | ||||
|     { "--output", "-o" }, | ||||
|     "The output image file to write to.", | ||||
|     "aeslanier.img"); | ||||
|  | ||||
| int mainReadAESLanier(int argc, const char* argv[]) | ||||
| { | ||||
| 	setReaderDefaultSource(":t=0-79:s=0"); | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user