mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Compare commits
220 Commits
FluxEngine
...
FluxEngine
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6da85bf64 | ||
|
|
cd19fcdadd | ||
|
|
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 | ||
|
|
29db67528d | ||
|
|
31d7477c6a | ||
|
|
56af9eaf18 | ||
|
|
5de0636fe7 | ||
|
|
f9117b8d11 | ||
|
|
10d385375f | ||
|
|
2f72c3f8f0 | ||
|
|
54edff9b94 | ||
|
|
112377f885 | ||
|
|
87e29fc386 | ||
|
|
b1db5c48b1 | ||
|
|
38fab7edcb | ||
|
|
d8172154c3 | ||
|
|
eb924780ab | ||
|
|
28e0ef0463 | ||
|
|
4b07c38782 | ||
|
|
e0256adf77 | ||
|
|
5748f017dd | ||
|
|
973f4c2c2d | ||
|
|
8e1774c69f | ||
|
|
56a36072f7 | ||
|
|
8755d108ed | ||
|
|
ea40cd73d1 | ||
|
|
0e28899b72 | ||
|
|
eee30db981 | ||
|
|
6959d18017 | ||
|
|
9f92ce0ef7 | ||
|
|
7658c1d774 | ||
|
|
31dc3504e6 | ||
|
|
af0c9d4261 | ||
|
|
155b9daef6 | ||
|
|
a2fdbc5c73 | ||
|
|
1e3581c5f3 | ||
|
|
a1d345856e | ||
|
|
7a775afaea | ||
|
|
c27c4fe312 | ||
|
|
ad295c683c | ||
|
|
3f8fdaa27a | ||
|
|
9f5d01787f | ||
|
|
3c4487c42e | ||
|
|
e5c2168a35 | ||
|
|
a06d8ff05a | ||
|
|
c91ca1b730 | ||
|
|
3b6ea24df5 | ||
|
|
b0e905fdd0 | ||
|
|
34a858346e | ||
|
|
499cb4f720 | ||
|
|
3960b1e4d7 | ||
|
|
98ea5e9600 | ||
|
|
ce6077fa22 |
@@ -15,7 +15,7 @@ install:
|
||||
|
||||
build_script:
|
||||
- make
|
||||
- zip -9 fluxengine.zip fluxengine.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
|
||||
|
||||
@@ -3,6 +3,10 @@ streams
|
||||
.*\.flux
|
||||
.*\.img
|
||||
.*\.raw
|
||||
.*\.orig
|
||||
.vscode
|
||||
remote
|
||||
FluxEngine.cydsn/CortexM3
|
||||
FluxEngine.cydsn/Generated_Source
|
||||
FluxEngine.cydsn/codegentemp
|
||||
|
||||
|
||||
37
.travis.yml
37
.travis.yml
@@ -1,37 +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
|
||||
|
||||
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,13 +28,85 @@
|
||||
<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" />
|
||||
<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_3" />
|
||||
<Data key="named_src_direct_connect" value="True" />
|
||||
<Data key="netlist_name" value="Clock_3" />
|
||||
<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="06c4d5d4-f15f-4b29-a1d0-c24b2e38b1ec">
|
||||
<Data key="check_tolerance" value="True" />
|
||||
<Data key="clock_version" value="v1" />
|
||||
<Data key="derive_type" value="NAMED_FREQ" />
|
||||
<Data key="desired_freq" value="24" />
|
||||
<Data key="desired_freq" value="12" />
|
||||
<Data key="desired_unit" value="6" />
|
||||
<Data key="divider" value="1" />
|
||||
<Data key="divider" value="2" />
|
||||
<Data key="domain" value="DIGITAL" />
|
||||
<Data key="enabled" value="True" />
|
||||
<Data key="minus_accuracy" value="0.25" />
|
||||
@@ -50,7 +122,7 @@
|
||||
<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" />
|
||||
<Data key="user_set_domain" value="True" />
|
||||
</Group>
|
||||
<Group key="24cd38f7-f472-4403-837f-86807c8f5333">
|
||||
<Data key="check_tolerance" value="True" />
|
||||
@@ -123,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" />
|
||||
@@ -146,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" />
|
||||
@@ -193,6 +336,101 @@
|
||||
<Data key="sync_with_bus_clk" value="True" />
|
||||
<Data key="user_set_domain" value="False" />
|
||||
</Group>
|
||||
<Group key="75187c05-9501-4450-b306-6ccdd3bb77db">
|
||||
<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="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" />
|
||||
<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_2" />
|
||||
<Data key="named_src_direct_connect" value="True" />
|
||||
<Data key="netlist_name" value="Clock_2" />
|
||||
<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="b0162966-0060-4af5-82d1-fcb491ad7619/be0a0e37-ad17-42ca-b5a1-1a654d736358">
|
||||
<Data key="check_tolerance" value="True" />
|
||||
<Data key="clock_version" value="v1" />
|
||||
@@ -216,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" />
|
||||
@@ -240,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" />
|
||||
@@ -537,15 +823,27 @@
|
||||
<Group key="DWRInstGuidMapping">
|
||||
<Group key="Clock">
|
||||
<Data key="0b2f9bbb-00ce-4115-a788-ffb9d046a9e5" value="Clock_4" />
|
||||
<Data key="1a7e8637-3b6b-4e84-839c-0dfc18fdaf5b" value="Clock_5" />
|
||||
<Data key="3f3708ae-fb62-4012-919b-9a3b9a1dfbc2" value="Clock_8" />
|
||||
<Data key="4eef02b9-8ad1-43c4-85f1-b3335faa5fc4" value="Clock_3" />
|
||||
<Data key="06c4d5d4-f15f-4b29-a1d0-c24b2e38b1ec" value="CounterClock" />
|
||||
<Data key="24cd38f7-f472-4403-837f-86807c8f5333" value="PULSE_CLOCK" />
|
||||
<Data key="63ed4137-0b09-4256-8a27-35c9a2653f1a" value="Clock_2" />
|
||||
<Data key="66f14071-bddd-4b4d-a9aa-a129cceaa7b6" value="Clock_3" />
|
||||
<Data key="71bc291d-84a7-40a8-b7b2-1c8a34326a31" value="CLOCK300" />
|
||||
<Data key="90ce0c72-9f10-44ef-a049-f0f525d59bea" value="CLOCK8" />
|
||||
<Data key="349ffa20-8576-4ac3-9a6f-34ef606de6cf" value="Clock_1" />
|
||||
<Data key="4033c29d-f4bc-4e94-ac95-aa587e869f88/696a0979-21fc-4185-bf38-6c79febcde7a" value="OUTPUT_VOLTAGE_ADC_theACLK" />
|
||||
<Data key="6616e828-6611-4893-a674-66c861d79d6c" value="SignalSamplingClock" />
|
||||
<Data key="12664fc6-9d70-44b1-8a49-887a292e1b7f" value="Clock_3" />
|
||||
<Data key="75187c05-9501-4450-b306-6ccdd3bb77db" value="Clock_5" />
|
||||
<Data key="09974428-e912-491f-8d2f-361ba50e7599" value="Clock_6" />
|
||||
<Data key="a5825a94-fa18-4e4f-a843-bc687cacbd56/696a0979-21fc-4185-bf38-6c79febcde7a" value="INPUT_VOLTAGE_ADC_theACLK" />
|
||||
<Data key="b762c287-7f87-4b21-982e-84be01dc5115" value="Clock_2" />
|
||||
<Data key="b0162966-0060-4af5-82d1-fcb491ad7619/be0a0e37-ad17-42ca-b5a1-1a654d736358" value="UART_IntClock" />
|
||||
<Data key="b722443b-8f81-46dc-bf9b-c95eb62bc181" value="Clock_1" />
|
||||
<Data key="cb7e877c-9fb4-4fc1-a708-f1e48eb5a68c" value="CounterClock" />
|
||||
<Data key="d3075dc6-05c8-4dc9-9959-cf7014c0e66f" value="Clock_7" />
|
||||
<Data key="e4a53a4c-40e1-4747-a72a-10193ffdf31c" value="Clock_1" />
|
||||
<Data key="efd5f185-0c32-4824-ba72-3ceb5356f5a7" value="Clock_1" />
|
||||
</Group>
|
||||
@@ -553,6 +851,8 @@
|
||||
<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" />
|
||||
@@ -561,17 +861,20 @@
|
||||
<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="fbd1f839-40f9-498e-a48b-5f3048ea5c3d/52f31aa9-2f0a-497d-9a1f-1424095e13e6" value="SW_Tx_UART_1_tx" />
|
||||
<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" />
|
||||
</Group>
|
||||
@@ -3675,6 +3978,25 @@
|
||||
<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" />
|
||||
</Group>
|
||||
<Group key="1">
|
||||
<Data key="Port Format" value="2,3" />
|
||||
</Group>
|
||||
<Group key="2">
|
||||
<Data key="Port Format" value="2,4" />
|
||||
</Group>
|
||||
<Group key="3">
|
||||
<Data key="Port Format" value="2,0" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="41e2d8ed-5494-4d8c-8ff7-f4f789cece51">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="2,7" />
|
||||
@@ -3757,6 +4079,43 @@
|
||||
<Data key="Port Format" value="12,1" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="b8380fb7-fdb8-449f-bd8d-c4ca96cdf55a">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="2,5" />
|
||||
</Group>
|
||||
<Group key="1">
|
||||
<Data key="Port Format" value="2,4" />
|
||||
</Group>
|
||||
<Group key="2">
|
||||
<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" />
|
||||
@@ -3792,7 +4151,7 @@
|
||||
</Group>
|
||||
<Group key="e851a3b9-efb8-48be-bbb8-b303b216c393">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="2,1" />
|
||||
<Data key="Port Format" value="3,0" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="e51063a9-4fad-40c7-a06b-7cc4b137dc18">
|
||||
@@ -3810,9 +4169,14 @@
|
||||
<Data key="Port Format" value="12,7" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="f9a7371a-8a7d-4144-8b08-69e3d2a3a663">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="3,1" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="fbd1f839-40f9-498e-a48b-5f3048ea5c3d/52f31aa9-2f0a-497d-9a1f-1424095e13e6">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="2,0" />
|
||||
<Data key="Port Format" value="12,7" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="fede1767-f3fd-4021-b3d7-8f9d88f36f9b">
|
||||
@@ -3840,7 +4204,7 @@
|
||||
<Data key="CYDEV_ECC_ENABLE" value="False" />
|
||||
<Data key="CYDEV_HEAP_SIZE" value="0x80" />
|
||||
<Data key="CYDEV_INSTRUCT_CACHE_ENABLED" value="True" />
|
||||
<Data key="CYDEV_PROTECTION_ENABLE" value="False" />
|
||||
<Data key="CYDEV_PROTECTION_ENABLE" value="True" />
|
||||
<Data key="CYDEV_STACK_SIZE" value="0x0800" />
|
||||
<Data key="CYDEV_TEMPERATURE" value="0C - 85/125C" />
|
||||
<Data key="CYDEV_TRACE_ENABLED" value="False" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
111
FluxEngine.cydsn/Sampler/Sampler.v
Normal file
111
FluxEngine.cydsn/Sampler/Sampler.v
Normal file
@@ -0,0 +1,111 @@
|
||||
|
||||
//`#start header` -- edit after this line, do not edit this line
|
||||
`include "cypress.v"
|
||||
`include "../SuperCounter/SuperCounter.v"
|
||||
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
// Generated on 12/11/2019 at 21:18
|
||||
// Component: Sampler
|
||||
module Sampler (
|
||||
output [2:0] debug_state,
|
||||
output reg [7:0] opcode,
|
||||
output req,
|
||||
input clock,
|
||||
input index,
|
||||
input rdata,
|
||||
input reset,
|
||||
input sampleclock
|
||||
);
|
||||
|
||||
//`#start body` -- edit after this line, do not edit this line
|
||||
|
||||
localparam STATE_RESET = 0;
|
||||
localparam STATE_WAITING = 1;
|
||||
localparam STATE_OPCODE = 2;
|
||||
|
||||
reg [1:0] state;
|
||||
reg [6:0] counter;
|
||||
|
||||
reg oldsampleclock;
|
||||
wire sampleclocked;
|
||||
assign sampleclocked = !oldsampleclock && sampleclock;
|
||||
|
||||
reg oldindex;
|
||||
wire indexed;
|
||||
assign indexed = !oldindex && index;
|
||||
|
||||
wire rdataed;
|
||||
reg oldrdata;
|
||||
assign rdataed = !oldrdata && rdata;
|
||||
|
||||
assign req = (state == STATE_OPCODE);
|
||||
|
||||
always @(posedge clock)
|
||||
begin
|
||||
if (reset)
|
||||
begin
|
||||
state <= STATE_RESET;
|
||||
opcode <= 0;
|
||||
oldsampleclock <= 0;
|
||||
oldindex <= 0;
|
||||
oldrdata <= 0;
|
||||
counter <= 0;
|
||||
end
|
||||
else
|
||||
case (state)
|
||||
STATE_RESET:
|
||||
state <= STATE_WAITING;
|
||||
|
||||
STATE_WAITING:
|
||||
begin
|
||||
/* If something has happened, emit any necessary interval byte. */
|
||||
if ((rdataed || indexed) && (counter != 0))
|
||||
begin
|
||||
opcode <= {0, counter};
|
||||
state <= STATE_OPCODE;
|
||||
end
|
||||
else if (indexed)
|
||||
begin
|
||||
oldindex <= 1;
|
||||
opcode <= 8'h81;
|
||||
state <= STATE_OPCODE;
|
||||
end
|
||||
else if (rdataed)
|
||||
begin
|
||||
oldrdata <= 1;
|
||||
opcode <= 8'h80;
|
||||
state <= STATE_OPCODE;
|
||||
end
|
||||
else if (sampleclocked)
|
||||
begin
|
||||
oldsampleclock <= 1;
|
||||
if (counter == 7'h7f)
|
||||
begin
|
||||
opcode <= {0, counter};
|
||||
state <= STATE_OPCODE;
|
||||
end
|
||||
counter <= counter + 1;
|
||||
end
|
||||
|
||||
/* Reset state once we've done the thing. */
|
||||
|
||||
if (oldrdata && !rdata)
|
||||
oldrdata <= 0;
|
||||
if (oldindex && !index)
|
||||
oldindex <= 0;
|
||||
if (oldsampleclock && !sampleclock)
|
||||
oldsampleclock <= 0;
|
||||
end
|
||||
|
||||
STATE_OPCODE: /* opcode or interval byte sent here */
|
||||
begin
|
||||
state <= STATE_WAITING;
|
||||
counter <= 0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
endmodule
|
||||
//`#start footer` -- edit after this line, do not edit this line
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
Binary file not shown.
105
FluxEngine.cydsn/Sequencer/Sequencer.v
Normal file
105
FluxEngine.cydsn/Sequencer/Sequencer.v
Normal file
@@ -0,0 +1,105 @@
|
||||
|
||||
//`#start header` -- edit after this line, do not edit this line
|
||||
`include "cypress.v"
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
// Generated on 11/24/2019 at 17:25
|
||||
// Component: Sequencer
|
||||
module Sequencer (
|
||||
output req, /* request new data on leading edge */
|
||||
output wdata,
|
||||
output [2:0] debug_state,
|
||||
input clock,
|
||||
input dataclock, /* incoming data on leading edge */
|
||||
input [7:0] opcode,
|
||||
input index,
|
||||
input sampleclock,
|
||||
input reset
|
||||
);
|
||||
|
||||
//`#start body` -- edit after this line, do not edit this line
|
||||
|
||||
localparam STATE_LOAD = 0;
|
||||
localparam STATE_WAITING = 1;
|
||||
localparam STATE_PULSING = 2;
|
||||
localparam STATE_INDEXING = 3;
|
||||
|
||||
localparam OPCODE_PULSE = 8'h80;
|
||||
localparam OPCODE_INDEX = 8'h81;
|
||||
|
||||
reg [1:0] state;
|
||||
reg [6:0] countdown;
|
||||
|
||||
assign req = (!reset && (state == STATE_LOAD));
|
||||
assign wdata = (state == STATE_PULSING);
|
||||
assign debug_state = state;
|
||||
|
||||
reg olddataclock;
|
||||
wire dataclocked;
|
||||
always @(posedge clock) olddataclock <= dataclock;
|
||||
assign dataclocked = !olddataclock && dataclock;
|
||||
|
||||
reg oldsampleclock;
|
||||
reg sampleclocked;
|
||||
|
||||
reg oldindex;
|
||||
wire indexed;
|
||||
always @(posedge clock) oldindex <= index;
|
||||
assign indexed = !oldindex && index;
|
||||
|
||||
always @(posedge clock)
|
||||
begin
|
||||
if (reset)
|
||||
begin
|
||||
state <= STATE_LOAD;
|
||||
countdown <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if (!oldsampleclock && sampleclock)
|
||||
sampleclocked <= 1;
|
||||
oldsampleclock <= sampleclock;
|
||||
|
||||
case (state)
|
||||
STATE_LOAD:
|
||||
/* Wait for a posedge on dataclocked, indicating an opcode has
|
||||
* arrived. */
|
||||
if (dataclocked)
|
||||
case (opcode)
|
||||
OPCODE_PULSE:
|
||||
state <= STATE_PULSING;
|
||||
|
||||
OPCODE_INDEX:
|
||||
state <= STATE_INDEXING;
|
||||
|
||||
default:
|
||||
begin
|
||||
countdown <= opcode[6:0];
|
||||
state <= STATE_WAITING;
|
||||
end
|
||||
endcase
|
||||
|
||||
STATE_WAITING:
|
||||
if (sampleclocked)
|
||||
begin
|
||||
sampleclocked <= 0;
|
||||
countdown <= countdown - 1;
|
||||
/* Nasty fudge factor here to account for one to two
|
||||
* sample ticks lost per pulse. */
|
||||
if (countdown <= 2)
|
||||
state <= STATE_LOAD;
|
||||
end
|
||||
|
||||
STATE_PULSING:
|
||||
state <= STATE_LOAD;
|
||||
|
||||
STATE_INDEXING:
|
||||
if (indexed)
|
||||
state <= STATE_LOAD;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
endmodule
|
||||
//`#start footer` -- edit after this line, do not edit this line
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
BIN
FluxEngine.cydsn/SuperCounter/SuperCounter.cysym
Normal file
BIN
FluxEngine.cydsn/SuperCounter/SuperCounter.cysym
Normal file
Binary file not shown.
156
FluxEngine.cydsn/SuperCounter/SuperCounter.v
Normal file
156
FluxEngine.cydsn/SuperCounter/SuperCounter.v
Normal file
@@ -0,0 +1,156 @@
|
||||
|
||||
//`#start header` -- edit after this line, do not edit this line
|
||||
`include "cypress.v"
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
// Generated on 11/16/2017 at 15:44
|
||||
// Component: FIFOout
|
||||
module SuperCounter (
|
||||
input clk,
|
||||
input reset,
|
||||
input count,
|
||||
output [7:0] d,
|
||||
output drq,
|
||||
output empty,
|
||||
output ack
|
||||
);
|
||||
|
||||
//`#start body` -- edit after this line, do not edit this line
|
||||
|
||||
parameter ResetValue = 0;
|
||||
parameter Delta = 1;
|
||||
|
||||
wire [7:0] po;
|
||||
assign d = po;
|
||||
|
||||
localparam STATE_RESET = 0;
|
||||
localparam STATE_WAIT = 1;
|
||||
localparam STATE_ADD = 2;
|
||||
|
||||
reg oldcount;
|
||||
wire counted;
|
||||
assign counted = count && !oldcount;
|
||||
|
||||
always @(posedge clk) oldcount <= count;
|
||||
|
||||
wire [2:0] cs;
|
||||
assign cs = reset ? STATE_RESET : (counted ? STATE_ADD : STATE_WAIT);
|
||||
|
||||
cy_psoc3_dp #(.d0_init(ResetValue), .d1_init(Delta),
|
||||
.cy_dpconfig(
|
||||
{
|
||||
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
|
||||
`CS_SHFT_OP_PASS, `CS_A0_SRC___D0, `CS_A1_SRC_NONE,
|
||||
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
|
||||
`CS_CMP_SEL_CFGA, /*CFGRAM0: STATE_RESET*/
|
||||
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
|
||||
`CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
|
||||
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
|
||||
`CS_CMP_SEL_CFGA, /*CFGRAM1: STATE_WAIT*/
|
||||
`CS_ALU_OP__ADD, `CS_SRCA_A0, `CS_SRCB_D1,
|
||||
`CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
|
||||
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
|
||||
`CS_CMP_SEL_CFGA, /*CFGRAM2: STATE_ADD*/
|
||||
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
|
||||
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
|
||||
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
|
||||
`CS_CMP_SEL_CFGA, /*CFGRAM3: */
|
||||
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
|
||||
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
|
||||
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
|
||||
`CS_CMP_SEL_CFGA, /*CFGRAM4: */
|
||||
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
|
||||
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
|
||||
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
|
||||
`CS_CMP_SEL_CFGA, /*CFGRAM5: */
|
||||
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
|
||||
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
|
||||
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
|
||||
`CS_CMP_SEL_CFGA, /*CFGRAM6: */
|
||||
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
|
||||
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
|
||||
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
|
||||
`CS_CMP_SEL_CFGA, /*CFGRAM7: */
|
||||
8'hFF, 8'h00, /*CFG9: */
|
||||
8'hFF, 8'hFF, /*CFG11-10: */
|
||||
`SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH,
|
||||
`SC_CI_A_ARITH, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL,
|
||||
`SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI,
|
||||
`SC_SI_A_DEFSI, /*CFG13-12: */
|
||||
`SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0,
|
||||
1'h0, `SC_FIFO1_BUS, `SC_FIFO0_BUS,
|
||||
`SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN,
|
||||
`SC_FB_NOCHN, `SC_CMP1_NOCHN,
|
||||
`SC_CMP0_NOCHN, /*CFG15-14: */
|
||||
10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX,
|
||||
`SC_FIFO_LEVEL,`SC_FIFO_ASYNC,`SC_EXTCRC_DSBL,
|
||||
`SC_WRK16CAT_DSBL /*CFG17-16: */
|
||||
}
|
||||
)) dp(
|
||||
/* input */ .reset(1'b0),
|
||||
/* input */ .clk(clk),
|
||||
/* input [02:00] */ .cs_addr(cs),
|
||||
/* input */ .route_si(1'b0),
|
||||
/* input */ .route_ci(1'b0),
|
||||
/* input */ .f0_load(1'b0),
|
||||
/* input */ .f1_load(1'b0),
|
||||
/* input */ .d0_load(1'b0),
|
||||
/* input */ .d1_load(1'b0),
|
||||
/* output */ .ce0(),
|
||||
/* output */ .cl0(),
|
||||
/* output */ .z0(),
|
||||
/* output */ .ff0(),
|
||||
/* output */ .ce1(),
|
||||
/* output */ .cl1(),
|
||||
/* output */ .z1(),
|
||||
/* output */ .ff1(),
|
||||
/* output */ .ov_msb(),
|
||||
/* output */ .co_msb(),
|
||||
/* output */ .cmsb(),
|
||||
/* output */ .so(),
|
||||
/* output */ .f0_bus_stat(),
|
||||
/* output */ .f0_blk_stat(),
|
||||
/* output */ .f1_bus_stat(),
|
||||
/* output */ .f1_blk_stat(),
|
||||
|
||||
/* input */ .ci(1'b0), // Carry in from previous stage
|
||||
/* output */ .co(),// Carry out to next stage
|
||||
/* input */ .sir(1'b0), // Shift in from right side
|
||||
/* output */ .sor(), // Shift out to right side
|
||||
/* input */ .sil(1'b0), // Shift in from left side
|
||||
/* output */ .sol(), // Shift out to left side
|
||||
/* input */ .msbi(1'b0), // MSB chain in
|
||||
/* output */ .msbo(), // MSB chain out
|
||||
/* input [01:00] */ .cei(2'b0), // Compare equal in from prev stage
|
||||
/* output [01:00] */ .ceo(), // Compare equal out to next stage
|
||||
/* input [01:00] */ .cli(2'b0), // Compare less than in from prv stage
|
||||
/* output [01:00] */ .clo(), // Compare less than out to next stage
|
||||
/* input [01:00] */ .zi(2'b0), // Zero detect in from previous stage
|
||||
/* output [01:00] */ .zo(), // Zero detect out to next stage
|
||||
/* input [01:00] */ .fi(2'b0), // 0xFF detect in from previous stage
|
||||
/* output [01:00] */ .fo(), // 0xFF detect out to next stage
|
||||
/* input [01:00] */ .capi(2'b0), // Software capture from previous stage
|
||||
/* output [01:00] */ .capo(), // Software capture to next stage
|
||||
/* input */ .cfbi(1'b0), // CRC Feedback in from previous stage
|
||||
/* output */ .cfbo(), // CRC Feedback out to next stage
|
||||
/* input [07:00] */ .pi(8'b0), // Parallel data port
|
||||
/* output [07:00] */ .po(po) // Parallel data port
|
||||
);
|
||||
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
endmodule
|
||||
//`#start footer` -- edit after this line, do not edit this line
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -17,20 +17,24 @@
|
||||
#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()));
|
||||
static uint8_t usb_buffer[BUFFER_SIZE] __attribute__((aligned()));
|
||||
static uint8_t xfer_buffer[BUFFER_SIZE] __attribute__((aligned()));
|
||||
static uint8_t dma_channel;
|
||||
#define NEXT_BUFFER(b) (((b)+1) % BUFFER_COUNT)
|
||||
|
||||
@@ -41,10 +45,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 +106,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,6 +135,16 @@ static void start_motor(void)
|
||||
CyWdtClear();
|
||||
}
|
||||
|
||||
static void stop_motor(void)
|
||||
{
|
||||
if (motor_on)
|
||||
{
|
||||
MOTOR_REG_Write(0);
|
||||
DRIVESELECT_REG_Write(0); /* deselect all drives */
|
||||
motor_on = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void wait_until_writeable(int ep)
|
||||
{
|
||||
while (USBFS_GetEPState(ep) != USBFS_IN_BUFFER_EMPTY)
|
||||
@@ -138,25 +184,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 +224,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 +261,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 +303,50 @@ static void cmd_bulk_test(struct any_frame* f)
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, buffer, sizeof(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_TEST_REPLY);
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_WRITE_TEST_REPLY);
|
||||
send_reply(&r);
|
||||
}
|
||||
|
||||
static void cmd_bulk_read_test(struct any_frame* f)
|
||||
{
|
||||
uint8_t buffer[64];
|
||||
|
||||
bool passed = true;
|
||||
for (int x=0; x<64; x++)
|
||||
{
|
||||
CyWdtClear();
|
||||
for (int y=0; y<256; y++)
|
||||
{
|
||||
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
while (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) != USBFS_OUT_BUFFER_FULL)
|
||||
;
|
||||
USBFS_ReadOutEP(FLUXENGINE_DATA_OUT_EP_NUM, buffer, sizeof(buffer));
|
||||
while (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) != USBFS_OUT_BUFFER_EMPTY)
|
||||
;
|
||||
|
||||
for (unsigned z=0; z<sizeof(buffer); z++)
|
||||
{
|
||||
if (buffer[z] != (uint8)(x+y+z))
|
||||
{
|
||||
print("fail %d+%d+%d == %d, not %d", x, y, z, buffer[z], (uint8)(x+y+z));
|
||||
passed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("passed=%d", passed);
|
||||
if (passed)
|
||||
{
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_READ_TEST_REPLY);
|
||||
send_reply(&r);
|
||||
}
|
||||
else
|
||||
send_error(F_ERROR_INVALID_VALUE);
|
||||
}
|
||||
|
||||
static void deinit_dma(void)
|
||||
{
|
||||
for (int i=0; i<BUFFER_COUNT; i++)
|
||||
@@ -249,7 +355,7 @@ static void deinit_dma(void)
|
||||
|
||||
static void init_capture_dma(void)
|
||||
{
|
||||
dma_channel = CAPTURE_DMA_DmaInitialize(
|
||||
dma_channel = SAMPLER_DMA_DmaInitialize(
|
||||
2 /* bytes */,
|
||||
true /* request per burst */,
|
||||
HI16(CYDEV_PERIPH_BASE),
|
||||
@@ -264,8 +370,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,36 +382,35 @@ static void cmd_read(struct read_frame* f)
|
||||
|
||||
/* Do slow setup *before* we go into the real-time bit. */
|
||||
|
||||
SAMPLER_CONTROL_Write(1); /* reset */
|
||||
|
||||
{
|
||||
uint8_t i = CyEnterCriticalSection();
|
||||
SAMPLER_DATAPATH_F0_SET_LEVEL_MID;
|
||||
SAMPLER_DATAPATH_F0_CLEAR;
|
||||
SAMPLER_DATAPATH_F0_SINGLE_BUFFER_UNSET;
|
||||
SAMPLER_FIFO_SET_LEVEL_NORMAL;
|
||||
SAMPLER_FIFO_CLEAR;
|
||||
SAMPLER_FIFO_SINGLE_BUFFER_UNSET;
|
||||
CyExitCriticalSection(i);
|
||||
}
|
||||
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
init_capture_dma();
|
||||
|
||||
/* Wait for the beginning of a rotation. */
|
||||
/* Wait for the beginning of a rotation, if requested. */
|
||||
|
||||
print("wait");
|
||||
index_irq = false;
|
||||
while (!index_irq)
|
||||
;
|
||||
index_irq = false;
|
||||
if (f->synced)
|
||||
{
|
||||
index_irq = false;
|
||||
while (!index_irq)
|
||||
;
|
||||
index_irq = false;
|
||||
}
|
||||
|
||||
crunch_state_t cs = {};
|
||||
cs.outputptr = usb_buffer;
|
||||
cs.outputptr = xfer_buffer;
|
||||
cs.outputlen = BUFFER_SIZE;
|
||||
|
||||
dma_writing_to_td = 0;
|
||||
dma_reading_from_td = -1;
|
||||
dma_underrun = false;
|
||||
int count = 0;
|
||||
SAMPLER_CONTROL_Write(0); /* !reset */
|
||||
CyDmaChSetInitialTd(dma_channel, td[dma_writing_to_td]);
|
||||
CyDmaClearPendingDrq(dma_channel);
|
||||
CyDmaChEnable(dma_channel, 1);
|
||||
@@ -313,73 +418,100 @@ static void cmd_read(struct read_frame* f)
|
||||
/* Wait for the first DMA transfer to complete, after which we can start the
|
||||
* USB transfer. */
|
||||
|
||||
while ((dma_writing_to_td == 0) && !index_irq)
|
||||
while (dma_writing_to_td == 0)
|
||||
;
|
||||
dma_reading_from_td = 0;
|
||||
bool dma_running = true;
|
||||
|
||||
/* Start transferring. */
|
||||
|
||||
int revolutions = f->revolutions;
|
||||
while (!dma_underrun)
|
||||
uint32_t start_time = clock;
|
||||
for (;;)
|
||||
{
|
||||
CyWdtClear();
|
||||
|
||||
/* Have we reached the index pulse? */
|
||||
if (index_irq)
|
||||
{
|
||||
index_irq = false;
|
||||
revolutions--;
|
||||
if (revolutions == 0)
|
||||
break;
|
||||
}
|
||||
/* If the sample session is over, stop reading but continue processing until
|
||||
* the DMA chain is empty. */
|
||||
|
||||
/* Wait for the next block to be read. */
|
||||
while (dma_reading_from_td == dma_writing_to_td)
|
||||
if ((clock - start_time) >= f->milliseconds)
|
||||
{
|
||||
/* On an underrun, give up immediately. */
|
||||
if (dma_underrun)
|
||||
goto abort;
|
||||
}
|
||||
|
||||
uint8_t dma_buffer_usage = 0;
|
||||
while (dma_buffer_usage < BUFFER_SIZE)
|
||||
{
|
||||
cs.inputptr = dma_buffer[dma_reading_from_td] + dma_buffer_usage;
|
||||
cs.inputlen = BUFFER_SIZE - dma_buffer_usage;
|
||||
crunch(&cs);
|
||||
dma_buffer_usage += BUFFER_SIZE - cs.inputlen;
|
||||
count++;
|
||||
if (cs.outputlen == 0)
|
||||
if (dma_running)
|
||||
{
|
||||
while (USBFS_GetEPState(FLUXENGINE_DATA_IN_EP_NUM) != USBFS_IN_BUFFER_EMPTY)
|
||||
{
|
||||
if (index_irq || dma_underrun)
|
||||
goto abort;
|
||||
}
|
||||
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE);
|
||||
cs.outputptr = usb_buffer;
|
||||
cs.outputlen = BUFFER_SIZE;
|
||||
CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN);
|
||||
while (CyDmaChGetRequest(dma_channel))
|
||||
;
|
||||
dma_running = false;
|
||||
dma_underrun = false;
|
||||
}
|
||||
}
|
||||
dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td);
|
||||
|
||||
/* If there's an underrun event, stop immediately. */
|
||||
|
||||
if (dma_underrun)
|
||||
goto abort;
|
||||
|
||||
/* If there are no more blocks to be read, check to see if we've finished. */
|
||||
|
||||
if (dma_reading_from_td == dma_writing_to_td)
|
||||
{
|
||||
/* Also if we've run out of blocks to send. */
|
||||
|
||||
if (!dma_running)
|
||||
goto abort;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, there's a block waiting, so attempt to send it. */
|
||||
|
||||
uint8_t dma_buffer_usage = 0;
|
||||
while (dma_buffer_usage < BUFFER_SIZE)
|
||||
{
|
||||
cs.inputptr = dma_buffer[dma_reading_from_td] + dma_buffer_usage;
|
||||
cs.inputlen = BUFFER_SIZE - dma_buffer_usage;
|
||||
crunch(&cs);
|
||||
dma_buffer_usage += BUFFER_SIZE - cs.inputlen;
|
||||
count++;
|
||||
|
||||
/* If there is no available space in the output buffer, flush the buffer via
|
||||
* USB and go again. */
|
||||
if (cs.outputlen == 0)
|
||||
{
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
memcpy(usb_buffer, xfer_buffer, FRAME_SIZE);
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE);
|
||||
|
||||
cs.outputptr = xfer_buffer;
|
||||
cs.outputlen = BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td);
|
||||
}
|
||||
}
|
||||
abort:
|
||||
CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN);
|
||||
while (CyDmaChGetRequest(dma_channel))
|
||||
;
|
||||
abort:;
|
||||
bool saved_dma_underrun = dma_underrun;
|
||||
|
||||
donecrunch(&cs);
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
unsigned zz = cs.outputlen;
|
||||
/* If there's a complete packet waiting, send it. */
|
||||
if (cs.outputlen != BUFFER_SIZE)
|
||||
{
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE);
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
}
|
||||
if ((cs.outputlen != 0) && (cs.outputlen != BUFFER_SIZE))
|
||||
{
|
||||
/* If there's a partial packet waiting, send it; this will also terminate the transfer. */
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE-cs.outputlen);
|
||||
if ((cs.outputlen == BUFFER_SIZE) || (cs.outputlen == 0))
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise just terminate the transfer. */
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, NULL, 0);
|
||||
}
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
deinit_dma();
|
||||
|
||||
if (dma_underrun)
|
||||
if (saved_dma_underrun)
|
||||
{
|
||||
print("underrun after %d packets");
|
||||
send_error(F_ERROR_UNDERRUN);
|
||||
@@ -389,7 +521,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)
|
||||
@@ -410,25 +542,28 @@ static void init_replay_dma(void)
|
||||
|
||||
CyDmaTdSetConfiguration(td[i], BUFFER_SIZE, td[nexti],
|
||||
CY_DMA_TD_INC_SRC_ADR | SEQUENCER_DMA__TD_TERMOUT_EN);
|
||||
CyDmaTdSetAddress(td[i], LO16((uint32)&dma_buffer[i]), LO16((uint32)&SEQUENCER_DATAPATH_F0_REG));
|
||||
CyDmaTdSetAddress(td[i], LO16((uint32)&dma_buffer[i]), LO16((uint32)REPLAY_FIFO_FIFO_PTR));
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_write(struct write_frame* f)
|
||||
{
|
||||
print("cmd_write");
|
||||
|
||||
if (f->bytes_to_write % FRAME_SIZE)
|
||||
{
|
||||
send_error(F_ERROR_INVALID_VALUE);
|
||||
return;
|
||||
}
|
||||
|
||||
SEQUENCER_CONTROL_Write(1); /* put the sequencer into reset */
|
||||
|
||||
SIDE_REG_Write(f->side);
|
||||
SEQUENCER_CONTROL_Write(1); /* reset */
|
||||
{
|
||||
uint8_t i = CyEnterCriticalSection();
|
||||
SEQUENCER_DATAPATH_F0_SET_LEVEL_NORMAL;
|
||||
SEQUENCER_DATAPATH_F0_CLEAR;
|
||||
SEQUENCER_DATAPATH_F0_SINGLE_BUFFER_UNSET;
|
||||
uint8_t i = CyEnterCriticalSection();
|
||||
REPLAY_FIFO_SET_LEVEL_MID;
|
||||
REPLAY_FIFO_CLEAR;
|
||||
REPLAY_FIFO_SINGLE_BUFFER_UNSET;
|
||||
CyExitCriticalSection(i);
|
||||
}
|
||||
seek_to(current_track);
|
||||
@@ -439,6 +574,7 @@ static void cmd_write(struct write_frame* f)
|
||||
int packets = f->bytes_to_write / FRAME_SIZE;
|
||||
int count_written = 0;
|
||||
int count_read = 0;
|
||||
int packetwaiting = 0;
|
||||
dma_writing_to_td = 0;
|
||||
dma_reading_from_td = -1;
|
||||
dma_underrun = false;
|
||||
@@ -450,6 +586,22 @@ static void cmd_write(struct write_frame* f)
|
||||
int old_reading_from_td = -1;
|
||||
for (;;)
|
||||
{
|
||||
CyWdtClear();
|
||||
|
||||
/* Make sure that we always have a USB read in progress whenever possible. */
|
||||
|
||||
if (!finished && !packetwaiting)
|
||||
{
|
||||
/* There is no read in progress; has data arrived in the external USB buffer? */
|
||||
|
||||
if (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL)
|
||||
{
|
||||
/* Yes, data has arrived, so initiate the copy. */
|
||||
|
||||
packetwaiting = USBFS_ReadOutEP(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer, FRAME_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read data from USB into the buffers. */
|
||||
|
||||
if (NEXT_BUFFER(dma_writing_to_td) != dma_reading_from_td)
|
||||
@@ -466,26 +618,33 @@ static void cmd_write(struct write_frame* f)
|
||||
/* There's no more data to read, so fake some. */
|
||||
|
||||
for (int i=0; i<BUFFER_SIZE; i++)
|
||||
usb_buffer[i+0] = 0x7f;
|
||||
cs.inputptr = usb_buffer;
|
||||
xfer_buffer[i+0] = 0x7f;
|
||||
cs.inputptr = xfer_buffer;
|
||||
cs.inputlen = BUFFER_SIZE;
|
||||
}
|
||||
else
|
||||
else if (packetwaiting)
|
||||
{
|
||||
while (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) != USBFS_OUT_BUFFER_FULL)
|
||||
/* There's a USB read into usb_buffer in progress, so check if it's finished. */
|
||||
|
||||
if (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) == USBFS_OUT_BUFFER_EMPTY)
|
||||
{
|
||||
if (writing && (dma_underrun || index_irq))
|
||||
goto abort;
|
||||
/* It's done, so copy out the data. */
|
||||
|
||||
memcpy(xfer_buffer, usb_buffer, FRAME_SIZE);
|
||||
cs.inputptr = xfer_buffer;
|
||||
cs.inputlen = packetwaiting;
|
||||
|
||||
count_read++;
|
||||
if ((packetwaiting < FRAME_SIZE) || (count_read == packets))
|
||||
finished = true;
|
||||
else
|
||||
{
|
||||
/* Wait for more USB data to show up. */
|
||||
|
||||
packetwaiting = 0;
|
||||
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
}
|
||||
}
|
||||
|
||||
int length = usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer);
|
||||
cs.inputptr = usb_buffer;
|
||||
cs.inputlen = length;
|
||||
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
|
||||
count_read++;
|
||||
if ((length < FRAME_SIZE) || (count_read == packets))
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,7 +663,8 @@ static void cmd_write(struct write_frame* f)
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have a full buffer, start writing. */
|
||||
/* Once all the buffers are full, start writing. */
|
||||
|
||||
if ((dma_reading_from_td == -1) && (dma_writing_to_td == BUFFER_COUNT-1))
|
||||
{
|
||||
dma_reading_from_td = old_reading_from_td = 0;
|
||||
@@ -519,7 +679,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)
|
||||
;
|
||||
@@ -530,7 +690,7 @@ static void cmd_write(struct write_frame* f)
|
||||
SEQUENCER_CONTROL_Write(0); /* start writing! */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (writing && (dma_underrun || index_irq))
|
||||
goto abort;
|
||||
|
||||
@@ -541,6 +701,7 @@ static void cmd_write(struct write_frame* f)
|
||||
}
|
||||
}
|
||||
abort:
|
||||
print("done %d %d", dma_reading_from_td, dma_writing_to_td);
|
||||
SEQUENCER_DMA_FINISHED_IRQ_Disable();
|
||||
|
||||
SEQUENCER_CONTROL_Write(1); /* reset */
|
||||
@@ -553,10 +714,12 @@ abort:
|
||||
CyDmaChDisable(dma_channel);
|
||||
}
|
||||
|
||||
//debug("p=%d cr=%d cw=%d f=%d l=%d w=%d index=%d underrun=%d", packets, count_read, count_written, finished, listening, writing, index_irq, dma_underrun);
|
||||
print("p=%d cr=%d cw=%d f=%d w=%d index=%d underrun=%d", packets, count_read, count_written, finished, writing, index_irq, dma_underrun);
|
||||
if (!finished)
|
||||
{
|
||||
while (count_read < packets)
|
||||
/* There's still some data to read, so just read and blackhole it ---
|
||||
* easier than trying to terminate the connection. */
|
||||
while (count_read != packets)
|
||||
{
|
||||
if (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL)
|
||||
{
|
||||
@@ -571,6 +734,7 @@ abort:
|
||||
}
|
||||
|
||||
deinit_dma();
|
||||
print("write finished");
|
||||
|
||||
if (dma_underrun)
|
||||
{
|
||||
@@ -605,17 +769,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];
|
||||
@@ -637,8 +889,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:
|
||||
@@ -660,28 +916,48 @@ 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);
|
||||
|
||||
detect_drives();
|
||||
CyWdtStart(CYWDT_1024_TICKS, CYWDT_LPMODE_DISABLED);
|
||||
|
||||
/* UART_PutString("GO\r"); */
|
||||
|
||||
for (;;)
|
||||
{
|
||||
CyWdtClear();
|
||||
@@ -690,23 +966,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");
|
||||
|
||||
26
Makefile
26
Makefile
@@ -1,24 +1,40 @@
|
||||
PACKAGES = zlib sqlite3 libusb-1.0
|
||||
|
||||
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++
|
||||
export AR = /mingw32/bin/ar rcs
|
||||
export STRIP = /mingw32/bin/strip
|
||||
export CFLAGS = -O3 -g --std=c++14 -I/mingw32/include/libusb-1.0
|
||||
export LDFLAGS = -O3
|
||||
export CFLAGS += -I/mingw32/include/libusb-1.0
|
||||
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
|
||||
export CFLAGS = -Og -g --std=c++14 $(shell pkg-config --cflags $(PACKAGES))
|
||||
export LDFLAGS = -Og
|
||||
export CFLAGS += $(shell pkg-config --cflags $(PACKAGES))
|
||||
export LDFLAGS +=
|
||||
export LIBS = $(shell pkg-config --libs $(PACKAGES))
|
||||
export EXTENSION =
|
||||
endif
|
||||
|
||||
CFLAGS += -Ilib -Idep/fmt
|
||||
CFLAGS += -Ilib -Idep/fmt -Iarch
|
||||
|
||||
export OBJDIR = .obj
|
||||
|
||||
|
||||
20
README.md
20
README.md
@@ -79,10 +79,10 @@ 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 |
|
||||
@@ -90,9 +90,13 @@ people who've had it work).
|
||||
| [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 |
|
||||
| [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
|
||||
@@ -105,6 +109,7 @@ at least, check the CRC so what data's there is probably good.
|
||||
|:-----------------------------------------|:-----:|:------:|-------|
|
||||
| [AES Superplus / No Problem](doc/disk-aeslanier.md) | 🦖 | | hard sectors! |
|
||||
| [Durango F85](doc/disk-durangof85.md) | 🦖 | | 5.25" |
|
||||
| [DVK MX](doc/disk-mx.md) | 🦖 | | Soviet PDP-11 clone |
|
||||
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8-inch |
|
||||
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8-inch _and_ hard sectors |
|
||||
{: .datatable }
|
||||
@@ -188,3 +193,8 @@ maintained by Victor Zverovich (`vitaut <https://github.com/vitaut>`) and
|
||||
Jonathan Müller (`foonathan <https://github.com/foonathan>`) with
|
||||
contributions from many other people. It is licensed under the terms of the
|
||||
BSD license. Please see the contents of the directory for the full text.
|
||||
|
||||
As an exception, `dep/emu` contains parts of the OpenBSD C library
|
||||
code, Todd Miller and William A. Rowe (and probably others). It is licensed
|
||||
under the terms of the 3-clause BSD license. Please see the contents of the
|
||||
directory for the full text. It's been lightly modified by me.
|
||||
|
||||
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());
|
||||
}
|
||||
43
arch/amiga/amiga.h
Normal file
43
arch/amiga/amiga.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef AMIGA_H
|
||||
#define AMIGA_H
|
||||
|
||||
#include "encoders/encoders.h"
|
||||
|
||||
#define AMIGA_SECTOR_RECORD 0xaaaa44894489LL
|
||||
|
||||
#define AMIGA_TRACKS_PER_DISK 80
|
||||
#define AMIGA_SECTORS_PER_TRACK 11
|
||||
#define AMIGA_RECORD_SIZE 0x21f
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
class SectorSet;
|
||||
|
||||
class AmigaDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~AmigaDecoder() {}
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
|
||||
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
|
||||
65
arch/amiga/decoder.cc
Normal file
65
arch/amiga/decoder.cc
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "globals.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "protocol.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "sector.h"
|
||||
#include "amiga.h"
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
/*
|
||||
* Amiga disks use MFM but it's not quite the same as IBM MFM. They only use
|
||||
* a single type of record with a different marker byte.
|
||||
*
|
||||
* See the big comment in the IBM MFM decoder for the gruesome details of how
|
||||
* MFM works.
|
||||
*/
|
||||
|
||||
static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD);
|
||||
|
||||
AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord()
|
||||
{
|
||||
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
|
||||
if (_fmr->eof() || !_sector->clock)
|
||||
return UNKNOWN_RECORD;
|
||||
return SECTOR_RECORD;
|
||||
}
|
||||
|
||||
void AmigaDecoder::decodeSectorRecord()
|
||||
{
|
||||
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
|
||||
const auto& rawbytes = toBytes(rawbits).slice(0, AMIGA_RECORD_SIZE*2);
|
||||
const auto& bytes = decodeFmMfm(rawbits).slice(0, AMIGA_RECORD_SIZE);
|
||||
|
||||
const uint8_t* ptr = bytes.begin() + 3;
|
||||
|
||||
Bytes header = amigaDeinterleave(ptr, 4);
|
||||
Bytes recoveryinfo = amigaDeinterleave(ptr, 16);
|
||||
|
||||
_sector->logicalTrack = header[1] >> 1;
|
||||
_sector->logicalSide = header[1] & 1;
|
||||
_sector->logicalSector = header[2];
|
||||
|
||||
uint32_t wantedheaderchecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(6, 40));
|
||||
if (gotheaderchecksum != wantedheaderchecksum)
|
||||
return;
|
||||
|
||||
uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(62, 1024));
|
||||
|
||||
_sector->data.clear();
|
||||
_sector->data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo);
|
||||
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
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,6 +9,9 @@
|
||||
#define BROTHER_DATA_RECORD_CHECKSUM 3
|
||||
#define BROTHER_DATA_RECORD_ENCODED_SIZE 415
|
||||
|
||||
#define BROTHER_TRACKS_PER_DISK 78
|
||||
#define BROTHER_SECTORS_PER_TRACK 12
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
|
||||
@@ -22,9 +25,15 @@ public:
|
||||
void decodeDataRecord();
|
||||
};
|
||||
|
||||
extern void writeBrotherSectorHeader(std::vector<bool>& bits, unsigned& cursor,
|
||||
int track, int sector);
|
||||
extern void writeBrotherSectorData(std::vector<bool>& bits, unsigned& cursor,
|
||||
const Bytes& data);
|
||||
class BrotherEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
virtual ~BrotherEncoder() {}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
|
||||
};
|
||||
|
||||
extern FlagGroup brotherEncoderFlags;
|
||||
|
||||
#endif
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "record.h"
|
||||
#include "brother.h"
|
||||
#include "sector.h"
|
||||
167
arch/brother/encoder.cc
Normal file
167
arch/brother/encoder.cc
Normal file
@@ -0,0 +1,167 @@
|
||||
#include "globals.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "brother.h"
|
||||
#include "crc.h"
|
||||
#include "sectorset.h"
|
||||
#include "writer.h"
|
||||
|
||||
FlagGroup brotherEncoderFlags;
|
||||
|
||||
static DoubleFlag clockRateUs(
|
||||
{ "--clock-rate" },
|
||||
"Encoded data clock rate (microseconds).",
|
||||
3.83);
|
||||
|
||||
static DoubleFlag postIndexGapMs(
|
||||
{ "--post-index-gap" },
|
||||
"Post-index gap before first sector header (milliseconds).",
|
||||
1.0);
|
||||
|
||||
static DoubleFlag sectorSpacingMs(
|
||||
{ "--sector-spacing" },
|
||||
"Time between successive sector headers (milliseconds).",
|
||||
16.2);
|
||||
|
||||
static DoubleFlag postHeaderSpacingMs(
|
||||
{ "--post-header-spacing" },
|
||||
"Time between a sector's header and data records (milliseconds).",
|
||||
0.69);
|
||||
|
||||
static StringFlag sectorSkew(
|
||||
{ "--sector-skew" },
|
||||
"Order in which to write sectors.",
|
||||
"05a3816b4927");
|
||||
|
||||
static int encode_header_gcr(uint16_t word)
|
||||
{
|
||||
switch (word)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: return gcr;
|
||||
#include "header_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
static int encode_data_gcr(uint8_t data)
|
||||
{
|
||||
switch (data)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint32_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_sector_header(std::vector<bool>& bits, unsigned& cursor,
|
||||
int track, int sector)
|
||||
{
|
||||
write_bits(bits, cursor, 0xffffffff, 31);
|
||||
write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32);
|
||||
write_bits(bits, cursor, encode_header_gcr(track), 16);
|
||||
write_bits(bits, cursor, encode_header_gcr(sector), 16);
|
||||
write_bits(bits, cursor, encode_header_gcr(0x2f), 16);
|
||||
}
|
||||
|
||||
static void write_sector_data(std::vector<bool>& bits, unsigned& cursor, const Bytes& data)
|
||||
{
|
||||
write_bits(bits, cursor, 0xffffffff, 32);
|
||||
write_bits(bits, cursor, BROTHER_DATA_RECORD, 32);
|
||||
|
||||
uint16_t fifo = 0;
|
||||
int width = 0;
|
||||
|
||||
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
|
||||
Error() << "unsupported sector size";
|
||||
|
||||
auto write_byte = [&](uint8_t byte)
|
||||
{
|
||||
fifo |= (byte << (8 - width));
|
||||
width += 8;
|
||||
|
||||
while (width >= 5)
|
||||
{
|
||||
uint8_t quintet = fifo >> 11;
|
||||
fifo <<= 5;
|
||||
width -= 5;
|
||||
|
||||
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
|
||||
}
|
||||
};
|
||||
|
||||
for (uint8_t byte : data)
|
||||
write_byte(byte);
|
||||
|
||||
uint32_t realCrc = crcbrother(data);
|
||||
write_byte(realCrc>>16);
|
||||
write_byte(realCrc>>8);
|
||||
write_byte(realCrc);
|
||||
write_byte(0x58); /* magic */
|
||||
write_byte(0xd4);
|
||||
while (width != 0)
|
||||
write_byte(0);
|
||||
}
|
||||
|
||||
static int charToInt(char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> BrotherEncoder::encode(
|
||||
int physicalTrack, int physicalSide, const SectorSet& allSectors)
|
||||
{
|
||||
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_DISK)
|
||||
|| (physicalSide != 0))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
const std::string& skew = sectorSkew.get();
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
for (int sectorCount=0; sectorCount<BROTHER_SECTORS_PER_TRACK; sectorCount++)
|
||||
{
|
||||
int sectorId = charToInt(skew.at(sectorCount));
|
||||
double headerMs = postIndexGapMs + sectorCount*sectorSpacingMs;
|
||||
unsigned headerCursor = headerMs*1e3 / clockRateUs;
|
||||
double dataMs = headerMs + postHeaderSpacingMs;
|
||||
unsigned dataCursor = dataMs*1e3 / clockRateUs;
|
||||
|
||||
const auto& sectorData = allSectors.get(physicalTrack, 0, sectorId);
|
||||
|
||||
fillBitmapTo(bits, cursor, headerCursor, { true, false });
|
||||
write_sector_header(bits, cursor, physicalTrack, sectorId);
|
||||
fillBitmapTo(bits, cursor, dataCursor, { true, false });
|
||||
write_sector_data(bits, cursor, sectorData->data);
|
||||
}
|
||||
|
||||
if (cursor >= bits.size())
|
||||
Error() << "track data overrun";
|
||||
fillBitmapTo(bits, cursor, bits.size(), { true, false });
|
||||
|
||||
// The pre-index gap is not normally reported.
|
||||
// std::cerr << "pre-index gap " << 200.0 - (double)cursor*clockRateUs/1e3 << std::endl;
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs*1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
@@ -73,8 +73,10 @@ const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56c);
|
||||
* encoding (you can't do 10 00). So this can't be spoofed by user data.
|
||||
*
|
||||
* shifted: 10 00 10 01 00 01 00 1
|
||||
*
|
||||
* It's repeated three times.
|
||||
*/
|
||||
const FluxPattern MFM_PATTERN(16, 0x4489);
|
||||
const FluxPattern MFM_PATTERN(48, 0x448944894489LL);
|
||||
|
||||
const FluxMatchers ANY_RECORD_PATTERN(
|
||||
{
|
||||
@@ -100,7 +102,8 @@ AbstractDecoder::RecordType IbmDecoder::advanceToNextRecord()
|
||||
if (_currentHeaderLength > 0)
|
||||
readRawBits(_currentHeaderLength*16);
|
||||
auto idbits = readRawBits(16);
|
||||
uint8_t id = decodeFmMfm(idbits).slice(0, 1)[0];
|
||||
const Bytes idbytes = decodeFmMfm(idbits);
|
||||
uint8_t id = idbytes.slice(0, 1)[0];
|
||||
seek(here);
|
||||
|
||||
switch (id)
|
||||
@@ -134,6 +137,9 @@ void IbmDecoder::decodeSectorRecord()
|
||||
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, _currentHeaderLength + 5));
|
||||
if (wantCrc == gotCrc)
|
||||
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
|
||||
|
||||
if (_ignoreSideByte)
|
||||
_sector->logicalSide = _sector->physicalSide;
|
||||
}
|
||||
|
||||
void IbmDecoder::decodeDataRecord()
|
||||
235
arch/ibm/encoder.cc
Normal file
235
arch/ibm/encoder.cc
Normal file
@@ -0,0 +1,235 @@
|
||||
#include "globals.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "ibm.h"
|
||||
#include "crc.h"
|
||||
#include "sectorset.h"
|
||||
#include "writer.h"
|
||||
#include "fmt/format.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/* IAM record separator:
|
||||
* 0xC2 is:
|
||||
* data: 1 1 0 0 0 0 1 0 = 0xc2
|
||||
* mfm: 01 01 00 10 10 10 01 00 = 0x5254
|
||||
* special: 01 01 00 10 00 10 01 00 = 0x5224
|
||||
*/
|
||||
#define MFM_IAM_SEPARATOR 0x5224
|
||||
|
||||
/* FM IAM record:
|
||||
* flux: XXXX-XXX-XXXX-X- = 0xf77a
|
||||
* clock: X X - X - X X X = 0xd7
|
||||
* data: X X X X X X - - = 0xfc
|
||||
*/
|
||||
#define FM_IAM_RECORD 0xf77a
|
||||
|
||||
/* MFM IAM record:
|
||||
* data: 1 1 1 1 1 1 0 0 = 0xfc
|
||||
* flux: 01 01 01 01 01 01 00 10 = 0x5552
|
||||
*/
|
||||
#define MFM_IAM_RECORD 0x5552
|
||||
|
||||
/* MFM record separator:
|
||||
* 0xA1 is:
|
||||
* data: 1 0 1 0 0 0 0 1 = 0xa1
|
||||
* mfm: 01 00 01 00 10 10 10 01 = 0x44a9
|
||||
* special: 01 00 01 00 10 00 10 01 = 0x4489
|
||||
* ^^^^^
|
||||
* When shifted out of phase, the special 0xa1 byte becomes an illegal
|
||||
* encoding (you can't do 10 00). So this can't be spoofed by user data.
|
||||
*
|
||||
* shifted: 10 00 10 01 00 01 00 1
|
||||
*
|
||||
* It's repeated three times.
|
||||
*/
|
||||
#define MFM_RECORD_SEPARATOR 0x4489
|
||||
#define MFM_RECORD_SEPARATOR_BYTE 0xa1
|
||||
|
||||
/* MFM IDAM byte:
|
||||
* data: 1 1 1 1 1 1 1 0 = 0xfe
|
||||
* mfm: 01 01 01 01 01 01 01 00 = 0x5554
|
||||
*/
|
||||
|
||||
/* MFM DAM byte:
|
||||
* data: 1 1 1 1 1 0 1 1 = 0xfb
|
||||
* mfm: 01 01 01 01 01 00 01 01 = 0x5545
|
||||
*/
|
||||
|
||||
static int charToInt(char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
}
|
||||
|
||||
void IbmEncoder::writeRawBits(uint32_t data, int width)
|
||||
{
|
||||
_cursor += width;
|
||||
_lastBit = data & 1;
|
||||
for (int i=0; i<width; i++)
|
||||
{
|
||||
unsigned pos = _cursor - i - 1;
|
||||
if (pos < _bits.size())
|
||||
_bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void IbmEncoder::writeBytes(const Bytes& bytes)
|
||||
{
|
||||
if (_parameters.useFm)
|
||||
encodeFm(_bits, _cursor, bytes);
|
||||
else
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
}
|
||||
|
||||
void IbmEncoder::writeBytes(int count, uint8_t byte)
|
||||
{
|
||||
Bytes bytes = { byte };
|
||||
for (int i=0; i<count; i++)
|
||||
writeBytes(bytes);
|
||||
}
|
||||
|
||||
static uint8_t decodeUint16(uint16_t raw)
|
||||
{
|
||||
Bytes b;
|
||||
ByteWriter bw(b);
|
||||
bw.write_be16(raw);
|
||||
return decodeFmMfm(b.toBits())[0];
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> IbmEncoder::encode(
|
||||
int physicalTrack, int physicalSide, const SectorSet& allSectors)
|
||||
{
|
||||
double clockRateUs = 1e3 / _parameters.clockRateKhz;
|
||||
if (!_parameters.useFm)
|
||||
clockRateUs /= 2.0;
|
||||
int bitsPerRevolution = (_parameters.trackLengthMs * 1000.0) / clockRateUs;
|
||||
_bits.resize(bitsPerRevolution);
|
||||
_cursor = 0;
|
||||
|
||||
uint8_t idamUnencoded = decodeUint16(_parameters.idamByte);
|
||||
uint8_t damUnencoded = decodeUint16(_parameters.damByte);
|
||||
|
||||
uint8_t sectorSize = 0;
|
||||
{
|
||||
int s = _parameters.sectorSize >> 7;
|
||||
while (s > 1)
|
||||
{
|
||||
s >>= 1;
|
||||
sectorSize += 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t gapFill = _parameters.useFm ? 0x00 : 0x4e;
|
||||
|
||||
writeBytes(_parameters.gap0, gapFill);
|
||||
if (_parameters.emitIam)
|
||||
{
|
||||
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
|
||||
if (!_parameters.useFm)
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
writeRawBits(MFM_IAM_SEPARATOR, 16);
|
||||
}
|
||||
writeRawBits(_parameters.useFm ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
|
||||
writeBytes(_parameters.gap1, gapFill);
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
for (char sectorChar : _parameters.sectorSkew)
|
||||
{
|
||||
int sectorId = charToInt(sectorChar);
|
||||
if (!first)
|
||||
writeBytes(_parameters.gap3, gapFill);
|
||||
first = false;
|
||||
|
||||
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
|
||||
if (!sectorData)
|
||||
Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId);
|
||||
|
||||
/* Writing the sector and data records are fantastically annoying.
|
||||
* The CRC is calculated from the *very start* of the record, and
|
||||
* include the malformed marker bytes. Our encoder doesn't know
|
||||
* about this, of course, with the result that we have to construct
|
||||
* the unencoded header, calculate the checksum, and then use the
|
||||
* same logic to emit the bytes which require special encoding
|
||||
* before encoding the rest of the header normally. */
|
||||
|
||||
{
|
||||
Bytes header;
|
||||
ByteWriter bw(header);
|
||||
|
||||
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
|
||||
if (!_parameters.useFm)
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
|
||||
}
|
||||
bw.write_8(idamUnencoded);
|
||||
bw.write_8(sectorData->logicalTrack);
|
||||
bw.write_8(sectorData->logicalSide);
|
||||
bw.write_8(sectorData->logicalSector + _parameters.startSectorId);
|
||||
bw.write_8(sectorSize);
|
||||
uint16_t crc = crc16(CCITT_POLY, header);
|
||||
bw.write_be16(crc);
|
||||
|
||||
int conventionalHeaderStart = 0;
|
||||
if (!_parameters.useFm)
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
writeRawBits(MFM_RECORD_SEPARATOR, 16);
|
||||
conventionalHeaderStart += 3;
|
||||
|
||||
}
|
||||
writeRawBits(_parameters.idamByte, 16);
|
||||
conventionalHeaderStart += 1;
|
||||
|
||||
writeBytes(header.slice(conventionalHeaderStart));
|
||||
}
|
||||
|
||||
writeBytes(_parameters.gap2, gapFill);
|
||||
|
||||
{
|
||||
Bytes data;
|
||||
ByteWriter bw(data);
|
||||
|
||||
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
|
||||
if (!_parameters.useFm)
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
|
||||
}
|
||||
bw.write_8(damUnencoded);
|
||||
|
||||
Bytes truncatedData = sectorData->data.slice(0, _parameters.sectorSize);
|
||||
bw += truncatedData;
|
||||
uint16_t crc = crc16(CCITT_POLY, data);
|
||||
bw.write_be16(crc);
|
||||
|
||||
int conventionalHeaderStart = 0;
|
||||
if (!_parameters.useFm)
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
writeRawBits(MFM_RECORD_SEPARATOR, 16);
|
||||
conventionalHeaderStart += 3;
|
||||
|
||||
}
|
||||
writeRawBits(_parameters.damByte, 16);
|
||||
conventionalHeaderStart += 1;
|
||||
|
||||
writeBytes(data.slice(conventionalHeaderStart));
|
||||
}
|
||||
}
|
||||
|
||||
if (_cursor >= _bits.size())
|
||||
Error() << "track data overrun";
|
||||
while (_cursor < _bits.size())
|
||||
writeBytes(1, gapFill);
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(_bits, clockRateUs*1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define IBM_H
|
||||
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
|
||||
/* IBM format (i.e. ordinary PC floppies). */
|
||||
|
||||
@@ -31,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
|
||||
@@ -153,6 +153,9 @@ void MacintoshDecoder::decodeSectorRecord()
|
||||
uint8_t formatByte = decode_data_gcr(header[3]);
|
||||
uint8_t wantedsum = decode_data_gcr(header[4]);
|
||||
|
||||
if (encodedSector > 11)
|
||||
return;
|
||||
|
||||
_sector->logicalTrack = _track->physicalTrack;
|
||||
_sector->logicalSide = decode_side(encodedSide);
|
||||
_sector->logicalSector = encodedSector;
|
||||
@@ -177,5 +180,29 @@ void MacintoshDecoder::decodeDataRecord()
|
||||
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
|
||||
|
||||
_sector->status = Sector::BAD_CHECKSUM;
|
||||
_sector->data = decode_crazy_data(inputbuffer, _sector->status);
|
||||
Bytes userData = decode_crazy_data(inputbuffer, _sector->status);
|
||||
_sector->data.clear();
|
||||
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
|
||||
}
|
||||
|
||||
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 */
|
||||
readRawBits(32); /* skip the track number */
|
||||
_logicalTrack = decodeFmMfm(readRawBits(32)).reader().read_be16();
|
||||
}
|
||||
else if (_currentSector == 10)
|
||||
{
|
||||
@@ -67,7 +67,7 @@ void MxDecoder::decodeSectorRecord()
|
||||
gotChecksum += br.read_le16();
|
||||
uint16_t wantChecksum = br.read_le16();
|
||||
|
||||
_sector->logicalTrack = _track->physicalTrack;
|
||||
_sector->logicalTrack = _logicalTrack;
|
||||
_sector->logicalSide = _track->physicalSide;
|
||||
_sector->logicalSector = _currentSector;
|
||||
_sector->data = bytes.slice(0, SECTOR_SIZE);
|
||||
@@ -15,6 +15,7 @@ public:
|
||||
private:
|
||||
nanoseconds_t _clock;
|
||||
int _currentSector;
|
||||
int _logicalTrack;
|
||||
};
|
||||
|
||||
#endif
|
||||
29
dep/emu/charclass.h
Normal file
29
dep/emu/charclass.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Public domain, 2008, Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* $OpenBSD: charclass.h,v 1.1 2008/10/01 23:04:13 millert Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
* POSIX character class support for fnmatch() and glob().
|
||||
*/
|
||||
static struct cclass {
|
||||
const char *name;
|
||||
int (*isctype)(int);
|
||||
} cclasses[] = {
|
||||
{ "alnum", isalnum },
|
||||
{ "alpha", isalpha },
|
||||
{ "blank", isblank },
|
||||
{ "cntrl", iscntrl },
|
||||
{ "digit", isdigit },
|
||||
{ "graph", isgraph },
|
||||
{ "lower", islower },
|
||||
{ "print", isprint },
|
||||
{ "punct", ispunct },
|
||||
{ "space", isspace },
|
||||
{ "upper", isupper },
|
||||
{ "xdigit", isxdigit },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
#define NCCLASSES (sizeof(cclasses) / sizeof(cclasses[0]) - 1)
|
||||
481
dep/emu/fnmatch.c
Normal file
481
dep/emu/fnmatch.c
Normal file
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* This has been lightly tweaked by me, David Given, to change the exported name
|
||||
* so that it doesn't conflict with the real fnmatch (if any).
|
||||
*/
|
||||
|
||||
/* $OpenBSD: fnmatch.c,v 1.16 2011/12/06 11:47:46 stsp Exp $ */
|
||||
|
||||
/* Copyright (c) 2011, VMware, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the VMware, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008 Todd C. Miller <millert@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Authored by William A. Rowe Jr. <wrowe; apache.org, vmware.com>, April 2011
|
||||
*
|
||||
* Derived from The Open Group Base Specifications Issue 7, IEEE Std 1003.1-2008
|
||||
* as described in;
|
||||
* http://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html
|
||||
*
|
||||
* Filename pattern matches defined in section 2.13, "Pattern Matching Notation"
|
||||
* from chapter 2. "Shell Command Language"
|
||||
* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13
|
||||
* where; 1. A bracket expression starting with an unquoted <circumflex> '^'
|
||||
* character CONTINUES to specify a non-matching list; 2. an explicit <period> '.'
|
||||
* in a bracket expression matching list, e.g. "[.abc]" does NOT match a leading
|
||||
* <period> in a filename; 3. a <left-square-bracket> '[' which does not introduce
|
||||
* a valid bracket expression is treated as an ordinary character; 4. a differing
|
||||
* number of consecutive slashes within pattern and string will NOT match;
|
||||
* 5. a trailing '\' in FNM_ESCAPE mode is treated as an ordinary '\' character.
|
||||
*
|
||||
* Bracket expansion defined in section 9.3.5, "RE Bracket Expression",
|
||||
* from chapter 9, "Regular Expressions"
|
||||
* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_03_05
|
||||
* with no support for collating symbols, equivalence class expressions or
|
||||
* character class expressions. A partial range expression with a leading
|
||||
* hyphen following a valid range expression will match only the ordinary
|
||||
* <hyphen> and the ending character (e.g. "[a-m-z]" will match characters
|
||||
* 'a' through 'm', a <hyphen> '-', or a 'z').
|
||||
*
|
||||
* Supports BSD extensions FNM_LEADING_DIR to match pattern to the end of one
|
||||
* path segment of string, and FNM_CASEFOLD to ignore alpha case.
|
||||
*
|
||||
* NOTE: Only POSIX/C single byte locales are correctly supported at this time.
|
||||
* Notably, non-POSIX locales with FNM_CASEFOLD produce undefined results,
|
||||
* particularly in ranges of mixed case (e.g. "[A-z]") or spanning alpha and
|
||||
* nonalpha characters within a range.
|
||||
*
|
||||
* XXX comments below indicate porting required for multi-byte character sets
|
||||
* and non-POSIX locale collation orders; requires mbr* APIs to track shift
|
||||
* state of pattern and string (rewinding pattern and string repeatedly).
|
||||
*
|
||||
* Certain parts of the code assume 0x00-0x3F are unique with any MBCS (e.g.
|
||||
* UTF-8, SHIFT-JIS, etc). Any implementation allowing '\' as an alternate
|
||||
* path delimiter must be aware that 0x5C is NOT unique within SHIFT-JIS.
|
||||
*/
|
||||
|
||||
#include "fnmatch.h"
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "charclass.h"
|
||||
|
||||
#define RANGE_MATCH 1
|
||||
#define RANGE_NOMATCH 0
|
||||
#define RANGE_ERROR (-1)
|
||||
|
||||
static int
|
||||
classmatch(const char *pattern, char test, int foldcase, const char **ep)
|
||||
{
|
||||
struct cclass *cc;
|
||||
const char *colon;
|
||||
size_t len;
|
||||
int rval = RANGE_NOMATCH;
|
||||
const char * const mismatch = pattern;
|
||||
|
||||
if (*pattern != '[' || pattern[1] != ':') {
|
||||
*ep = mismatch;
|
||||
return(RANGE_ERROR);
|
||||
}
|
||||
|
||||
pattern += 2;
|
||||
|
||||
if ((colon = strchr(pattern, ':')) == NULL || colon[1] != ']') {
|
||||
*ep = mismatch;
|
||||
return(RANGE_ERROR);
|
||||
}
|
||||
*ep = colon + 2;
|
||||
len = (size_t)(colon - pattern);
|
||||
|
||||
if (foldcase && strncmp(pattern, "upper:]", 7) == 0)
|
||||
pattern = "lower:]";
|
||||
for (cc = cclasses; cc->name != NULL; cc++) {
|
||||
if (!strncmp(pattern, cc->name, len) && cc->name[len] == '\0') {
|
||||
if (cc->isctype((unsigned char)test))
|
||||
rval = RANGE_MATCH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cc->name == NULL) {
|
||||
/* invalid character class, treat as normal text */
|
||||
*ep = mismatch;
|
||||
rval = RANGE_ERROR;
|
||||
}
|
||||
return(rval);
|
||||
}
|
||||
|
||||
/* Most MBCS/collation/case issues handled here. Wildcard '*' is not handled.
|
||||
* EOS '\0' and the FNM_PATHNAME '/' delimiters are not advanced over,
|
||||
* however the "\/" sequence is advanced to '/'.
|
||||
*
|
||||
* Both pattern and string are **char to support pointer increment of arbitrary
|
||||
* multibyte characters for the given locale, in a later iteration of this code
|
||||
*/
|
||||
static int fnmatch_ch(const char **pattern, const char **string, int flags)
|
||||
{
|
||||
const char * const mismatch = *pattern;
|
||||
const int nocase = !!(flags & FNM_CASEFOLD);
|
||||
const int escape = !(flags & FNM_NOESCAPE);
|
||||
const int slash = !!(flags & FNM_PATHNAME);
|
||||
int result = FNM_NOMATCH;
|
||||
const char *startch;
|
||||
int negate;
|
||||
|
||||
if (**pattern == '[')
|
||||
{
|
||||
++*pattern;
|
||||
|
||||
/* Handle negation, either leading ! or ^ operators (never both) */
|
||||
negate = ((**pattern == '!') || (**pattern == '^'));
|
||||
if (negate)
|
||||
++*pattern;
|
||||
|
||||
/* ']' is an ordinary character at the start of the range pattern */
|
||||
if (**pattern == ']')
|
||||
goto leadingclosebrace;
|
||||
|
||||
while (**pattern)
|
||||
{
|
||||
if (**pattern == ']') {
|
||||
++*pattern;
|
||||
/* XXX: Fix for MBCS character width */
|
||||
++*string;
|
||||
return (result ^ negate);
|
||||
}
|
||||
|
||||
if (escape && (**pattern == '\\')) {
|
||||
++*pattern;
|
||||
|
||||
/* Patterns must be terminated with ']', not EOS */
|
||||
if (!**pattern)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Patterns must be terminated with ']' not '/' */
|
||||
if (slash && (**pattern == '/'))
|
||||
break;
|
||||
|
||||
/* Match character classes. */
|
||||
if (classmatch(*pattern, **string, nocase, pattern)
|
||||
== RANGE_MATCH) {
|
||||
result = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
leadingclosebrace:
|
||||
/* Look at only well-formed range patterns;
|
||||
* "x-]" is not allowed unless escaped ("x-\]")
|
||||
* XXX: Fix for locale/MBCS character width
|
||||
*/
|
||||
if (((*pattern)[1] == '-') && ((*pattern)[2] != ']'))
|
||||
{
|
||||
startch = *pattern;
|
||||
*pattern += (escape && ((*pattern)[2] == '\\')) ? 3 : 2;
|
||||
|
||||
/* NOT a properly balanced [expr] pattern, EOS terminated
|
||||
* or ranges containing a slash in FNM_PATHNAME mode pattern
|
||||
* fall out to to the rewind and test '[' literal code path
|
||||
*/
|
||||
if (!**pattern || (slash && (**pattern == '/')))
|
||||
break;
|
||||
|
||||
/* XXX: handle locale/MBCS comparison, advance by MBCS char width */
|
||||
if ((**string >= *startch) && (**string <= **pattern))
|
||||
result = 0;
|
||||
else if (nocase && (isupper(**string) || isupper(*startch)
|
||||
|| isupper(**pattern))
|
||||
&& (tolower(**string) >= tolower(*startch))
|
||||
&& (tolower(**string) <= tolower(**pattern)))
|
||||
result = 0;
|
||||
|
||||
++*pattern;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* XXX: handle locale/MBCS comparison, advance by MBCS char width */
|
||||
if ((**string == **pattern))
|
||||
result = 0;
|
||||
else if (nocase && (isupper(**string) || isupper(**pattern))
|
||||
&& (tolower(**string) == tolower(**pattern)))
|
||||
result = 0;
|
||||
|
||||
++*pattern;
|
||||
}
|
||||
|
||||
/* NOT a properly balanced [expr] pattern; Rewind
|
||||
* and reset result to test '[' literal
|
||||
*/
|
||||
*pattern = mismatch;
|
||||
result = FNM_NOMATCH;
|
||||
}
|
||||
else if (**pattern == '?') {
|
||||
/* Optimize '?' match before unescaping **pattern */
|
||||
if (!**string || (slash && (**string == '/')))
|
||||
return FNM_NOMATCH;
|
||||
result = 0;
|
||||
goto fnmatch_ch_success;
|
||||
}
|
||||
else if (escape && (**pattern == '\\') && (*pattern)[1]) {
|
||||
++*pattern;
|
||||
}
|
||||
|
||||
/* XXX: handle locale/MBCS comparison, advance by the MBCS char width */
|
||||
if (**string == **pattern)
|
||||
result = 0;
|
||||
else if (nocase && (isupper(**string) || isupper(**pattern))
|
||||
&& (tolower(**string) == tolower(**pattern)))
|
||||
result = 0;
|
||||
|
||||
/* Refuse to advance over trailing slash or nulls
|
||||
*/
|
||||
if (!**string || !**pattern || (slash && ((**string == '/') || (**pattern == '/'))))
|
||||
return result;
|
||||
|
||||
fnmatch_ch_success:
|
||||
++*pattern;
|
||||
++*string;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int fnmatch(const char *pattern, const char *string, int flags)
|
||||
{
|
||||
static const char dummystring[2] = {' ', 0};
|
||||
const int escape = !(flags & FNM_NOESCAPE);
|
||||
const int slash = !!(flags & FNM_PATHNAME);
|
||||
const char *strendseg;
|
||||
const char *dummyptr;
|
||||
const char *matchptr;
|
||||
int wild;
|
||||
/* For '*' wild processing only; surpress 'used before initialization'
|
||||
* warnings with dummy initialization values;
|
||||
*/
|
||||
const char *strstartseg = NULL;
|
||||
const char *mismatch = NULL;
|
||||
int matchlen = 0;
|
||||
|
||||
if (strnlen(pattern, PATH_MAX) == PATH_MAX ||
|
||||
strnlen(string, PATH_MAX) == PATH_MAX)
|
||||
return (FNM_NOMATCH);
|
||||
|
||||
if (*pattern == '*')
|
||||
goto firstsegment;
|
||||
|
||||
while (*pattern && *string)
|
||||
{
|
||||
/* Pre-decode "\/" which has no special significance, and
|
||||
* match balanced slashes, starting a new segment pattern
|
||||
*/
|
||||
if (slash && escape && (*pattern == '\\') && (pattern[1] == '/'))
|
||||
++pattern;
|
||||
if (slash && (*pattern == '/') && (*string == '/')) {
|
||||
++pattern;
|
||||
++string;
|
||||
}
|
||||
|
||||
firstsegment:
|
||||
/* At the beginning of each segment, validate leading period behavior.
|
||||
*/
|
||||
if ((flags & FNM_PERIOD) && (*string == '.'))
|
||||
{
|
||||
if (*pattern == '.')
|
||||
++pattern;
|
||||
else if (escape && (*pattern == '\\') && (pattern[1] == '.'))
|
||||
pattern += 2;
|
||||
else
|
||||
return FNM_NOMATCH;
|
||||
++string;
|
||||
}
|
||||
|
||||
/* Determine the end of string segment
|
||||
*
|
||||
* Presumes '/' character is unique, not composite in any MBCS encoding
|
||||
*/
|
||||
if (slash) {
|
||||
strendseg = strchr(string, '/');
|
||||
if (!strendseg)
|
||||
strendseg = strchr(string, '\0');
|
||||
}
|
||||
else {
|
||||
strendseg = strchr(string, '\0');
|
||||
}
|
||||
|
||||
/* Allow pattern '*' to be consumed even with no remaining string to match
|
||||
*/
|
||||
while (*pattern)
|
||||
{
|
||||
if ((string > strendseg)
|
||||
|| ((string == strendseg) && (*pattern != '*')))
|
||||
break;
|
||||
|
||||
if (slash && ((*pattern == '/')
|
||||
|| (escape && (*pattern == '\\')
|
||||
&& (pattern[1] == '/'))))
|
||||
break;
|
||||
|
||||
/* Reduce groups of '*' and '?' to n '?' matches
|
||||
* followed by one '*' test for simplicity
|
||||
*/
|
||||
for (wild = 0; ((*pattern == '*') || (*pattern == '?')); ++pattern)
|
||||
{
|
||||
if (*pattern == '*') {
|
||||
wild = 1;
|
||||
}
|
||||
else if (string < strendseg) { /* && (*pattern == '?') */
|
||||
/* XXX: Advance 1 char for MBCS locale */
|
||||
++string;
|
||||
}
|
||||
else { /* (string >= strendseg) && (*pattern == '?') */
|
||||
return FNM_NOMATCH;
|
||||
}
|
||||
}
|
||||
|
||||
if (wild)
|
||||
{
|
||||
strstartseg = string;
|
||||
mismatch = pattern;
|
||||
|
||||
/* Count fixed (non '*') char matches remaining in pattern
|
||||
* excluding '/' (or "\/") and '*'
|
||||
*/
|
||||
for (matchptr = pattern, matchlen = 0; 1; ++matchlen)
|
||||
{
|
||||
if ((*matchptr == '\0')
|
||||
|| (slash && ((*matchptr == '/')
|
||||
|| (escape && (*matchptr == '\\')
|
||||
&& (matchptr[1] == '/')))))
|
||||
{
|
||||
/* Compare precisely this many trailing string chars,
|
||||
* the resulting match needs no wildcard loop
|
||||
*/
|
||||
/* XXX: Adjust for MBCS */
|
||||
if (string + matchlen > strendseg)
|
||||
return FNM_NOMATCH;
|
||||
|
||||
string = strendseg - matchlen;
|
||||
wild = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*matchptr == '*')
|
||||
{
|
||||
/* Ensure at least this many trailing string chars remain
|
||||
* for the first comparison
|
||||
*/
|
||||
/* XXX: Adjust for MBCS */
|
||||
if (string + matchlen > strendseg)
|
||||
return FNM_NOMATCH;
|
||||
|
||||
/* Begin first wild comparison at the current position */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Skip forward in pattern by a single character match
|
||||
* Use a dummy fnmatch_ch() test to count one "[range]" escape
|
||||
*/
|
||||
/* XXX: Adjust for MBCS */
|
||||
if (escape && (*matchptr == '\\') && matchptr[1]) {
|
||||
matchptr += 2;
|
||||
}
|
||||
else if (*matchptr == '[') {
|
||||
dummyptr = dummystring;
|
||||
fnmatch_ch(&matchptr, &dummyptr, flags);
|
||||
}
|
||||
else {
|
||||
++matchptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Incrementally match string against the pattern
|
||||
*/
|
||||
while (*pattern && (string < strendseg))
|
||||
{
|
||||
/* Success; begin a new wild pattern search
|
||||
*/
|
||||
if (*pattern == '*')
|
||||
break;
|
||||
|
||||
if (slash && ((*string == '/')
|
||||
|| (*pattern == '/')
|
||||
|| (escape && (*pattern == '\\')
|
||||
&& (pattern[1] == '/'))))
|
||||
break;
|
||||
|
||||
/* Compare ch's (the pattern is advanced over "\/" to the '/',
|
||||
* but slashes will mismatch, and are not consumed)
|
||||
*/
|
||||
if (!fnmatch_ch(&pattern, &string, flags))
|
||||
continue;
|
||||
|
||||
/* Failed to match, loop against next char offset of string segment
|
||||
* until not enough string chars remain to match the fixed pattern
|
||||
*/
|
||||
if (wild) {
|
||||
/* XXX: Advance 1 char for MBCS locale */
|
||||
string = ++strstartseg;
|
||||
if (string + matchlen > strendseg)
|
||||
return FNM_NOMATCH;
|
||||
|
||||
pattern = mismatch;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
return FNM_NOMATCH;
|
||||
}
|
||||
}
|
||||
|
||||
if (*string && !(slash && (*string == '/')))
|
||||
return FNM_NOMATCH;
|
||||
|
||||
if (*pattern && !(slash && ((*pattern == '/')
|
||||
|| (escape && (*pattern == '\\')
|
||||
&& (pattern[1] == '/')))))
|
||||
return FNM_NOMATCH;
|
||||
}
|
||||
|
||||
/* Where both pattern and string are at EOS, declare success
|
||||
*/
|
||||
if (!*string && !*pattern)
|
||||
return 0;
|
||||
|
||||
/* pattern didn't match to the end of string */
|
||||
return FNM_NOMATCH;
|
||||
}
|
||||
21
dep/emu/fnmatch.h
Normal file
21
dep/emu/fnmatch.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef FNMATCH_H
|
||||
#define FNMATCH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define FNM_NOMATCH 1 /* Match failed. */
|
||||
|
||||
#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
|
||||
#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
|
||||
#define FNM_PERIOD 0x04 /* Period must be matched by period. */
|
||||
#define FNM_CASEFOLD 0x08 /* Fold cases */
|
||||
|
||||
extern int fnmatch(const char *pattern, const char *string, int flags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
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 900kB/s in both directions.
|
||||
FluxEngine needs about 400kB/s for a DD disk and about 850kB/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:
|
||||
@@ -23,6 +23,39 @@ You should end up with an `amiga.adf` which is 901120 bytes long (for a
|
||||
normal DD disk) --- it ought to be a perfectly normal ADF file which you can
|
||||
use in an emulator.
|
||||
|
||||
If you want the metadata as well, specify a 528 byte sector size for the
|
||||
output image:
|
||||
|
||||
```
|
||||
fluxengine read amiga -o amiga.adf:b=528
|
||||
```
|
||||
|
||||
You will end up with a 929280 byte long image which you probably _can't_ use
|
||||
in an emulator; each sector will contain the 512 bytes of user payload
|
||||
followed by the 16 bytes of metadata.
|
||||
|
||||
Writing disks
|
||||
-------------
|
||||
|
||||
Just do:
|
||||
|
||||
```
|
||||
fluxengine write amiga -i amiga.adf
|
||||
```
|
||||
|
||||
This will rake a normal 901120 byte long ADF file and write it to a DD disk.
|
||||
Note that writing to an HD disk will probably not work (this will depend on
|
||||
your drive and disk and potential FluxEngine bugs I'm still working on ---
|
||||
please [get in touch](https://github.com/davidgiven/fluxengine/issues/new) if
|
||||
you have any insight here).
|
||||
|
||||
If you want to write the metadata as well, specify a 528 byte sector size for
|
||||
the output image and supply a 929280 byte long file as described above.
|
||||
|
||||
```
|
||||
fluxengine write amiga -i amiga.adf:b=528
|
||||
```
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
|
||||
|
||||
@@ -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
|
||||
-------------
|
||||
@@ -50,6 +58,10 @@ with holes where missing sectors should be. This was easiest. If anyone can
|
||||
suggest a better way, please [get in
|
||||
touch](https://github.com/davidgiven/fluxengine/issues/new).
|
||||
|
||||
The 12 bytes of metadata _follow_ the 512 bytes of user payload in the sector
|
||||
image. If you don't want it, specify a geometry in the output file with a
|
||||
512-byte sectore size like `-o mac.img:c=80:h=1:s=12:b=512`.
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
|
||||
|
||||
65
doc/disk-mx.md
Normal file
65
doc/disk-mx.md
Normal file
@@ -0,0 +1,65 @@
|
||||
Disk: DVK MX
|
||||
============
|
||||
|
||||
The DVK (in Russian, ДВК, Диалоговый вычислительный комплекс or Dialogue
|
||||
Computing Complex) was a late 1970s Soviet personal computer, a cut-down
|
||||
version of the professional SM EVM (СМ ЭВМ, abbreviation of Система Малых ЭВМ
|
||||
--- literally System of Mini Computers), which _itself_ was an unlicensed
|
||||
clone of the PDP-11. The MX board was an early floppy drive controller board
|
||||
for it.
|
||||
|
||||
<div style="text-align: center">
|
||||
<a href="http://www.leningrad.su/museum/show_big.php?n=1006"><img src="dvk3m.jpg" style="max-width: 60%" alt="A Durango F85, held precariously"></a>
|
||||
</div>
|
||||
|
||||
The MX format is interesting in that it has to be read a track at a time. The
|
||||
format contains the usual ID prologue at the beginning of the track, then
|
||||
eleven data blocks and checksums, then the epilogue, then it stops. The
|
||||
actual encoding is normal FM. There were four different disk variants, in all
|
||||
combinations of single- and double-sided and 40- and 80-tracked; but every
|
||||
track contained eleven 256-byte sectors.
|
||||
|
||||
The format varies subtly depending on whether you're using the 'new' driver
|
||||
or the 'old' driver. FluxEngine should read both.
|
||||
|
||||
A track is:
|
||||
|
||||
* 8 x 0x0000 words (FM encoded as 01010101...)
|
||||
* 1 x 0x00F3 --- start of track
|
||||
* 1 x 0xnnnn --- track number
|
||||
* 11 of:
|
||||
* 128 words (256 bytes) of data
|
||||
* 16 bit checksum
|
||||
* **if 'new' format:**
|
||||
* 3 x 0x83nn --- `n = (track_number<<1) + side_number`
|
||||
* **if 'old' format:**
|
||||
* 3 x 0x8301
|
||||
|
||||
The checksum is just the unsigned integer sum of all the words in the sector.
|
||||
Words are all stored little-endian.
|
||||
|
||||
Reading discs
|
||||
-------------
|
||||
|
||||
```
|
||||
fluxengine read mx
|
||||
```
|
||||
|
||||
You should end up with an `mx.img` which will vary in length depending on the format. The default is double-sided 80-track. For the other formats, use:
|
||||
|
||||
* single-sided 40-track: `-s :s=0:t=0-79x2`
|
||||
* double-sided 40-track: `-s :s=0-1:t=0-79x2`
|
||||
* single-sided 40-track: `-s :s=0:t=0-79`
|
||||
* double-sided 40-track: `-s :s=0-1:t=0-79`
|
||||
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
|
||||
- [The Soviet Digital Electronics
|
||||
Museum](http://www.leningrad.su/museum/main.php) (source of the image
|
||||
above)
|
||||
|
||||
- [a random post on the HxC2001 support
|
||||
forum](http://torlus.com/floppy/forum/viewtopic.php?t=1384) with lots of
|
||||
information on the format
|
||||
BIN
doc/dvk3m.jpg
Normal file
BIN
doc/dvk3m.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 159 KiB |
20
doc/faq.md
20
doc/faq.md
@@ -48,7 +48,7 @@ haven't had the chance to try it end-for-end. I really need a hard-sectored
|
||||
|
||||
**Q.** Does it work with flippy disks?
|
||||
|
||||
Uhhh... probably not.
|
||||
Uhhh... maybe?
|
||||
|
||||
So the problem with flippy disks (5.25" single-sided disks which could be
|
||||
inserted upside down to read the second side) is the index hole. Trouble is,
|
||||
@@ -79,16 +79,26 @@ the other. But a flippy disk has both sets of tracks in the same place,
|
||||
because they're both accessed using the side 0 head...
|
||||
|
||||
The only real way round this is to modify a 5.25" drive. That's _seriously_
|
||||
not in FluxEngine's remit. Sorry.
|
||||
not in FluxEngine's remit, but I've had some [excellent documentation
|
||||
contributed](Index_sensor_mod_FDD_1.1.pdf) on how to do this. I've never done
|
||||
it myself; if you try this and it works/doesn't work, as always, [get in
|
||||
touch](https://github.com/davidgiven/fluxengine/issues/new).
|
||||
|
||||
**Q.** Is this like KryoFlux / Catweasel / DiskFerret? Do you support KryoFlux
|
||||
Another option is to fake the index signal to the drive completely. The
|
||||
FluxEngine emits suitable pulses for a 300RPM drive on pin 3[0] and the
|
||||
equivalent pulses for a 360RPM drive on pin 3[1]. Disclaimer: I have never used
|
||||
these.
|
||||
|
||||
**Q.** Is this like Supercard Pro / KryoFlux / Catweasel / DiskFerret? Do you
|
||||
*support KryoFlux
|
||||
stream files?
|
||||
|
||||
**A.** It's very like all of these; the idea's old, and lots of people have
|
||||
tried it (you can get away with any sufficiently fast microcontroller and
|
||||
enough RAM). FluxEngine can read from KryoFlux stream files natively, and
|
||||
there's a tool which will let you convert at least one kind of Catweasel file
|
||||
to FluxEngine's native flux file format.
|
||||
there's a tool which will let you convert at least one kind of Catweasel
|
||||
files and Supercard Pro files to and from FluxEngine's native flux file
|
||||
format.
|
||||
|
||||
**Q.** Can I use this to make exact copies of disks?
|
||||
|
||||
|
||||
@@ -59,53 +59,42 @@ Some useful and/or interesting numbers:
|
||||
|
||||
## Why don't I use an Arduino / STM32 / ESP32 / Raspberry Pi / etc?
|
||||
|
||||
I've got a _lot_ of questions on this, and multiple Github issues of people
|
||||
-I've got a _lot_ of questions on this, and multiple Github issues of people
|
||||
debating it. It's complicated, but it's essentially a tradeoff between speed
|
||||
and complexity.
|
||||
and complexity.-
|
||||
|
||||
FluxEngine's read process involves generating a lot of data using a fairly
|
||||
brute force sampling approach --- about 150kB per disk revolution, and
|
||||
sometimes it needs to record multiple revolutions. Most microcontrollers
|
||||
don't have enough RAM to buffer this, so instead I have to stream it over USB
|
||||
back to the host PC in real time. The disk won't wait, so I need to stream data faster
|
||||
than the disk is producing it: the total is about 800kB/s.
|
||||
**Update as of 2020-01-08:**
|
||||
|
||||
Handling USB is pretty CPU-hungry, so my candidate microntroller has to be
|
||||
able to cope with the ruinously strict real-time requirements of the
|
||||
sampler's 12MHz clock as well as keeping up with 13,000 USB interrupts a
|
||||
second (one for each 64-byte frame) in order to transfer the data.
|
||||
Right. Well.
|
||||
|
||||
The Atmels and STM32s I found were perfectly capable of doing the real-time
|
||||
sampling, using hand-tool assembly, but I very much doubt whether they could
|
||||
do the USB streaming as well (although I want to move away from the Cypress
|
||||
onto something less proprietary and easier to source, so I'd like to be
|
||||
proven wrong here).
|
||||
This section used to have a long explanation as to why these other platforms
|
||||
were unsuitable --- essentially, they're generally missing out on either the
|
||||
realtimeness to sample the data correctly (Raspberry Pi) or enough CPU to
|
||||
stream the data over USB while also sampling it (Arduino).
|
||||
|
||||
The Raspberry Pi easily has enough processing power and memory, but it's also
|
||||
got terrible GPIO pin read performance --- [about
|
||||
1kHz](https://raspberrypi.stackexchange.com/questions/9646/how-fast-is-gpiodma-multi-i2s-input/10197#10197).
|
||||
That's a long way from the 12MHz I need.
|
||||
This is correct, but it turns out that the STM32 has some built-in features
|
||||
which support the FluxEngine's use case almost exactly: you can configure the
|
||||
DMA engine to sample the interval between pulses and write them directly into
|
||||
memory, and you can configure the PWM engine the read samples from memory and
|
||||
use them to time pulses to the output. There's a bit less functionality, so you
|
||||
can't do things like measure the signal voltages, and they're less convenient
|
||||
as you need an adapter cable or board, but this will allow you to replicate the
|
||||
FluxEngine hardware on a $2 Blue Pill.
|
||||
|
||||
The PSoC5LP part I'm using has enough CPU to handle the USB side of things,
|
||||
and it _also_ has a whole set of FPGA-like soft programmable features,
|
||||
including 24 mini-ALU systems that are ideally suited to exactly this kind of
|
||||
sampling. I can read the disk and generate the byte stream describing the
|
||||
flux pattern entirely in 'hardware', without involving the main CPU at all.
|
||||
This is then DMAed directly into a set of ring buffers read for the USB
|
||||
system to pick up and relay back to the PC. It's incredibly simple and works
|
||||
well. (The same applies to writing flux back onto the disk.)
|
||||
I am _not_ planning on replacing the PSoC5 with a Blue Pill, because someone
|
||||
already has: [the GreaseWeazle](https://github.com/keirf/Greaseweazle/wiki) is
|
||||
a completely open source firmware package which will read and write Supercard
|
||||
Pro files via a standard Blue Pill. The GreaseWeazle's USB protocol is
|
||||
different from the FluxEngine's so they're not directly interchangeable. You
|
||||
can, however, read a Supercard Pro file with a GreaseWeazle and then use the
|
||||
FluxEngine client to decode it. It should work the other way around, too, but
|
||||
FluxEngine's SCP export [is curently
|
||||
broken](https://github.com/davidgiven/fluxengine/issues/134).
|
||||
|
||||
The development board I'm using, the
|
||||
[CY8CKIT-059](https://www.cypress.com/documentation/development-kitsboards/cy8ckit-059-psoc-5lp-prototyping-kit-onboard-programmer-and),
|
||||
also has another big advantage: it's the right shape. It's got 17 holes in a
|
||||
row connected to GPIO pins, and it's a native 5V part, which means I can just
|
||||
connect a floppy drive connector directly to the board without needing to
|
||||
build any hardware. No adapter board, no level shifting, no special cable,
|
||||
nothing. This makes the FluxEngine hardware incredibly easy to assemble,
|
||||
which therefore means cheap.
|
||||
I _am_ considering adding direct support for the GreaseWeazle to the FluxEngine
|
||||
client, which will let you just plug one in and make it go as a direct
|
||||
replacement to the FluxEngine hardware.
|
||||
|
||||
Speaking of which, the CY8CKIT-059 is $10. (Before shipping, which is
|
||||
admittedly expensive.)
|
||||
|
||||
### Some useful links
|
||||
|
||||
@@ -123,8 +112,12 @@ admittedly expensive.)
|
||||
sheet](http://www.bitsavers.org/pdf/mitsubishi/floppy/M4851/TJ2-G30211A_M4851_DSHH_48TPI_OEM_Manual_Nov83.pdf):
|
||||
the equivalent data sheet for a representative 5.25" drive.
|
||||
|
||||
- [The DRG Business Machines YD-174 manual](https://electrickery.hosting.philpem.me.uk/comp/divcomp/doc/YE_Data_YD-174_8inchFloppyDriveTechnicalManual.pdf):
|
||||
the equivalent manual (data sheets hadn't been invented then) for a
|
||||
representative 8" drive.
|
||||
|
||||
- [KryoFlux stream file
|
||||
documentation](https://www.kryoflux.com/download/kryoflux_stream_protocol_rev1.1.pdf):
|
||||
the format of KryoFlux stream files (partially supported by FluxEngine)
|
||||
|
||||
|
||||
|
||||
|
||||
262
doc/using.md
262
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,7 +73,49 @@ 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
|
||||
|
||||
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
|
||||
heads, 9 sectors, 512 bytes per sector. For output files (`--output`) the
|
||||
geometry will be autodetected if left unspecified. For input files you
|
||||
normally have to specify it.
|
||||
|
||||
- `.ldbs`: John Elliott's [LDBS disk image
|
||||
format](http://www.seasip.info/Unix/LibDsk/ldbs.html), which is
|
||||
consumable by the [libdsk](http://www.seasip.info/Unix/LibDsk/) suite of
|
||||
tools. This allows things like variable numbers of sectors per track
|
||||
(e.g. Macintosh or Commodore 64) and also provides information about
|
||||
whether sectors were read correctly. You can use libdsk to convert this
|
||||
to other formats, using a command like this:
|
||||
|
||||
```
|
||||
$ dsktrans out.ldbs -otype tele out.td0
|
||||
```
|
||||
|
||||
...to convert to TeleDisk format. (Note you have to use dsktrans rather
|
||||
than dskconv due to a minor bug in the geometry hadnling.)
|
||||
|
||||
FluxEngine's LDBS support is currently limited to write only, and
|
||||
it doesn't store a lot of the more esoteric LDBS features like format
|
||||
types, timings, and data rates.
|
||||
|
||||
- `.d64`: the venerable Commodore 64 disk image format as used by the 1540,
|
||||
1541, etc. This is a special-purpose format due to the weird layout of
|
||||
1540 disks and while you can use this for non-Commodore disks the result
|
||||
will be gibberish. Use this to image Commodore 64 disks and load the
|
||||
result into an emulator.
|
||||
|
||||
FluxEngine's D64 support is currently limited to write only. It will work
|
||||
with up to 40 logical tracks.
|
||||
|
||||
### High density disks
|
||||
|
||||
@@ -131,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.
|
||||
@@ -139,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 |
@@ -1,20 +0,0 @@
|
||||
#ifndef AMIGA_H
|
||||
#define AMIGA_H
|
||||
|
||||
#define AMIGA_SECTOR_RECORD 0xaaaa44894489LL
|
||||
|
||||
#define AMIGA_RECORD_SIZE 0x21f
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
|
||||
class AmigaDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~AmigaDecoder() {}
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,97 +0,0 @@
|
||||
#include "globals.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "protocol.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "sector.h"
|
||||
#include "amiga.h"
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
/*
|
||||
* Amiga disks use MFM but it's not quite the same as IBM MFM. They only use
|
||||
* a single type of record with a different marker byte.
|
||||
*
|
||||
* See the big comment in the IBM MFM decoder for the gruesome details of how
|
||||
* MFM works.
|
||||
*/
|
||||
|
||||
static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD);
|
||||
|
||||
static Bytes deinterleave(const uint8_t*& input, size_t len)
|
||||
{
|
||||
assert(!(len & 1));
|
||||
const uint8_t* odds = &input[0];
|
||||
const uint8_t* evens = &input[len/2];
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
|
||||
for (size_t i=0; i<len/2; i++)
|
||||
{
|
||||
uint8_t o = *odds++;
|
||||
uint8_t e = *evens++;
|
||||
|
||||
/* This is the 'Interleave bits with 64-bit multiply' technique from
|
||||
* http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
|
||||
*/
|
||||
uint16_t result =
|
||||
(((e * 0x0101010101010101ULL & 0x8040201008040201ULL)
|
||||
* 0x0102040810204081ULL >> 49) & 0x5555) |
|
||||
(((o * 0x0101010101010101ULL & 0x8040201008040201ULL)
|
||||
* 0x0102040810204081ULL >> 48) & 0xAAAA);
|
||||
|
||||
bw.write_be16(result);
|
||||
}
|
||||
|
||||
input += len;
|
||||
return output;
|
||||
}
|
||||
|
||||
static uint32_t checksum(const Bytes& bytes)
|
||||
{
|
||||
ByteReader br(bytes);
|
||||
uint32_t checksum = 0;
|
||||
|
||||
assert((bytes.size() & 3) == 0);
|
||||
while (!br.eof())
|
||||
checksum ^= br.read_be32();
|
||||
|
||||
return checksum & 0x55555555;
|
||||
}
|
||||
|
||||
AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord()
|
||||
{
|
||||
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
|
||||
if (_fmr->eof() || !_sector->clock)
|
||||
return UNKNOWN_RECORD;
|
||||
return SECTOR_RECORD;
|
||||
}
|
||||
|
||||
void AmigaDecoder::decodeSectorRecord()
|
||||
{
|
||||
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
|
||||
const auto& rawbytes = toBytes(rawbits).slice(0, AMIGA_RECORD_SIZE*2);
|
||||
const auto& bytes = decodeFmMfm(rawbits).slice(0, AMIGA_RECORD_SIZE);
|
||||
|
||||
const uint8_t* ptr = bytes.begin() + 3;
|
||||
|
||||
Bytes header = deinterleave(ptr, 4);
|
||||
Bytes recoveryinfo = deinterleave(ptr, 16);
|
||||
|
||||
_sector->logicalTrack = header[1] >> 1;
|
||||
_sector->logicalSide = header[1] & 1;
|
||||
_sector->logicalSector = header[2];
|
||||
|
||||
uint32_t wantedheaderchecksum = deinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotheaderchecksum = checksum(rawbytes.slice(6, 40));
|
||||
if (gotheaderchecksum != wantedheaderchecksum)
|
||||
return;
|
||||
|
||||
uint32_t wanteddatachecksum = deinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotdatachecksum = checksum(rawbytes.slice(62, 1024));
|
||||
_sector->data = deinterleave(ptr, 512);
|
||||
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
#include "globals.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "brother.h"
|
||||
#include "crc.h"
|
||||
|
||||
static int encode_header_gcr(uint16_t word)
|
||||
{
|
||||
switch (word)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: return gcr;
|
||||
#include "header_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
static int encode_data_gcr(uint8_t data)
|
||||
{
|
||||
switch (data)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint32_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;
|
||||
}
|
||||
}
|
||||
|
||||
void writeBrotherSectorHeader(std::vector<bool>& bits, unsigned& cursor,
|
||||
int track, int sector)
|
||||
{
|
||||
write_bits(bits, cursor, 0xffffffff, 31);
|
||||
write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32);
|
||||
write_bits(bits, cursor, encode_header_gcr(track), 16);
|
||||
write_bits(bits, cursor, encode_header_gcr(sector), 16);
|
||||
write_bits(bits, cursor, encode_header_gcr(0x2f), 16);
|
||||
}
|
||||
|
||||
void writeBrotherSectorData(std::vector<bool>& bits, unsigned& cursor, const Bytes& data)
|
||||
{
|
||||
write_bits(bits, cursor, 0xffffffff, 32);
|
||||
write_bits(bits, cursor, BROTHER_DATA_RECORD, 32);
|
||||
|
||||
uint16_t fifo = 0;
|
||||
int width = 0;
|
||||
|
||||
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
|
||||
Error() << "unsupported sector size";
|
||||
|
||||
auto write_byte = [&](uint8_t byte)
|
||||
{
|
||||
fifo |= (byte << (8 - width));
|
||||
width += 8;
|
||||
|
||||
while (width >= 5)
|
||||
{
|
||||
uint8_t quintet = fifo >> 11;
|
||||
fifo <<= 5;
|
||||
width -= 5;
|
||||
|
||||
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
|
||||
}
|
||||
};
|
||||
|
||||
for (uint8_t byte : data)
|
||||
write_byte(byte);
|
||||
|
||||
uint32_t realCrc = crcbrother(data);
|
||||
write_byte(realCrc>>16);
|
||||
write_byte(realCrc>>8);
|
||||
write_byte(realCrc);
|
||||
write_byte(0x58); /* magic */
|
||||
write_byte(0xd4);
|
||||
while (width != 0)
|
||||
write_byte(0);
|
||||
}
|
||||
|
||||
|
||||
33
lib/bytes.cc
33
lib/bytes.cc
@@ -2,6 +2,7 @@
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include "common/crunch.h"
|
||||
#include <fstream>
|
||||
#include <zlib.h>
|
||||
|
||||
static std::shared_ptr<std::vector<uint8_t>> createVector(unsigned size)
|
||||
@@ -146,6 +147,28 @@ Bytes Bytes::slice(unsigned start, unsigned len) const
|
||||
}
|
||||
}
|
||||
|
||||
Bytes Bytes::slice(unsigned start) const
|
||||
{
|
||||
int len = 0;
|
||||
if (start < size())
|
||||
len = size() - start;
|
||||
return slice(start, len);
|
||||
}
|
||||
|
||||
std::vector<bool> Bytes::toBits() const
|
||||
{
|
||||
std::vector<bool> bits;
|
||||
for (uint8_t byte : *this)
|
||||
{
|
||||
for (int i=0; i<8; i++)
|
||||
{
|
||||
bits.push_back(byte & 0x80);
|
||||
byte <<= 1;
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
uint8_t toByte(
|
||||
std::vector<bool>::const_iterator start,
|
||||
std::vector<bool>::const_iterator end)
|
||||
@@ -280,6 +303,16 @@ Bytes Bytes::uncrunch() const
|
||||
return output;
|
||||
}
|
||||
|
||||
void Bytes::writeToFile(const std::string& filename) const
|
||||
{
|
||||
std::ofstream f(filename, std::ios::out | std::ios::binary);
|
||||
if (!f.is_open())
|
||||
Error() << fmt::format("cannot open output file '{}'", filename);
|
||||
|
||||
f.write((const char*) cbegin(), size());
|
||||
f.close();
|
||||
}
|
||||
|
||||
ByteReader Bytes::reader() const
|
||||
{
|
||||
return ByteReader(*this);
|
||||
|
||||
@@ -47,15 +47,19 @@ public:
|
||||
{ resize(0); return *this; }
|
||||
|
||||
Bytes slice(unsigned start, unsigned len) const;
|
||||
Bytes slice(unsigned start) const;
|
||||
Bytes swab() const;
|
||||
Bytes compress() const;
|
||||
Bytes decompress() const;
|
||||
Bytes crunch() const;
|
||||
Bytes uncrunch() const;
|
||||
std::vector<bool> toBits() const;
|
||||
|
||||
ByteReader reader() const;
|
||||
ByteWriter writer();
|
||||
|
||||
void writeToFile(const std::string& filename) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::vector<uint8_t>> _data;
|
||||
unsigned _low;
|
||||
|
||||
@@ -9,14 +9,37 @@ void crunch(crunch_state_t* state)
|
||||
uint8_t data = *state->inputptr++;
|
||||
state->inputlen--;
|
||||
|
||||
if (data & 0x80)
|
||||
if (data == 0x80)
|
||||
{
|
||||
/* Multiple 0x80s in a row get swallowed as they're
|
||||
* meaningless. */
|
||||
state->haspending = true;
|
||||
}
|
||||
else if (data & 0x80)
|
||||
{
|
||||
state->fifo = (state->fifo << 2) | 2 | (data & 1);
|
||||
state->fifolen += 2;
|
||||
if (state->haspending)
|
||||
{
|
||||
state->fifo = (state->fifo << 3) | 4;
|
||||
state->fifolen += 3;
|
||||
state->haspending = false;
|
||||
}
|
||||
|
||||
state->fifo = (state->fifo << 3) | 4 | (data & 1);
|
||||
state->fifolen += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->fifo = (state->fifo << 8) | data;
|
||||
if (state->haspending && (data >= 0x40))
|
||||
{
|
||||
state->fifo = (state->fifo << 3) | 4;
|
||||
state->fifolen += 3;
|
||||
state->haspending = false;
|
||||
}
|
||||
|
||||
state->fifo = (state->fifo << 8) | data;
|
||||
if (state->haspending)
|
||||
state->fifo |= 0xc0;
|
||||
state->haspending = false;
|
||||
state->fifolen += 8;
|
||||
}
|
||||
|
||||
@@ -45,6 +68,14 @@ void uncrunch(crunch_state_t* state)
|
||||
{
|
||||
while (state->inputlen && state->outputlen)
|
||||
{
|
||||
if (state->haspending)
|
||||
{
|
||||
*state->outputptr++ = state->pendingbyte;
|
||||
state->outputlen--;
|
||||
state->haspending = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state->fifolen < 8)
|
||||
{
|
||||
if (state->inputlen)
|
||||
@@ -58,13 +89,21 @@ void uncrunch(crunch_state_t* state)
|
||||
}
|
||||
|
||||
uint8_t data = state->fifo >> (state->fifolen - 8);
|
||||
if (data & 0x80)
|
||||
{
|
||||
data = ((data >> 6) & 0x01) | 0x80;
|
||||
state->fifolen -= 2;
|
||||
}
|
||||
else
|
||||
state->fifolen -= 8;
|
||||
switch (data & 0xc0)
|
||||
{
|
||||
case 0x80:
|
||||
data = ((data >> 5) & 0x01) | 0x80;
|
||||
state->fifolen -= 3;
|
||||
break;
|
||||
|
||||
case 0xc0:
|
||||
state->haspending = true;
|
||||
state->pendingbyte = data & 0x3f;
|
||||
data = 0x80;
|
||||
/* fall through */
|
||||
default:
|
||||
state->fifolen -= 8;
|
||||
}
|
||||
|
||||
if (data)
|
||||
{
|
||||
|
||||
@@ -11,7 +11,8 @@ extern "C" {
|
||||
* sending it over USB. The encoding used is:
|
||||
*
|
||||
* 0nnn.nnnn: value 0x00..0x7f
|
||||
* 1n : value 0x80|n
|
||||
* 11nn.nnnn: value 0x80 then 0x00..0x3f
|
||||
* 10n : value 0x80|n
|
||||
*
|
||||
* The end of the buffer is terminated with zeroes, which are ignored
|
||||
* (not written to the output).
|
||||
@@ -25,8 +26,10 @@ typedef struct crunch_state_t
|
||||
uint32_t inputlen;
|
||||
uint8_t* outputptr;
|
||||
uint32_t outputlen;
|
||||
uint16_t fifo;
|
||||
uint32_t fifo;
|
||||
uint8_t fifolen;
|
||||
bool haspending;
|
||||
uint8_t pendingbyte;
|
||||
}
|
||||
crunch_state_t;
|
||||
|
||||
|
||||
158
lib/dataspec.cc
158
lib/dataspec.cc
@@ -5,39 +5,41 @@
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
MissingModifierException::MissingModifierException(const std::string& mod):
|
||||
mod(mod),
|
||||
std::runtime_error(fmt::format("missing mandatory modifier '{}'", mod))
|
||||
{}
|
||||
|
||||
std::vector<std::string> DataSpec::split(
|
||||
const std::string& s, const std::string& delimiter)
|
||||
{
|
||||
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;
|
||||
@@ -59,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;
|
||||
}
|
||||
|
||||
@@ -69,36 +86,113 @@ 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)
|
||||
{
|
||||
locations.clear();
|
||||
|
||||
for (size_t i = 1; i < words.size(); i++)
|
||||
{
|
||||
auto mod = parseMod(words[i]);
|
||||
if ((mod.name != "t") && (mod.name != "s") && (mod.name != "d"))
|
||||
Error() << fmt::format("unknown data modifier '{}'", mod.name);
|
||||
modifiers[mod.name] = mod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto& drives = modifiers["d"].data;
|
||||
const DataSpec::Modifier& DataSpec::at(const std::string& mod) const
|
||||
{
|
||||
try
|
||||
{
|
||||
return modifiers.at(mod);
|
||||
}
|
||||
catch (const std::out_of_range& e)
|
||||
{
|
||||
throw MissingModifierException(mod);
|
||||
}
|
||||
}
|
||||
|
||||
bool DataSpec::has(const std::string& mod) const
|
||||
{
|
||||
return modifiers.find(mod) != modifiers.end();
|
||||
}
|
||||
|
||||
FluxSpec::FluxSpec(const DataSpec& spec)
|
||||
{
|
||||
try
|
||||
{
|
||||
filename = spec.filename;
|
||||
|
||||
locations.clear();
|
||||
|
||||
const auto& drives = spec.at("d").data;
|
||||
if (drives.size() != 1)
|
||||
Error() << "you must specify exactly one drive";
|
||||
drive = *drives.begin();
|
||||
|
||||
const auto& tracks = modifiers["t"].data;
|
||||
const auto& sides = modifiers["s"].data;
|
||||
const auto& tracks = spec.at("t").data;
|
||||
const auto& sides = spec.at("s").data;
|
||||
for (auto track : tracks)
|
||||
{
|
||||
for (auto side : sides)
|
||||
locations.push_back({ drive, track, side });
|
||||
}
|
||||
|
||||
for (const auto& e : spec.modifiers)
|
||||
{
|
||||
const auto name = e.second.name;
|
||||
if ((name != "t") && (name != "s") && (name != "d"))
|
||||
Error() << fmt::format("unknown fluxspec modifier '{}'", name);
|
||||
}
|
||||
}
|
||||
catch (const MissingModifierException& e)
|
||||
{
|
||||
Error() << e.what() << " in fluxspec '" << spec << "'";
|
||||
}
|
||||
}
|
||||
|
||||
ImageSpec::ImageSpec(const DataSpec& spec)
|
||||
{
|
||||
try
|
||||
{
|
||||
filename = spec.filename;
|
||||
|
||||
if (!spec.has("c") && !spec.has("h") && !spec.has("s") && !spec.has("b"))
|
||||
{
|
||||
cylinders = heads = sectors = bytes = 0;
|
||||
initialised = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
cylinders = spec.at("c").only();
|
||||
heads = spec.at("h").only();
|
||||
sectors = spec.at("s").only();
|
||||
bytes = spec.at("b").only();
|
||||
initialised = true;
|
||||
}
|
||||
}
|
||||
catch (const MissingModifierException& e)
|
||||
{
|
||||
Error() << e.what() << " in imagespec '" << spec << "'";
|
||||
}
|
||||
|
||||
for (const auto& e : spec.modifiers)
|
||||
{
|
||||
const auto name = e.second.name;
|
||||
if ((name != "c") && (name != "h") && (name != "s") && (name != "b"))
|
||||
Error() << fmt::format("unknown fluxspec modifier '{}'", name);
|
||||
}
|
||||
}
|
||||
|
||||
ImageSpec::ImageSpec(const std::string filename,
|
||||
unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes):
|
||||
filename(filename),
|
||||
cylinders(cylinders),
|
||||
heads(heads),
|
||||
sectors(sectors),
|
||||
bytes(bytes),
|
||||
initialised(true)
|
||||
{}
|
||||
|
||||
DataSpec::operator std::string(void) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
124
lib/dataspec.h
124
lib/dataspec.h
@@ -1,8 +1,59 @@
|
||||
#ifndef DATASPEC_H
|
||||
#define DATASPEC_H
|
||||
|
||||
class MissingModifierException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
MissingModifierException(const std::string& mod);
|
||||
const std::string mod;
|
||||
};
|
||||
|
||||
class DataSpec
|
||||
{
|
||||
public:
|
||||
struct Modifier
|
||||
{
|
||||
std::string name;
|
||||
std::set<unsigned> data;
|
||||
std::string source;
|
||||
|
||||
bool operator == (const Modifier& other) const
|
||||
{ return (name == other.name) && (data == other.data); }
|
||||
|
||||
bool operator != (const Modifier& other) const
|
||||
{ return (name != other.name) || (data != other.data); }
|
||||
|
||||
unsigned only() const
|
||||
{
|
||||
if (data.size() != 1)
|
||||
Error() << "modifier " << name << " can only have one value";
|
||||
return *(data.begin());
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
static std::vector<std::string> split(
|
||||
const std::string& s, const std::string& delimiter);
|
||||
static std::set<unsigned> parseRange(const std::string& spec);
|
||||
|
||||
static Modifier parseMod(const std::string& spec);
|
||||
|
||||
public:
|
||||
DataSpec(const std::string& spec)
|
||||
{ set(spec); }
|
||||
|
||||
void set(const std::string& spec);
|
||||
operator std::string () const;
|
||||
|
||||
const Modifier& at(const std::string& mod) const;
|
||||
bool has(const std::string& mod) const;
|
||||
|
||||
std::string filename;
|
||||
std::map<std::string, Modifier> modifiers;
|
||||
};
|
||||
|
||||
class FluxSpec
|
||||
{
|
||||
public:
|
||||
struct Location
|
||||
{
|
||||
@@ -17,36 +68,29 @@ public:
|
||||
{ return (drive != other.drive) || (track != other.track) || (side != other.side); }
|
||||
};
|
||||
|
||||
struct Modifier
|
||||
{
|
||||
std::string name;
|
||||
std::set<unsigned> data;
|
||||
std::string source;
|
||||
|
||||
bool operator == (const Modifier& other) const
|
||||
{ return (name == other.name) && (data == other.data); }
|
||||
|
||||
bool operator != (const Modifier& other) const
|
||||
{ return (name != other.name) || (data != other.data); }
|
||||
};
|
||||
public:
|
||||
FluxSpec(const DataSpec& dataspec);
|
||||
|
||||
public:
|
||||
static std::vector<std::string> split(
|
||||
const std::string& s, const std::string& delimiter);
|
||||
static Modifier parseMod(const std::string& spec);
|
||||
|
||||
public:
|
||||
DataSpec(const std::string& spec)
|
||||
{ set(spec); }
|
||||
|
||||
void set(const std::string& spec);
|
||||
operator std::string () const;
|
||||
|
||||
std::string filename;
|
||||
std::map<std::string, Modifier> modifiers;
|
||||
std::vector<Location> locations;
|
||||
unsigned drive;
|
||||
unsigned revolutions;
|
||||
};
|
||||
|
||||
class ImageSpec
|
||||
{
|
||||
public:
|
||||
ImageSpec(const DataSpec& dataspec);
|
||||
ImageSpec(const std::string filename,
|
||||
unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes);
|
||||
|
||||
public:
|
||||
std::string filename;
|
||||
unsigned cylinders;
|
||||
unsigned heads;
|
||||
unsigned sectors;
|
||||
unsigned bytes;
|
||||
bool initialised : 1;
|
||||
};
|
||||
|
||||
static inline std::ostream& operator << (std::ostream& os, const DataSpec& dataSpec)
|
||||
@@ -75,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();
|
||||
@@ -41,21 +41,32 @@ void AbstractDecoder::decodeToSectors(Track& track)
|
||||
|
||||
/* Read the sector record. */
|
||||
|
||||
recordStart = fmr.tell();
|
||||
sector.position = recordStart = fmr.tell();
|
||||
decodeSectorRecord();
|
||||
pushRecord(recordStart, fmr.tell());
|
||||
Fluxmap::Position recordEnd = fmr.tell();
|
||||
pushRecord(recordStart, recordEnd);
|
||||
if (sector.status == Sector::DATA_MISSING)
|
||||
{
|
||||
/* The data is in a separate record. */
|
||||
|
||||
r = advanceToNextRecord();
|
||||
sector.headerStartTime = recordStart.ns();
|
||||
sector.headerEndTime = recordEnd.ns();
|
||||
for (;;)
|
||||
{
|
||||
r = advanceToNextRecord();
|
||||
if (r != UNKNOWN_RECORD)
|
||||
break;
|
||||
if (fmr.readNextMatchingOpcode(F_OP_PULSE) == 0)
|
||||
break;
|
||||
}
|
||||
recordStart = fmr.tell();
|
||||
if (r == DATA_RECORD)
|
||||
{
|
||||
recordStart = fmr.tell();
|
||||
decodeDataRecord();
|
||||
pushRecord(recordStart, fmr.tell());
|
||||
}
|
||||
recordEnd = fmr.tell();
|
||||
pushRecord(recordStart, recordEnd);
|
||||
}
|
||||
sector.dataStartTime = recordStart.ns();
|
||||
sector.dataEndTime = recordEnd.ns();
|
||||
|
||||
if (sector.status != Sector::MISSING)
|
||||
track.sectors.push_back(sector);
|
||||
@@ -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,13 +18,18 @@ DoubleFlag pulseDebounceThreshold(
|
||||
static DoubleFlag clockDecodeThreshold(
|
||||
{ "--bit-error-threshold" },
|
||||
"Amount of error to tolerate in pulse timing, in fractions of a clock.",
|
||||
0.20);
|
||||
0.40);
|
||||
|
||||
static DoubleFlag clockIntervalBias(
|
||||
{ "--clock-interval-bias" },
|
||||
"Adjust intervals between pulses by this many clocks before decoding.",
|
||||
-0.02);
|
||||
|
||||
static DoubleFlag minimumClockUs(
|
||||
{ "--minimum-clock-us" },
|
||||
"Refuse to detect clocks shorter than this, to avoid false positives.",
|
||||
0.75);
|
||||
|
||||
int FluxmapReader::readOpcode(unsigned& ticks)
|
||||
{
|
||||
ticks = 0;
|
||||
@@ -222,7 +227,9 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu
|
||||
seek(positions[intervalCount-match.intervals]);
|
||||
_pos.zeroes = match.zeroes;
|
||||
matching = match.matcher;
|
||||
return match.clock * NS_PER_TICK;
|
||||
nanoseconds_t detectedClock = match.clock * NS_PER_TICK;
|
||||
if (detectedClock > (minimumClockUs*1000))
|
||||
return match.clock * NS_PER_TICK;
|
||||
}
|
||||
|
||||
for (unsigned i=0; i<intervalCount; i++)
|
||||
|
||||
@@ -51,3 +51,48 @@ Bytes decodeFmMfm(
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void encodeFm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input)
|
||||
{
|
||||
if (bits.size() == 0)
|
||||
return;
|
||||
unsigned len = bits.size()-1;
|
||||
|
||||
for (uint8_t b : input)
|
||||
{
|
||||
for (int i=0; i<8; i++)
|
||||
{
|
||||
bool bit = b & 0x80;
|
||||
b <<= 1;
|
||||
|
||||
if (cursor >= len)
|
||||
return;
|
||||
|
||||
bits[cursor++] = true;
|
||||
bits[cursor++] = bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input, bool& lastBit)
|
||||
{
|
||||
if (bits.size() == 0)
|
||||
return;
|
||||
unsigned len = bits.size()-1;
|
||||
|
||||
for (uint8_t b : input)
|
||||
{
|
||||
for (int i=0; i<8; i++)
|
||||
{
|
||||
bool bit = b & 0x80;
|
||||
b <<= 1;
|
||||
|
||||
if (cursor >= len)
|
||||
return;
|
||||
|
||||
bits[cursor++] = !lastBit && !bit;
|
||||
bits[cursor++] = bit;
|
||||
lastBit = bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
18
lib/encoders/encoders.h
Normal file
18
lib/encoders/encoders.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef ENCODERS_H
|
||||
#define ENCODERS_H
|
||||
|
||||
class Fluxmap;
|
||||
class SectorSet;
|
||||
|
||||
class AbstractEncoder
|
||||
{
|
||||
public:
|
||||
virtual ~AbstractEncoder() {}
|
||||
|
||||
public:
|
||||
virtual std::unique_ptr<Fluxmap> encode(
|
||||
int physicalTrack, int physicalSide, const SectorSet& allSectors) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
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,
|
||||
|
||||
@@ -21,6 +21,7 @@ public:
|
||||
|
||||
public:
|
||||
nanoseconds_t duration() const { return _duration; }
|
||||
unsigned ticks() const { return _ticks; }
|
||||
size_t bytes() const { return _bytes.size(); }
|
||||
const Bytes& rawBytes() const { return _bytes; }
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ static bool ends_with(const std::string& value, const std::string& ending)
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::create(const DataSpec& spec)
|
||||
std::unique_ptr<FluxSink> FluxSink::create(const FluxSpec& spec)
|
||||
{
|
||||
const auto& filename = spec.filename;
|
||||
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
#ifndef FLUXSINK_H
|
||||
#define FLUXSINK_H
|
||||
|
||||
#include "flags.h"
|
||||
|
||||
extern FlagGroup hardwareFluxSinkFlags;
|
||||
extern FlagGroup sqliteFluxSinkFlags;
|
||||
|
||||
class Fluxmap;
|
||||
class DataSpec;
|
||||
class FluxSpec;
|
||||
|
||||
class FluxSink
|
||||
{
|
||||
public:
|
||||
virtual ~FluxSink() {}
|
||||
|
||||
private:
|
||||
static std::unique_ptr<FluxSink> createSqliteFluxSink(const std::string& filename);
|
||||
static std::unique_ptr<FluxSink> createHardwareFluxSink(unsigned drive);
|
||||
|
||||
public:
|
||||
static std::unique_ptr<FluxSink> create(const DataSpec& spec);
|
||||
static std::unique_ptr<FluxSink> create(const FluxSpec& spec);
|
||||
|
||||
public:
|
||||
virtual void writeFlux(int track, int side, Fluxmap& fluxmap) = 0;
|
||||
|
||||
@@ -4,8 +4,15 @@
|
||||
#include "usb.h"
|
||||
#include "fluxsink/fluxsink.h"
|
||||
|
||||
FlagGroup hardwareFluxSinkFlags;
|
||||
|
||||
static bool high_density = false;
|
||||
|
||||
static IntFlag indexMode(
|
||||
{ "--write-index-mode" },
|
||||
"index pulse source (0=drive, 1=300 RPM fake source, 2=360 RPM fake source",
|
||||
0);
|
||||
|
||||
void setHardwareFluxSinkDensity(bool high_density)
|
||||
{
|
||||
::high_density = high_density;
|
||||
@@ -26,7 +33,7 @@ public:
|
||||
public:
|
||||
void writeFlux(int track, int side, Fluxmap& fluxmap)
|
||||
{
|
||||
usbSetDrive(_drive, high_density);
|
||||
usbSetDrive(_drive, high_density, indexMode);
|
||||
usbSeek(track);
|
||||
|
||||
Bytes crunched = fluxmap.rawBytes().crunch();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user