mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Compare commits
264 Commits
FluxEngine
...
FluxEngine
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
035dd1fad1 | ||
|
|
d2df79a665 | ||
|
|
103e0a13bb | ||
|
|
d1b5eec84a | ||
|
|
6b1e6b31ed | ||
|
|
f6f6db913e | ||
|
|
ec0a6416fd | ||
|
|
1787402be9 | ||
|
|
5f6d99f138 | ||
|
|
d1e2b0d1f8 | ||
|
|
c2c51bbe33 | ||
|
|
bb806e3853 | ||
|
|
a11d0e75c8 | ||
|
|
5406ff0ea3 | ||
|
|
c88317b44a | ||
|
|
6898062d66 | ||
|
|
6e1f264e6a | ||
|
|
082be14232 | ||
|
|
231aa44d03 | ||
|
|
cdb12f35d4 | ||
|
|
e831ee8b44 | ||
|
|
40e9a6082f | ||
|
|
53cec292d0 | ||
|
|
3f85309ee5 | ||
|
|
70944f8521 | ||
|
|
2ab00c42ff | ||
|
|
a572742caa | ||
|
|
400e5f8580 | ||
|
|
74f0fd89b6 | ||
|
|
09f9bea7a2 | ||
|
|
8bffb38117 | ||
|
|
eb5d545c35 | ||
|
|
a79a545730 | ||
|
|
3863dab944 | ||
|
|
e53b7ecd8b | ||
|
|
7d88673ed5 | ||
|
|
41f2da71e4 | ||
|
|
cb4ee0fd74 | ||
|
|
088381a5a6 | ||
|
|
629af2a697 | ||
|
|
884edfd497 | ||
|
|
83dd9e462e | ||
|
|
70a6dfd98a | ||
|
|
7f5d96382b | ||
|
|
fd4d1c4bb7 | ||
|
|
7eaf3de572 | ||
|
|
4b608de3fb | ||
|
|
b47e6e852b | ||
|
|
a8a8ce4a36 | ||
|
|
c61376d5a1 | ||
|
|
d3a5bb08d3 | ||
|
|
f1506d0dbd | ||
|
|
15e6d4959e | ||
|
|
41216fd1cd | ||
|
|
b8786866db | ||
|
|
82bd1bead4 | ||
|
|
6e2bdcad79 | ||
|
|
ef3c9f3d03 | ||
|
|
5427f24df2 | ||
|
|
b374340303 | ||
|
|
c78ed2c6ad | ||
|
|
3b02bc8cf1 | ||
|
|
c7e48a7e76 | ||
|
|
77d125c03d | ||
|
|
8aa52aeefd | ||
|
|
0bab038454 | ||
|
|
6c3b49f4d0 | ||
|
|
03dd689f17 | ||
|
|
c375c948c0 | ||
|
|
cbcf457ce3 | ||
|
|
4855f825e2 | ||
|
|
85bc1637f2 | ||
|
|
73398b83a9 | ||
|
|
2727e66d40 | ||
|
|
8b6be5a501 | ||
|
|
4fee29307c | ||
|
|
35f8249c67 | ||
|
|
d1467a14b8 | ||
|
|
3e6b9eb74d | ||
|
|
ce2e8fb4b5 | ||
|
|
7eaa75c05d | ||
|
|
e86de4483a | ||
|
|
203a74713f | ||
|
|
59ed2a6793 | ||
|
|
a03283ce64 | ||
|
|
984cdaeb03 | ||
|
|
a1ed4a9171 | ||
|
|
93caf8e549 | ||
|
|
3841942153 | ||
|
|
5706877b67 | ||
|
|
d60900262b | ||
|
|
54ea34400b | ||
|
|
db2ab8841a | ||
|
|
adba93ae0a | ||
|
|
98587d04a7 | ||
|
|
0051b64648 | ||
|
|
603009ba15 | ||
|
|
adb9809692 | ||
|
|
06eb10d2a0 | ||
|
|
2244299bd9 | ||
|
|
6ca06ecafb | ||
|
|
9a5958f80b | ||
|
|
2b53ac057c | ||
|
|
5deba8af41 | ||
|
|
3c54a663b8 | ||
|
|
1fd65452c4 | ||
|
|
30646ccb07 | ||
|
|
5be7249a30 | ||
|
|
067af18103 | ||
|
|
8dbd2a72a7 | ||
|
|
c29e131a3b | ||
|
|
a9e30c1e49 | ||
|
|
972c8c6b61 | ||
|
|
2007ff7546 | ||
|
|
64694580cd | ||
|
|
deaab94494 | ||
|
|
1509e1f89d | ||
|
|
29e1ddc2ff | ||
|
|
1fe6434563 | ||
|
|
0367b7e77d | ||
|
|
e6da85bf64 | ||
|
|
cd19fcdadd | ||
|
|
1954f02cfb | ||
|
|
39b23200b0 | ||
|
|
0644d6d965 | ||
|
|
a075694d8e | ||
|
|
b1ea5a9a35 | ||
|
|
00087cbb6b | ||
|
|
1b48ea20c4 | ||
|
|
3d0f019fc4 | ||
|
|
a08bfc183f | ||
|
|
c5aef9b051 | ||
|
|
fc2655ecd6 | ||
|
|
a737c723d3 | ||
|
|
37aa8b62b0 | ||
|
|
a401173f6d | ||
|
|
ce76dc4279 | ||
|
|
1025bd857b | ||
|
|
025802b2d0 | ||
|
|
adbcb2cd31 | ||
|
|
c47a563790 | ||
|
|
04c09d1a5b | ||
|
|
323da8272a | ||
|
|
38700c79fc | ||
|
|
d504d1890a | ||
|
|
d53e757cfb | ||
|
|
4983239458 | ||
|
|
376985828a | ||
|
|
dce0a26820 | ||
|
|
14e0a67e7d | ||
|
|
1656947764 | ||
|
|
647862cdbd | ||
|
|
4a8d83838c | ||
|
|
8acf8e181d | ||
|
|
2df9920209 | ||
|
|
1a6c6b5420 | ||
|
|
edc56d44d6 | ||
|
|
ef4eff0195 | ||
|
|
df8d45bf66 | ||
|
|
89a27619ff | ||
|
|
387a86969a | ||
|
|
acb5059d17 | ||
|
|
a4002d2617 | ||
|
|
a63a90bbd0 | ||
|
|
d25f96dd24 | ||
|
|
e8febe6508 | ||
|
|
ad3a930c6a | ||
|
|
be41c1de76 | ||
|
|
d528978667 | ||
|
|
827fcf69d2 | ||
|
|
711ff545e0 | ||
|
|
5befa31050 | ||
|
|
8e5c2d0ebb | ||
|
|
f95fceeb3d | ||
|
|
003b20dbf0 | ||
|
|
cd9bbaa4b6 | ||
|
|
71e622bf72 | ||
|
|
2a065a08df | ||
|
|
6087228378 | ||
|
|
efd74e0d7b | ||
|
|
b68a9dcc4f | ||
|
|
008855daa9 | ||
|
|
7a9d36de2a | ||
|
|
c56e982c9a | ||
|
|
002cc171a2 | ||
|
|
32e721b47a | ||
|
|
1e82f697a9 | ||
|
|
fa09631e32 | ||
|
|
e06436ce1e | ||
|
|
b2f443e1ad | ||
|
|
2e07be0cf7 | ||
|
|
bf0b14d094 | ||
|
|
c9f5803194 | ||
|
|
5293560c02 | ||
|
|
c49823aa9d | ||
|
|
c4ef4882ae | ||
|
|
a8eca06cf0 | ||
|
|
065257b5aa | ||
|
|
29bdfc043a | ||
|
|
933ffe7ab4 | ||
|
|
e517f28563 | ||
|
|
91ffcf59c3 | ||
|
|
51c618f325 | ||
|
|
9dc1067032 | ||
|
|
9e75dc3af1 | ||
|
|
efa4c933b3 | ||
|
|
6af80d1e5e | ||
|
|
0c48897814 | ||
|
|
60e5e35947 | ||
|
|
86c4e959ca | ||
|
|
b0c675c589 | ||
|
|
d77841c3b7 | ||
|
|
4ed1fb6bac | ||
|
|
bcc9e9d9a5 | ||
|
|
ec327e25a4 | ||
|
|
d0ed5b32f7 | ||
|
|
7c66e1b0d4 | ||
|
|
4475e9f085 | ||
|
|
5c9639ec5a | ||
|
|
792cc88192 | ||
|
|
21fe586724 | ||
|
|
5a0fb2761a | ||
|
|
ef4581ed39 | ||
|
|
73419704c2 | ||
|
|
a8b92d4780 | ||
|
|
98140b0646 | ||
|
|
4429ce1f84 | ||
|
|
1f50941a2c | ||
|
|
a7de04848c | ||
|
|
c264fec6e9 | ||
|
|
4488b2542f | ||
|
|
2f1a5189d6 | ||
|
|
effaeff51e | ||
|
|
1210549f59 | ||
|
|
7200de9702 | ||
|
|
5dd5c8516a | ||
|
|
f7fb2a844b | ||
|
|
20b1b2a4a8 | ||
|
|
f8b8bc2295 | ||
|
|
2d4d56d09f | ||
|
|
39599b76c8 | ||
|
|
c2c40ccfbb | ||
|
|
ab42eb23f4 | ||
|
|
05eff0e528 | ||
|
|
23311b4b68 | ||
|
|
5e97df8d15 | ||
|
|
898e8c551c | ||
|
|
ad69c6bd27 | ||
|
|
661399cc83 | ||
|
|
edbb4b1daa | ||
|
|
6389e8a756 | ||
|
|
c187b79d80 | ||
|
|
edbe624c5a | ||
|
|
44e2334815 | ||
|
|
b448ab7917 | ||
|
|
072a097003 | ||
|
|
a66e704bab | ||
|
|
ed0d578b18 | ||
|
|
32bb956710 | ||
|
|
f436d6b582 | ||
|
|
d2f8c27cb6 | ||
|
|
eaa3c57425 | ||
|
|
549f12a2ab | ||
|
|
aea254fbe7 |
@@ -1,6 +1,7 @@
|
||||
version: '{branch}.{build}'
|
||||
clone_depth: 1
|
||||
skip_tags: true
|
||||
image: Visual Studio 2019
|
||||
|
||||
environment:
|
||||
MSYSTEM: MINGW32
|
||||
@@ -15,7 +16,7 @@ install:
|
||||
|
||||
build_script:
|
||||
- make
|
||||
- zip -9 fluxengine.zip fluxengine.exe brother120tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
|
||||
- zip -9 fluxengine.zip fluxengine.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
|
||||
|
||||
artifacts:
|
||||
- path: fluxengine.zip
|
||||
|
||||
41
.github/workflows/ccpp.yml
vendored
Normal file
41
.github/workflows/ccpp.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: C/C++ CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: apt
|
||||
run: sudo apt update && sudo apt install libusb-1.0-0-dev libsqlite3-dev ninja-build
|
||||
- name: make
|
||||
run: make
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: brew
|
||||
run: brew install sqlite pkg-config libusb ninja
|
||||
- name: make
|
||||
run: make
|
||||
|
||||
# build-windows:
|
||||
# runs-on: windows-latest
|
||||
# steps:
|
||||
# - uses: numworks/setup-msys2@v1
|
||||
# with:
|
||||
# path-type: inherit
|
||||
# - uses: actions/checkout@v1
|
||||
# - name: pacman
|
||||
# run: |
|
||||
# msys2do pacman -S --noconfirm --needed make ninja mingw-w64-i686-libusb mingw-w64-i686-sqlite3 mingw-w64-i686-zlib mingw-w64-i686-gcc zip
|
||||
# - name: build
|
||||
# run: |
|
||||
# msys2do make
|
||||
|
||||
39
.travis.yml
39
.travis.yml
@@ -1,39 +0,0 @@
|
||||
language: shell
|
||||
git:
|
||||
depth: 1
|
||||
|
||||
matrix:
|
||||
include:
|
||||
-
|
||||
os: linux
|
||||
sudo: false
|
||||
dist: xenial
|
||||
compiler: gcc
|
||||
env: CXX=g++-8
|
||||
script:
|
||||
- make
|
||||
-
|
||||
os: osx
|
||||
osx_image: xcode10.2
|
||||
compiler: clang
|
||||
env:
|
||||
- HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- llvm-toolchain-precise-3.8
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- ninja-build
|
||||
- libusb-1.0-0-dev
|
||||
- libsqlite3-dev
|
||||
- g++-8
|
||||
homebrew:
|
||||
packages:
|
||||
- ninja
|
||||
|
||||
script:
|
||||
- make
|
||||
|
||||
|
||||
@@ -1,246 +1,246 @@
|
||||
:400000000080002011000000910D0000910D0000064A08B5136843F020031360044B1A6803F53F5302331A6000F0D0FEE8460040FA46004010B5054C237833B9044B13B11C
|
||||
:400040000448AFF300800123237010BD6081FF1F0000000020310000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF00000000F8
|
||||
:400080006481FF1F20310000C880FF1F0000000072B6034A13680133136062B6704700BF8081FF1F0A4A0B4B516801310B40002BBEBF03F1FF3363F00F030133536051688F
|
||||
:4000C0009368994202BF024B01221A73704700BF8081FF1F0F0000800A4A0B4B916801310B40002BBEBF03F1FF3363F00F030133936091685368994202BF024B01221A7381
|
||||
:40010000704700BF8081FF1F0F000080024B012200205A7302F030B88081FF1F10B5C4B2204601F015F80128FAD110BD70B5C4B220460E4601F014F8314605460246204623
|
||||
:4001400001F0D0F8204601F003F80128FAD0284670BD000038B5094CA57B3DB9012001F071FF4FF47A7002F0BFF9E573236823610123A373BDE8384002F0F4B98081FF1FB9
|
||||
:4001800038B50446C5B2284601F068FF012002F0C1F944F00200C0B201F060FF012002F0B9F9284601F05AFFBDE83840062002F09BB9000038B5044D0024285D013402F0F2
|
||||
:4001C00059F9102CF9D138BD9481FF1F0FB400B593B014AB53F8042B402102A8019302F087FB02A801F0DBFF01F0E5FF13B05DF804EB04B07047000010B5044601780648E6
|
||||
:40020000FFF7E4FF0420FFF789FF62782146BDE81040042000F0F4BF3231000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB0000F8B5234C1D
|
||||
:400240000646FFF787FFE37B93B92148FFF7BEFF01F0FEFE18B90120FFF792FFF8E70120FFF78EFF01200023E073636202F052F93246616A1748FFF7A9FF144D0027636AD9
|
||||
:400280009E421BD001F0E4FE28B16B6A13B11248FFF79CFF6762636A9E4205DD0020FFF76FFF6B6A013305E005DA0120FFF768FF6B6A013B6B6202F055F9E0E7322002F0F3
|
||||
:4002C00013F9BDE8F8400548FFF780BF8081FF1F3F3100004631000063310000813100002DE9F04F9BB062B602F0A6F9BB49042002F0CAF9BA4801F0B7FEBA4801F0EAFE9E
|
||||
:40030000B94801F01BFF002001F03CFF01F046FF0221002000F0D2FEB44D0321084602F001F92E462C4602F01DF9AB7B5BB12A692B689B1A41F28832934204D9002001F022
|
||||
:4003400081FE0023AB7300F0F1FE18B9A848FFF73DFF04E000F0F0FE0028F7D109E000F0E5FE0028FBD0A348FFF730FF032001F00DF8032000F0ECFE0128D4D19E4903208C
|
||||
:40038000FFF7D4FE94F828109C48FFF71FFF94F82830023B102B00F25F83DFE813F011005D031A005D0320005D0338005D035C005D0399015D0324035D0343035D034F0387
|
||||
:4003C00003238DF828308DF8293008238DF82A3030E394F82A00FFF731FF894B27E3FFF7B9FE00236373637B002BFCD0002373733268637B002BFCD0336807218DF82810D2
|
||||
:400400009B1A04218DF82910ADF82A3012E30220FFF784FE4FF000090DF1280A4FF480780027C8EB0903DA1907F80A200137402FF9D10220FFF772FE3A465146022000F0BF
|
||||
:40044000DFFEB8F10108EBD109F10109B9F1400FE4D16C4BC2E294F82A0001F019FE606AFFF7ECFE012001F093FE02F0ADF9664BDFF89C811A78002742F004021A701A78CF
|
||||
:4004800042F001021A701A7802F0FE021A701A7802F0FE021A7002F09BF90220FFF73EFE41F6FF734FF480420121022001F0F6FD84F8680001F0C4FF08F807000137102F9B
|
||||
:4004C000F8D1DFF850A100270AF15D081FFA88F90137102F14BF3A4600221AF8010F22440623127D402101F0DFFF4A4646F24B419AF8000001F0EAFF09F14009102F1FFA93
|
||||
:4005000089F9E5D100237373637B002BFCD00027142239460AA87773DFF8F09002F0B4F940230D9338464FF0FF337760CDF83090B360377301F02CFE736896F868003344C9
|
||||
:40054000197D01F035FF96F8680001F0F3FE012196F8680001F0C6FE636813B96B7B002BFAD0002794F82BA0A76094F80CB0BBF1000F6AD101F0F6FF6B7B43B1BAF1010A70
|
||||
:4005800085F80DB003D160E02B7B002B5DD1A26863689A42F8D04FF0000BA3680AA808EB83135B440A93CBF140030B9300F072FA0B9B0137C3F140039B440D9B5FFA8BFB75
|
||||
:4005C0009BBB022000F0C4FD012825D0637B002B3BD12B7B002BF4D037E000BF910000000D010000A5000000D90000008081FF1F8F310000A2310000A881FF1FAC31000041
|
||||
:4006000024310000263100009B640040F085FF1F9481FF1F9381FF1F4022B949022000F0EFFD4023CDF830900D93BBF13F0FB4D9A268B44B01321340002BBEBF03F1FF3309
|
||||
:4006400063F00F030133A3608FE7042194F8680001F0BAFE94F8680001F0C6FE0028F9D10AA800F04BFA0220FFF758FDDDF83480B8F1400F06D0C8F1400292B2A049022016
|
||||
:4006800000F0BEFD0D9A32F0400203D11146022000F0B6FD0220FFF741FDFFF78BFD237B33B19948FFF792FD0220FFF7B7FD06E0964B09A81B88ADF82430FFF79DFD627B8B
|
||||
:4006C0003946237B9248CDF80080FFF77FFDC6E19048FFF77BFDE76A17F03F0701D00320BBE1012001F008FD95F82A0001F0D0FC02F06AF8884BDFF824821A7802F0FB0281
|
||||
:400700001A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F059F8686AFFF78CFD01214FF4804341F6FF72084601F0E8FC85F8680001F082FE08F80700DE
|
||||
:400740000137102FF8D1DFF8D891002709F15D031FFA83F807930137102F14BF3A46002219F8010F22440523127D402101F09CFE414646F2484299F8000001F0A7FE08F1CB
|
||||
:400780004008102F1FFA88F8E5D10027BB46B946BA46F36A4FF0FF389B09142239460AA877600593C6F80880377302F06DF8402301200D9300F0EAFDCDF818806268514BEA
|
||||
:4007C00001321340002BBCBF03F1FF3363F00F03A168B8BF01338B4200F0A480BAF1000F07D0237B002B40F0B0806B7B002B40F0AC800B9B002B34D1B9F1000F0BD07F227B
|
||||
:400800003F495A540133402BFAD10A910B9328E0BAF1000F06D1012000F09AFC01288046F6D107E0237B002B40F08F806B7B002BF1D08AE03249FFF779FC8146304B0B90FB
|
||||
:4008400040460A9300F0A2FDB9F13F0F07F1010706DD059BDB1BD3F1000949EB030900E0C1460B9BDBB16368079A0AA802EB83120D9BC3F1400313440C9300F04EF90D9B1B
|
||||
:400880006BB96A681F4B01321340002BBEBF03F1FF3363F00F030133636040230D93A36801333FD16B680F2B3CD14FF00008C5F8088001F055FC85F80C80AB6895F86800DD
|
||||
:4008C0002B44197D01F074FD95F8680001F032FD012195F8680001F005FD85F80D80637B002BFCD04FF00008012086F80D8001F043FC404601F000FCCDF8188015E000BF6C
|
||||
:40090000F085FF1F0F000080BB31000028310000D5310000EE310000986400409481FF1F9381FF1FBAF1000F05D0237B6BB96B7B5BB94FF0010AA368069A93423FF43EAF55
|
||||
:40094000AB680BF1010B069338E701F00FFC012001F0D2FB002001F00FFC042194F8680001F032FD94F8680001F03EFD0028F9D196F8680001F0CCFC737B327B029301235F
|
||||
:4009800003920193CDF800905B463A4605993748FFF71CFCB9F1000F16D1059BBB420ADD012000F0D5FB01288046F6D13049FFF7BDFB3F2803DC012000F0FCFC04E04046D2
|
||||
:4009C00000F0E4FC0137E8E7FFF7F4FB2948FFF7FDFB237B0BB102203FE0274B1B8809A8ADF8243027E094F82A0001F051FB606AFFF724FC2148FFF7E9FB00236373637B8E
|
||||
:400A0000002BFCD0012001F0B7FB00237373637B002BFCD0002001F0AFFB1948FFF7D6FB184B04E00020E073FFF708FC164B1B88ADF828300AA8FFF7DFFB10E094F82A005D
|
||||
:400A400094F8B034834205D085F8B00401F09AFB0023EB730D4BEAE70120FFF7DFFB032000F094FC0A48FFF7B1FB5CE4F8310000F085FF1F283200002A3100003732000069
|
||||
:400A8000453200002C3100002E310000303100005232000010B54268002A2ED0C368002B2BD00368048A591C01601B78013A13F0800F817C42601DBF03F0010242EA8403E3
|
||||
:400AC0000231083114BF43F0020343EA0423817414BF03820382837C072BDCD9028A083B42FA03F38268511C81601370C368013BC360837C083B8374CDE710BD07B5827CB9
|
||||
:400B000042B102AA002102F8011D026001224260FFF7C0FF03B05DF804FB30B543686BB3C2685AB3827C072A0CD8046890F91050611C01602178013B41EA052108320182EC
|
||||
:400B400043608274827C018AA2F108042141CBB2090608D5C3F3801363F07F03023A03F08103827402E08474002BD7D08268511C81601370C368013BC360CFE730BD00008E
|
||||
:400B8000F8B572B6644B61221A70A3F5F06301221A80192406229C7083F88022522203F5C0731A705D4B5E4A1B785E4EDBB2137040F618025C4B00251A8041F2512223F82F
|
||||
:400BC000022C33784FF4F07003F0010343EA450501F0F2FD013C05F0030521D0032DF0D1524B4FF480721A8007221A70504A002448211470917002221C705C7103F8032C9D
|
||||
:400C00004C4A4D4D1378062643F001031370424B01221A70494A137843F02003137000E0FEE706FB045300219A881868013401F02BFE072CF5D14248424D002450F8041F12
|
||||
:400C400004F1105303F14A0221F0FF064B33C9B20B4452005B0002329A4206D012F8027C12F801EC07F806E0F5E7A8420C44E5D1354A00231360936013619361334B344950
|
||||
:400C80001A68344B344C1A60344B1A68344B1A60344A137843F002031370137C43F002031374314B1A7842F040021A700A7842F010020A702D4A07CA03C42D49228008684A
|
||||
:400CC0002C4A28341060097911712B4A07CA03C422802A4AE83C07CA03C42280284A083407CA03C4274922800A78A4F6785442F008020A70244A01F5AA5112780C31D2B28D
|
||||
:400D000002F00700120908704A70204A442111700FCB07C42380F8BD004800400F010049A146004025420040224200400440004006400040A243004057320000E8460040A7
|
||||
:400D4000FCFFFF4790000048007600403C0C0048C243004020760040C0510040400C00482876004003500140A0430040040C0048100C0048CB510040180C0048240C004883
|
||||
:400D8000300C004822430040CF0100497658004008B501F04DFD03680C2B00D1FEE7FEE7084908B50B68084A1844821A802A01DC086005E001F03CFD0C2303604FF0FF33E2
|
||||
:400DC000184608BDCC80FF1F2087FF1F80B51148114B0025C0B1A3F1100192C922460439161BB74204D051F8046F42F8046BF7E7114653F8046C8C1AA64202D041F8045B31
|
||||
:400E0000F9E701381033E5E701F018FDFFF768FAFEE700BF0100000014340000124A134B10B51A60124A134C1368134843F4007313600023032B98BF54F823204FEA8301A4
|
||||
:400E400088BF0E4A0133302B4250F3D10C4B1A780C4B1A700C4B084A1A60FFF791FEBDE8104001F029BA00BF0004FA050CED00E014ED00E0000000000080FF1F910D0000FB
|
||||
:400E8000BC760040C080FF1F08ED00E0F8B501F09BFC4B4A01271378022643F001031370137C484C43F001031374474B02F5E3521F700B3203F8946C1378054603F07F037D
|
||||
:400EC0001370002001F026FB2378404A03F0F90323701378384603F0DF03137023783B43237001F017FB282001F014FB384B30461A7802F07F021A701A7802F0BF021A70E7
|
||||
:400F000023783343237001F005FB2378314A43F0040323700023137053702F4AFF2199540133092BFBD1284601F052FC0721172001F038FB2949172001F026FB07211820CB
|
||||
:400F400001F030FB2649182001F01EFB0721152001F028FB2349152001F016FB0721032001F020FB2049032001F00EFB0721042001F018FB1D49042001F006FB072105209A
|
||||
:400F800001F010FB1A49052001F0FEFA0721062001F008FB1749062001F0F6FA0021162001F000FB1449162001F0EEFA07210C2001F0F8FABDE8F84010490C2001F0E4BA44
|
||||
:400FC000A5430040944300409D60004012600040F8510040846000406186FF1F5B18000095160000591800008D170000B9170000E91700002118000061180000D51800006F
|
||||
:40100000214B224A10B5187000231370204A40201370204A0F2413701F4A13701F4A13701F4A13701F4A13701F4B4FF400021A604FF080721A604FF400121A6008221A6081
|
||||
:4010400010221A6020221A601860184804704FF480001860164B1A70933B19B91A7802F0FE0202E01A7842F001021A70114B03221A70802203F8202C012001F09DFB0E4B48
|
||||
:4010800004221A7010BD00BF7C86FF1F8286FF1F8086FF1F8186FF1F7D86FF1F6C86FF1F7F86FF1FF486FF1F00E100E09E6000409C600040286000401260004070B5074C4C
|
||||
:4010C000054623780E461BB9FFF7E0FE0123237031462846BDE87040FFF792BF3186FF1F0A4A002313700A4A13700A4A13700A4A13700A4A13700A4A13700A4A13700A4BA8
|
||||
:4011000003221A70802203F8202C70478286FF1F8086FF1F8186FF1F7D86FF1F6C86FF1F7F86FF1FF486FF1F28600040014B1878704700BF8186FF1F044B1A7802F0FF00F4
|
||||
:401140001AB118780022C0B21A7070478086FF1F024A0C2303FB0020407870478886FF1F431E072B0CD8074A064B00010344805C5B7800F00F0043EA0020023880B2704768
|
||||
:4011800000207047FC5F00401A4A38B50C2303FB00231B79090C13F0800F00F1FF35044619BF8AB24FF480438BB24FF48042032D18D8DFE805F002070C110021084601F0B4
|
||||
:4011C00057F80DE00021084601F036F808E00021084601F015F803E00021084600F0F4FF054B1855EDB2072D03D801F029F9034B185538BD8886FF1F5886FF1F6186FF1F40
|
||||
:40120000431E072B2DE9F0470446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB2C3F500739D424FF00C0303FB007388BFD588DB7884BFC5F50075ADB2254A43EA07
|
||||
:4012400015230601B354B244EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FB047200215170B9F1000F28D03DB31B4F385D01F04DF811232946FE2218F8040069
|
||||
:4012800001F012F906F5C04278321FFA89F118F8040001F01BF9124D18F80410385D01F087F80121385D01F01DF8735D43F002037355735D03F0FD037355BDE8F08703FB25
|
||||
:4012C00004746379DBB28AF80230BDE8F08700BF8886FF1FFC5F00406186FF1F5886FF1F706000402DE9F047044615468846002940D0431E072B3FD8FFF732FFA84203D21D
|
||||
:401300002046FFF72DFF05461D4E335DFF2B03D141462046FFF738FFDFF868A027011AF8040000F0F5FF1223FE222946305D01F0BBF807F5C0411FFA88F27831305D01F01A
|
||||
:40134000C5F8DFF84490315D1AF8040001F030F801211AF8040000F0C5FF17F8093043F0020307F8093017F8093003F0FD0307F8093002E00D4600E000252846BDE8F0876D
|
||||
:401380006186FF1F5886FF1F70600040431E072B0AD8064A0C2303FB002300225A705A79034BD2B200011A54704700BF8886FF1FFE5F0040431E072B9FBF024B000108225F
|
||||
:4013C0001A547047FE5F004030B51A4A1A491B4D0878138803449BB21380194A00231488D8B2A4B27CB1082B0CD050680078C0B2E85450680133013050601088013880B2E9
|
||||
:401400001080ECE718460B780E4C082B0E4A00D040B10E4D2B7883F080032B700F232370022301E0022323701370094B1870087030BD00BFF886FF1FF486FF1F0060004059
|
||||
:401440007086FF1F6D86FF1F8286FF1F7E86FF1FF586FF1F074B02221A70074B80221A70064B0F221A70064A00231370054A0120137070478286FF1F7E86FF1F6D86FF1FE3
|
||||
:40148000F486FF1FF586FF1F30B5164B16491B780A8803F00F03023BDBB21A4492B20A80124C134A0020118889B279B173B15568215C013BC9B229705168DBB20131516086
|
||||
:4014C00011880130013989B21180ECE7094A1370094A137883F080031370084B0B221A7030BD00BF29600040F886FF1F006000407086FF1FF586FF1F7E86FF1F6D86FF1F81
|
||||
:40150000064A06231370064A01201370054B80221A70054B00221A70704700BF8286FF1F6D86FF1F7E86FF1FF586FF1F054B9A683AB19A68044910709A6809885180002249
|
||||
:401540009A6070477086FF1FF886FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A137008BD0220FFF7E1FF0D4B1B7803F06003202B05D0402B06D043B900F012FC04
|
||||
:4015800004E001F029F901E000F046FD10B9034B03221A7008BD00BF286000406D86FF1F0060004008B5084A084B0120197813880B449BB21380064B00221A70FFF7B6FFD7
|
||||
:4015C000044B03221A7008BDF886FF1FF486FF1F8286FF1F6D86FF1F08B50C4B1B78DBB2042B07D0062B09D0022B0DD1BDE80840FFF7D8BFBDE80840FFF746BF0320FFF7F3
|
||||
:4016000095FF034B03221A7008BD00BF8286FF1F6D86FF1F08B5054B002201201A70FFF785FF034B03221A7008BD00BF8286FF1F6D86FF1F08B50A4B1A7832B11A780949E7
|
||||
:4016400042F080020A7000221A70074B002201201A70FFF76BFF054B03221A7008BD00BF6C86FF1F086000408286FF1F6D86FF1F074B1B78DBB2042B05D0062B05D0022BF6
|
||||
:4016800005D1FFF7A1BEFFF7C5BFFFF7D3BF70478286FF1F38B51D4C2378DBB2DD0634D518060AD503F00F03012B2ED1FFF74EFF174B1B78190609D538BD5A0602D5FFF732
|
||||
:4016C000D7FF03E09D0620D5FFF786FF23781B061BD4104B1A78104B1B7813430F4A13701278934211D10A4A0849154613782078DBB2000605D41378DBB20B700B7803F08D
|
||||
:401700000F0328788342F1D138BD38BD286000406D86FF1F7E86FF1FF586FF1F29600040054A00231380054A916819B191680B7092685380704700BFF886FF1F7086FF1FAB
|
||||
:401740000E4808B503889BB213B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FF094AD1799379028843EA012392B2934238BF0380FFF728FE012008BD7086FF1F40
|
||||
:401780008286FF1F7E86FF1F00600040084B01221A700F3B9B7C074B1A7B02F00302012A1EBFDA7B82F08002DA7301225A7370470B6000408886FF1F094B02221A700F3B9A
|
||||
:4017C00093F82230074B1A7E02F00302012A1EBFDA7E82F08002DA7601225A76704700BF0B6000408886FF1F0B4B04221A700F3B93F83230094B93F8242002F00302012AC5
|
||||
:401800001EBF93F8272082F0800283F82720012283F82520704700BF0B6000408886FF1F0B4B08221A700F3B93F84230094B93F8302002F00302012A1EBF93F8332082F044
|
||||
:40184000800283F83320012283F83120704700BF0B6000408886FF1F7047FFF741BC0000F0B5184B184E19780C27C9B201234FF0000C31B3CA0720D5144A4FEA031E7244FE
|
||||
:40188000947850782040C5070DD507FB03652C79240608D5147804F0FE0414706D790C4CEDB204F80E50840706D507FB036425792D0658BF84F801C090700133DBB24908ED
|
||||
:4018C000D7E7F0BD9F6000408886FF1F70600040FE5F004000F0E8BC70B50446184B88B003AA03F11006154618685968083303C5B3422A46F7D11B782B70FCB12223237091
|
||||
:4019000001AD03232846637000F0C6FE002220461146AB5C08AC04EB131414F8144C03F00F03847008AC234413F8143C0132082AC1700371417100F10400EAD108B070BD02
|
||||
:40194000813200002DE9F0431C4D01222E460C201F274FF0800E4FF0080C194B00FB02581401234418705F70164998F805902144B9F1000F07D098F8044024064CBF8870D3
|
||||
:4019800081F802C001E081F802E000FB0261CC880132E4B29C71CC88092AC4F30724DC71CC88E4B21C71C988C1F307215971D4D1054BFF221A70BDE8F08300BF8886FF1F61
|
||||
:4019C00070600040FC5F00400A600040064B074A1B7802EBC30253681A7C824286BF03EBC0035869002070477C86FF1FD03200002DE9F84F424B1A78002A7ED01878414DD4
|
||||
:401A00000138C0B2FFF7E2FFA8463F4AC3681478007ADFF800C1E4B203EBC0000C2600274FF0010E834268D01A78A24263D11CF80420597891425ED19A7893F8039002F025
|
||||
:401A40007F0206FB02FA05EB0A01CF7093F802B009F0030981F804B093F803B005F80AB0B3F804A0A1F808A093F902A0BAF1000F0BDAB9F1010F0CBF4FF007094FF00D09C4
|
||||
:401A800081F8059081F801E009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEA02191A4906FB0282494481F802E0B2F808A0CAF3072A81F800A0B2F808A05FFABD
|
||||
:401AC0008AFA81F801A0B2F806A011495FFA8AFA494481F806A0B2F80690C9F3072981F80790B2F806905FFA89F981F80490D288C2F307224A71083394E7BDE8F88F00BF1D
|
||||
:401B00008186FF1F8886FF1F7D86FF1FFC5F0040706000406E86FF1F08B5064B18780138C0B2FFF753FF20B143681B7900EBC300406908BD8186FF1F00212DE9F84F0B46C5
|
||||
:401B40004E4E0C2707FB01F401313219092933554FF000059370494CD3701381937253705371EFD118B1464B1D70464B1D70464B1A78002A7FD0187801250138C0B2FFF771
|
||||
:401B800025FFA8464368DFF8F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010202F0FE021A70027AD20007FB0541C36803EB02094B4531D093F802A00AF07F06FF
|
||||
:401BC000AE4229D10E89B3F804B0B6B25E4538BFA1F808B01E7893F801B01EF80660B3451AD181F804A0DE780E7093F902A0DE78BAF1000F06F0030607DA012E0CBF07264B
|
||||
:401C00000D264E7181F8018006E0012E0CBF052609264E7181F801C00833CBE70135092DC3D1C1680A328B1C0A440C200833934209D013F8081C13F80A5C01F07F0100FB1D
|
||||
:401C400001418D72F2E7FFF767FF114B0121186000230C2000FB0142D3801289013113449BB203F00102134409299BB2F2D1BDE8F84FFFF767BEBDE8F88F00BF8886FF1FFA
|
||||
:401C80006E86FF1FF686FF1F8186FF1F7F86FF1F8486FF1F114B1B7903F07F035A1E072A19D80F490C2202FB031291781B0141F0010191700021D170517841F002015170F8
|
||||
:401CC000127912F0800F074A1A4414BF8D2389239370FFF715BC0020704700BF006000408886FF1FFC5F004030B4194B1A7902F07F02531E072B27D8164B0C2404FB02336E
|
||||
:401D00009978154D01F0FE0199700021D97029461201505D114400F07F0050555A7802F0FD025A701A795B78120605D5012B01D18C7006E00D2303E0012B0CBF082309230F
|
||||
:401D40008B7030BCFFF7DCBB002030BC704700BF006000408886FF1FFC5F004010B50D4B0D4C21791878C9B20138C0B2FFF72EFE43681B798B4201D2012909D8074A084893
|
||||
:401D8000535CDBB24354A3780120DBB2535410BD002010BD8186FF1F006000406E86FF1FF686FF1F38B58A4A8A4C13780021DBB221801806517840F18D800A2900F2058174
|
||||
:401DC000DFE811F05D00030103010301030103010B0003017E0003018200D3787C49012B09D17D4B1A787D4B03EBC2035B685B686360122310E0CB78022B12D18878FFF7F5
|
||||
:401E0000E5FD002800F0E180436863606368DA7863689B7843EA02232380BDE83840FFF78FBCCB78032B26D16D4B00228878D5B2854209D3664A91786A4AEE2908BF13467B
|
||||
:401E4000634A917881B106E0187801320028F1D018780344EAE764499278097C914203D16248FFF739FD614B1A78002A00F0AD801A78228018E0BDE8384000F0ADBC13F05D
|
||||
:401E8000030313D0022B40F0A0802380504B0C211B7903F07F02564B01FB02339A78554BD2B21A7000225A706360B6E702222280514A11784F4AC9B2117053706260ACE7D4
|
||||
:401EC000012323804D4BEFE70123238013794C4A1344E9E701390A2977D8DFE801F037764F76067676760A7620009378454ADBB25AE0937803F0FF0153B9404B1A78914254
|
||||
:401F00005FD01970404B01201870FFF715FE58E0481EC0B2FFF75AFD0028EED155E0FFF71DFF002851D02A4A384913791279DBB2D2B20A70364A3249D25CCB5C9A4240D005
|
||||
:401F4000314B01221A70FFF753FD3AE003F00303012B2BD009D3022B37D11D4B9B78002B33D1BDE83840FFF7BFBE194B9B78012B2BD1214A137803F0FD0315E003F00303F7
|
||||
:401F8000012B13D008D3022B1FD1114B9B78E3B9BDE83840FFF77EBE0D4B9B78012B14D1154A137843F0020313700AE0084B1A795AB998781B791749DBB2CA5C22EA0002F7
|
||||
:401FC000CA54BDE83840FFF79BBA002038BD00BF006000407086FF1F7C86FF1FD032000034330000BC320000A7330000FA86FF1F8886FF1F3286FF1F7F86FF1F8186FF1F9A
|
||||
:402000006E86FF1F6C86FF1F8086FF1F7D86FF1FF686FF1F8386FF1F074B1A78120609D55B78012B06D1054B054A5A6012781A80FFF786BB00207047006000407086FF1F89
|
||||
:4020400094320000014B1870704700BF77650040014B1878704700BF69650040014B1870704700BF7D640040074A0223136002F688321268E0215064044A11706FF44071BC
|
||||
:402080000A441360704700BF80E100E001E400E0014B1870704700BF7764004073B515461E460B4C04230022019200920A4601461846237000F0A4F932462946207800F04E
|
||||
:4020C0005FF90221207800F049F9207802B070BDD080FF1F064A0123136002F688321268E0211064034A1170A2F540721360704780E100E000E400E0014B1870704700BF9E
|
||||
:402100007B64004073B515461E460B4C05230022019200920A4601461846237000F070F932462946207800F02BF90221207800F015F9207802B070BDD180FF1F064A042349
|
||||
:40214000136002F688321268E0219064034A1170A2F202321360704780E100E002E400E0014B04221A60704700E100E0014B04221A60704780E100E0014B1870704700BF72
|
||||
:402180007E640040014B1870704700BF76640040014B1870704700BF7C640040704738B505460078012428B100F0AEFA285D0134E4B2F8E738BD08B50D2000F0A5FABDE8F0
|
||||
:4021C00008400A2000F0A0BAF7B516461F460B4C00230325019300930A4601462846257000F00EF93A463146207800F0C9F80221207800F0B3F8207803B0F0BDE080FF1F50
|
||||
:40220000F7B516461F460B4C00230225019300930A4601462846257000F0F2F83A463146207800F0ADF82946207800F097F8207803B0F0BDE180FF1FF7B516461F460B4C10
|
||||
:4022400000230125019300930A4601462846257000F0D6F83A463146207800F091F80221207800F07BF8207803B0F0BDE280FF1F73B515461E460B4C0023019300930A4620
|
||||
:4022800001461846237000F0BBF832462946207800F076F80221207800F060F8207802B070BD00BFE380FF1F024B1878C0F38010704700BF8F450040074A7F2380211370EB
|
||||
:4022C0005170064A013BDBB202F80839002BF9D1034A1370704700BFE480FF1FF87B00400078004017280FD8084B0001C25C11B142F0200201E002F0DF02C254C25C42F0D9
|
||||
:402300000102C25400207047012070471070004017280BD8064B0001C25C02F0FE02C254C25C02F0DF02C25400207047012070471070004017280DD8074900010B460344F8
|
||||
:402340001A7942F004021A71435C43F00103435400207047012070471070004017280BD8064A0001835C490003F0F10301F00E011943815400207047012070471070004009
|
||||
:4023800041F6FF73994208BF4FF400519A4208BF4FF4005217289FBFC00000F1804000F5EC4081809ABFC280002001207047000017289FBF034B00011954002088BF012062
|
||||
:4023C000704700BF1970004017289FBF054B00011A5C01F007019DBF1143195400200120704700BF1470004017289FBF034B0001185C00F0070088BFFF2070471470004071
|
||||
:40240000172810B51AD8C00001F07F0100F1804441EAC21204F5EC44D2B222709DF8082003F00F0343EA0213DBB263709DF80C30002003F00F03A370E07010BD012010BD0A
|
||||
:4024400010B500F0C1F90A4A5378182B0AD91478013B5370E30003F1804303F5F0431B78137000E0FF2400F0B3F9204610BD00BFE480FF1F030610B5044611D400F0A4F9A5
|
||||
:40248000084AE300117803F1804303F5F04319705378147001335370BDE8104000F098B910BD00BFE480FF1F30B504060CD411F4704509D1C40004F1804404F5F0442180BB
|
||||
:4024C000A270E370284630BD012030BD03065FBFC00000F1804000F5F04081805ABFC280002001207047000038B50446084DB4F5004F05D9286800F05FF9A4F50044F6E709
|
||||
:40250000034B58686043BDE8384000F055B900BFEC80FF1F024B1B7A584300F04DB900BFEC80FF1F0E4B00F003001A78490102F0FC02104318701A7801F0600142F080023F
|
||||
:402540001A701A7802F07F021A701A7802F09F020A431A701A7842F010021A70704700BF83430040014B01221A70704784430040044B00F00F021B6853F8220043F8221006
|
||||
:40258000704700BF08ED00E0054A00F01F00126800F1100352F8230042F82310704700BF08ED00E000F01F0000F16040490100F56440C9B2017070470F4B10B50F4900240E
|
||||
:4025C0000F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040313600A4B4FF47A72DB68B3FBF2F3084A1360084B4FF400421C60C3F8E82010BD3486FF1F65260000D6
|
||||
:4026000010E000E0EC80FF1F14E000E018E000E0024A136843F002031360704710E000E008B5FFF7F5FF034A136843F00103136008BD00BF10E000E010B5054CA3691BB938
|
||||
:40264000FFF7BAFF0123A361BDE81040FFF7E8BF3486FF1F024B1868C0F30040704700BF10E000E038B5FFF7F5FF012808D1054D002455F8243003B198470134052CF8D161
|
||||
:4026800038BD00BF3886FF1F024B03EB80035868596070473486FF1F134B144A1B78DBB20360127843EA0223114A0360127843EA0243104A0360127843EA026303600E4B5A
|
||||
:4026C0000E4A1B78DBB24360127843EA02230C4A4360127843EA02430A4A4360127843EA02634360704700BF0301004904010049EC460040020100490101004900010049D4
|
||||
:402700000501004906010049FEB5494652465B460EB40746244909688A46244A12682448022100F071F8030020480068C018204900F06AF8143883460121C9430C460125D1
|
||||
:40274000002600F041F8814651460B7823400B705846013000F030F83800F04028400B78234003430B70584600F026F80136072EF2D9002001300138013001200B78234048
|
||||
:4027800003430B705846043000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF30080D480FF1FF880FF1F00C20100000000000230800803D000BFB1
|
||||
:4027C00001380046FCD17047EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D113790C2103F07F02044B01FB02339B7A00E01379002070471B
|
||||
:40280000006000408886FF1F002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB11217047704700BF014B1868704700BF5C81FF1F0E4B70B5DD
|
||||
:402840001E460E4C0025E41AA410A54204D056F8253098470135F8E700F0C2FD084B094C1E46E41AA4100025A54204D056F8253098470135F8E770BDEC330000EC33000022
|
||||
:40288000EC330000F433000003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B604FF0FF301DE04FF40273ADF80C300CBF234604F1FF330293D7
|
||||
:4028C00005934FF6FF7300910491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B00221A701BB030BD000007B5009313460A46014603480068FFF748
|
||||
:40290000CBFF03B05DF804FB5C81FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD02568096902236F1A656905EB450595FBF3F57B1C43449D4238BF80
|
||||
:402940001D4653050FD5294600F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A4600F098FB064670B92169504600F0E8FA0C23CAF80030A3890F
|
||||
:402980004FF0FF3043F04003A381BDE8F08726613E44266046466561ED1BA560464528BF464649463246206800F0B3FAA36800209B1BA36023681E442660BDE8F087000068
|
||||
:4029C0002DE9F04F9DB003938B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F80030CDE040236B610023099320238DF82930DFF89CB1302309
|
||||
:402A00008DF82A3037463C4614F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF771FF013000F0A780099B4B4409933B78002B00F0A08000233C
|
||||
:402A40004FF0FF3204930793059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46801A06FA00F018430490EFE7D90644BF20228DF853201A077A
|
||||
:402A800044BF2B228DF8532022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F00200079204900BE027463B780134303B092B03D800FB0232012148
|
||||
:402AC000F5E701B107923B782E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E0002319460593781C0A2407463A780130303A092A03D804FB4D
|
||||
:402B000001210123F5E703B1059103223978224800F0E6F940B14023CBEB000003FA00F0049B013718430490397806221B487E1C8DF8281000F0D4F988B1194B33B9039BE6
|
||||
:402B4000073323F007030833039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A9404600F093F8B0F1FF3F824603D0099B5344099342E7AB8986
|
||||
:402B80005B0601D4099801E04FF0FF301DB0BDE8F08F00BFBB330000C1330000C5330000000000000D2900002DE9F04791461F460A698B6806469342B8BF1346C9F800305E
|
||||
:402BC00091F843200C46DDF8208012B10133C9F800302368990642BFD9F800300233C9F80030256815F0060510D104F1190A07E00123524639463046C04701301AD0013562
|
||||
:402C0000E368D9F800209B1A9D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F845102244023382F8431003E04FF0FF30BDE8F08704F143025A
|
||||
:402C400039463046C0470130F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500259342C4BF9B1AED184FF000091A344D4509D0012322463946F9
|
||||
:402C80003046C0470130D5D009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9B01F1430E00F0AE8011D8632A22D009D8002A00F0BB80582A08
|
||||
:402CC00040F0CA8081F84520834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B8800A6842F020020A603EE0752A24D0782A3AD0ADE01A6801F11B
|
||||
:402D00004205111D1960136884F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F10401196002D0B2F9003000E01368002B3CDA2D225B4284F8432037E0CC
|
||||
:402D400021681A6811F0800F02D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A14BF0A2208221BE078225A4984F845202268186812F0800F15
|
||||
:402D800000F104051D6003D1550601D5038800E00368D00744BF42F0200222601BB9226822F0200222601022002084F8430001E049490A226568002DA56008DB206820F0D3
|
||||
:402DC000040020602BB9002D7DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D1082A0BD12368DA0708D5236962689A42DEBF302305F8013CFE
|
||||
:402E000005F1FF35C5EB0E0323612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F104001860136801D0198000E0196000232361754616E01A68111D03
|
||||
:402E40001960156800216268284600F049F808B1401B6060636804E004F1420584F8422001232361002384F84330CDF800803B4603AA21463046FFF797FE013002D14FF05D
|
||||
:402E8000FF3026E023692A4639463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046C0470130E7D00135E368039A9B1A9D42F2DBE068039B9842E3
|
||||
:402EC000B8BF184605E00B7804F1420584F842308AE705B0BDE8F08381320000CC33000010B5C9B202449042034605D01C7801308C42F8D1184610BD002010BD10B5431E6B
|
||||
:402F00000A44914204D011F8014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21AD34204D013F8014D01F8014DF8E710BD994204D011F8014B47
|
||||
:402F400002F8014FF8E710BD38B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B96360146030E0A3420DD92268A018834201BF18685B6812188C
|
||||
:402F8000226063600C6023E0A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D110685268014419605A600DE002D90C232B6009E021686018AA
|
||||
:402FC000824201BF106852680918216062605C602846BDE8384000F098B838BD5486FF1F70B5CD1C25F0030508350C2D38BF0C25002D064601DBA94202D90C23336046E077
|
||||
:4030000000F082F8234B1C681A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B681360636018BF0C4615E00C464968E9E7174C23681BB930465F
|
||||
:4030400000F052F820602946304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B00231D20F00700C31A0ED05A42E25070BD211A304600F034F86A
|
||||
:403080000130EBD10C233360304600F03EF8002070BD00BF5486FF1F5086FF1FF8B5074615460E4621B91146BDE8F840FFF798BF1AB9FFF749FF2846F8BD00F027F88542BF
|
||||
:4030C0000ED929463846FFF78BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD38B5064C0023054608462360FDF754FE431C02D1236803B1C7
|
||||
:403100002B6038BD1C87FF1F7047704751F8040C0028BEBF091851F8043CC0180438704700000000050209020B020D020F02110213027265706C79203078253032780068AB
|
||||
:403140006F6D696E6700626567696E6E696E67207365656B2066726F6D20256420746F20256400756E65787065637465646C7920646574656374656420747261636B203092
|
||||
:403180000066696E6973686564207365656B0057616974696E6720666F72205553422E2E2E0055534220726561647900636F6D6D616E642030782530327800756E6465724C
|
||||
:4031C00072756E206166746572202564207061636B65747300636F756E743D256420693D256420643D2564207A7A3D256400636D645F777269746500703D25642063723D0C
|
||||
:4032000025642063773D256420663D256420773D256420696E6465783D256420756E64657272756E3D25640077726974652066696E6973686564007374617274206572612A
|
||||
:4032400073696E670073746F702065726173696E670069646C650000510040100030510040400000000140001000140140000800400140000A004C01400002005001402015
|
||||
:40328000003031323334353637383941424344454600000028000000000104000100000000000000000157494E5553420000303030303100000000000000000012034D0012
|
||||
:4032C0005300460054003100300030000100000001000000D832000001000000A7330000000000000000000001000000F03200000100000079330000040000001233000050
|
||||
:4033000000000000000000000000000010330000FF00000001024000FF00000082024000FF00000003034000FF00000084034000FF00020304030904160346006C007500E2
|
||||
:40334000780045006E00670069006E0065002A0343006F0077006C00610072006B00200054006500630068006E006F006C006F0067006900650073000009022E0001010040
|
||||
:4033800080320904000004FF00000107050102400000070582024000000705030340000A0705840340000A12010002FF0001080912006E0100020180014300232D302B20B9
|
||||
:4033C00000686C4C00656667454647003031323334353637383961626364656600000000F8B500BFF8BC08BC9E467047590000001D0E0000F8B500BFF8BC08BC9E467047C5
|
||||
:403400003500000018340000C880FF1F98000000C005000000000000000000002087FF1FFFFF0000675000400C00000007000000FFFFFFFF7F8000003F0000000000007DC4
|
||||
:4034400000FA0000400000000090D0030000000000000000000000000000000000000000000000000000000000000000B933000000000000000000000000000000000000C3
|
||||
:403480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC80FF1F0000000000000000000000000000000072
|
||||
:4034C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000CC
|
||||
:40350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008B
|
||||
:40354000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004B
|
||||
:40358000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B
|
||||
:4035C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000CB
|
||||
:40360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008A
|
||||
:40364000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004A
|
||||
:40368000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A
|
||||
:4036C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000CA
|
||||
:403700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000089
|
||||
:403740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049
|
||||
:403780000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009
|
||||
:4037C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C9
|
||||
:403800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000088
|
||||
:403840000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000048
|
||||
:403880000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008
|
||||
:4038C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C8
|
||||
:403900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000087
|
||||
:403940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047
|
||||
:403980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007
|
||||
:4039C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C7
|
||||
:403A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086
|
||||
:403A40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046
|
||||
:403A80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006
|
||||
:403AC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C6
|
||||
:403B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000085
|
||||
:403B40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045
|
||||
:403B80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005
|
||||
:403BC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C5
|
||||
:403C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084
|
||||
:403C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044
|
||||
:403C80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004
|
||||
:4000000000800020110000003110000031100000064A08B5136843F020031360044B1A6803F53F5302331A6001F020F8E8460040FA46004010B5054C237833B9044B13B18B
|
||||
:400040000448AFF300800123237010BD6881FF1F00000000B8380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF0000000051
|
||||
:400080006C81FF1FB8380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8881FF1FCC
|
||||
:4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8881FF1F3F0000800E4BDA681AB901215F
|
||||
:4001000019745A7410E05A7C1A741A7C0AB1002207E059699A69D8688A1A82428CBF002201225A745A699A611B7C13B1002002F043B970478881FF1F10B5C4B2204601F054
|
||||
:4001400057F90128FAD110BD08B572B60F4B0F495A6901325A61DA690132C82A08BF0022DA611A6AD8690132A72A08BF00220A621B6A002B0CBF02230023002814BF184660
|
||||
:4001800043F0010002F080FE62B608BD8881FF1F38B50446C5B2284602F0B0F8062002F0CDFA44F00200C0B202F0A8F8062002F0C5FA284602F0A2F8BDE83840062002F0C2
|
||||
:4001C000A7BA10B5642402F093F828B9FFF7E0FF013CF8D1204610BD012010BD70B5C4B2054620460E4601F003F9012805D0204601F01CFA2846FFF79FFF204601F000F9AD
|
||||
:40020000314605460246204601F0BCF9204601F0EFF80028FAD1284670BD000038B5044D0024285D013402F039FA402CF9D138BDAC81FF1F08B502F053FC002002F05CFCBD
|
||||
:4002400002F06EFC02F078FC80B208BD10B50446012002F06BF8642002F05AFAFFF7EAFF2080002002F062F8642002F051FAFFF7E1FF608010BD08B502F05EFD002002F02B
|
||||
:4002800067FD02F079FD02F083FD80B208BD10B50446FFF796FF322002F03AFAFFF7EBFF20800120FFF774FF322002F031FAFFF7E2FF608010BD0FB400B593B014AB53F835
|
||||
:4002C000042B402102A8019302F0DEFE02A802F07AF802F084F813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF723FF62782146BDE81040042001F0D0B8B5
|
||||
:40030000CC38000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8643043B1002001F0D8FF002002F06BFD002384F864304E
|
||||
:4003400010BD00BF8881FF1F38B5104D837895F8672004469A4204D0FFF7E4FF002385F86A302368C5F865302279094B1A71A378002B14BF0220012002F04AFDE07802F02E
|
||||
:4003800041FD2079BDE8384002F078BD8881FF1FED81FF1F38B50D4C94F8645065B904F16500FFF7D1FF012001F09CFF4FF47A7002F0AEF984F86A506369E366012384F84D
|
||||
:4003C0006430BDE8384002F0E1B900BF8881FF1FF8B5214C0546FFF7DDFF94F86A3003B15DB91E48FFF767FFFFF7EBFE0120002384F86A00236702F0A1F92A46216F184872
|
||||
:40040000FFF759FF144E0027236F9D4216D001F06FFF00B13767236F9D4205DD0120FFF7B7FE336F013305E005DA0020FFF7B0FE336F013B336702F0A9F9E5E7322002F047
|
||||
:4004400067F92A2DCCBF0020012002F023FDBDE8F8400448FFF72FBF8881FF1FD9380000E0380000FD3800002DE9F04F99B062B602F0F6F99D49042002F01AFA9C4801F0D9
|
||||
:4004800043FF9C4802F0E6FC9B4801F077FF02F0C7FB02F099FA002002F0BAFC01F092FF0221002000F05AFF944C012001F0D2F8002384F86730FFF76DFFFFF782FE84F8A3
|
||||
:4004C0007400FFF72FFF012384F86730FFF762FFFFF777FE84F87500FFF724FF884B94F87400884994F875202546002A14BF0A461A46002808BF19468348FFF7DCFE032177
|
||||
:40050000084602F023F9264602F040F994F8643043B1EA6E6B699B1A41F28832934201D9FFF700FF00F052FF18B97848FFF7C3FE04E000F051FF0028F7D10BE000F046FF85
|
||||
:4005400010B902F023F9F9E77148FFF7B4FE032001F06CF8032000F04BFF0128D4D16D48FFF7F2FE6C490320FFF738FE94F876106A48FFF7A0FE94F87630023B142B00F229
|
||||
:40058000D483DFE813F01500D2031E00D2032400D2034F00D2037500D203D700D203BF01D2030603D2032A03D2033103D2034B0303238DF820308DF821300F238DF8223084
|
||||
:4005C00028E394F87800FFF703FF554B1FE3FFF7E1FE002323746069227C02F0FF0132B96B691B1AB3F57A7FF6DD0B4611E083B10022174696F878107069277494F810E007
|
||||
:40060000BEF1000F02D16B691B1AF7E701329142F3DA07228DF8202004228DF82120ADF82230F7E20220FFF787FD4FF000080DF1200A02F0ABF84FF480790027C9EB080303
|
||||
:40064000DA1907F80A200137402FF9D10220FFF773FD3A465146022000F022FFB9F10109EBD108F10108B8F1400FE2D12D4B38E04FF0010A4FF000080DF1200B02F086F844
|
||||
:400680004FF0000959460120FFF7A8FD08EB090300270493049B1BF807203B44DBB29A4209D08DE80C0041463B464A461E48FFF702FE4FF0000A0137402FEBD109F101091B
|
||||
:4006C000B9F5807FDED108F10108B8F1400FD5D151461648FFF7EFFDBAF1000F00F01A81134B1B8807A8ADF81C3094E249010000F900000091000000C50000008881FF1F0B
|
||||
:400700000F3900000B390000123900002A3900003D390000ED81FF1FFE81FF1F47390000BC380000BE3800005639000072390000C0380000206FFFF74BFE94F8780001F081
|
||||
:40074000F5FD94F8780001F0D9FD02F009FCB94BDFF8FC821A78002702F0FB021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F0F7FB0220FFF7DCFCF9
|
||||
:40078000012141F6FF734FF48042084602F046FB84F8B60001F068FF08F807000137402FF8D1DFF8B0A200270AF195091FFA89F80137402F14BF3A4600221AF8010F2244C0
|
||||
:4007C000062392F82420402101F082FF424646F24E419AF8000001F08DFF08F14008402F1FFA88F8E4D196F8793053B196F87C30F36000233374237C002BFCD000233374E1
|
||||
:40080000F36000234FF0FF32236062602372236894F8B600234493F8241001F0DDFE94F8B60001F09BFE012194F8B60001F06EFE2368002BFCD0002398467360D6F814A0D4
|
||||
:40084000012701F0A3FF6369B4F87A20CAEB030393420DD367B1042195F8B60001F0C8FE94F8B60001F0D4FE0028F9D107463072237AFBB96A682B689A4202D1002FE0D1D9
|
||||
:4008800018E00220FFF758FC6968402209EB8111022000F005FE6A68674B01321340002BBEBF03F1FF3363F03F03013308F101086360C6E70220277AFFF73EFC0022114687
|
||||
:4008C000022000F0EDFD0220FFF736FCFFB2FFF7A5FC002001F012FD37B15848FFF7EBFC0220FFF70FFD06E0554B08A81B88ADF82030FFF7F5FC227C4146237A5148FFF7BB
|
||||
:40090000DAFC15E25048FFF7D6FCD4F87A7017F03F0701D0032009E2286FFFF759FD95F8780001F003FD95F8780001F0E7FC012001F002FD02F014FB444BDFF814811A7857
|
||||
:4009400042F004021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F003FB01214FF4804341F6FF72084601F0E9FC85F8B60001F077FE08F8070001378C
|
||||
:40098000402FF8D1DFF8CC90002709F195031FFA83F804930137402F14BF3A46002219F8010F2244052392F82420402101F090FE414646F24B5299F8000001F09BFE08F1FF
|
||||
:4009C0004008402F1FFA88F8E4D100274FF0FF33376098467360BB463B46D6F87A9037725FEA99190CBF4FF0010A4FF0000A2168114A01310A40002ABCBF02F1FF3262F03B
|
||||
:400A00003F026068B8BF013282426FD02BB1227A002A7AD12A7C002A77D12068049A059302EB8010BAF1000F16D040223F2102F0F7FA1CE09E6400403F0000807C390000C9
|
||||
:400A4000C238000096390000A93900009B650040AC81FF1FAB81FF1F014601370120FFF7BDFBC7EB0903D3F1000A4AEB030A2168B34A01310A40002ABEBF02F1FF3262F0BB
|
||||
:400A80003F02013222606268059B01322ED12A683F2A2BD14FF00008C5F8048001F080FC85F808806B6895F8B6002B4493F8241001F092FD95F8B60001F050FD012195F8BF
|
||||
:400AC000B60001F023FD95F87E30EB6085F81080237C002BFCD04FF00008012086F8108001F06AFC404601F027FC00E023B1237A5BB92B7C4BB90123626842453FF477AFC1
|
||||
:400B00000BF1010BD5F8048071E701F04FFC012001F012FC002001F04FFC042194F8B60001F066FD94F8B60001F072FD80460028F8D196F8B60001F0FFFC337C327A029318
|
||||
:400B4000012303920193CDF800A05B463A4649467C48FFF7B0FBC6F80C80BAF1000F0BD0FFF75CFB002001F0C9FB237A63B17648FFF7A1FB0220D9E0B945F1D07349012035
|
||||
:400B8000FFF72CFB0137F7E77148FFF794FB714B3DE094F8780001F0C9FB206FFFF718FC6D48FFF788FB94F87930E36000232374237C002BFCD0012001F0FEFB00233374CE
|
||||
:400BC000237C002BFCD0002001F0F6FB00236348F360FFF770FB624B19E0002084F86A00FFF7F6FB5F4B12E094F8743023B195F875200AB985F8782094F875201AB113B9F6
|
||||
:400C0000012385F878305848FFF79EFB574B1B88ADF8203008A8FFF763FB89E0FFF782FB02F07CF8002002F01FF82A2701F04AFF002001F0EDFE3A46002108A802F0F0F9E0
|
||||
:400C400017238DF820308DF8217001F09FFD002001F048FB002002F0DBF8C82001F058FD0DF12200FFF7F2FA0DF13600FFF70FFB01F08CFD012002F0CBF8322001F048FD4D
|
||||
:400C80000DF12600FFF7E2FA0DF13A00FFF7FFFA012001F027FB4FF4967001F039FD01F075FD0DF12E00FFF7D1FA0DF14200FFF7EEFA002001F016FB4FF4967001F028FD84
|
||||
:400CC00001F064FD022002F0A3F8322001F020FD0DEB0700FFF7BAFA0DF13E00FFF7D7FA012001F0FFFA4FF4967001F011FD01F04DFD0DF13200FFF7A9FA0DF14600FFF756
|
||||
:400D0000C6FA002001F0EEFA4FF4967001F000FD01F03CFD002002F07BF8002384F86A3001F07EFF01F050FE74E70120FFF7EAFA032000F07BFC0E48FFF7BDFAFFF7E4BBB6
|
||||
:400D40003F000080B3390000E33900004092FF1FED390000C4380000F5390000033A0000C6380000C8380000FE81FF1FCA380000103A00002DE9F04172B6884B61221A70F9
|
||||
:400D8000A3F5F06301221A801924854A9C7092E803008033062283F8002283E80300522203F580731A707F4B7F4A1B787F4EDBB2137040F618027E4B00251A8041F25122A6
|
||||
:400DC00023F8022C33784FF4F07003F0010343EA450502F0BDF8013C05F003052ED0032DF0D1744B4FF480721A8007221A70724A002548211570917002221D705D7103F893
|
||||
:400E0000032C0422DA716D4A6D4C13786D4E43F00103137012F8013C062743F0030302F8013C2378012243F0800323705B4B1A70654A137843F02003137000E0FEE707FBAD
|
||||
:400E4000056300219A881868013502F0E9F8072DF5D15E485E4E002550F8041F05F1105303F1480221F0FF074933C9B20B4452005B0002329A4206D012F802EC12F801CC0C
|
||||
:400E80000EF807C0F5E7B0420D44E5D1514A002313609360136193614F4B504F1A68504BDFF888811A604F4B1A684F4B1A604F4A137843F002031370137C43F002031374F7
|
||||
:400EC0002378A2F5863243F040032370413A137843F010031370464A464B07CA03C31A80454A2833106843F8250C127903F8212C424A07CA03C31A80414AE83B07CA03C33B
|
||||
:400F00001A80404A083307CA03C31A803E4A3F4BA2F5616203CBC2F8100EC2F8141E1378042043F008031370394B02F5AA521B783D78DBB298F80060EDB203F007010C329F
|
||||
:400F40001B091170F6B2537045F003033B7046F0030388F800302F4B48221A702E4A402313702E49937013729372082382F81F3220220A7048710A72294A0A20137001F0C7
|
||||
:400F8000DDFB284B88F8006044223D70264D1A7094E80F0007C52B80BDE8F081004800405C0900480F010049A146004025420040224200400440004006400040A2430040AF
|
||||
:400FC000A0430040153A0000E8460040FCFFFF478C0000480076004064090048F8460040207600406809004828760040035001401C090048C051004028090048300900485A
|
||||
:401000003C090048480900483251004054090048CF0100491D51004001590040235B0040585B004076580040B0430040F946004008B501F0C9FF03680C2B00D1FEE7FEE79D
|
||||
:40104000084908B50B68084A1844821A802A01DC086005E001F0B8FF0C2303604FF0FF33184608BDCC80FF1F9093FF1F80B51148114B0025C0B1A3F1100192C92246043933
|
||||
:40108000161BB74204D051F8046F42F8046BF7E7114653F8046C8C1AA64202D041F8045BF9E701381033E5E701F094FFFFF7DCF9FEE700BF01000000E43B0000124A134BF0
|
||||
:4010C00010B51A60124A134C1368134843F4007313600023032B98BF54F823204FEA830188BF0E4A0133302B4250F3D10C4B1A780C4B1A700C4B084A1A60FFF73BFEBDE8CA
|
||||
:40110000104001F0EDB900BF0004FA050CED00E014ED00E0000000000080FF1F31100000BC760040C080FF1F08ED00E0F8B501F017FF4B4A01271378022643F001031370EA
|
||||
:40114000137C484C43F001031374474B02F5E3521F700B3203F8946C1378054603F07F031370002001F0EAFA2378404A03F0F90323701378384603F0DF03137023783B4325
|
||||
:40118000237001F0DBFA282001F0D8FA384B30461A7802F07F021A701A7802F0BF021A7023783343237001F0C9FA2378314A43F0040323700023137053702F4AFF2199544A
|
||||
:4011C0000133092BFBD1284601F0CEFE0721172001F0FCFA2949172001F0EAFA0721182001F0F4FA2649182001F0E2FA0721152001F0ECFA2349152001F0DAFA0721052032
|
||||
:4012000001F0E4FA2049052001F0D2FA0721062001F0DCFA1D49062001F0CAFA0721084601F0D4FA1A49072001F0C2FA0721082001F0CCFA1749082001F0BAFA0021162047
|
||||
:4012400001F0C4FA1449162001F0B2FA07210C2001F0BCFABDE8F84010490C2001F0A8BAA5430040944300409D60004012600040F851004084600040B592FF1FFB1A00008A
|
||||
:4012800035190000F91A00002D1A0000591A0000891A0000C11A0000011B0000751B0000214B224A10B5187000231370204A40201370204A0F2413701F4A13701F4A1370D9
|
||||
:4012C0001F4A13701F4A13701F4B4FF400021A604FF080721A604FF400121A6020221A601860802018604FF480701860174804704FF480001860164B1A70933B19B91A7851
|
||||
:4013000002F0FE0202E01A7842F001021A70114B03221A70802203F8202C012001F018FE0D4B04221A7010BDD092FF1FD692FF1FD492FF1FD592FF1FD192FF1FC092FF1F97
|
||||
:40134000D392FF1F4893FF1F00E100E09E6000409C600040286000401260004070B5074C054623780E461BB9FFF7E0FE0123237031462846BDE87040FFF792BF8092FF1F7A
|
||||
:401380000A4A002313700A4A13700A4A13700A4A13700A4A13700A4A13700A4A13700A4B03221A70802203F8202C7047D692FF1FD492FF1FD592FF1FD192FF1FC092FF1F05
|
||||
:4013C000D392FF1F4893FF1F28600040014B1878704700BFD592FF1F044B1A7802F0FF001AB118780022C0B21A707047D492FF1F024A0C2303FB002040787047DC92FF1FB8
|
||||
:40140000431E072B0CD8074A064B00010344805C5B7800F00F0043EA0020023880B2704700207047FC5F00401A4A38B50C2303FB00231B79090C13F0800F00F1FF35044670
|
||||
:4014400019BF8AB24FF480438BB24FF48042032D18D8DFE805F002070C110021084601F01BF80DE00021084600F0FAFF08E00021084600F0D9FF03E00021084600F0B8FF3E
|
||||
:40148000054B1855EDB2072D03D801F0EDF8034B185538BDDC92FF1FAC92FF1FB592FF1F431E072B2DE9F0470446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB274
|
||||
:4014C000C3F500739D424FF00C0303FB007388BFD588DB7884BFC5F50075ADB2254A43EA15230601B354B244EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FBBF
|
||||
:40150000047200215170B9F1000F28D03DB31B4F385D01F011F811232946FE2218F8040001F0D6F806F5C04278321FFA89F118F8040001F0DFF8124D18F80410385D01F004
|
||||
:401540004BF80121385D00F0E1FF735D43F002037355735D03F0FD037355BDE8F08703FB04746379DBB28AF80230BDE8F08700BFDC92FF1FFC5F0040B592FF1FAC92FF1FD4
|
||||
:40158000706000402DE9F047044615468846002940D0431E072B3FD8FFF732FFA84203D22046FFF72DFF05461D4E335DFF2B03D141462046FFF738FFDFF868A027011AF893
|
||||
:4015C000040000F0B9FF1223FE222946305D01F07FF807F5C0411FFA88F27831305D01F089F8DFF84490315D1AF8040000F0F4FF01211AF8040000F089FF17F8093043F0F1
|
||||
:40160000020307F8093017F8093003F0FD0307F8093002E00D4600E000252846BDE8F087B592FF1FAC92FF1F70600040431E072B0AD8064A0C2303FB002300225A705A7991
|
||||
:40164000034BD2B200011A54704700BFDC92FF1FFE5F0040431E072B9FBF024B000108221A547047FE5F004030B51A4A1A491B4D0878138803449BB21380194A00231488E7
|
||||
:40168000D8B2A4B27CB1082B0CD050680078C0B2E85450680133013050601088013880B21080ECE718460B780E4C082B0E4A00D040B10E4D2B7883F080032B700F23237022
|
||||
:4016C000022301E0022323701370094B1870087030BD00BF4C93FF1F4893FF1F00600040C492FF1FC192FF1FD692FF1FD292FF1F4993FF1F074B02221A70074B80221A70AE
|
||||
:40170000064B0F221A70064A00231370054A012013707047D692FF1FD292FF1FC192FF1F4893FF1F4993FF1F30B5164B16491B780A8803F00F03023BDBB21A4492B20A80CC
|
||||
:40174000124C134A0020118889B279B173B15568215C013BC9B229705168DBB20131516011880130013989B21180ECE7094A1370094A137883F080031370084B0B221A70DF
|
||||
:4017800030BD00BF296000404C93FF1F00600040C492FF1F4993FF1FD292FF1FC192FF1F064A06231370064A01201370054B80221A70054B00221A70704700BFD692FF1F52
|
||||
:4017C000C192FF1FD292FF1F4993FF1F054B9A683AB19A68044910709A680988518000229A607047C492FF1F4C93FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A1E
|
||||
:40180000137008BD0220FFF7E1FF0D4B1B7803F06003202B05D0402B06D043B900F012FC04E001F0A5FB01E000F046FD10B9034B03221A7008BD00BF28600040C192FF1FC0
|
||||
:401840000060004008B5084A084B0120197813880B449BB21380064B00221A70FFF7B6FF044B03221A7008BD4C93FF1F4893FF1FD692FF1FC192FF1F08B50C4B1B78DBB25E
|
||||
:40188000042B07D0062B09D0022B0DD1BDE80840FFF7D8BFBDE80840FFF746BF0320FFF795FF034B03221A7008BD00BFD692FF1FC192FF1F08B5054B002201201A70FFF7B6
|
||||
:4018C00085FF034B03221A7008BD00BFD692FF1FC192FF1F08B50A4B1A7832B11A78094942F080020A7000221A70074B002201201A70FFF76BFF054B03221A7008BD00BFA5
|
||||
:40190000C092FF1F08600040D692FF1FC192FF1F074B1B78DBB2042B05D0062B05D0022B05D1FFF7A1BEFFF7C5BFFFF7D3BF7047D692FF1F38B51D4C2378DBB2DD0634D51B
|
||||
:4019400018060AD503F00F03012B2ED1FFF74EFF174B1B78190609D538BD5A0602D5FFF7D7FF03E09D0620D5FFF786FF23781B061BD4104B1A78104B1B7813430F4A13705F
|
||||
:401980001278934211D10A4A0849154613782078DBB2000605D41378DBB20B700B7803F00F0328788342F1D138BD38BD28600040C192FF1FD292FF1F4993FF1F29600040AD
|
||||
:4019C000054A00231380054A916819B191680B7092685380704700BF4C93FF1FC492FF1F0E4808B503889BB213B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FFAC
|
||||
:401A0000094AD1799379028843EA012392B2934238BF0380FFF728FE012008BDC492FF1FD692FF1FD292FF1F00600040084B01221A700F3B9B7C074B1A7B02F00302012A07
|
||||
:401A40001EBFDA7B82F08002DA7301225A7370470B600040DC92FF1F094B02221A700F3B93F82230074B1A7E02F00302012A1EBFDA7E82F08002DA7601225A76704700BFFE
|
||||
:401A80000B600040DC92FF1F0B4B04221A700F3B93F83230094B93F8242002F00302012A1EBF93F8272082F0800283F82720012283F82520704700BF0B600040DC92FF1F78
|
||||
:401AC0000B4B08221A700F3B93F84230094B93F8302002F00302012A1EBF93F8332082F0800283F83320012283F83120704700BF0B600040DC92FF1F7047FFF741BC000081
|
||||
:401B0000F0B5184B184E19780C27C9B201234FF0000C31B3CA0720D5144A4FEA031E7244947850782040C5070DD507FB03652C79240608D5147804F0FE0414706D790C4C35
|
||||
:401B4000EDB204F80E50840706D507FB036425792D0658BF84F801C090700133DBB24908D7E7F0BD9F600040DC92FF1F70600040FE5F004000F0ACBC70B50446184B88B021
|
||||
:401B800003AA03F11006154618685968083303C5B3422A46F7D11B782B70FCB12223237001AD03232846637000F08AFE002220461146AB5C08AC04EB131414F8144C03F053
|
||||
:401BC0000F03847008AC234413F8143C0132082AC1700371417100F10400EAD108B070BD3F3A00002DE9F0431C4D01222E460C201F274FF0800E4FF0080C194B00FB02580B
|
||||
:401C00001401234418705F70164998F805902144B9F1000F07D098F8044024064CBF887081F802C001E081F802E000FB0261CC880132E4B29C71CC88092AC4F30724DC71A2
|
||||
:401C4000CC88E4B21C71C988C1F307215971D4D1054BFF221A70BDE8F08300BFDC92FF1F70600040FC5F00400A600040064B074A1B7802EBC30253681A7C824286BF03EB6F
|
||||
:401C8000C003586900207047D092FF1FA03A00002DE9F84F424B1A78002A7ED01878414D0138C0B2FFF7E2FFA8463F4AC3681478007ADFF800C1E4B203EBC0000C260027FE
|
||||
:401CC0004FF0010E834268D01A78A24263D11CF80420597891425ED19A7893F8039002F07F0206FB02FA05EB0A01CF7093F802B009F0030981F804B093F803B005F80AB0A6
|
||||
:401D0000B3F804A0A1F808A093F902A0BAF1000F0BDAB9F1010F0CBF4FF007094FF00D0981F8059081F801E009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEAA3
|
||||
:401D400002191A4906FB0282494481F802E0B2F808A0CAF3072A81F800A0B2F808A05FFA8AFA81F801A0B2F806A011495FFA8AFA494481F806A0B2F80690C9F3072981F8FE
|
||||
:401D80000790B2F806905FFA89F981F80490D288C2F307224A71083394E7BDE8F88F00BFD592FF1FDC92FF1FD192FF1FFC5F004070600040C292FF1F08B5064B187801384A
|
||||
:401DC000C0B2FFF753FF20B143681B7900EBC300406908BDD592FF1F00212DE9F84F0B464E4E0C2707FB01F401313219092933554FF000059370494CD37013819372537031
|
||||
:401E00005371EFD118B1464B1D70464B1D70464B1A78002A7FD0187801250138C0B2FFF725FFA8464368DFF8F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010273
|
||||
:401E400002F0FE021A70027AD20007FB0541C36803EB02094B4531D093F802A00AF07F06AE4229D10E89B3F804B0B6B25E4538BFA1F808B01E7893F801B01EF80660B34576
|
||||
:401E80001AD181F804A0DE780E7093F902A0DE78BAF1000F06F0030607DA012E0CBF07260D264E7181F8018006E0012E0CBF052609264E7181F801C00833CBE70135092D89
|
||||
:401EC000C3D1C1680A328B1C0A440C200833934209D013F8081C13F80A5C01F07F0100FB01418D72F2E7FFF767FF114B0121186000230C2000FB0142D38012890131134463
|
||||
:401F00009BB203F00102134409299BB2F2D1BDE8F84FFFF767BEBDE8F88F00BFDC92FF1FC292FF1F4A93FF1FD592FF1FD392FF1FD892FF1F114B1B7903F07F035A1E072A3C
|
||||
:401F400019D80F490C2202FB031291781B0141F0010191700021D170517841F002015170127912F0800F074A1A4414BF8D2389239370FFF715BC0020704700BF0060004065
|
||||
:401F8000DC92FF1FFC5F004030B4194B1A7902F07F02531E072B27D8164B0C2404FB02339978154D01F0FE0199700021D97029461201505D114400F07F0050555A7802F013
|
||||
:401FC000FD025A701A795B78120605D5012B01D18C7006E00D2303E0012B0CBF082309238B7030BCFFF7DCBB002030BC704700BF00600040DC92FF1FFC5F004010B50D4BA5
|
||||
:402000000D4C21791878C9B20138C0B2FFF72EFE43681B798B4201D2012909D8074A0848535CDBB24354A3780120DBB2535410BD002010BDD592FF1F00600040C292FF1FB6
|
||||
:402040004A93FF1F38B58A4A8A4C13780021DBB221801806517840F18D800A2900F20581DFE811F05D00030103010301030103010B0003017E0003018200D3787C49012B9C
|
||||
:4020800009D17D4B1A787D4B03EBC2035B685B686360122310E0CB78022B12D18878FFF7E5FD002800F0E180436863606368DA7863689B7843EA02232380BDE83840FFF7EE
|
||||
:4020C0008FBCCB78032B26D16D4B00228878D5B2854209D3664A91786A4AEE2908BF1346634A917881B106E0187801320028F1D018780344EAE764499278097C914203D180
|
||||
:402100006248FFF739FD614B1A78002A00F0AD801A78228018E0BDE8384000F029BF13F0030313D0022B40F0A0802380504B0C211B7903F07F02564B01FB02339A78554BC3
|
||||
:40214000D2B21A7000225A706360B6E702222280514A11784F4AC9B2117053706260ACE7012323804D4BEFE70123238013794C4A1344E9E701390A2977D8DFE801F0377608
|
||||
:402180004F76067676760A7620009378454ADBB25AE0937803F0FF0153B9404B1A7891425FD01970404B01201870FFF715FE58E0481EC0B2FFF75AFD0028EED155E0FFF793
|
||||
:4021C0001DFF002851D02A4A384913791279DBB2D2B20A70364A3249D25CCB5C9A4240D0314B01221A70FFF753FD3AE003F00303012B2BD009D3022B37D11D4B9B78002B47
|
||||
:4022000033D1BDE83840FFF7BFBE194B9B78012B2BD1214A137803F0FD0315E003F00303012B13D008D3022B1FD1114B9B78E3B9BDE83840FFF77EBE0D4B9B78012B14D1B7
|
||||
:40224000154A137843F0020313700AE0084B1A795AB998781B791749DBB2CA5C22EA0002CA54BDE83840FFF79BBA002038BD00BF00600040C492FF1FD092FF1FA03A000049
|
||||
:40228000043B00008C3A0000773B00006893FF1FDC92FF1F8192FF1FD392FF1FD592FF1FC292FF1FC092FF1FD492FF1FD192FF1F4A93FF1FD792FF1F074B1A78120609D546
|
||||
:4022C0005B78012B06D1054B054A5A6012781A80FFF786BB0020704700600040C492FF1F643A0000014B1870704700BF7A640040014B1878704700BF6B650040014B18702D
|
||||
:40230000704700BF79640040064A0123136002F688321268E0211064034A1170A2F540721360704780E100E000E400E0014B1870704700BF7A650040014B1870704700BF89
|
||||
:402340007865004073B515461E460B4C05230022019200920A4601461846237000F064F932462946207800F01FF90221207800F009F9207802B070BDD080FF1F064A04232E
|
||||
:40238000136002F688321268E0219064034A1170A2F202321360704780E100E002E400E0014B04221A60704700E100E0014B04221A60704780E100E0014B1870704700BF30
|
||||
:4023C0007E640040704738B505460078012428B100F066FD285D0134E4B2F8E738BD08B50D2000F05DFDBDE808400A2000F058BDF7B516461F460B4C002303250193009359
|
||||
:402400000A4601462846257000F00EF93A463146207800F0C9F80221207800F0B3F8207803B0F0BDE080FF1FF7B516461F460B4C00230225019300930A4601462846257030
|
||||
:4024400000F0F2F83A463146207800F0ADF82946207800F097F8207803B0F0BDE180FF1FF7B516461F460B4C00230125019300930A4601462846257000F0D6F83A463146DE
|
||||
:40248000207800F091F80221207800F07BF8207803B0F0BDE280FF1F73B515461E460B4C0023019300930A4601461846237000F0BBF832462946207800F076F8022120782A
|
||||
:4024C00000F060F8207802B070BD00BFE380FF1F024B1878C0F38010704700BF8F450040074A7F23802113705170064A013BDBB202F80839002BF9D1034A1370704700BFCC
|
||||
:40250000E480FF1FF87B00400078004017280FD8084B0001C25C11B142F0200201E002F0DF02C254C25C42F00102C25400207047012070471070004017280BD8064B0001EA
|
||||
:40254000C25C02F0FE02C254C25C02F0DF02C25400207047012070471070004017280DD8074900010B4603441A7942F004021A71435C43F001034354002070470120704740
|
||||
:402580001070004017280BD8064A0001835C490003F0F10301F00E011943815400207047012070471070004041F6FF73994208BF4FF400519A4208BF4FF4005217289FBF85
|
||||
:4025C000C00000F1804000F5EC4081809ABFC280002001207047000017289FBF034B00011954002088BF0120704700BF1970004017289FBF054B00011A5C01F007019DBFDC
|
||||
:402600001143195400200120704700BF1470004017289FBF034B0001185C00F0070088BFFF20704714700040172810B51AD8C00001F07F0100F1804441EAC21204F5EC4422
|
||||
:40264000D2B222709DF8082003F00F0343EA0213DBB263709DF80C30002003F00F03A370E07010BD012010BD10B500F079FC0A4A5378182B0AD91478013B5370E30003F1FB
|
||||
:40268000804303F5F0431B78137000E0FF2400F06BFC204610BD00BFE480FF1F030610B5044611D400F05CFC084AE300117803F1804303F5F0431970537814700133537094
|
||||
:4026C000BDE8104000F050BC10BD00BFE480FF1F30B504060CD411F4704509D1C40004F1804404F5F0442180A270E370284630BD012030BD03065FBFC00000F1804000F5D2
|
||||
:40270000F04081805ABFC280002001207047000038B50446084DB4F5004F05D9286800F017FCA4F50044F6E7034B58686043BDE8384000F00DBC00BFEC80FF1F024B1B7AB4
|
||||
:40274000584300F005BC00BFEC80FF1F0E4B00F003001A78490102F0FC02104318701A7801F0600142F080021A701A7802F07F021A701A7802F09F020A431A701A7842F060
|
||||
:4027800010021A70704700BF83430040014B01221A70704784430040044B00F00F021B6853F8220043F82210704700BF08ED00E0054A00F01F00126800F1100352F8230009
|
||||
:4027C00042F82310704700BF08ED00E000F01F0000F16040490100F56440C9B2017070470F4B10B50F4900240F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040334
|
||||
:4028000013600A4B4FF47A72DB68B3FBF2F3084A1360084B4FF400421C60C3F8E82010BD8492FF1F8D28000010E000E0EC80FF1F14E000E018E000E0024A136843F0020334
|
||||
:402840001360704710E000E008B5FFF7F5FF034A136843F00103136008BD00BF10E000E010B5054CA3691BB9FFF7BAFF0123A361BDE81040FFF7E8BF8492FF1F024B186891
|
||||
:40288000C0F30040704700BF10E000E038B5FFF7F5FF012808D1054D002455F8243003B198470134052CF8D138BD00BF8892FF1F024B03EB80035868596070478492FF1F1F
|
||||
:4028C000134B144A1B78DBB20360127843EA0223114A0360127843EA0243104A0360127843EA026303600E4B0E4A1B78DBB24360127843EA02230C4A4360127843EA024382
|
||||
:402900000A4A4360127843EA02634360704700BF0301004904010049EC460040020100490101004900010049050100490601004910B500F015FB204A044613780A2043F07D
|
||||
:4029400002031370137C43F00203137412F80A3C43F0010302F80A3C937943F00103937102F5AB52137843F003031370134B18221A7013F8012C42F0400203F8012C13F837
|
||||
:40298000012C02F0FC0203F8012CCE2203F8062CA3F597530222183B1A70094A137843F008031370FFF7CAFE064B10222046BDE810401A6000F0D8BAAB4300400E59004026
|
||||
:4029C0002F5B004080E200E008B500F0C9FA0F4A137803F0FE031370A2F5AA521D3A137803F0FD031370137C03F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE0388
|
||||
:402A00009371BDE8084000F0AFBA00BF08590040044A137803F03F0343EA8010C0B21070704700BF08590040082804D00A280CBF8223C22300E0422308380E4AC0B2042812
|
||||
:402A4000137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470A590040503A00005293FF1FB7
|
||||
:402A80005493FF1F5893FF1F08B5102000F0A6F907210420FFF79AFE07490420FFF788FE064A0C20137843F006031370FFF7BCFF034B00221A8008BD812B0000095900402E
|
||||
:402AC0005093FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF72ABFA092FF1F044B1A7802F0FB021A701A7842F001021A7070470859004010B5084B1C7814F0F1
|
||||
:402B0000010403D10028F9D0002404E02046FFF715FE024B1B78204610BD00BF09590040034A044B1B881088181A00B2704700BF5893FF1FA25B00400E4A13881BB223B1D5
|
||||
:402B400011880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475293FF1F5493FF1F5093FF1F29
|
||||
:402B80007047000010B500F0EBF9214A044613780A2043F001031370137C43F00103137412F80A3C43F0020302F80A3C937943F00203937102F5AA521832137843F0030343
|
||||
:402BC0001370144B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222123B1A70094A137843F008031370FFF79FFD074BEE
|
||||
:402C000008222046BDE810401A6000F0ADB900BFAB43004006590040275B004080E200E008B500F09DF90F4A137803F0FE031370A2F5AA52153A137803F0FE031370137C9E
|
||||
:402C400003F0FE03137412F80A3C03F0FD0302F80A3C937903F0FD039371BDE8084000F083B900BF00590040044A137803F03F0343EA8010C0B21070704700BF0059004016
|
||||
:402C8000082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBDD
|
||||
:402CC000F1F305490B60054B1A807047025900405A3A00005E93FF1F6493FF1F5C93FF1F08B5102000F084F807210320FFF76EFD07490320FFF75CFD064A0C20137843F035
|
||||
:402D000006031370FFF7BCFF034B00221A8008BDD92D0000015900406093FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF728BFA192FF1F044B1A7802F0FB02D6
|
||||
:402D40001A701A7842F001021A7070470059004010B5084B1C7814F0010403D10028F9D0002404E02046FFF7E9FC024B1B78204610BD00BF01590040034A044B1B88108822
|
||||
:402D8000181A00B2704700BF5C93FF1FA05B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF86
|
||||
:402DC0005B42134493FBF1F000B270475E93FF1F6493FF1F6093FF1F70470000034A00F0F800137803431370704700BF02410040034A00F0F800137803431370704700BF9A
|
||||
:402E000006410040014B1870704700BF78640040014B1870704700BF7965004073B515461E460B4C04230022019200920A46014618462370FFF7F8FB324629462078FFF7AB
|
||||
:402E4000B3FB02212078FFF79DFB207802B070BDFC80FF1F074A0223136002F688321268E0215064044A11706FF440710A441360704700BF80E100E001E400E0014B18708C
|
||||
:402E8000704700BF7C640040014B1870704700BF7B640040014B1870704700BF7F640040FEB5494652465B460EB40746244909688A46244A12682448022100F071F803009B
|
||||
:402EC00020480068C018204900F06AF8143883460121C9430C460125002600F041F8814651460B7823400B705846013000F030F83800F04028400B78234003430B705846AA
|
||||
:402F000000F026F80136072EF2D9002001300138013001200B78234003430B705846043000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF3008039
|
||||
:402F4000D480FF1FF880FF1F00C20100000000000230800803D000BF01380046FCD17047EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D181
|
||||
:402F800013790C2103F07F02044B01FB02339B7A00E013790020704700600040DC92FF1F002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB0B
|
||||
:402FC00011217047704700BF014B1868704700BF6081FF1F0E4B70B51E460E4C0025E41AA410A54204D056F8253098470135F8E700F0DEFD084B094C1E46E41AA41000251E
|
||||
:40300000A54204D056F8253098470135F8E770BDBC3B0000BC3B0000BC3B0000C43B000003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B6026
|
||||
:403040004FF0FF301DE04FF40273ADF80C300CBF234604F1FF33029305934FF6FF7300910491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B0022F6
|
||||
:403080001A701BB030BD000007B5009313460A46014603480068FFF7CBFF03B05DF804FB6081FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD025685F
|
||||
:4030C000096902236F1A656905EB450595FBF3F57B1C43449D4238BF1D4653050FD5294600F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A462B
|
||||
:4031000000F098FB064670B92169504600F0E8FA0C23CAF80030A3894FF0FF3043F04003A381BDE8F08726613E44266046466561ED1BA560464528BF4646494632462068B9
|
||||
:4031400000F0B3FAA36800209B1BA36023681E442660BDE8F08700002DE9F04F9DB003938B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F8D4
|
||||
:403180000030CDE040236B610023099320238DF82930DFF89CB130238DF82A3037463C4614F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF7EA
|
||||
:4031C00071FF013000F0A780099B4B4409933B78002B00F0A08000234FF0FF3204930793059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46E7
|
||||
:40320000801A06FA00F018430490EFE7D90644BF20228DF853201A0744BF2B228DF8532022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F002006C
|
||||
:40324000079204900BE027463B780134303B092B03D800FB02320121F5E701B107923B782E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E010
|
||||
:40328000002319460593781C0A2407463A780130303A092A03D804FB01210123F5E703B1059103223978224800F0E6F940B14023CBEB000003FA00F0049B01371843049053
|
||||
:4032C000397806221B487E1C8DF8281000F0D4F988B1194B33B9039B073323F007030833039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A937
|
||||
:40330000404600F093F8B0F1FF3F824603D0099B5344099342E7AB895B0601D4099801E04FF0FF301DB0BDE8F08F00BF8B3B0000913B0000953B000000000000A5300000D1
|
||||
:403340002DE9F04791461F460A698B6806469342B8BF1346C9F8003091F843200C46DDF8208012B10133C9F800302368990642BFD9F800300233C9F80030256815F00605F2
|
||||
:4033800010D104F1190A07E00123524639463046C04701301AD00135E368D9F800209B1A9D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F84E
|
||||
:4033C00045102244023382F8431003E04FF0FF30BDE8F08704F1430239463046C0470130F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500252C
|
||||
:403400009342C4BF9B1AED184FF000091A344D4509D00123224639463046C0470130D5D009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9BE3
|
||||
:4034400001F1430E00F0AE8011D8632A22D009D8002A00F0BB80582A40F0CA8081F84520834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B880A0
|
||||
:403480000A6842F020020A603EE0752A24D0782A3AD0ADE01A6801F14205111D1960136884F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F104011960E2
|
||||
:4034C00002D0B2F9003000E01368002B3CDA2D225B4284F8432037E021681A6811F0800F02D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A40
|
||||
:4035000014BF0A2208221BE078225A4984F845202268186812F0800F00F104051D6003D1550601D5038800E00368D00744BF42F0200222601BB9226822F020022260102266
|
||||
:40354000002084F8430001E049490A226568002DA56008DB206820F0040020602BB9002D7DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D131
|
||||
:40358000082A0BD12368DA0708D5236962689A42DEBF302305F8013C05F1FF35C5EB0E0323612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F1040087
|
||||
:4035C0001860136801D0198000E0196000232361754616E01A68111D1960156800216268284600F049F808B1401B6060636804E004F1420584F8422001232361002384F8A9
|
||||
:403600004330CDF800803B4603AA21463046FFF797FE013002D14FF0FF3026E023692A4639463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046F8
|
||||
:40364000C0470130E7D00135E368039A9B1A9D42F2DBE068039B9842B8BF184605E00B7804F1420584F842308AE705B0BDE8F0833F3A00009C3B000010B5C9B202449042CF
|
||||
:40368000034605D01C7801308C42F8D1184610BD002010BD10B5431E0A44914204D011F8014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21A41
|
||||
:4036C000D34204D013F8014D01F8014DF8E710BD994204D011F8014B02F8014FF8E710BD38B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B912
|
||||
:403700006360146030E0A3420DD92268A018834201BF18685B681218226063600C6023E0A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D1F2
|
||||
:4037400010685268014419605A600DE002D90C232B6009E021686018824201BF106852680918216062605C602846BDE8384000F098B838BDA892FF1F70B5CD1C25F003051A
|
||||
:4037800008350C2D38BF0C25002D064601DBA94202D90C23336046E000F082F8234B1C681A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B6896
|
||||
:4037C0001360636018BF0C4615E00C464968E9E7174C23681BB9304600F052F820602946304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B005E
|
||||
:40380000231D20F00700C31A0ED05A42E25070BD211A304600F034F80130EBD10C233360304600F03EF8002070BD00BFA892FF1FA492FF1FF8B5074615460E4621B91146D1
|
||||
:40384000BDE8F840FFF798BF1AB9FFF749FF2846F8BD00F027F885420ED929463846FFF78BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD40
|
||||
:4038800038B5064C0023054608462360FDF7D8FB431C02D1236803B12B6038BD8C93FF1F7047704751F8040C0028BEBF091851F8043CC018043870470000000005020902A2
|
||||
:4038C0000B020D020F021102130215027265706C792030782530327800686F6D696E6700626567696E6E696E67207365656B2066726F6D20256420746F2025640066696E08
|
||||
:403900006973686564207365656B00796573006E6F00647269766520303A20257320647269766520313A2025730057616974696E6720666F72205553422E2E2E00555342F3
|
||||
:4039400020726561647900636F6D6D616E6420307825303278006661696C2025642B25642B2564203D3D2025642C206E6F74202564007061737365643D256400756E646544
|
||||
:403980007272756E206166746572202564207061636B65747300636F756E743D256420693D256420643D256400636D645F777269746500703D25642063723D256420637729
|
||||
:4039C0003D256420663D256420773D256420696E6465783D256420756E64657272756E3D256400756E64657272756E21007375636365737300737461727420657261736912
|
||||
:403A00006E670073746F702065726173696E670069646C650000510040100040510040300000000140001000140140000800400140000A004C0140000200500140200030F9
|
||||
:403A400031323334353637383941424344454600000100000004000000100001000000040000001028000000000104000100000000000000000157494E5553420000303043
|
||||
:403A800030303100000000000000000012034D005300460054003100300030000100000001000000A83A000001000000773B0000000000000000000001000000C03A000003
|
||||
:403AC00001000000493B000004000000E23A0000000000000000000000000000E03A0000FF00000001024000FF00000082024000FF00000003034000FF00000084034000F7
|
||||
:403B0000FF00020304030904160346006C007500780045006E00670069006E0065002A0343006F0077006C00610072006B00200054006500630068006E006F006C006F0003
|
||||
:403B400067006900650073000009022E0001010080320904000004FF00000107050102400000070582024000000705030340000A0705840340000A12010002FF0001080921
|
||||
:403B800012006E0100020180014300232D302B2000686C4C00656667454647003031323334353637383961626364656600000000F8B500BFF8BC08BC9E4670475900000094
|
||||
:403BC000BD100000F8B500BFF8BC08BC9E46704735000000E83B0000C880FF1FA00000002812000000000000000000009093FF1FFF000000675000400C0000000700000097
|
||||
:403C0000FFFFFFFF7F8000003F0000000000007D00FA0000400000000090D003FF000000000000000000000000000000000000000000000000000000000000000000000031
|
||||
:403C4000893B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
|
||||
:403C80000081FF1F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000065
|
||||
:403CC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C4
|
||||
:403D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083
|
||||
:403D40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043
|
||||
@@ -4098,56 +4098,56 @@
|
||||
:40FF80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041
|
||||
:40FFC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
|
||||
:0200000480007A
|
||||
:40000000014500400752004003640040030101400A0301400D050140480601404907014054080140660901405D0A0140530B01404F0C0140530D0140560E0140380F0140F6
|
||||
:400040000D1501404E160140521701404E18014059190140461A01404E1B01400A4001400D4101400D420140074301400444014009450140064601400F4701400648014029
|
||||
:4000800008490140154C0140064D014007500140035101407E020841090210801180601461117C402B802E0A3B7E80208940E201861087018E019D40A021AC01E203E40803
|
||||
:4000C000E6C0EE0888028D808E2098409B019D40A021A201A610AC40E241E402EE080021024E030405030602080F09100A100E010F03100E12701308140E1503182E19037F
|
||||
:400100001A411D031E0822042301247F27022A0E2B032D032E01307F310F35103E013F10580459045B045C905F01800F8220830F840F85088610890F8C018D088E32910891
|
||||
:400140009702980199089A069D08A308A404A504A609A903AB0CAD01AE0FB038B407B70FBA20BF40D804D904DB04DC09DF010004024003020490050806880A040D090E0A99
|
||||
:400180001020120114401510162017401A041B021D01200124402612278028882A042B012D902E022F203080310832113501360437A0398A3A203C013D203E884040410141
|
||||
:4001C00068026DA08201834886808A418B108C088D448E048F409440984099889A209B019D40A021A201A402A612A908B440B602C0FDC2F2C4F5CAFFCCFFCEFFE207EE0C7B
|
||||
:40020000000F0210060F09C30B3C0C020E340F02134814801701180219501A051BAD212F23D0240F26202B842C012ECA30383240331F3407368037E039083A203E403F4047
|
||||
:40024000400545E0470C481249FF4AFF4BFF50045601580459045A045B045C995D095F0162C081018210842085018A048B048D018E02900193019409980999019E089F01D2
|
||||
:40028000A009A101A501A809A901AC60AF02B018B107B207B440B507B620B922BA02BE40D804D904DC99DF01009604400502071008800A580D080E0810041242140615200F
|
||||
:4002C0001708180519491AB31B051C401D081E0A1FD0211123C0271129042A012B0A2D022F04300231103340340136043710388039063B103C203D804E825608642067206F
|
||||
:400300006C476D0D6E206F087484759476977740788079407B018310878088148A848B088C448E1090109248931494A1955796A6978898529A409CA09D429E3F9FD1A010DB
|
||||
:40034000A170A20AA302A426A503A6A1A73DA901AA04AC08B001B210B310B680B780C0BFC26FC4ABCAAFCCEDCE3FD210E271E690E844EC100004011C030304020502080260
|
||||
:4003800009020C020F02100311021502160319021A021C011D031F042002210224022502280229082B012C022D103203331F340436043E443F04580459045C095F018301E2
|
||||
:4003C000854087308B028D488F109149940295019B049C019D49A301A548A720AB49AD01B002B140B307B401B538BB08BE11C032C106C204C604C7E0C824C9FFCAFFCBFF96
|
||||
:40040000CD20CEF0CF01D020D110D608D804D904DA04DB04DC90DD99DF01E108E440E520E680E708E840E920EB08EE40EFA801040241030808040A660C800E10100211016D
|
||||
:40044000121418101A091D011F1023102444260E29402BA42C042E0A2F0230103102324834813628380839023A203B803C453F20450847114D10540157405EA0640A6C01E7
|
||||
:400480006E406F02864088808EC090089210930494A3954596E3978898519A409CA69D429EA59FD0A010A178A424A501A605A76DAB10AC01B404B640C00FC2AFC40FCAFF09
|
||||
:4004C000CCFFCEFFD0E0D220D630D830E241E480E640EC010003020C030204800708088009030D031010130314031503167418101A401B041C801D032201230324022701EA
|
||||
:40050000281929032A622C102D032E28327F33033480350F368038083AF03F0452015610580459045B045C995D905F018001810F850886028A208B028D088E08907F91047F
|
||||
:400540009508967F9A3F9B0F9E3F9F08A03FA108A508A610A903AA04AB0CAC3FAE40AF01B23FB440B50FBE14BF10D804D904DB04DF010008010802010348041005020720DA
|
||||
:400580000AA60C200D880F801001110512041380142016021711181019201C221D181E042220240825302610272829082A812B042C042E802F603120320133483481362037
|
||||
:4005C000370838823A243D0A3E605850599069806A106D806F01814085408A038B048C04930494A595449640978099809A409C039D709EA0A184A310A510A615A705AB50DA
|
||||
:40060000AE04C07FC2FFC4FFCAFFCCFFCEFFE210E408E6C0EA04EE01010104020A010B501010151019D01C01210D240827052804290A2DA030083103320333C034103530DB
|
||||
:400640003604370C3E5140644502480149FF4AFF4BFF4D204EF0511056085804590B5A045B045C995D995F01610862406340648066406740810782018308840385568608C8
|
||||
:400680008721880189068A0C8B788D7F8E019001911192069326940697029A029B049E069F01A006A140A306A406A701A806A906AA01AC07AD06B37FB60FBE40BF04D8048C
|
||||
:4006C000D904DF01001002100485051008400A820C010E2A10201110154016A8185019841B101D80221523052504284029042A502D042EA02F40321434013550360438209C
|
||||
:400700003B013D203E483F0A4040482049205B5062506A807802830189108C20C0F6C2F9C4F6CAFFCCF6CEF5D001D204D60CD80CDE01E080830486028C088F209C089E02BB
|
||||
:40074000A304A720AD20B102B440E404E6080080048009060A030B010C300D040F01103D110412C014A915011702180C19081A401C8020802108245628802C8033083507CF
|
||||
:4007800036FF3B083E40580459045C905F018004810882098302840F85058610880189108A328C018D058E06910592C0952C970299059C409D059E80A00FA220A305A5059E
|
||||
:4007C000A60FA913AB20AD0FAF10B007B238B440B53FB680B8A0BA02BE50BF10D608D804D904DB04DC09DD90DF010006030A04900508078009400A400C010D080E0A124090
|
||||
:4008000013081448162219A21B0A1F8022202404250826102734288129042A202F803080310832113620370539993F9059105B40614063086A4081108350840189808A10EE
|
||||
:400840008B2090D0919992229310940498C0990C9A609B899D409E029F20A081A140A205A340A508A720A890AD11AF01B104B204B4C0B614C0FFC2F9C4FCCA8FCCEFCE3F53
|
||||
:40088000D60CD80CE004E202E685EA0EEE090201037F0401070208010B3F0C010D3F0E021001113F12021401173F1A011B041C011D3F20012308240127102A012B202C0123
|
||||
:4008C0002D012F403340353F36033E403F14580459045F018132821D841D85028710880189328A308C0C8D1F8E228F40901D91339304941D953299329A079C1D9D029F603C
|
||||
:40090000A001A208A332A41DA532A81DA933AB0CAD01AE1DAF38B10FB43CB570B603BE50BF11D804D904DB04DF01014802080382040205100610070209400AA40E620F08D3
|
||||
:4009400010111122138015051601172018011B101C02208023082620272028112B842D052E802F09312132083340359136083702380839403A223C083E113F404A014B4035
|
||||
:4009800058408220851089529082912592D19344942095109708985099D99AD19B809D029E20A004A130A218A38EA441A503A620A730A904AA40AC02AF80B0A1B401B5108D
|
||||
:4009C000B740C0FFC2FFC4FFCAFFCCFFCEFFD608E080E4A0E605EA52EC20EE900101020204010710093F0A040C010F081001113F1608172018081B021D3F1E012010230488
|
||||
:400A00002601273F2A012B3F2C012F3F3218333F34073A203F045608580459045B045C095D905F01803F830186028701883F8D018E089101923F962097029A3F9B019D0833
|
||||
:400A40009E3FA001A304A501A610A83FAD01AE04B107B308B63FB707BB82BE40BF04D804D904DB04DC90DF01002103220480051106100A910B040D020E210F08100512503A
|
||||
:400A80001524168118281A141B201F802020240225032762280429402A012B242C012E082F80300131103240330835103680370A388039043A103B023E413F0858805C40D5
|
||||
:400AC0005F2062808040872088808C018E489081912492C1930694289604980199519AD19B089F40A210A30EA503A720B304B612C0FFC2FFC4FFCADFCCFFCEDFD638D808A8
|
||||
:400B0000E240E8C01A801F0832403308358037083880C630CCF0CE10302037203B023D10810889808F0894809D809F08CC30CE60E2805108804086408F01948095109702C2
|
||||
:400B40009F20A108A420D420E280E44080209F20A420A640AD10E280EE1014808F20C404E408670887048E409B20A740A880AB40D801E602821096409B20A220A740AA20DE
|
||||
:400B80000A400B010E100F048301910496509A209B20A220A740AA20B104C20FE80426408B02A640AF20C820EA4073407F029340A302AF40DC80DE20EE40041008080C1038
|
||||
:400BC0000F80522056205B205D048018880291049A209B20A220A740AF04C001C20DD407D601E401740288109810A002DE04E20401010B010D010F0111011B011D0100BF9C
|
||||
:400C0000012A1105BF0000A09F001F000000000000000000100000004000000000000000C0000000FF0000B84700470000010000800000008000800000000000000400007C
|
||||
:400C4000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006F
|
||||
:400000000145004009520040015B0040016500400101014003030140010501400307014052080140500901404F0A0140480B0140490C0140470D0140400E0140360F01409E
|
||||
:4000400002150140031701405C18014057190140471A0140531B01400B400140124101400B4201400D43014002440140074501400B4601400C470140104801401149014069
|
||||
:40008000154C01400D4D014005500140045101407E02080209411080110218041902600C61157C402721290AE204E601EA20EE02E202E608EA10EE04000801080404050568
|
||||
:4000C0000708080809080C400D080E1011011201130E143A16401708180819081C021E042305240825042701280629082A782C202D012E402F02307F310F327F330F380AE4
|
||||
:400100003B0A580459045B045C995F018203830187018A108C018D018E248F08907C9282930E961097019A809D019E1C9F04A101A210A302A510A610AA10AB01AC01AE48FD
|
||||
:40014000B01FB10FB4F0B510BA20BB02BF10D608D80BD90BDB04DC99DD90DF01012202010488051007800A860B100C020DA00E2011011208134414021504162017201804BE
|
||||
:4001800019021A041B011CA01E202008220126012790282029202A012C022E282F4032493514360139013B443C803D2A5980614069906F0278417B0288049141928093D450
|
||||
:4001C00094249602970A982299249A019B409C019D019EC0A188A208A402A504A709B404B502B604C0FBC2FFC4EFCAF7CCEBCEFBD608D808DE01E608EA10EE0400FC0108A1
|
||||
:4002000002020310071408200AD00BE40CE10D100E080F8212031314161017141D1F1E101F4020802110223C2321261028402A302B142CE12E04301F310F33F034E03B08A2
|
||||
:40024000580B590B5B045C995F0180368209852A86128740882D89098A128B148E098F209080924093C09440968098079C249D089F13A03FA180A320A840A908ADEAB0076D
|
||||
:40028000B1E0B280B301B438B518B640B706BE44D808D904DB04DC99DF0100820120040A07050A420B100EA80F01110112091304171219201A401D8A1E081F4020602101B0
|
||||
:4002C000231025102652275029202A012C022F483249364239033B543D283E806102620169406C0C6F0178017F0187058C028D04900292809345946495029642971298206E
|
||||
:4003000099049B069C019D219E449F01A041A208A418A701B040B204B7C0C0FBC2FDC45FCAB5CC9BCE7FDE11E40800020104040806140A010C1C101012081301140215050D
|
||||
:400340001601170220032B022D062E022F0130043107320336183A083F015608580459045B045C095D905F018128829083118401853F86448A9C8C208D3F8EC090C191FFA2
|
||||
:40038000922895149690972199019A039B0E9D409E90A0FCA202A902AA90AB01ADC0AE90B140B330B41FB50FB6E0B780BA80D608D80BD90BDB04DC99DD90DF0101A604240E
|
||||
:4003C0000501070208400A240B400EA0120113121401164019801A041B401D841EA02080229C2344260129602C082F0230023294370539063A053B403F01580259025A0213
|
||||
:400400005B405F806140670168036A806B026C016F06780182019440950296E4974698209C429D209E049F02A412A580A741B240B420B540C0FFC23FC49BCA3CCCCFCE8BAA
|
||||
:40044000D618D818DE01EA20EE0203900401060407900A100B9C0D010E100F4812101520161017C01A031B031DFC1E1C1F02201C2202239025C127242A102B902C012E08A9
|
||||
:400480002F9033E0341F351F3B08420147E0482049FF4AFF4BFF4F83580859085A045B045C995D095F018310871C8B108D018F0893109F03A310A501A704AB10AD1CAF0254
|
||||
:4004C000B71FD908DB04DC90DF0100400120032008420A281110134219101A0820042120220826802701284229202A042B202C422D202F20312032483402368039123B4544
|
||||
:400500003C203D103F454180520159105A445B0160046208638269406E8078807C807F018201C007C20FC40BCAFFCC9ECEFFD008D60FD80FDE18EA01EE08E020E623EE0BF2
|
||||
:40054000022005880612083209880C080E010F011049118812321432151017A0182119461A1C1BB81D9A1E091F20206321422204230424322588282029882A022C322D04B6
|
||||
:400580003020313F324033C1341F37C1398A3A203E055608580459045B045C995D905F018401860289018D028E039002920199089B049D049F08A004B002B102B204B304D8
|
||||
:4005C000B401B501B708B822B988BE15BF55C043C520C802C9FFCAFFCBFFCD20CEF0D110D804D904DA04DB04DD09DF01E108E240E340E480E640E740000801400320051496
|
||||
:40060000060107400A800D800EA8108016441799181019021B101C401E081F0420012111220424A02505264227A02D202F52324033203504360137A039903C243E4040504B
|
||||
:40064000488049204A0459405A205D045E085F4064016580670268046A806C036E406F01831085118B4491D0938094A89504970A984099449B509C019D119EC89F20A0500A
|
||||
:40068000A188A211A402A535A708A980C0F5C2F8C4F8CAF0CCFCCE7CD003D61CD810E022E620EA04EE0B010129022C013102350136013E403F115608580459045B045D9048
|
||||
:4006C0005F0180478208830885218626874E880189018A388B708D4E8E40914E92029426974E98269B029D809E11A010A201A321A520A604A701A811AA26AB04AC26AD0FF1
|
||||
:40070000AF10B040B380B43FB57FB63FBAA0BB20BF04C003C50EC70CC811C9FFCAFFCBFFD004D601D804D904DA04DB04DC99DD09DF01E2C0040205940DA20E201002150580
|
||||
:40074000162017A01B401D111E011F102040221024042530260827082B042E642F40344035043602371038013C803D283E0145084F04570858905C406001630868026C042B
|
||||
:400780006D016E046F02768981028410860888808D028E049002910492409380940C96209724984099869A049BB09D119E899F08A040A18CA212A530A74AA881AE04B74049
|
||||
:4007C000C0F0C2F0C4F1CAF4CCF0CEF1D040D61CD80CE008E404E602E8041B011F083180330836843B408340C630CCF0CE10E22032043380364037023B043F808180A0042B
|
||||
:40080000A340A580A604AE80AF41B004CCF0CE60E680EE40531057208510960897049F02A004A644A780D460E240860491209608972498809F02A004A640A7A0AA04B48062
|
||||
:40084000E610EE201680C40458405E019A80A404AC04D401D6011B04844096019C40A404A710B680B710C608EA08EE0808080B080E020F4087048A209601A404A710AB043D
|
||||
:40088000C20FE00425808004871089808B04912097249880A004A720AB02AE40AF80C820E6C0EED0511054045880700477809008912098809B80A004AB80AF20D4E0DC8000
|
||||
:4008C000DE20EA80EE1005200A400C100F201C0852225620610186029641A220A404A710AA41AC08AF40C001C20DC601D407D80270018001852086018810960198109920A0
|
||||
:40090000AA20B501DC01E204EC0201010D010F0111011D0100FF01AB02021105BF0000A09F001F000000000000000000100000004000000000000000C0000000FF0000B88D
|
||||
:4009400047004700000100008000000282008200000000000007070007000000270018012700180100040000000500000000000000000000000000000000000000000000C4
|
||||
:400980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037
|
||||
:4009C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F7
|
||||
:400A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B6
|
||||
:400A40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076
|
||||
:400A80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036
|
||||
:400AC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F6
|
||||
:400B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B5
|
||||
:400B40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075
|
||||
:400B80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035
|
||||
:400BC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F5
|
||||
:400C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B4
|
||||
:400C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074
|
||||
:400C80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034
|
||||
:400CC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F4
|
||||
:400D000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B3
|
||||
@@ -4615,12 +4615,12 @@
|
||||
:0200000490105A
|
||||
:04000000BC90ACAF55
|
||||
:0200000490303A
|
||||
:0200000037A225
|
||||
:02000000CEC56B
|
||||
:0200000490402A
|
||||
:4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0
|
||||
:400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
|
||||
:400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
|
||||
:4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
:0200000490501A
|
||||
:0C00000000012E16106900002E2D480B88
|
||||
:0C00000000012E16106900002E2FDF2ECC
|
||||
:00000001FF
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -25,5 +25,3 @@ void `$INSTANCE_NAME`_Enable()
|
||||
void `$INSTANCE_NAME`_Disable()
|
||||
{
|
||||
}
|
||||
|
||||
/* [] END OF FILE */
|
||||
|
||||
@@ -20,33 +20,34 @@ module FIFOout (
|
||||
wire [7:0] po;
|
||||
assign d = po;
|
||||
|
||||
localparam STATE_WAITFORREQ = 1'b0;
|
||||
localparam STATE_READ = 1'b1;
|
||||
localparam STATE_WAITFORREQ = 0;
|
||||
localparam STATE_READFROMFIFO = 1;
|
||||
localparam STATE_WAITFORNREQ = 2;
|
||||
|
||||
reg state;
|
||||
reg oldreq;
|
||||
reg [1:0] state;
|
||||
wire readfromfifo;
|
||||
|
||||
assign ack = (state != STATE_READ);
|
||||
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)
|
||||
begin
|
||||
if (req && !oldreq)
|
||||
begin
|
||||
state <= STATE_READ;
|
||||
end
|
||||
oldreq <= req;
|
||||
end
|
||||
if (!empty && req)
|
||||
state <= STATE_READFROMFIFO;
|
||||
end
|
||||
|
||||
STATE_READ:
|
||||
begin
|
||||
state <= STATE_WAITFORREQ;
|
||||
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
|
||||
|
||||
@@ -55,11 +56,11 @@ cy_psoc3_dp #(.cy_dpconfig(
|
||||
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
|
||||
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
|
||||
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
|
||||
`CS_CMP_SEL_CFGA, /*CFGRAM0: STATE_WAITFORREQ*/
|
||||
`CS_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: STATE_LOAD*/
|
||||
`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,
|
||||
@@ -102,7 +103,7 @@ cy_psoc3_dp #(.cy_dpconfig(
|
||||
)) dp(
|
||||
/* input */ .reset(1'b0),
|
||||
/* input */ .clk(clk),
|
||||
/* input [02:00] */ .cs_addr({2'b0, state}),
|
||||
/* input [02:00] */ .cs_addr({2'b0, readfromfifo}),
|
||||
/* input */ .route_si(1'b0),
|
||||
/* input */ .route_ci(1'b0),
|
||||
/* input */ .f0_load(1'b0),
|
||||
|
||||
@@ -52,6 +52,30 @@
|
||||
<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" />
|
||||
@@ -171,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" />
|
||||
@@ -194,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" />
|
||||
@@ -265,6 +360,53 @@
|
||||
<Data key="sync_with_bus_clk" value="True" />
|
||||
<Data key="user_set_domain" value="False" />
|
||||
</Group>
|
||||
<Group key="09974428-e912-491f-8d2f-361ba50e7599">
|
||||
<Data key="check_tolerance" value="True" />
|
||||
<Data key="clock_version" value="v1" />
|
||||
<Data key="derive_type" value="NAMED_DIVIDER" />
|
||||
<Data key="desired_freq" value="0" />
|
||||
<Data key="desired_unit" value="15" />
|
||||
<Data key="divider" value="0" />
|
||||
<Data key="domain" value="DIGITAL" />
|
||||
<Data key="enabled" value="True" />
|
||||
<Data key="minus_accuracy" value="0.25" />
|
||||
<Data key="minus_tolerance" value="5" />
|
||||
<Data key="name" value="Clock_6" />
|
||||
<Data key="named_src_direct_connect" value="True" />
|
||||
<Data key="netlist_name" value="Clock_6" />
|
||||
<Data key="placement" value="AUTO" />
|
||||
<Data key="plus_accuracy" value="0.25" />
|
||||
<Data key="plus_tolerance" value="5" />
|
||||
<Data key="scope" value="LOCAL" />
|
||||
<Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" />
|
||||
<Data key="src_clk_name" value="BUS_CLK" />
|
||||
<Data key="start_on_reset" value="True" />
|
||||
<Data key="sync_with_bus_clk" value="True" />
|
||||
<Data key="user_set_domain" value="False" />
|
||||
</Group>
|
||||
<Group key="a5825a94-fa18-4e4f-a843-bc687cacbd56/696a0979-21fc-4185-bf38-6c79febcde7a">
|
||||
<Data key="check_tolerance" value="False" />
|
||||
<Data key="clock_version" value="v1" />
|
||||
<Data key="derive_type" value="AUTO" />
|
||||
<Data key="desired_freq" value="1600000" />
|
||||
<Data key="desired_unit" value="0" />
|
||||
<Data key="divider" value="40" />
|
||||
<Data key="domain" value="DIGITAL" />
|
||||
<Data key="enabled" value="True" />
|
||||
<Data key="minus_accuracy" value="0.25" />
|
||||
<Data key="minus_tolerance" value="5" />
|
||||
<Data key="name" value="INPUT_VOLTAGE_ADC_theACLK" />
|
||||
<Data key="netlist_name" value="\INPUT_VOLTAGE_ADC:theACLK\" />
|
||||
<Data key="placement" value="AUTO" />
|
||||
<Data key="plus_accuracy" value="0.25" />
|
||||
<Data key="plus_tolerance" value="5" />
|
||||
<Data key="scope" value="LOCAL" />
|
||||
<Data key="src_clk_id" value="61737EF6-3B74-48f9-8B91-F7473A442AE7" />
|
||||
<Data key="src_clk_name" value="MASTER_CLK" />
|
||||
<Data key="start_on_reset" value="True" />
|
||||
<Data key="sync_with_bus_clk" value="True" />
|
||||
<Data key="user_set_domain" value="False" />
|
||||
</Group>
|
||||
<Group key="b762c287-7f87-4b21-982e-84be01dc5115">
|
||||
<Data key="check_tolerance" value="True" />
|
||||
<Data key="clock_version" value="v1" />
|
||||
@@ -312,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" />
|
||||
@@ -336,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" />
|
||||
@@ -624,7 +814,7 @@
|
||||
</Group>
|
||||
<Group key="Component">
|
||||
<Group key="v1">
|
||||
<Data key="cy_boot" value="cy_boot_v5_80" />
|
||||
<Data key="cy_boot" value="cy_boot_v5_81" />
|
||||
<Data key="Em_EEPROM_Dynamic" value="Em_EEPROM_Dynamic_v2_20" />
|
||||
<Data key="LIN_Dynamic" value="LIN_Dynamic_v5_0" />
|
||||
</Group>
|
||||
@@ -634,31 +824,42 @@
|
||||
<Group key="Clock">
|
||||
<Data key="0b2f9bbb-00ce-4115-a788-ffb9d046a9e5" value="Clock_4" />
|
||||
<Data key="1a7e8637-3b6b-4e84-839c-0dfc18fdaf5b" value="Clock_5" />
|
||||
<Data key="3f3708ae-fb62-4012-919b-9a3b9a1dfbc2" value="Clock_8" />
|
||||
<Data key="4eef02b9-8ad1-43c4-85f1-b3335faa5fc4" value="Clock_3" />
|
||||
<Data key="06c4d5d4-f15f-4b29-a1d0-c24b2e38b1ec" value="CounterClock" />
|
||||
<Data key="24cd38f7-f472-4403-837f-86807c8f5333" value="PULSE_CLOCK" />
|
||||
<Data key="63ed4137-0b09-4256-8a27-35c9a2653f1a" value="Clock_2" />
|
||||
<Data key="66f14071-bddd-4b4d-a9aa-a129cceaa7b6" value="Clock_3" />
|
||||
<Data key="71bc291d-84a7-40a8-b7b2-1c8a34326a31" value="CLOCK300" />
|
||||
<Data key="90ce0c72-9f10-44ef-a049-f0f525d59bea" value="CLOCK8" />
|
||||
<Data key="349ffa20-8576-4ac3-9a6f-34ef606de6cf" value="Clock_1" />
|
||||
<Data key="4033c29d-f4bc-4e94-ac95-aa587e869f88/696a0979-21fc-4185-bf38-6c79febcde7a" value="OUTPUT_VOLTAGE_ADC_theACLK" />
|
||||
<Data key="6616e828-6611-4893-a674-66c861d79d6c" value="SignalSamplingClock" />
|
||||
<Data key="12664fc6-9d70-44b1-8a49-887a292e1b7f" value="Clock_3" />
|
||||
<Data key="75187c05-9501-4450-b306-6ccdd3bb77db" value="Clock_5" />
|
||||
<Data key="09974428-e912-491f-8d2f-361ba50e7599" value="Clock_6" />
|
||||
<Data key="a5825a94-fa18-4e4f-a843-bc687cacbd56/696a0979-21fc-4185-bf38-6c79febcde7a" value="INPUT_VOLTAGE_ADC_theACLK" />
|
||||
<Data key="b762c287-7f87-4b21-982e-84be01dc5115" value="Clock_2" />
|
||||
<Data key="b0162966-0060-4af5-82d1-fcb491ad7619/be0a0e37-ad17-42ca-b5a1-1a654d736358" value="UART_IntClock" />
|
||||
<Data key="b722443b-8f81-46dc-bf9b-c95eb62bc181" value="Clock_1" />
|
||||
<Data key="cb7e877c-9fb4-4fc1-a708-f1e48eb5a68c" value="CounterClock" />
|
||||
<Data key="d3075dc6-05c8-4dc9-9959-cf7014c0e66f" value="Clock_7" />
|
||||
<Data key="e4a53a4c-40e1-4747-a72a-10193ffdf31c" value="Clock_1" />
|
||||
<Data key="efd5f185-0c32-4824-ba72-3ceb5356f5a7" value="Clock_1" />
|
||||
</Group>
|
||||
<Group key="Pin">
|
||||
<Data key="3e1862bb-be82-47b0-9549-7ebfe76b6f7b" value="Pin_2" />
|
||||
<Data key="4a398466-709f-4228-9500-96178658e13e" value="RDATA" />
|
||||
<Data key="5a3407c1-b434-4438-a7b4-b9dfd2280495" value="MOTEA" />
|
||||
<Data key="8d318d8b-cf7b-4b6b-b02c-ab1c5c49d0ba" value="SW1" />
|
||||
<Data key="8fc20a4f-e4d1-44b3-a5d4-546e8628d61e" value="LED" />
|
||||
<Data key="12e00eac-69b5-4717-85c8-25ef6b224d4c" value="DEBUG_PINS" />
|
||||
<Data key="41e2d8ed-5494-4d8c-8ff7-f4f789cece51" value="REDWC" />
|
||||
<Data key="264be2d3-9481-494b-8d9c-c1905a45e9cc" value="FDD" />
|
||||
<Data key="472f8fdb-f772-44fb-8897-cc690694237b" value="WDATA" />
|
||||
<Data key="736cb12b-c863-43d4-a8f0-42f06023f8b5" value="SIDE1" />
|
||||
<Data key="4249c923-fcff-453b-8629-bec6fddd00c1" value="STEP" />
|
||||
<Data key="27315b0e-6a8c-4b7f-be77-73ab434fa803" value="Pin_1" />
|
||||
<Data key="1425177d-0d0e-4468-8bcc-e638e5509a9b" value="UartRx" />
|
||||
<Data key="a5d987c6-e45b-45b9-ad93-656fab06d720" value="TRK00" />
|
||||
<Data key="a93ef5b3-00f4-42c0-8fad-0e275a7e2537" value="MOTEB" />
|
||||
@@ -670,10 +871,12 @@
|
||||
<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_PIN" />
|
||||
<Data key="e16b5ef8-00d3-40a4-bc1c-194983c8eb3d" value="LOW_CURRENT" />
|
||||
<Data key="e851a3b9-efb8-48be-bbb8-b303b216c393" value="INDEX300" />
|
||||
<Data key="e51063a9-4fad-40c7-a06b-7cc4b137dc18" value="DSKCHG" />
|
||||
<Data key="ea7ee228-8b3f-426c-8bb8-cd7a81937769" value="DIR" />
|
||||
<Data key="ed092b9b-d398-4703-be89-cebf998501f6" value="UartTx" />
|
||||
<Data key="f9a7371a-8a7d-4144-8b08-69e3d2a3a663" value="INDEX360" />
|
||||
<Data key="fbd1f839-40f9-498e-a48b-5f3048ea5c3d/52f31aa9-2f0a-497d-9a1f-1424095e13e6" value="UART_tx" />
|
||||
<Data key="fede1767-f3fd-4021-b3d7-8f9d88f36f9b" value="DRVSA" />
|
||||
<Data key="fff78075-035e-43d7-8577-bc5be4d21926" value="WGATE" />
|
||||
@@ -3763,6 +3966,11 @@
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="Pin2">
|
||||
<Group key="3e1862bb-be82-47b0-9549-7ebfe76b6f7b">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="0,6" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="4a398466-709f-4228-9500-96178658e13e">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="1,5" />
|
||||
@@ -3778,6 +3986,11 @@
|
||||
<Data key="Port Format" value="2,2" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="8fc20a4f-e4d1-44b3-a5d4-546e8628d61e">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="2,1" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="12e00eac-69b5-4717-85c8-25ef6b224d4c">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="2,2" />
|
||||
@@ -3859,6 +4072,11 @@
|
||||
<Data key="Port Format" value="1,0" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="27315b0e-6a8c-4b7f-be77-73ab434fa803">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="0,7" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="1425177d-0d0e-4468-8bcc-e638e5509a9b">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="12,6" />
|
||||
@@ -3944,9 +4162,14 @@
|
||||
<Data key="Port Format" value="12,3" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="e16b5ef8-00d3-40a4-bc1c-194983c8eb3d">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="3,2" />
|
||||
</Group>
|
||||
</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">
|
||||
@@ -3964,6 +4187,11 @@
|
||||
<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="12,7" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
83
FluxEngine.cydsn/Sampler/Sampler.v
Normal file
83
FluxEngine.cydsn/Sampler/Sampler.v
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
//`#start header` -- edit after this line, do not edit this line
|
||||
`include "cypress.v"
|
||||
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
// Generated on 12/11/2019 at 21:18
|
||||
// Component: Sampler
|
||||
module Sampler (
|
||||
output [2:0] debug_state,
|
||||
output reg [7:0] opcode,
|
||||
output reg req,
|
||||
input clock,
|
||||
input index,
|
||||
input rdata,
|
||||
input reset,
|
||||
input sampleclock
|
||||
);
|
||||
|
||||
//`#start body` -- edit after this line, do not edit this line
|
||||
|
||||
// NOTE: Reset pulse is used in both clock domains, and must be long enough
|
||||
// to be detected in both.
|
||||
|
||||
reg [5:0] counter;
|
||||
|
||||
reg index_q;
|
||||
reg rdata_q;
|
||||
|
||||
reg index_edge;
|
||||
reg rdata_edge;
|
||||
|
||||
reg req_toggle;
|
||||
|
||||
always @(posedge sampleclock)
|
||||
begin
|
||||
if (reset)
|
||||
begin
|
||||
index_edge <= 0;
|
||||
rdata_edge <= 0;
|
||||
index_q <= 0;
|
||||
rdata_q <= 0;
|
||||
counter <= 0;
|
||||
req_toggle <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
/* Both index and rdata are active high -- positive-going edges
|
||||
* indicate the start of an index pulse and read pulse, respectively.
|
||||
*/
|
||||
|
||||
index_edge <= index && !index_q;
|
||||
index_q <= index;
|
||||
|
||||
rdata_edge <= rdata && !rdata_q;
|
||||
rdata_q <= rdata;
|
||||
|
||||
if (rdata_edge || index_edge || (counter == 6'h3f)) begin
|
||||
opcode <= { rdata_edge, index_edge, counter };
|
||||
req_toggle <= ~req_toggle;
|
||||
counter <= 1; /* remember to count this tick */
|
||||
end else begin
|
||||
counter <= counter + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
reg req_toggle_q;
|
||||
|
||||
always @(posedge clock)
|
||||
begin
|
||||
if (reset) begin
|
||||
req_toggle_q <= 0;
|
||||
req <= 0;
|
||||
end else begin
|
||||
req_toggle_q <= req_toggle;
|
||||
req <= (req_toggle != req_toggle_q);
|
||||
end
|
||||
end
|
||||
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
endmodule
|
||||
//`#start footer` -- edit after this line, do not edit this line
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
@@ -18,21 +18,16 @@ module Sequencer (
|
||||
|
||||
//`#start body` -- edit after this line, do not edit this line
|
||||
|
||||
localparam STATE_IDLE = 0;
|
||||
localparam STATE_LOAD = 1;
|
||||
localparam STATE_WAITING = 2;
|
||||
localparam STATE_PULSING = 3;
|
||||
localparam STATE_INDEXING = 4;
|
||||
localparam STATE_LOAD = 0;
|
||||
localparam STATE_WRITING = 1;
|
||||
|
||||
localparam OPCODE_PULSE = 8'h80;
|
||||
localparam OPCODE_INDEX = 8'h81;
|
||||
reg state;
|
||||
reg [5:0] countdown;
|
||||
reg pulsepending;
|
||||
|
||||
reg [2:0] state;
|
||||
reg [6:0] countdown;
|
||||
|
||||
assign req = (state == STATE_LOAD);
|
||||
assign wdata = (state == STATE_PULSING);
|
||||
assign debug_state = state;
|
||||
assign req = (!reset && (state == STATE_LOAD));
|
||||
assign wdata = (!reset && (state == STATE_WRITING) && (countdown == 0) && pulsepending);
|
||||
assign debug_state = 0;
|
||||
|
||||
reg olddataclock;
|
||||
wire dataclocked;
|
||||
@@ -40,9 +35,7 @@ always @(posedge clock) olddataclock <= dataclock;
|
||||
assign dataclocked = !olddataclock && dataclock;
|
||||
|
||||
reg oldsampleclock;
|
||||
wire sampleclocked;
|
||||
always @(posedge clock) oldsampleclock <= sampleclock;
|
||||
assign sampleclocked = !oldsampleclock && sampleclock;
|
||||
reg sampleclocked;
|
||||
|
||||
reg oldindex;
|
||||
wire indexed;
|
||||
@@ -53,46 +46,44 @@ always @(posedge clock)
|
||||
begin
|
||||
if (reset)
|
||||
begin
|
||||
state <= STATE_IDLE;
|
||||
state <= STATE_LOAD;
|
||||
countdown <= 0;
|
||||
pulsepending <= 0;
|
||||
oldsampleclock <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if (!oldsampleclock && sampleclock)
|
||||
sampleclocked <= 1;
|
||||
oldsampleclock <= sampleclock;
|
||||
|
||||
case (state)
|
||||
STATE_IDLE:
|
||||
state <= STATE_LOAD;
|
||||
|
||||
STATE_LOAD:
|
||||
begin
|
||||
/* A posedge on dataclocked indicates that another 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
|
||||
begin
|
||||
pulsepending <= opcode[7];
|
||||
countdown <= opcode[5:0];
|
||||
|
||||
state <= STATE_WRITING;
|
||||
end
|
||||
end
|
||||
|
||||
STATE_WAITING:
|
||||
STATE_WRITING:
|
||||
begin
|
||||
if (sampleclocked)
|
||||
begin
|
||||
if (countdown == 0)
|
||||
state <= STATE_LOAD;
|
||||
else
|
||||
countdown <= countdown - 1;
|
||||
sampleclocked <= 0;
|
||||
end
|
||||
|
||||
STATE_PULSING:
|
||||
state <= STATE_LOAD;
|
||||
|
||||
STATE_INDEXING:
|
||||
if (indexed)
|
||||
state <= STATE_LOAD;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
//`#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.
@@ -5,7 +5,6 @@
|
||||
#include <setjmp.h>
|
||||
#include "project.h"
|
||||
#include "../protocol.h"
|
||||
#include "../lib/common/crunch.h"
|
||||
|
||||
#define MOTOR_ON_TIME 5000 /* milliseconds */
|
||||
#define STEP_INTERVAL_TIME 6 /* ms */
|
||||
@@ -14,19 +13,26 @@
|
||||
#define DISKSTATUS_WPT 1
|
||||
#define DISKSTATUS_DSKCHG 2
|
||||
|
||||
#define STEP_TOWARDS0 1
|
||||
#define STEP_AWAYFROM0 0
|
||||
#define STEP_TOWARDS0 0
|
||||
#define STEP_AWAYFROM0 1
|
||||
|
||||
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;
|
||||
/* Duration in ms. 0 causes every pulse to be an index pulse. Durations since
|
||||
* last pulse greater than this value imply sector pulse. Otherwise is an index
|
||||
* pulse. */
|
||||
static volatile uint32_t hardsec_index_threshold = 0;
|
||||
|
||||
static bool motor_on = false;
|
||||
static uint32_t motor_on_time = 0;
|
||||
static bool homed = false;
|
||||
static int current_track = 0;
|
||||
static uint8_t current_drive_flags = 0;
|
||||
static struct set_drive_frame current_drive_flags;
|
||||
|
||||
#define BUFFER_COUNT 16
|
||||
#define BUFFER_COUNT 64 /* the maximum */
|
||||
#define BUFFER_SIZE 64
|
||||
static uint8_t td[BUFFER_COUNT];
|
||||
static uint8_t dma_buffer[BUFFER_COUNT][BUFFER_SIZE] __attribute__((aligned()));
|
||||
@@ -41,21 +47,60 @@ 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;
|
||||
}
|
||||
|
||||
CY_ISR(index_irq_cb)
|
||||
{
|
||||
index_irq = true;
|
||||
/* Hard sectored media has sector pulses at the beginning of every sector
|
||||
* and the index pulse is an extra pulse in the middle of the last sector.
|
||||
* When the extra pulse is seen, the next sector pulse is also the start of
|
||||
* the track. */
|
||||
static bool hardsec_index_irq_primed = false;
|
||||
static uint32_t hardsec_last_pulse_time = 0;
|
||||
|
||||
if (!hardsec_index_threshold)
|
||||
{
|
||||
index_irq = true;
|
||||
hardsec_index_irq_primed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
index_irq = hardsec_index_irq_primed;
|
||||
if (index_irq)
|
||||
hardsec_index_irq_primed = false;
|
||||
else
|
||||
hardsec_index_irq_primed =
|
||||
clock - hardsec_last_pulse_time <= hardsec_index_threshold;
|
||||
hardsec_last_pulse_time = clock;
|
||||
}
|
||||
|
||||
/* Stop writing the instant the index pulse comes along; it may take a few
|
||||
* moments for the main code to notice the pulse, and we don't want to overwrite
|
||||
* the beginning of the track. */
|
||||
ERASE_REG_Write(0);
|
||||
if (index_irq)
|
||||
ERASE_REG_Write(0);
|
||||
}
|
||||
|
||||
CY_ISR(capture_dma_finished_irq_cb)
|
||||
@@ -85,10 +130,25 @@ static void print(const char* msg, ...)
|
||||
UART_PutCRLF();
|
||||
}
|
||||
|
||||
static void set_drive_flags(struct set_drive_frame* flags)
|
||||
{
|
||||
if (current_drive_flags.drive != flags->drive)
|
||||
{
|
||||
stop_motor();
|
||||
homed = false;
|
||||
}
|
||||
|
||||
current_drive_flags = *flags;
|
||||
DRIVESELECT_REG_Write(flags->drive ? 2 : 1); /* select drive 1 or 0 */
|
||||
DENSITY_REG_Write(flags->high_density); /* density bit */
|
||||
INDEX_REG_Write(flags->index_mode);
|
||||
}
|
||||
|
||||
static void start_motor(void)
|
||||
{
|
||||
if (!motor_on)
|
||||
{
|
||||
set_drive_flags(¤t_drive_flags);
|
||||
MOTOR_REG_Write(1);
|
||||
CyDelay(1000);
|
||||
homed = false;
|
||||
@@ -99,12 +159,28 @@ static void start_motor(void)
|
||||
CyWdtClear();
|
||||
}
|
||||
|
||||
static void stop_motor(void)
|
||||
{
|
||||
if (motor_on)
|
||||
{
|
||||
MOTOR_REG_Write(0);
|
||||
DRIVESELECT_REG_Write(0); /* deselect all drives */
|
||||
motor_on = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void wait_until_writeable(int ep)
|
||||
{
|
||||
while (USBFS_GetEPState(ep) != USBFS_IN_BUFFER_EMPTY)
|
||||
;
|
||||
}
|
||||
|
||||
static void wait_until_readable(int ep)
|
||||
{
|
||||
while (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_FULL)
|
||||
;
|
||||
}
|
||||
|
||||
static void send_reply(struct any_frame* f)
|
||||
{
|
||||
print("reply 0x%02x", f->f.type);
|
||||
@@ -122,9 +198,15 @@ static void send_error(int code)
|
||||
/* buffer must be big enough for a frame */
|
||||
static int usb_read(int ep, uint8_t buffer[FRAME_SIZE])
|
||||
{
|
||||
if (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_FULL)
|
||||
{
|
||||
USBFS_EnableOutEP(ep);
|
||||
wait_until_readable(ep);
|
||||
}
|
||||
|
||||
int length = USBFS_GetEPCount(ep);
|
||||
USBFS_ReadOutEP(ep, buffer, length);
|
||||
while (USBFS_GetEPState(ep) == USBFS_OUT_BUFFER_FULL)
|
||||
while (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_EMPTY)
|
||||
;
|
||||
return length;
|
||||
}
|
||||
@@ -138,25 +220,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 +260,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)
|
||||
{
|
||||
@@ -186,6 +275,8 @@ static void seek_to(int track)
|
||||
CyWdtClear();
|
||||
}
|
||||
CyDelay(STEP_SETTLING_TIME);
|
||||
|
||||
TK43_REG_Write(track < 43); /* high if 0..42, low if 43 or up */
|
||||
print("finished seek");
|
||||
}
|
||||
|
||||
@@ -204,30 +295,48 @@ static void cmd_recalibrate(void)
|
||||
send_reply(&r);
|
||||
}
|
||||
|
||||
static void cmd_measure_speed(struct any_frame* f)
|
||||
static void cmd_measure_speed(struct measurespeed_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)
|
||||
{
|
||||
int target_pulse_count = f->hard_sector_count + 1;
|
||||
start_clock = clock;
|
||||
for (int x=0; x<target_pulse_count; x++)
|
||||
{
|
||||
index_irq = false;
|
||||
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 +345,44 @@ static void cmd_bulk_test(struct any_frame* f)
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, buffer, sizeof(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_TEST_REPLY);
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_WRITE_TEST_REPLY);
|
||||
send_reply(&r);
|
||||
}
|
||||
|
||||
static void cmd_bulk_read_test(struct any_frame* f)
|
||||
{
|
||||
uint8_t buffer[64];
|
||||
|
||||
bool passed = true;
|
||||
for (int x=0; x<64; x++)
|
||||
{
|
||||
CyWdtClear();
|
||||
for (int y=0; y<256; y++)
|
||||
{
|
||||
usb_read(FLUXENGINE_DATA_OUT_EP_NUM, buffer);
|
||||
for (unsigned z=0; z<sizeof(buffer); z++)
|
||||
{
|
||||
if (buffer[z] != (uint8)(x+y+z))
|
||||
{
|
||||
print("fail %d+%d+%d == %d, not %d", x, y, z, buffer[z], (uint8)(x+y+z));
|
||||
passed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("passed=%d", passed);
|
||||
if (passed)
|
||||
{
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_READ_TEST_REPLY);
|
||||
send_reply(&r);
|
||||
}
|
||||
else
|
||||
send_error(F_ERROR_INVALID_VALUE);
|
||||
}
|
||||
|
||||
static void deinit_dma(void)
|
||||
{
|
||||
for (int i=0; i<BUFFER_COUNT; i++)
|
||||
@@ -249,8 +391,8 @@ static void deinit_dma(void)
|
||||
|
||||
static void init_capture_dma(void)
|
||||
{
|
||||
dma_channel = CAPTURE_DMA_DmaInitialize(
|
||||
2 /* bytes */,
|
||||
dma_channel = SAMPLER_DMA_DmaInitialize(
|
||||
1 /* bytes */,
|
||||
true /* request per burst */,
|
||||
HI16(CYDEV_PERIPH_BASE),
|
||||
HI16(CYDEV_SRAM_BASE));
|
||||
@@ -264,47 +406,46 @@ 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]));
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_read(struct read_frame* f)
|
||||
{
|
||||
SIDE_REG_Write(f->side);
|
||||
seek_to(current_track);
|
||||
SIDE_REG_Write(f->side);
|
||||
STEP_REG_Write(f->side); /* for drives which multiplex SIDE and DIR */
|
||||
|
||||
/* 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. */
|
||||
|
||||
index_irq = false;
|
||||
while (!index_irq)
|
||||
;
|
||||
index_irq = false;
|
||||
|
||||
crunch_state_t cs = {};
|
||||
cs.outputptr = usb_buffer;
|
||||
cs.outputlen = BUFFER_SIZE;
|
||||
if (f->synced)
|
||||
{
|
||||
hardsec_index_threshold = f->hardsec_threshold_ms;
|
||||
index_irq = false;
|
||||
while (!index_irq)
|
||||
;
|
||||
index_irq = false;
|
||||
hardsec_index_threshold = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -312,73 +453,68 @@ static void cmd_read(struct read_frame* f)
|
||||
/* Wait for the first DMA transfer to complete, after which we can start the
|
||||
* USB transfer. */
|
||||
|
||||
while ((dma_writing_to_td == 0) && !index_irq)
|
||||
while (dma_writing_to_td == 0)
|
||||
;
|
||||
dma_reading_from_td = 0;
|
||||
bool dma_running = true;
|
||||
|
||||
/* Start transferring. */
|
||||
|
||||
int revolutions = f->revolutions;
|
||||
while (!dma_underrun)
|
||||
uint32_t start_time = clock;
|
||||
for (;;)
|
||||
{
|
||||
CyWdtClear();
|
||||
|
||||
/* Have we reached the index pulse? */
|
||||
if (index_irq)
|
||||
{
|
||||
index_irq = false;
|
||||
revolutions--;
|
||||
if (revolutions == 0)
|
||||
break;
|
||||
}
|
||||
/* If the sample session is over, stop reading but continue processing until
|
||||
* the DMA chain is empty. */
|
||||
|
||||
/* Wait for the next block to be read. */
|
||||
while (dma_reading_from_td == dma_writing_to_td)
|
||||
if ((clock - start_time) >= f->milliseconds)
|
||||
{
|
||||
/* On an underrun, give up immediately. */
|
||||
if (dma_underrun)
|
||||
goto abort;
|
||||
}
|
||||
|
||||
uint8_t dma_buffer_usage = 0;
|
||||
while (dma_buffer_usage < BUFFER_SIZE)
|
||||
{
|
||||
cs.inputptr = dma_buffer[dma_reading_from_td] + dma_buffer_usage;
|
||||
cs.inputlen = BUFFER_SIZE - dma_buffer_usage;
|
||||
crunch(&cs);
|
||||
dma_buffer_usage += BUFFER_SIZE - cs.inputlen;
|
||||
count++;
|
||||
if (cs.outputlen == 0)
|
||||
if (dma_running)
|
||||
{
|
||||
while (USBFS_GetEPState(FLUXENGINE_DATA_IN_EP_NUM) != USBFS_IN_BUFFER_EMPTY)
|
||||
{
|
||||
if (index_irq || dma_underrun)
|
||||
goto abort;
|
||||
}
|
||||
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE);
|
||||
cs.outputptr = usb_buffer;
|
||||
cs.outputlen = BUFFER_SIZE;
|
||||
CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN);
|
||||
while (CyDmaChGetRequest(dma_channel))
|
||||
;
|
||||
dma_running = false;
|
||||
dma_underrun = false;
|
||||
}
|
||||
}
|
||||
dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td);
|
||||
|
||||
/* If there's an underrun event, stop immediately. */
|
||||
|
||||
if (dma_underrun)
|
||||
goto abort;
|
||||
|
||||
/* If there are no more blocks to be read, check to see if we've finished. */
|
||||
|
||||
if (dma_reading_from_td == dma_writing_to_td)
|
||||
{
|
||||
/* Also if we've run out of blocks to send. */
|
||||
|
||||
if (!dma_running)
|
||||
goto abort;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, there's a block waiting, so attempt to send it. */
|
||||
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, dma_buffer[dma_reading_from_td], BUFFER_SIZE);
|
||||
count++;
|
||||
dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td);
|
||||
}
|
||||
}
|
||||
abort:;
|
||||
CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN);
|
||||
while (CyDmaChGetRequest(dma_channel))
|
||||
;
|
||||
bool saved_dma_underrun = dma_underrun;
|
||||
|
||||
donecrunch(&cs);
|
||||
/* Terminate the transfer (all transfers are an exact number of fragments). */
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
unsigned zz = cs.outputlen;
|
||||
if (cs.outputlen != BUFFER_SIZE)
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE-cs.outputlen);
|
||||
if ((cs.outputlen == BUFFER_SIZE) || (cs.outputlen == 0))
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, NULL, 0);
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, NULL, 0);
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
deinit_dma();
|
||||
|
||||
if (dma_underrun)
|
||||
STEP_REG_Write(0);
|
||||
if (saved_dma_underrun)
|
||||
{
|
||||
print("underrun after %d packets");
|
||||
send_error(F_ERROR_UNDERRUN);
|
||||
@@ -388,7 +524,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)
|
||||
@@ -423,35 +559,34 @@ static void cmd_write(struct write_frame* f)
|
||||
return;
|
||||
}
|
||||
|
||||
SEQUENCER_CONTROL_Write(1); /* put the sequencer into reset */
|
||||
|
||||
seek_to(current_track);
|
||||
SIDE_REG_Write(f->side);
|
||||
STEP_REG_Write(f->side); /* for drives which multiplex SIDE and DIR */
|
||||
|
||||
SEQUENCER_CONTROL_Write(1); /* put the sequencer into reset */
|
||||
{
|
||||
uint8_t i = CyEnterCriticalSection();
|
||||
REPLAY_FIFO_SET_LEVEL_NORMAL;
|
||||
REPLAY_FIFO_SET_LEVEL_MID;
|
||||
REPLAY_FIFO_CLEAR;
|
||||
REPLAY_FIFO_SINGLE_BUFFER_UNSET;
|
||||
CyExitCriticalSection(i);
|
||||
}
|
||||
seek_to(current_track);
|
||||
|
||||
init_replay_dma();
|
||||
bool writing = false; /* to the disk */
|
||||
bool finished = false;
|
||||
int packets = f->bytes_to_write / FRAME_SIZE;
|
||||
bool finished = (packets == 0);
|
||||
int count_written = 0;
|
||||
int count_read = 0;
|
||||
dma_writing_to_td = 0;
|
||||
dma_reading_from_td = -1;
|
||||
dma_underrun = false;
|
||||
|
||||
crunch_state_t cs = {};
|
||||
cs.outputlen = BUFFER_SIZE;
|
||||
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
|
||||
int old_reading_from_td = -1;
|
||||
for (;;)
|
||||
{
|
||||
//CyWdtClear();
|
||||
|
||||
/* Read data from USB into the buffers. */
|
||||
|
||||
if (NEXT_BUFFER(dma_writing_to_td) != dma_reading_from_td)
|
||||
@@ -459,54 +594,25 @@ static void cmd_write(struct write_frame* f)
|
||||
if (writing && (dma_underrun || index_irq))
|
||||
goto abort;
|
||||
|
||||
/* Read crunched data, if necessary. */
|
||||
|
||||
if (cs.inputlen == 0)
|
||||
uint8_t* buffer = dma_buffer[dma_writing_to_td];
|
||||
if (finished)
|
||||
{
|
||||
if (finished)
|
||||
{
|
||||
/* There's no more data to read, so fake some. */
|
||||
|
||||
for (int i=0; i<BUFFER_SIZE; i++)
|
||||
usb_buffer[i+0] = 0x7f;
|
||||
cs.inputptr = usb_buffer;
|
||||
cs.inputlen = BUFFER_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) != USBFS_OUT_BUFFER_FULL)
|
||||
{
|
||||
if (writing && (dma_underrun || index_irq))
|
||||
goto abort;
|
||||
}
|
||||
|
||||
int length = usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer);
|
||||
cs.inputptr = usb_buffer;
|
||||
cs.inputlen = length;
|
||||
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
|
||||
count_read++;
|
||||
if ((length < FRAME_SIZE) || (count_read == packets))
|
||||
finished = true;
|
||||
}
|
||||
/* There's no more data to read, so fake some. */
|
||||
|
||||
memset(buffer, 0x3f, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/* If there *is* data waiting in the buffer, uncrunch it. */
|
||||
|
||||
if (cs.inputlen != 0)
|
||||
else
|
||||
{
|
||||
cs.outputptr = dma_buffer[dma_writing_to_td] + BUFFER_SIZE - cs.outputlen;
|
||||
uncrunch(&cs);
|
||||
if (cs.outputlen == 0)
|
||||
{
|
||||
/* Completed a DMA buffer; queue it for writing. */
|
||||
|
||||
dma_writing_to_td = NEXT_BUFFER(dma_writing_to_td);
|
||||
cs.outputlen = BUFFER_SIZE;
|
||||
}
|
||||
(void) usb_read(FLUXENGINE_DATA_OUT_EP_NUM, buffer);
|
||||
count_read++;
|
||||
|
||||
if (count_read == packets)
|
||||
finished = true;
|
||||
}
|
||||
dma_writing_to_td = NEXT_BUFFER(dma_writing_to_td);
|
||||
|
||||
/* Once all the buffers are full, start writing. */
|
||||
|
||||
/* If we have a full buffer, start writing. */
|
||||
if ((dma_reading_from_td == -1) && (dma_writing_to_td == BUFFER_COUNT-1))
|
||||
{
|
||||
dma_reading_from_td = old_reading_from_td = 0;
|
||||
@@ -521,7 +627,8 @@ static void cmd_write(struct write_frame* f)
|
||||
|
||||
/* Wait for the index marker. While this happens, the DMA engine
|
||||
* will prime the FIFO. */
|
||||
|
||||
|
||||
hardsec_index_threshold = f->hardsec_threshold_ms;
|
||||
index_irq = false;
|
||||
while (!index_irq)
|
||||
;
|
||||
@@ -532,7 +639,7 @@ static void cmd_write(struct write_frame* f)
|
||||
SEQUENCER_CONTROL_Write(0); /* start writing! */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (writing && (dma_underrun || index_irq))
|
||||
goto abort;
|
||||
|
||||
@@ -556,31 +663,29 @@ abort:
|
||||
}
|
||||
|
||||
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);
|
||||
hardsec_index_threshold = 0;
|
||||
if (!finished)
|
||||
{
|
||||
while (count_read < packets)
|
||||
/* There's still some data to read, so just read and blackhole it ---
|
||||
* easier than trying to terminate the connection. */
|
||||
while (count_read != packets)
|
||||
{
|
||||
if (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL)
|
||||
{
|
||||
int length = usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer);
|
||||
if (length < FRAME_SIZE)
|
||||
break;
|
||||
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
count_read++;
|
||||
}
|
||||
(void) usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer);
|
||||
count_read++;
|
||||
}
|
||||
USBFS_DisableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
}
|
||||
|
||||
deinit_dma();
|
||||
print("write finished");
|
||||
|
||||
STEP_REG_Write(0);
|
||||
if (dma_underrun)
|
||||
{
|
||||
print("underrun!");
|
||||
send_error(F_ERROR_UNDERRUN);
|
||||
return;
|
||||
}
|
||||
|
||||
print("success");
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_WRITE_REPLY);
|
||||
send_reply((struct any_frame*) &r);
|
||||
}
|
||||
@@ -592,6 +697,7 @@ static void cmd_erase(struct erase_frame* f)
|
||||
/* Disk is now spinning. */
|
||||
|
||||
print("start erasing");
|
||||
hardsec_index_threshold = f->hardsec_threshold_ms;
|
||||
index_irq = false;
|
||||
while (!index_irq)
|
||||
;
|
||||
@@ -600,6 +706,7 @@ static void cmd_erase(struct erase_frame* f)
|
||||
while (!index_irq)
|
||||
;
|
||||
ERASE_REG_Write(0);
|
||||
hardsec_index_threshold = 0;
|
||||
print("stop erasing");
|
||||
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_ERASE_REPLY);
|
||||
@@ -608,17 +715,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,11 +832,15 @@ static void handle_command(void)
|
||||
break;
|
||||
|
||||
case F_FRAME_MEASURE_SPEED_CMD:
|
||||
cmd_measure_speed(f);
|
||||
cmd_measure_speed((struct measurespeed_frame*) 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:
|
||||
@@ -663,28 +862,49 @@ static void handle_command(void)
|
||||
case F_FRAME_SET_DRIVE_CMD:
|
||||
cmd_set_drive((struct set_drive_frame*) f);
|
||||
break;
|
||||
|
||||
case F_FRAME_MEASURE_VOLTAGES_CMD:
|
||||
cmd_measure_voltages();
|
||||
break;
|
||||
|
||||
default:
|
||||
send_error(F_ERROR_BAD_COMMAND);
|
||||
}
|
||||
}
|
||||
|
||||
static void detect_drives(void)
|
||||
{
|
||||
current_drive_flags.drive = 0;
|
||||
start_motor();
|
||||
drive0_present = home();
|
||||
stop_motor();
|
||||
|
||||
current_drive_flags.drive = 1;
|
||||
start_motor();
|
||||
drive1_present = home();
|
||||
stop_motor();
|
||||
|
||||
print("drive 0: %s drive 1: %s", drive0_present ? "yes" : "no", drive1_present ? "yes" : "no");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
CyGlobalIntEnable;
|
||||
CySysTickStart();
|
||||
CySysTickSetCallback(4, system_timer_cb);
|
||||
INDEX_IRQ_StartEx(&index_irq_cb);
|
||||
CAPTURE_DMA_FINISHED_IRQ_StartEx(&capture_dma_finished_irq_cb);
|
||||
SAMPLER_DMA_FINISHED_IRQ_StartEx(&capture_dma_finished_irq_cb);
|
||||
SEQUENCER_DMA_FINISHED_IRQ_StartEx(&replay_dma_finished_irq_cb);
|
||||
DRIVE_REG_Write(0);
|
||||
INPUT_VOLTAGE_ADC_Stop();
|
||||
OUTPUT_VOLTAGE_ADC_Stop();
|
||||
DRIVESELECT_REG_Write(0);
|
||||
UART_Start();
|
||||
USBFS_Start(0, USBFS_DWR_VDDD_OPERATION);
|
||||
USBFS_DisableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
|
||||
detect_drives();
|
||||
CyWdtStart(CYWDT_1024_TICKS, CYWDT_LPMODE_DISABLED);
|
||||
|
||||
/* UART_PutString("GO\r"); */
|
||||
|
||||
for (;;)
|
||||
{
|
||||
CyWdtClear();
|
||||
@@ -693,23 +913,21 @@ int main(void)
|
||||
{
|
||||
uint32_t time_on = clock - motor_on_time;
|
||||
if (time_on > MOTOR_ON_TIME)
|
||||
{
|
||||
MOTOR_REG_Write(0);
|
||||
motor_on = false;
|
||||
}
|
||||
stop_motor();
|
||||
}
|
||||
|
||||
if (!USBFS_GetConfiguration() || USBFS_IsConfigurationChanged())
|
||||
{
|
||||
print("Waiting for USB...");
|
||||
while (!USBFS_GetConfiguration())
|
||||
;
|
||||
CyWdtClear();
|
||||
print("USB ready");
|
||||
USBFS_EnableOutEP(FLUXENGINE_CMD_OUT_EP_NUM);
|
||||
}
|
||||
|
||||
if (USBFS_GetEPState(FLUXENGINE_CMD_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL)
|
||||
{
|
||||
set_drive_flags(¤t_drive_flags);
|
||||
handle_command();
|
||||
USBFS_EnableOutEP(FLUXENGINE_CMD_OUT_EP_NUM);
|
||||
print("idle");
|
||||
|
||||
20
Makefile
20
Makefile
@@ -1,8 +1,13 @@
|
||||
PACKAGES = zlib sqlite3 libusb-1.0
|
||||
|
||||
export CFLAGS = -Os -g --std=c++14 \
|
||||
-ffunction-sections -fdata-sections
|
||||
export LDFLAGS = -Os
|
||||
export CFLAGS = --std=c++14 -ffunction-sections -fdata-sections
|
||||
export LDFLAGS =
|
||||
|
||||
export COPTFLAGS = -Os
|
||||
export LDOPTFLAGS = -Os -s
|
||||
|
||||
export CDBGFLAGS = -O0 -g
|
||||
export LDDBGFLAGS = -O0 -g
|
||||
|
||||
ifeq ($(OS), Windows_NT)
|
||||
export CXX = /mingw32/bin/g++
|
||||
@@ -13,6 +18,13 @@ export LDFLAGS +=
|
||||
export LIBS = -static -lz -lsqlite3 -lusb-1.0
|
||||
export EXTENSION = .exe
|
||||
else
|
||||
|
||||
packages-exist = $(shell pkg-config --exists $(PACKAGES) && echo yes)
|
||||
ifneq ($(packages-exist),yes)
|
||||
$(warning These pkg-config packages are installed: $(shell pkg-config --list-all | sort | awk '{print $$1}'))
|
||||
$(error You must have these pkg-config packages installed: $(PACKAGES))
|
||||
endif
|
||||
|
||||
export CXX = g++
|
||||
export AR = ar rcs
|
||||
export STRIP = strip
|
||||
@@ -27,7 +39,7 @@ CFLAGS += -Ilib -Idep/fmt -Iarch
|
||||
export OBJDIR = .obj
|
||||
|
||||
all: .obj/build.ninja
|
||||
@ninja -f .obj/build.ninja -v
|
||||
@ninja -f .obj/build.ninja
|
||||
|
||||
clean:
|
||||
@echo CLEAN
|
||||
|
||||
48
README.md
48
README.md
@@ -24,17 +24,18 @@ Don't believe me? Watch the demo reel!
|
||||
<iframe width="373" height="210" src="https://www.youtube.com/embed/m_s1iw8eW7o" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div>
|
||||
|
||||
**Important note.** On 2019-02-09 I did a hardware redesign and moved the pins on
|
||||
the board. Sorry for the inconvenience, but it means you don't have to modify
|
||||
the board any more to make it work. If you built the hardware prior to then,
|
||||
you'll need to adjust it.
|
||||
**New!** The FluxEngine client software now works with
|
||||
[GreaseWeazle](https://github.com/keirf/Greaseweazle/wiki) hardware. So, if you
|
||||
can't find a PSoC5 development kit, or don't want to use the Cypress Windows
|
||||
tools for programming it, you can use one of these instead. Very nearly all
|
||||
FluxEngine features are available with the GreaseWeazle and it works out-of-the
|
||||
box. See the [dedicated GreaseWeazle documentation page](doc/greaseweazle.md)
|
||||
for more information.
|
||||
|
||||
**Another important note.** On 2019-07-03 I've revamped the build process and
|
||||
the (command line) user interface. It should be much nicer now, not least in
|
||||
that there's a single client binary with all the functionality in it. The
|
||||
interface is a little different, but not much. The build process is now
|
||||
better (simpler). See [the building](doc/building.md) and
|
||||
[using](doc/using.md) pages for more details.
|
||||
**Important note.** On 2020-04-02 I changed the bytecode format (and firmware).
|
||||
Flux files will need to be upgraded with `fluxengine upgradefluxfile`. The new
|
||||
format should be more reliable and use way, way less bandwidth. Sorry for the
|
||||
inconvenience.
|
||||
|
||||
Where?
|
||||
------
|
||||
@@ -59,6 +60,11 @@ following friendly articles:
|
||||
- [Using a FluxEngine](doc/using.md) ∾ what to do with your new hardware ∾
|
||||
flux files and image files ∾ knowing what you're doing
|
||||
|
||||
- [Using GreaseWeazle hardware with the FluxEngine client
|
||||
software](doc/greaseweazle.md) ∾ what works ∾ what doesn't work ∾ where to
|
||||
go for help
|
||||
|
||||
|
||||
- [Troubleshooting dubious disks](doc/problems.md) ∾ it's not an exact science ∾
|
||||
the sector map ∾ clock detection and the histogram
|
||||
|
||||
@@ -79,20 +85,24 @@ people who've had it work).
|
||||
|
||||
| Format | Read? | Write? | Notes |
|
||||
|:-----------------------------------------|:-----:|:------:|-------|
|
||||
| IBM PC compatible | 🦄 | | and compatibles (like the Atari ST) |
|
||||
| [Acorn ADFS](doc/disk-acornadfs.md) | 🦄 | | single- and double- sided |
|
||||
| [Acorn DFS](doc/disk-acorndfs.md) | 🦄 | | |
|
||||
| [Ampro Little Board](doc/disk-ampro.md) | 🦖 | | |
|
||||
| [IBM PC compatible](doc/disk-ibm.md) | 🦄 | 🦄 | and compatibles (like the Atari ST) |
|
||||
| [Acorn ADFS](doc/disk-acornadfs.md) | 🦄 | 🦖* | single- and double- sided |
|
||||
| [Acorn DFS](doc/disk-acorndfs.md) | 🦄 | 🦖* | |
|
||||
| [Ampro Little Board](doc/disk-ampro.md) | 🦖 | 🦖* | |
|
||||
| [Apple II DOS 3.3](doc/disk-apple2.md) | 🦄 | | doesn't do logical sector remapping |
|
||||
| [Amiga](doc/disk-amiga.md) | 🦄 | | |
|
||||
| [Commodore 64 1541](doc/disk-c64.md) | 🦖 | | and probably the other GCR formats |
|
||||
| [Brother 120kB](doc/disk-brother.md) | 🦄 | | |
|
||||
| [Brother 240kB](doc/disk-brother.md) | 🦄 | 🦄 | |
|
||||
| [Brother FB-100](doc/disk-fb100.md) | 🦖 | | Tandy Model 100, Husky Hunter, knitting machines |
|
||||
| [Macintosh 800kB](doc/disk-macintosh.md) | 🦄 | | and probably the 400kB too |
|
||||
| [TRS-80](doc/disk-trs80.md) | 🦖 | | a minor variation of the IBM scheme |
|
||||
| [Macintosh 800kB](doc/disk-macintosh.md) | 🦄 | 🦄 | and probably the 400kB too |
|
||||
| [TRS-80](doc/disk-trs80.md) | 🦖 | 🦖* | a minor variation of the IBM scheme |
|
||||
{: .datatable }
|
||||
|
||||
`*`: these formats are variations of the generic IBM format, and since the
|
||||
IBM writer is completely generic, it should be configurable for these
|
||||
formats... theoretically. I don't have the hardware to try it.
|
||||
|
||||
### Even older disk formats
|
||||
|
||||
These formats are for particularly old, weird architectures, even by the
|
||||
@@ -106,8 +116,10 @@ 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 |
|
||||
| [Micropolis](doc/disk-micropolis.md) | 🦄 | | Micropolis 100tpi drives |
|
||||
| [TI DS990 FD1000](doc/disk-tids990.md) | 🦄 | 🦄 | 8" |
|
||||
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8" |
|
||||
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8" _and_ hard sectors |
|
||||
{: .datatable }
|
||||
|
||||
### Notes
|
||||
|
||||
101
arch/amiga/amiga.cc
Normal file
101
arch/amiga/amiga.cc
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "globals.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "amiga.h"
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
uint32_t amigaChecksum(const Bytes& bytes)
|
||||
{
|
||||
ByteReader br(bytes);
|
||||
uint32_t checksum = 0;
|
||||
|
||||
assert((bytes.size() & 3) == 0);
|
||||
while (!br.eof())
|
||||
checksum ^= br.read_be32();
|
||||
|
||||
return checksum & 0x55555555;
|
||||
}
|
||||
|
||||
static uint8_t everyother(uint16_t x)
|
||||
{
|
||||
/* aabb ccdd eeff gghh */
|
||||
x &= 0x6666; /* 0ab0 0cd0 0ef0 0gh0 */
|
||||
x >>= 1; /* 00ab 00cd 00ef 00gh */
|
||||
x |= x << 2; /* abab cdcd efef ghgh */
|
||||
x &= 0x3c3c; /* 00ab cd00 00ef gh00 */
|
||||
x >>= 2; /* 0000 abcd 0000 efgh */
|
||||
x |= x >> 4; /* 0000 abcd abcd efgh */
|
||||
return x;
|
||||
}
|
||||
|
||||
Bytes amigaInterleave(const Bytes& input)
|
||||
{
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
|
||||
/* Write all odd bits. (Numbering starts at 0...) */
|
||||
|
||||
{
|
||||
ByteReader br(input);
|
||||
while (!br.eof())
|
||||
{
|
||||
uint16_t x = br.read_be16();
|
||||
x &= 0xaaaa; /* a0b0 c0d0 e0f0 g0h0 */
|
||||
x |= x >> 1; /* aabb ccdd eeff gghh */
|
||||
x = everyother(x); /* 0000 0000 abcd efgh */
|
||||
bw.write_8(x);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write all even bits. */
|
||||
|
||||
{
|
||||
ByteReader br(input);
|
||||
while (!br.eof())
|
||||
{
|
||||
uint16_t x = br.read_be16();
|
||||
x &= 0x5555; /* 0a0b 0c0d 0e0f 0g0h */
|
||||
x |= x << 1; /* aabb ccdd eeff gghh */
|
||||
x = everyother(x); /* 0000 0000 abcd efgh */
|
||||
bw.write_8(x);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
Bytes amigaDeinterleave(const uint8_t*& input, size_t len)
|
||||
{
|
||||
assert(!(len & 1));
|
||||
const uint8_t* odds = &input[0];
|
||||
const uint8_t* evens = &input[len/2];
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
|
||||
for (size_t i=0; i<len/2; i++)
|
||||
{
|
||||
uint8_t o = *odds++;
|
||||
uint8_t e = *evens++;
|
||||
|
||||
/* This is the 'Interleave bits with 64-bit multiply' technique from
|
||||
* http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
|
||||
*/
|
||||
uint16_t result =
|
||||
(((e * 0x0101010101010101ULL & 0x8040201008040201ULL)
|
||||
* 0x0102040810204081ULL >> 49) & 0x5555) |
|
||||
(((o * 0x0101010101010101ULL & 0x8040201008040201ULL)
|
||||
* 0x0102040810204081ULL >> 48) & 0xAAAA);
|
||||
|
||||
bw.write_be16(result);
|
||||
}
|
||||
|
||||
input += len;
|
||||
return output;
|
||||
}
|
||||
|
||||
Bytes amigaDeinterleave(const Bytes& input)
|
||||
{
|
||||
const uint8_t* ptr = input.cbegin();
|
||||
return amigaDeinterleave(ptr, input.size());
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
#ifndef AMIGA_H
|
||||
#define AMIGA_H
|
||||
|
||||
#include "encoders/encoders.h"
|
||||
|
||||
#define AMIGA_SECTOR_RECORD 0xaaaa44894489LL
|
||||
|
||||
#define AMIGA_TRACKS_PER_DISK 80
|
||||
#define AMIGA_SECTORS_PER_TRACK 11
|
||||
#define AMIGA_RECORD_SIZE 0x21f
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
class SectorSet;
|
||||
|
||||
class AmigaDecoder : public AbstractDecoder
|
||||
{
|
||||
@@ -15,6 +20,24 @@ public:
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
|
||||
std::set<unsigned> requiredSectors(Track& track) const;
|
||||
};
|
||||
|
||||
class AmigaEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
virtual ~AmigaEncoder() {}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
|
||||
};
|
||||
|
||||
extern FlagGroup amigaEncoderFlags;
|
||||
|
||||
extern uint32_t amigaChecksum(const Bytes& bytes);
|
||||
extern Bytes amigaInterleave(const Bytes& input);
|
||||
extern Bytes amigaDeinterleave(const uint8_t*& input, size_t len);
|
||||
extern Bytes amigaDeinterleave(const Bytes& input);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,47 +21,6 @@
|
||||
|
||||
static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD);
|
||||
|
||||
static Bytes deinterleave(const uint8_t*& input, size_t len)
|
||||
{
|
||||
assert(!(len & 1));
|
||||
const uint8_t* odds = &input[0];
|
||||
const uint8_t* evens = &input[len/2];
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
|
||||
for (size_t i=0; i<len/2; i++)
|
||||
{
|
||||
uint8_t o = *odds++;
|
||||
uint8_t e = *evens++;
|
||||
|
||||
/* This is the 'Interleave bits with 64-bit multiply' technique from
|
||||
* http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
|
||||
*/
|
||||
uint16_t result =
|
||||
(((e * 0x0101010101010101ULL & 0x8040201008040201ULL)
|
||||
* 0x0102040810204081ULL >> 49) & 0x5555) |
|
||||
(((o * 0x0101010101010101ULL & 0x8040201008040201ULL)
|
||||
* 0x0102040810204081ULL >> 48) & 0xAAAA);
|
||||
|
||||
bw.write_be16(result);
|
||||
}
|
||||
|
||||
input += len;
|
||||
return output;
|
||||
}
|
||||
|
||||
static uint32_t checksum(const Bytes& bytes)
|
||||
{
|
||||
ByteReader br(bytes);
|
||||
uint32_t checksum = 0;
|
||||
|
||||
assert((bytes.size() & 3) == 0);
|
||||
while (!br.eof())
|
||||
checksum ^= br.read_be32();
|
||||
|
||||
return checksum & 0x55555555;
|
||||
}
|
||||
|
||||
AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord()
|
||||
{
|
||||
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
|
||||
@@ -73,27 +32,36 @@ AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord()
|
||||
void AmigaDecoder::decodeSectorRecord()
|
||||
{
|
||||
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
|
||||
if (rawbits.size() < (AMIGA_RECORD_SIZE*16))
|
||||
return;
|
||||
const auto& rawbytes = toBytes(rawbits).slice(0, AMIGA_RECORD_SIZE*2);
|
||||
const auto& bytes = decodeFmMfm(rawbits).slice(0, AMIGA_RECORD_SIZE);
|
||||
|
||||
const uint8_t* ptr = bytes.begin() + 3;
|
||||
|
||||
Bytes header = deinterleave(ptr, 4);
|
||||
Bytes recoveryinfo = deinterleave(ptr, 16);
|
||||
Bytes header = amigaDeinterleave(ptr, 4);
|
||||
Bytes recoveryinfo = amigaDeinterleave(ptr, 16);
|
||||
|
||||
_sector->logicalTrack = header[1] >> 1;
|
||||
_sector->logicalSide = header[1] & 1;
|
||||
_sector->logicalSector = header[2];
|
||||
|
||||
uint32_t wantedheaderchecksum = deinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotheaderchecksum = checksum(rawbytes.slice(6, 40));
|
||||
uint32_t wantedheaderchecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(6, 40));
|
||||
if (gotheaderchecksum != wantedheaderchecksum)
|
||||
return;
|
||||
|
||||
uint32_t wanteddatachecksum = deinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotdatachecksum = checksum(rawbytes.slice(62, 1024));
|
||||
uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(62, 1024));
|
||||
|
||||
_sector->data.clear();
|
||||
_sector->data.writer().append(deinterleave(ptr, 512)).append(recoveryinfo);
|
||||
_sector->data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo);
|
||||
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
std::set<unsigned> AmigaDecoder::requiredSectors(Track& track) const
|
||||
{
|
||||
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
||||
return sectors;
|
||||
}
|
||||
|
||||
|
||||
129
arch/amiga/encoder.cc
Normal file
129
arch/amiga/encoder.cc
Normal file
@@ -0,0 +1,129 @@
|
||||
#include "globals.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "amiga.h"
|
||||
#include "crc.h"
|
||||
#include "sectorset.h"
|
||||
#include "writer.h"
|
||||
|
||||
FlagGroup amigaEncoderFlags;
|
||||
|
||||
static DoubleFlag clockRateUs(
|
||||
{ "--clock-rate" },
|
||||
"Encoded data clock rate (microseconds).",
|
||||
2.00);
|
||||
|
||||
static DoubleFlag postIndexGapMs(
|
||||
{ "--post-index-gap" },
|
||||
"Post-index gap before first sector header (milliseconds).",
|
||||
0.5);
|
||||
|
||||
static bool lastBit;
|
||||
|
||||
static int charToInt(char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||
{
|
||||
for (bool bit : src)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bit;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
|
||||
{
|
||||
cursor += width;
|
||||
for (int i=0; i<width; i++)
|
||||
{
|
||||
unsigned pos = cursor - i - 1;
|
||||
if (pos < bits.size())
|
||||
bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
{
|
||||
assert(!(bytes.size() & 3));
|
||||
Bytes interleaved = amigaInterleave(bytes);
|
||||
encodeMfm(bits, cursor, interleaved, lastBit);
|
||||
}
|
||||
|
||||
static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, uint32_t data)
|
||||
{
|
||||
Bytes b(4);
|
||||
ByteWriter bw(b);
|
||||
bw.write_be32(data);
|
||||
write_interleaved_bytes(bits, cursor, b);
|
||||
}
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector)
|
||||
{
|
||||
if ((sector->data.size() != 512) && (sector->data.size() != 528))
|
||||
Error() << "unsupported sector size --- you must pick 512 or 528";
|
||||
|
||||
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6*8);
|
||||
|
||||
std::vector<bool> headerBits(20*16);
|
||||
unsigned headerCursor = 0;
|
||||
|
||||
Bytes header =
|
||||
{
|
||||
0xff, /* Amiga 1.0 format byte */
|
||||
(uint8_t) ((sector->logicalTrack<<1) | sector->logicalSide),
|
||||
(uint8_t) sector->logicalSector,
|
||||
(uint8_t) (AMIGA_SECTORS_PER_TRACK - sector->logicalSector)
|
||||
};
|
||||
write_interleaved_bytes(headerBits, headerCursor, header);
|
||||
Bytes recoveryInfo(16);
|
||||
if (sector->data.size() == 528)
|
||||
recoveryInfo = sector->data.slice(512, 16);
|
||||
write_interleaved_bytes(headerBits, headerCursor, recoveryInfo);
|
||||
|
||||
std::vector<bool> dataBits(512*16);
|
||||
unsigned dataCursor = 0;
|
||||
write_interleaved_bytes(dataBits, dataCursor, sector->data);
|
||||
|
||||
write_bits(bits, cursor, headerBits);
|
||||
uint32_t headerChecksum = amigaChecksum(toBytes(headerBits));
|
||||
write_interleaved_bytes(bits, cursor, headerChecksum);
|
||||
uint32_t dataChecksum = amigaChecksum(toBytes(dataBits));
|
||||
write_interleaved_bytes(bits, cursor, dataChecksum);
|
||||
write_bits(bits, cursor, dataBits);
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> AmigaEncoder::encode(
|
||||
int physicalTrack, int physicalSide, const SectorSet& allSectors)
|
||||
{
|
||||
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
fillBitmapTo(bits, cursor, postIndexGapMs * 1000 / clockRateUs, { true, false });
|
||||
lastBit = false;
|
||||
|
||||
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
|
||||
{
|
||||
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
|
||||
write_sector(bits, cursor, sectorData);
|
||||
}
|
||||
|
||||
if (cursor >= bits.size())
|
||||
Error() << "track data overrun";
|
||||
fillBitmapTo(bits, cursor, bits.size(), { true, false });
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs*1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
#define BROTHER_DATA_RECORD_CHECKSUM 3
|
||||
#define BROTHER_DATA_RECORD_ENCODED_SIZE 415
|
||||
|
||||
#define BROTHER_TRACKS_PER_DISK 78
|
||||
#define BROTHER_TRACKS_PER_240KB_DISK 78
|
||||
#define BROTHER_TRACKS_PER_120KB_DISK 39
|
||||
#define BROTHER_SECTORS_PER_TRACK 12
|
||||
|
||||
class Sector;
|
||||
@@ -28,8 +29,16 @@ public:
|
||||
class BrotherEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
BrotherEncoder(int format, int bias):
|
||||
_format(format),
|
||||
_bias(bias)
|
||||
{}
|
||||
|
||||
virtual ~BrotherEncoder() {}
|
||||
|
||||
private:
|
||||
int _format;
|
||||
int _bias;
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
|
||||
};
|
||||
|
||||
@@ -129,9 +129,25 @@ static int charToInt(char c)
|
||||
std::unique_ptr<Fluxmap> BrotherEncoder::encode(
|
||||
int physicalTrack, int physicalSide, const SectorSet& allSectors)
|
||||
{
|
||||
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_DISK)
|
||||
|| (physicalSide != 0))
|
||||
int logicalTrack;
|
||||
if (physicalSide != 0)
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
physicalTrack -= _bias;
|
||||
switch (_format)
|
||||
{
|
||||
case 120:
|
||||
if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2))
|
||||
|| (physicalTrack & 1))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
logicalTrack = physicalTrack/2;
|
||||
break;
|
||||
|
||||
case 240:
|
||||
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
logicalTrack = physicalTrack;
|
||||
break;
|
||||
}
|
||||
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
const std::string& skew = sectorSkew.get();
|
||||
@@ -146,15 +162,15 @@ std::unique_ptr<Fluxmap> BrotherEncoder::encode(
|
||||
double dataMs = headerMs + postHeaderSpacingMs;
|
||||
unsigned dataCursor = dataMs*1e3 / clockRateUs;
|
||||
|
||||
const auto& sectorData = allSectors.get(physicalTrack, 0, sectorId);
|
||||
const auto& sectorData = allSectors.get(logicalTrack, 0, sectorId);
|
||||
|
||||
fillBitmapTo(bits, cursor, headerCursor, { true, false });
|
||||
write_sector_header(bits, cursor, physicalTrack, sectorId);
|
||||
write_sector_header(bits, cursor, logicalTrack, sectorId);
|
||||
fillBitmapTo(bits, cursor, dataCursor, { true, false });
|
||||
write_sector_data(bits, cursor, sectorData->data);
|
||||
}
|
||||
|
||||
if (cursor > bits.size())
|
||||
if (cursor >= bits.size())
|
||||
Error() << "track data overrun";
|
||||
fillBitmapTo(bits, cursor, bits.size(), { true, false });
|
||||
|
||||
|
||||
@@ -57,11 +57,11 @@ const FluxPattern FM_TRS80DAM1_PATTERN(16, 0xf56b);
|
||||
|
||||
/*
|
||||
* TRS80DAM2 record:
|
||||
* flux: XXXX-X-X-XX-XXX- = 0xf56c
|
||||
* flux: XXXX-X-X-XX-XXX- = 0xf56e
|
||||
* clock: X X - - - X X X = 0xc7
|
||||
* data: X X X X X - X - = 0xfa
|
||||
*/
|
||||
const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56c);
|
||||
const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56e);
|
||||
|
||||
/* MFM record separator:
|
||||
* 0xA1 is:
|
||||
@@ -73,8 +73,10 @@ const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56c);
|
||||
* encoding (you can't do 10 00). So this can't be spoofed by user data.
|
||||
*
|
||||
* shifted: 10 00 10 01 00 01 00 1
|
||||
*
|
||||
* It's repeated three times.
|
||||
*/
|
||||
const FluxPattern MFM_PATTERN(16, 0x4489);
|
||||
const FluxPattern MFM_PATTERN(48, 0x448944894489LL);
|
||||
|
||||
const FluxMatchers ANY_RECORD_PATTERN(
|
||||
{
|
||||
@@ -100,7 +102,8 @@ AbstractDecoder::RecordType IbmDecoder::advanceToNextRecord()
|
||||
if (_currentHeaderLength > 0)
|
||||
readRawBits(_currentHeaderLength*16);
|
||||
auto idbits = readRawBits(16);
|
||||
uint8_t id = decodeFmMfm(idbits).slice(0, 1)[0];
|
||||
const Bytes idbytes = decodeFmMfm(idbits);
|
||||
uint8_t id = idbytes.slice(0, 1)[0];
|
||||
seek(here);
|
||||
|
||||
switch (id)
|
||||
|
||||
236
arch/ibm/encoder.cc
Normal file
236
arch/ibm/encoder.cc
Normal file
@@ -0,0 +1,236 @@
|
||||
#include "globals.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "ibm.h"
|
||||
#include "crc.h"
|
||||
#include "sectorset.h"
|
||||
#include "writer.h"
|
||||
#include "fmt/format.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/* IAM record separator:
|
||||
* 0xC2 is:
|
||||
* data: 1 1 0 0 0 0 1 0 = 0xc2
|
||||
* mfm: 01 01 00 10 10 10 01 00 = 0x5254
|
||||
* special: 01 01 00 10 00 10 01 00 = 0x5224
|
||||
*/
|
||||
#define MFM_IAM_SEPARATOR 0x5224
|
||||
|
||||
/* FM IAM record:
|
||||
* flux: XXXX-XXX-XXXX-X- = 0xf77a
|
||||
* clock: X X - X - X X X = 0xd7
|
||||
* data: X X X X X X - - = 0xfc
|
||||
*/
|
||||
#define FM_IAM_RECORD 0xf77a
|
||||
|
||||
/* MFM IAM record:
|
||||
* data: 1 1 1 1 1 1 0 0 = 0xfc
|
||||
* flux: 01 01 01 01 01 01 00 10 = 0x5552
|
||||
*/
|
||||
#define MFM_IAM_RECORD 0x5552
|
||||
|
||||
/* MFM record separator:
|
||||
* 0xA1 is:
|
||||
* data: 1 0 1 0 0 0 0 1 = 0xa1
|
||||
* mfm: 01 00 01 00 10 10 10 01 = 0x44a9
|
||||
* special: 01 00 01 00 10 00 10 01 = 0x4489
|
||||
* ^^^^^
|
||||
* When shifted out of phase, the special 0xa1 byte becomes an illegal
|
||||
* encoding (you can't do 10 00). So this can't be spoofed by user data.
|
||||
*
|
||||
* shifted: 10 00 10 01 00 01 00 1
|
||||
*
|
||||
* It's repeated three times.
|
||||
*/
|
||||
#define MFM_RECORD_SEPARATOR 0x4489
|
||||
#define MFM_RECORD_SEPARATOR_BYTE 0xa1
|
||||
|
||||
/* MFM IDAM byte:
|
||||
* data: 1 1 1 1 1 1 1 0 = 0xfe
|
||||
* mfm: 01 01 01 01 01 01 01 00 = 0x5554
|
||||
*/
|
||||
|
||||
/* MFM DAM byte:
|
||||
* data: 1 1 1 1 1 0 1 1 = 0xfb
|
||||
* mfm: 01 01 01 01 01 00 01 01 = 0x5545
|
||||
*/
|
||||
|
||||
static int charToInt(char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
}
|
||||
|
||||
void IbmEncoder::writeRawBits(uint32_t data, int width)
|
||||
{
|
||||
_cursor += width;
|
||||
_lastBit = data & 1;
|
||||
for (int i=0; i<width; i++)
|
||||
{
|
||||
unsigned pos = _cursor - i - 1;
|
||||
if (pos < _bits.size())
|
||||
_bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void IbmEncoder::writeBytes(const Bytes& bytes)
|
||||
{
|
||||
if (_parameters.useFm)
|
||||
encodeFm(_bits, _cursor, bytes);
|
||||
else
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
}
|
||||
|
||||
void IbmEncoder::writeBytes(int count, uint8_t byte)
|
||||
{
|
||||
Bytes bytes = { byte };
|
||||
for (int i=0; i<count; i++)
|
||||
writeBytes(bytes);
|
||||
}
|
||||
|
||||
static uint8_t decodeUint16(uint16_t raw)
|
||||
{
|
||||
Bytes b;
|
||||
ByteWriter bw(b);
|
||||
bw.write_be16(raw);
|
||||
return decodeFmMfm(b.toBits())[0];
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> IbmEncoder::encode(
|
||||
int physicalTrack, int physicalSide, const SectorSet& allSectors)
|
||||
{
|
||||
double clockRateUs = 1e3 / _parameters.clockRateKhz;
|
||||
if (!_parameters.useFm)
|
||||
clockRateUs /= 2.0;
|
||||
int bitsPerRevolution = (_parameters.trackLengthMs * 1000.0) / clockRateUs;
|
||||
_bits.resize(bitsPerRevolution);
|
||||
_cursor = 0;
|
||||
|
||||
uint8_t idamUnencoded = decodeUint16(_parameters.idamByte);
|
||||
uint8_t damUnencoded = decodeUint16(_parameters.damByte);
|
||||
|
||||
uint8_t sectorSize = 0;
|
||||
{
|
||||
int s = _parameters.sectorSize >> 7;
|
||||
while (s > 1)
|
||||
{
|
||||
s >>= 1;
|
||||
sectorSize += 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t gapFill = _parameters.useFm ? 0x00 : 0x4e;
|
||||
|
||||
writeBytes(_parameters.gap0, gapFill);
|
||||
if (_parameters.emitIam)
|
||||
{
|
||||
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
|
||||
if (!_parameters.useFm)
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
writeRawBits(MFM_IAM_SEPARATOR, 16);
|
||||
}
|
||||
writeRawBits(_parameters.useFm ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
|
||||
writeBytes(_parameters.gap1, gapFill);
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
for (char sectorChar : _parameters.sectorSkew)
|
||||
{
|
||||
int sectorId = charToInt(sectorChar);
|
||||
if (!first)
|
||||
writeBytes(_parameters.gap3, gapFill);
|
||||
first = false;
|
||||
|
||||
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
|
||||
if (!sectorData)
|
||||
Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId);
|
||||
|
||||
/* Writing the sector and data records are fantastically annoying.
|
||||
* The CRC is calculated from the *very start* of the record, and
|
||||
* include the malformed marker bytes. Our encoder doesn't know
|
||||
* about this, of course, with the result that we have to construct
|
||||
* the unencoded header, calculate the checksum, and then use the
|
||||
* same logic to emit the bytes which require special encoding
|
||||
* before encoding the rest of the header normally. */
|
||||
|
||||
{
|
||||
Bytes header;
|
||||
ByteWriter bw(header);
|
||||
|
||||
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
|
||||
if (!_parameters.useFm)
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
|
||||
}
|
||||
bw.write_8(idamUnencoded);
|
||||
bw.write_8(sectorData->logicalTrack);
|
||||
bw.write_8(sectorData->logicalSide);
|
||||
bw.write_8(sectorData->logicalSector + _parameters.startSectorId);
|
||||
bw.write_8(sectorSize);
|
||||
uint16_t crc = crc16(CCITT_POLY, header);
|
||||
bw.write_be16(crc);
|
||||
|
||||
int conventionalHeaderStart = 0;
|
||||
if (!_parameters.useFm)
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
writeRawBits(MFM_RECORD_SEPARATOR, 16);
|
||||
conventionalHeaderStart += 3;
|
||||
|
||||
}
|
||||
writeRawBits(_parameters.idamByte, 16);
|
||||
conventionalHeaderStart += 1;
|
||||
|
||||
writeBytes(header.slice(conventionalHeaderStart));
|
||||
}
|
||||
|
||||
writeBytes(_parameters.gap2, gapFill);
|
||||
|
||||
{
|
||||
Bytes data;
|
||||
ByteWriter bw(data);
|
||||
|
||||
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
|
||||
if (!_parameters.useFm)
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
|
||||
}
|
||||
bw.write_8(damUnencoded);
|
||||
|
||||
Bytes truncatedData = sectorData->data.slice(0, _parameters.sectorSize);
|
||||
bw += truncatedData;
|
||||
hexdump(std::cout, data.slice(0, 64));
|
||||
uint16_t crc = crc16(CCITT_POLY, data);
|
||||
bw.write_be16(crc);
|
||||
|
||||
int conventionalHeaderStart = 0;
|
||||
if (!_parameters.useFm)
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
writeRawBits(MFM_RECORD_SEPARATOR, 16);
|
||||
conventionalHeaderStart += 3;
|
||||
|
||||
}
|
||||
writeRawBits(_parameters.damByte, 16);
|
||||
conventionalHeaderStart += 1;
|
||||
|
||||
writeBytes(data.slice(conventionalHeaderStart));
|
||||
}
|
||||
}
|
||||
|
||||
if (_cursor >= _bits.size())
|
||||
Error() << "track data overrun";
|
||||
while (_cursor < _bits.size())
|
||||
writeBytes(1, gapFill);
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(_bits, clockRateUs*1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define IBM_H
|
||||
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
|
||||
/* IBM format (i.e. ordinary PC floppies). */
|
||||
|
||||
@@ -31,68 +32,68 @@ struct IbmIdam
|
||||
class IbmDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
IbmDecoder(unsigned sectorBase, bool ignoreSideByte=false):
|
||||
IbmDecoder(unsigned sectorBase, bool ignoreSideByte=false,
|
||||
const std::set<unsigned> requiredSectors=std::set<unsigned>()):
|
||||
_sectorBase(sectorBase),
|
||||
_ignoreSideByte(ignoreSideByte)
|
||||
_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
|
||||
|
||||
@@ -118,10 +118,10 @@ static Bytes decode_crazy_data(const Bytes& input, Sector::Status& status)
|
||||
uint8_t decode_side(uint8_t side)
|
||||
{
|
||||
/* Mac disks, being weird, use the side byte to encode both the side (in
|
||||
* bit 5) and also whether we're above track 0x3f (in bit 6).
|
||||
* bit 5) and also whether we're above track 0x3f (in bit 0).
|
||||
*/
|
||||
|
||||
return !!(side & 0x40);
|
||||
return !!(side & 0x20);
|
||||
}
|
||||
|
||||
AbstractDecoder::RecordType MacintoshDecoder::advanceToNextRecord()
|
||||
@@ -184,3 +184,25 @@ void MacintoshDecoder::decodeDataRecord()
|
||||
_sector->data.clear();
|
||||
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
|
||||
}
|
||||
|
||||
std::set<unsigned> MacintoshDecoder::requiredSectors(Track& track) const
|
||||
{
|
||||
int count;
|
||||
if (track.physicalTrack < 16)
|
||||
count = 12;
|
||||
else if (track.physicalTrack < 32)
|
||||
count = 11;
|
||||
else if (track.physicalTrack < 48)
|
||||
count = 10;
|
||||
else if (track.physicalTrack < 64)
|
||||
count = 9;
|
||||
else
|
||||
count = 8;
|
||||
|
||||
std::set<unsigned> sectors;
|
||||
while (count--)
|
||||
sectors.insert(count);
|
||||
return sectors;
|
||||
}
|
||||
|
||||
|
||||
|
||||
242
arch/macintosh/encoder.cc
Normal file
242
arch/macintosh/encoder.cc
Normal file
@@ -0,0 +1,242 @@
|
||||
#include "globals.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "macintosh.h"
|
||||
#include "crc.h"
|
||||
#include "sectorset.h"
|
||||
#include "writer.h"
|
||||
#include "fmt/format.h"
|
||||
#include <ctype.h>
|
||||
|
||||
FlagGroup macintoshEncoderFlags;
|
||||
|
||||
static DoubleFlag postIndexGapUs(
|
||||
{ "--post-index-gap-us" },
|
||||
"Post-index gap before first sector header (microseconds).",
|
||||
0);
|
||||
|
||||
static DoubleFlag clockCompensation(
|
||||
{ "--clock-compensation-factor" },
|
||||
"Scale the output clock by this much.",
|
||||
1.0);
|
||||
|
||||
static bool lastBit;
|
||||
|
||||
static double clockRateUsForTrack(unsigned track)
|
||||
{
|
||||
if (track < 16)
|
||||
return 2.623;
|
||||
if (track < 32)
|
||||
return 2.861;
|
||||
if (track < 48)
|
||||
return 3.148;
|
||||
if (track < 64)
|
||||
return 3.497;
|
||||
return 3.934;
|
||||
}
|
||||
|
||||
static unsigned sectorsForTrack(unsigned track)
|
||||
{
|
||||
if (track < 16)
|
||||
return 12;
|
||||
if (track < 32)
|
||||
return 11;
|
||||
if (track < 48)
|
||||
return 10;
|
||||
if (track < 64)
|
||||
return 9;
|
||||
return 8;
|
||||
}
|
||||
|
||||
static int encode_data_gcr(uint8_t gcr)
|
||||
{
|
||||
switch (gcr)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
/* This is extremely inspired by the MESS implementation, written by Nathan Woods
|
||||
* and R. Belmont: https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp
|
||||
*/
|
||||
static Bytes encode_crazy_data(const Bytes& input)
|
||||
{
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
ByteReader br(input);
|
||||
|
||||
uint8_t w1, w2, w3, w4;
|
||||
|
||||
static const int LOOKUP_LEN = MAC_SECTOR_LENGTH / 3;
|
||||
|
||||
uint8_t b1[LOOKUP_LEN + 1];
|
||||
uint8_t b2[LOOKUP_LEN + 1];
|
||||
uint8_t b3[LOOKUP_LEN + 1];
|
||||
|
||||
uint32_t c1 = 0;
|
||||
uint32_t c2 = 0;
|
||||
uint32_t c3 = 0;
|
||||
for (int j=0;; j++)
|
||||
{
|
||||
c1 = (c1 & 0xff) << 1;
|
||||
if (c1 & 0x0100)
|
||||
c1++;
|
||||
|
||||
uint8_t val = br.read_8();
|
||||
c3 += val;
|
||||
if (c1 & 0x0100)
|
||||
{
|
||||
c3++;
|
||||
c1 &= 0xff;
|
||||
}
|
||||
b1[j] = (val ^ c1) & 0xff;
|
||||
|
||||
val = br.read_8();
|
||||
c2 += val;
|
||||
if (c3 > 0xff)
|
||||
{
|
||||
c2++;
|
||||
c3 &= 0xff;
|
||||
}
|
||||
b2[j] = (val ^ c3) & 0xff;
|
||||
|
||||
if (br.pos == 524)
|
||||
break;
|
||||
|
||||
val = br.read_8();
|
||||
c1 += val;
|
||||
if (c2 > 0xff)
|
||||
{
|
||||
c1++;
|
||||
c2 &= 0xff;
|
||||
}
|
||||
b3[j] = (val ^ c2) & 0xff;
|
||||
}
|
||||
uint32_t c4 = ((c1 & 0xc0) >> 6) | ((c2 & 0xc0) >> 4) | ((c3 & 0xc0) >> 2);
|
||||
b3[LOOKUP_LEN] = 0;
|
||||
|
||||
for (int i = 0; i <= LOOKUP_LEN; i++)
|
||||
{
|
||||
w1 = b1[i] & 0x3f;
|
||||
w2 = b2[i] & 0x3f;
|
||||
w3 = b3[i] & 0x3f;
|
||||
w4 = ((b1[i] & 0xc0) >> 2);
|
||||
w4 |= ((b2[i] & 0xc0) >> 4);
|
||||
w4 |= ((b3[i] & 0xc0) >> 6);
|
||||
|
||||
bw.write_8(w4);
|
||||
bw.write_8(w1);
|
||||
bw.write_8(w2);
|
||||
|
||||
if (i != LOOKUP_LEN)
|
||||
bw.write_8(w3);
|
||||
}
|
||||
|
||||
bw.write_8(c4 & 0x3f);
|
||||
bw.write_8(c3 & 0x3f);
|
||||
bw.write_8(c2 & 0x3f);
|
||||
bw.write_8(c1 & 0x3f);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
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 uint8_t encode_side(uint8_t track, uint8_t side)
|
||||
{
|
||||
/* Mac disks, being weird, use the side byte to encode both the side (in
|
||||
* bit 5) and also whether we're above track 0x3f (in bit 0).
|
||||
*/
|
||||
|
||||
return (side ? 0x20 : 0x00) | ((track>0x3f) ? 0x01 : 0x00);
|
||||
}
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector)
|
||||
{
|
||||
if ((sector->data.size() != 512) && (sector->data.size() != 524))
|
||||
Error() << "unsupported sector size --- you must pick 512 or 524";
|
||||
|
||||
write_bits(bits, cursor, 0xff, 1*8); /* pad byte */
|
||||
for (int i=0; i<7; i++)
|
||||
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6*8); /* sync */
|
||||
write_bits(bits, cursor, MAC_SECTOR_RECORD, 3*8);
|
||||
|
||||
uint8_t encodedTrack = sector->physicalTrack & 0x3f;
|
||||
uint8_t encodedSector = sector->logicalSector;
|
||||
uint8_t encodedSide = encode_side(sector->physicalTrack, sector->logicalSide);
|
||||
uint8_t formatByte = MAC_FORMAT_BYTE;
|
||||
uint8_t headerChecksum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
|
||||
|
||||
write_bits(bits, cursor, encode_data_gcr(encodedTrack), 1*8);
|
||||
write_bits(bits, cursor, encode_data_gcr(encodedSector), 1*8);
|
||||
write_bits(bits, cursor, encode_data_gcr(encodedSide), 1*8);
|
||||
write_bits(bits, cursor, encode_data_gcr(formatByte), 1*8);
|
||||
write_bits(bits, cursor, encode_data_gcr(headerChecksum), 1*8);
|
||||
|
||||
write_bits(bits, cursor, 0xdeaaff, 3*8);
|
||||
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6*8); /* sync */
|
||||
write_bits(bits, cursor, MAC_DATA_RECORD, 3*8);
|
||||
write_bits(bits, cursor, encode_data_gcr(sector->logicalSector), 1*8);
|
||||
|
||||
Bytes wireData;
|
||||
wireData.writer().append(sector->data.slice(512, 12)).append(sector->data.slice(0, 512));
|
||||
for (uint8_t b : encode_crazy_data(wireData))
|
||||
write_bits(bits, cursor, encode_data_gcr(b), 1*8);
|
||||
|
||||
write_bits(bits, cursor, 0xdeaaff, 3*8);
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> MacintoshEncoder::encode(
|
||||
int physicalTrack, int physicalSide, const SectorSet& allSectors)
|
||||
{
|
||||
if ((physicalTrack < 0) || (physicalTrack >= MAC_TRACKS_PER_DISK))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
|
||||
double clockRateUs = clockRateUsForTrack(physicalTrack) * clockCompensation;
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
fillBitmapTo(bits, cursor, postIndexGapUs / clockRateUs, { true, false });
|
||||
lastBit = false;
|
||||
|
||||
unsigned numSectors = sectorsForTrack(physicalTrack);
|
||||
for (int sectorId=0; sectorId<numSectors; sectorId++)
|
||||
{
|
||||
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
|
||||
write_sector(bits, cursor, sectorData);
|
||||
}
|
||||
|
||||
if (cursor >= bits.size())
|
||||
Error() << fmt::format("track data overrun by {} bits", cursor - bits.size());
|
||||
fillBitmapTo(bits, cursor, bits.size(), { true, false });
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs*1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
#ifndef MACINTOSH_H
|
||||
#define MACINTOSH_H
|
||||
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
|
||||
#define MAC_SECTOR_RECORD 0xd5aa96 /* 1101 0101 1010 1010 1001 0110 */
|
||||
#define MAC_DATA_RECORD 0xd5aaad /* 1101 0101 1010 1010 1010 1101 */
|
||||
|
||||
#define MAC_SECTOR_LENGTH 524 /* yes, really */
|
||||
#define MAC_ENCODED_SECTOR_LENGTH 703
|
||||
#define MAC_FORMAT_BYTE 0x22
|
||||
|
||||
#define MAC_TRACKS_PER_DISK 80
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
@@ -18,7 +24,21 @@ public:
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
void decodeDataRecord();
|
||||
|
||||
std::set<unsigned> requiredSectors(Track& track) const;
|
||||
};
|
||||
|
||||
class MacintoshEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
virtual ~MacintoshEncoder() {}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
|
||||
};
|
||||
|
||||
extern FlagGroup macintoshEncoderFlags;
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
61
arch/micropolis/decoder.cc
Normal file
61
arch/micropolis/decoder.cc
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "globals.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "sector.h"
|
||||
#include "micropolis.h"
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
/* The sector has a preamble of MFM 0x00s and uses 0xFF as a sync pattern. */
|
||||
static const FluxPattern SECTOR_SYNC_PATTERN(32, 0xaaaa5555);
|
||||
|
||||
AbstractDecoder::RecordType MicropolisDecoder::advanceToNextRecord()
|
||||
{
|
||||
_fmr->seekToIndexMark();
|
||||
const FluxMatcher* matcher = nullptr;
|
||||
_sector->clock = _fmr->seekToPattern(SECTOR_SYNC_PATTERN, matcher);
|
||||
if (matcher == &SECTOR_SYNC_PATTERN) {
|
||||
readRawBits(16);
|
||||
return SECTOR_RECORD;
|
||||
}
|
||||
return UNKNOWN_RECORD;
|
||||
}
|
||||
|
||||
/* Adds all bytes, with carry. */
|
||||
static uint8_t checksum(const Bytes& bytes) {
|
||||
ByteReader br(bytes);
|
||||
uint16_t sum = 0;
|
||||
while (!br.eof()) {
|
||||
if (sum > 0xFF) {
|
||||
sum -= 0x100 - 1;
|
||||
}
|
||||
sum += br.read_8();
|
||||
}
|
||||
/* The last carry is ignored */
|
||||
return sum & 0xFF;
|
||||
}
|
||||
|
||||
void MicropolisDecoder::decodeSectorRecord()
|
||||
{
|
||||
auto rawbits = readRawBits(MICROPOLIS_ENCODED_SECTOR_SIZE*16);
|
||||
auto bytes = decodeFmMfm(rawbits).slice(0, MICROPOLIS_ENCODED_SECTOR_SIZE);
|
||||
ByteReader br(bytes);
|
||||
|
||||
br.read_8(); /* sync */
|
||||
_sector->logicalTrack = br.read_8();
|
||||
_sector->logicalSide = _sector->physicalSide;
|
||||
_sector->logicalSector = br.read_8();
|
||||
if (_sector->logicalSector > 15)
|
||||
return;
|
||||
if (_sector->logicalTrack > 77)
|
||||
return;
|
||||
|
||||
br.read(10); /* OS data or padding */
|
||||
_sector->data = br.read(256);
|
||||
uint8_t wantChecksum = br.read_8();
|
||||
uint8_t gotChecksum = checksum(bytes.slice(1, 2+266));
|
||||
br.read(5); /* 4 byte ECC and ECC-present flag */
|
||||
|
||||
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
18
arch/micropolis/micropolis.h
Normal file
18
arch/micropolis/micropolis.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef ZILOGMCZ_H
|
||||
#define ZILOGMCZ_H
|
||||
|
||||
#define MICROPOLIS_ENCODED_SECTOR_SIZE (1+2+266+6)
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
|
||||
class MicropolisDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~MicropolisDecoder() {}
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -37,7 +37,7 @@ AbstractDecoder::RecordType MxDecoder::advanceToNextRecord()
|
||||
const FluxMatcher* matcher = nullptr;
|
||||
_sector->clock = _clock = _fmr->seekToPattern(ID_PATTERN, matcher);
|
||||
readRawBits(32); /* skip the ID mark */
|
||||
_logicalTrack = decodeFmMfm(readRawBits(32)).reader().read_be16();
|
||||
_logicalTrack = decodeFmMfm(readRawBits(32)).slice(0, 32).reader().read_be16();
|
||||
}
|
||||
else if (_currentSector == 10)
|
||||
{
|
||||
|
||||
87
arch/tids990/decoder.cc
Normal file
87
arch/tids990/decoder.cc
Normal file
@@ -0,0 +1,87 @@
|
||||
#include "globals.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "tids990/tids990.h"
|
||||
#include "crc.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "sector.h"
|
||||
#include "record.h"
|
||||
#include "track.h"
|
||||
#include <string.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
/* The Texas Instruments DS990 uses MFM with a scheme similar to a simplified
|
||||
* version of the IBM record scheme (it's actually easier to parse than IBM).
|
||||
* There are 26 sectors per track, each holding a rather weird 288 bytes.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Sector record:
|
||||
* data: 0 1 0 1 0 1 0 1 .0 0 0 0 1 0 1 0 = 0x550a
|
||||
* mfm: 00 01 00 01 00 01 00 01.00 10 10 10 01 00 01 00 = 0x11112a44
|
||||
* special: 00 01 00 01 00 01 00 01.00 10 00 10 01 00 01 00 = 0x11112244
|
||||
* ^^
|
||||
* 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.
|
||||
*/
|
||||
const FluxPattern SECTOR_RECORD_PATTERN(32, 0x11112244);
|
||||
|
||||
/*
|
||||
* Data record:
|
||||
* data: 0 1 0 1 0 1 0 1 .0 0 0 0 1 0 1 1 = 0x550c
|
||||
* mfm: 00 01 00 01 00 01 00 01.00 10 10 10 01 00 01 01 = 0x11112a45
|
||||
* special: 00 01 00 01 00 01 00 01.00 10 00 10 01 00 01 01 = 0x11112245
|
||||
* ^^
|
||||
* 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.
|
||||
*/
|
||||
const FluxPattern DATA_RECORD_PATTERN(32, 0x11112245);
|
||||
|
||||
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
|
||||
|
||||
AbstractDecoder::RecordType TiDs990Decoder::advanceToNextRecord()
|
||||
{
|
||||
const FluxMatcher* matcher = nullptr;
|
||||
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
|
||||
if (matcher == &SECTOR_RECORD_PATTERN)
|
||||
return RecordType::SECTOR_RECORD;
|
||||
if (matcher == &DATA_RECORD_PATTERN)
|
||||
return RecordType::DATA_RECORD;
|
||||
return RecordType::UNKNOWN_RECORD;
|
||||
}
|
||||
|
||||
void TiDs990Decoder::decodeSectorRecord()
|
||||
{
|
||||
auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE*16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE);
|
||||
|
||||
ByteReader br(bytes);
|
||||
uint16_t gotChecksum = crc16(CCITT_POLY, bytes.slice(1, TIDS990_SECTOR_RECORD_SIZE-3));
|
||||
|
||||
br.seek(2);
|
||||
_sector->logicalSide = br.read_8() >> 3;
|
||||
_sector->logicalTrack = br.read_8();
|
||||
br.read_8(); /* number of sectors per track */
|
||||
_sector->logicalSector = br.read_8();
|
||||
br.read_be16(); /* sector size */
|
||||
uint16_t wantChecksum = br.read_be16();
|
||||
|
||||
if (wantChecksum == gotChecksum)
|
||||
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
|
||||
}
|
||||
|
||||
void TiDs990Decoder::decodeDataRecord()
|
||||
{
|
||||
auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE*16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE);
|
||||
|
||||
ByteReader br(bytes);
|
||||
uint16_t gotChecksum = crc16(CCITT_POLY, bytes.slice(1, TIDS990_DATA_RECORD_SIZE-3));
|
||||
|
||||
br.seek(2);
|
||||
_sector->data = br.read(TIDS990_PAYLOAD_SIZE);
|
||||
uint16_t wantChecksum = br.read_be16();
|
||||
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
176
arch/tids990/encoder.cc
Normal file
176
arch/tids990/encoder.cc
Normal file
@@ -0,0 +1,176 @@
|
||||
#include "globals.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "tids990.h"
|
||||
#include "crc.h"
|
||||
#include "sectorset.h"
|
||||
#include "writer.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
FlagGroup tids990EncoderFlags;
|
||||
|
||||
static IntFlag trackLengthMs(
|
||||
{ "--tids990-track-length-ms" },
|
||||
"Length of a track in milliseconds.",
|
||||
166);
|
||||
|
||||
static IntFlag sectorCount(
|
||||
{ "--tids990-sector-count" },
|
||||
"Number of sectors per track.",
|
||||
26);
|
||||
|
||||
static IntFlag clockRateKhz(
|
||||
{ "--tids990-clock-rate-khz" },
|
||||
"Clock rate of data to write.",
|
||||
500);
|
||||
|
||||
static HexIntFlag am1Byte(
|
||||
{ "--tids990-am1-byte" },
|
||||
"16-bit RAW bit pattern to use for the AM1 ID byte",
|
||||
0x2244);
|
||||
|
||||
static HexIntFlag am2Byte(
|
||||
{ "--tids990-am2-byte" },
|
||||
"16-bit RAW bit pattern to use for the AM2 ID byte",
|
||||
0x2245);
|
||||
|
||||
static IntFlag gap1(
|
||||
{ "--tids990-gap1-bytes" },
|
||||
"Size of gap 1 (the post-index gap).",
|
||||
80);
|
||||
|
||||
static IntFlag gap2(
|
||||
{ "--tids990-gap2-bytes" },
|
||||
"Size of gap 2 (the post-ID gap).",
|
||||
21);
|
||||
|
||||
static IntFlag gap3(
|
||||
{ "--tids990-gap3-bytes" },
|
||||
"Size of gap 3 (the post-data or format gap).",
|
||||
51);
|
||||
|
||||
static StringFlag sectorSkew(
|
||||
{ "--tids990-sector-skew" },
|
||||
"Order to emit sectors.",
|
||||
"1mhc72nid83oje94pkfa50lgb6");
|
||||
|
||||
static int charToInt(char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
}
|
||||
|
||||
void TiDs990Encoder::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 TiDs990Encoder::writeBytes(const Bytes& bytes)
|
||||
{
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
}
|
||||
|
||||
void TiDs990Encoder::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> TiDs990Encoder::encode(
|
||||
int physicalTrack, int physicalSide, const SectorSet& allSectors)
|
||||
{
|
||||
double clockRateUs = 1e3 / clockRateKhz / 2.0;
|
||||
int bitsPerRevolution = (trackLengthMs * 1000.0) / clockRateUs;
|
||||
_bits.resize(bitsPerRevolution);
|
||||
_cursor = 0;
|
||||
|
||||
uint8_t am1Unencoded = decodeUint16(am1Byte);
|
||||
uint8_t am2Unencoded = decodeUint16(am2Byte);
|
||||
|
||||
writeBytes(gap1, 0x55);
|
||||
|
||||
bool first = true;
|
||||
for (char sectorChar : sectorSkew.get())
|
||||
{
|
||||
int sectorId = charToInt(sectorChar);
|
||||
if (!first)
|
||||
writeBytes(gap3, 0x55);
|
||||
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(12, 0x55);
|
||||
bw.write_8(am1Unencoded);
|
||||
bw.write_8(sectorData->logicalSide << 3);
|
||||
bw.write_8(sectorData->logicalTrack);
|
||||
bw.write_8(sectorCount);
|
||||
bw.write_8(sectorData->logicalSector);
|
||||
bw.write_be16(sectorData->data.size());
|
||||
uint16_t crc = crc16(CCITT_POLY, header);
|
||||
bw.write_be16(crc);
|
||||
|
||||
writeRawBits(am1Byte, 16);
|
||||
writeBytes(header.slice(1));
|
||||
}
|
||||
|
||||
writeBytes(gap2, 0x55);
|
||||
|
||||
{
|
||||
Bytes data;
|
||||
ByteWriter bw(data);
|
||||
|
||||
writeBytes(12, 0x55);
|
||||
bw.write_8(am2Unencoded);
|
||||
|
||||
bw += sectorData->data;
|
||||
uint16_t crc = crc16(CCITT_POLY, data);
|
||||
bw.write_be16(crc);
|
||||
|
||||
writeRawBits(am2Byte, 16);
|
||||
writeBytes(data.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
if (_cursor >= _bits.size())
|
||||
Error() << "track data overrun";
|
||||
while (_cursor < _bits.size())
|
||||
writeBytes(1, 0x55);
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(_bits, clockRateUs*1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
47
arch/tids990/tids990.h
Normal file
47
arch/tids990/tids990.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef TIDS990_H
|
||||
#define TIDS990_H
|
||||
|
||||
#define TIDS990_PAYLOAD_SIZE 288 /* bytes */
|
||||
#define TIDS990_SECTOR_RECORD_SIZE 10 /* bytes */
|
||||
#define TIDS990_DATA_RECORD_SIZE (TIDS990_PAYLOAD_SIZE + 4) /* bytes */
|
||||
|
||||
class Sector;
|
||||
class SectorSet;
|
||||
class Fluxmap;
|
||||
class Track;
|
||||
|
||||
class TiDs990Decoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~TiDs990Decoder() {}
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
void decodeDataRecord();
|
||||
};
|
||||
|
||||
class TiDs990Encoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
virtual ~TiDs990Encoder() {}
|
||||
|
||||
private:
|
||||
void writeRawBits(uint32_t data, int width);
|
||||
void writeBytes(const Bytes& bytes);
|
||||
void writeBytes(int count, uint8_t value);
|
||||
void writeSync();
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
|
||||
|
||||
private:
|
||||
std::vector<bool> _bits;
|
||||
unsigned _cursor;
|
||||
bool _lastBit;
|
||||
};
|
||||
|
||||
extern FlagGroup tids990EncoderFlags;
|
||||
|
||||
#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.
192
doc/building.md
192
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.
|
||||
@@ -173,6 +175,7 @@ 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
|
||||
|
||||
The client software is where the intelligence, such as it is, is. It's pretty
|
||||
@@ -181,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.
|
||||
|
||||
@@ -197,11 +200,184 @@ dependencies and you should be able to put it anywhere.
|
||||
If it doesn't build, please [get in
|
||||
touch](https://github.com/davidgiven/fluxengine/issues/new).
|
||||
|
||||
|
||||
## Connecting it up
|
||||
|
||||
You should now have a working board, so it's time to test it.
|
||||
|
||||
1. Plug the motherboard end of your floppy disk cable into the FluxEngine.
|
||||
|
||||
The **red stripe goes on the right**. The **lower set of
|
||||
holes connect to the board**. See the pinout below.
|
||||
|
||||
If you're using header pins, the upper row of holes in the connector
|
||||
should overhang the edge of the board. If you're using a floppy drive
|
||||
motherboard connector, you're golden, of course (unless you have one of
|
||||
those annoying unkeyed cables, or have accidentally soldered the
|
||||
connector on in the wrong place --- don't laugh, I've done it.)
|
||||
|
||||
2. Plug the drive end of your floppy disk cable into the drive (or drives).
|
||||
|
||||
Floppy disk cables typically have [two pairs of floppy disk drive
|
||||
connectors with a twist between
|
||||
them](http://www.nullmodem.com/Floppy.htm). (Each pair has one connector
|
||||
for a 3.5" drive and a different one for a 5.25" drive.) (Some cables
|
||||
are cheap and just have the 3.5" connectors. Some are _very_ cheap and
|
||||
have a single 3.5" connector, after the twist.)
|
||||
|
||||
If you have **two** drives, plug them into both connectors. FluxEngine,
|
||||
sadly, non-standard disk numbering (there are reasons). Drive 0 is the
|
||||
one nearest the motherboard; that is, before the twist. Drive 1 is the
|
||||
one at the end of the cable; that is, after the twist. Drive 0 is the
|
||||
default. You can tell the client to select drive 1 by using `-s :d=1`.
|
||||
|
||||
If you have **one** drive, you may plug it into _either_ connector.
|
||||
FluxEngine will autodetect it and treat it as drive 0. However, you'll
|
||||
get the most reliable electrical signal if you plug it in at the end of
|
||||
the cable.
|
||||
|
||||
**A note on termination:** some 5.25" drives require jumper configuration
|
||||
to tell them whether they're at the end of the cable or in the middle of
|
||||
the cable. 3.5" drives don't, and my 5.25" drives don't, so I can't
|
||||
advise there. Consult your drive datasheet for details.
|
||||
|
||||
3. **Important.** Make sure that no disk you care about is in the drive.
|
||||
(Because if your wiring is wrong and a disk is inserted, you'll corrupt
|
||||
it.)
|
||||
|
||||
4. Connect the floppy drive to power. Nothing should happen. If you've
|
||||
connected something in backwards, you'll see the drive light up, the motor
|
||||
start, and if you didn't take the disk out, one track has just been wiped.
|
||||
If this happens, check your wiring.
|
||||
|
||||
5. Strip off the little piece of protective plastic on the USB socket on the
|
||||
board --- the little socket at the end, not the big programmer plug.
|
||||
|
||||
6. Connect the FluxEngine to your PC via USB.
|
||||
|
||||
7. Insert a scratch disk and do `fluxengine rpm` from the shell. The motor
|
||||
should work and it'll tell you that the disk is spinning at about 300
|
||||
rpm for a 3.5" disk, or 360 rpm for a 5.25" disk. If it doesn't, please
|
||||
[get in touch](https://github.com/davidgiven/fluxengine/issues/new).
|
||||
|
||||
8. Do `fluxengine test bandwidth` from the shell. It'll measure your USB
|
||||
bandwidth. Ideally you should be getting above 800kB/s in both directions.
|
||||
FluxEngine needs about 300kB/s for a DD disk and about 600kB/s for a HD
|
||||
disk, so if you're getting less than this, try a different USB port.
|
||||
|
||||
9. Insert a standard PC formatted floppy disk into the drive (probably a good
|
||||
idea to remove the old disk first). Then do `fluxengine read ibm`. It
|
||||
should read the disk, emitting copious diagnostics, and spit out an
|
||||
`ibm.img` file containing the decoded disk image (either 1440kB or 720kB
|
||||
depending).
|
||||
|
||||
10. Profit!
|
||||
|
||||
## Technical details
|
||||
|
||||
The board pinout and the way it's connected to the floppy bus is described
|
||||
below.
|
||||
|
||||
```ditaa
|
||||
:-E -s 0.75
|
||||
+-----+
|
||||
|||||||
|
||||
+----+-----+----+
|
||||
+cAAA +
|
||||
+ Debug board +
|
||||
+----+-----+----+
|
||||
+ GND|cDDD | VDD+
|
||||
+----+ +----+
|
||||
INDEX300 ---+ 3.0| | GND+--------------------------+
|
||||
+----+ +----+ +--+--+ |
|
||||
INDEX360 ---+ 3.1| | 1.7+------ DISKCHG --+34+33+--+
|
||||
+----+ +----+ +--+--+
|
||||
TK43 ---+ 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/SIDE1 ---+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:
|
||||
|
||||
- `DIR/SIDE1` is the step direction pin. During reads or writes, `SIDE1` is
|
||||
also multiplexed onto it, because some drives expect this. This is harmless
|
||||
on other drives because the `DIR` pin is ignored during reads or writes.
|
||||
|
||||
- `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.
|
||||
|
||||
- `TK43` is an optional output pin which goes low when the drive is seeking
|
||||
to track 43 or above. This is useful when using 8" floppy drives, which
|
||||
require reduced write current when writing to these tracks.
|
||||
|
||||
## Next steps
|
||||
|
||||
The board's now assembled and programmed. Plug it into your drive, strip the
|
||||
plastic off the little USB connector and plug that into your computer, and
|
||||
you're ready to start using it.
|
||||
You should now be ready to go. You'll want to read [the client
|
||||
documentation](using.md) for information about how to actually do interesting
|
||||
things.
|
||||
|
||||
I _do_ make updates to the firmware whenever necessary, so you may need to
|
||||
reprogram it at intervals; you may want to take this into account if you
|
||||
|
||||
@@ -10,7 +10,7 @@ Bizarrely, the data in each sector is stored with all the odd bits first, and
|
||||
then all the even bits. This is tied into the checksum algorithm, which is
|
||||
distinctly subpar and not particularly good at detecting errors.
|
||||
|
||||
Reading discs
|
||||
Reading disks
|
||||
-------------
|
||||
|
||||
Just do:
|
||||
@@ -34,6 +34,28 @@ You will end up with a 929280 byte long image which you probably _can't_ use
|
||||
in an emulator; each sector will contain the 512 bytes of user payload
|
||||
followed by the 16 bytes of metadata.
|
||||
|
||||
Writing disks
|
||||
-------------
|
||||
|
||||
Just do:
|
||||
|
||||
```
|
||||
fluxengine write amiga -i amiga.adf
|
||||
```
|
||||
|
||||
This will rake a normal 901120 byte long ADF file and write it to a DD disk.
|
||||
Note that writing to an HD disk will probably not work (this will depend on
|
||||
your drive and disk and potential FluxEngine bugs I'm still working on ---
|
||||
please [get in touch](https://github.com/davidgiven/fluxengine/issues/new) if
|
||||
you have any insight here).
|
||||
|
||||
If you want to write the metadata as well, specify a 528 byte sector size for
|
||||
the output image and supply a 929280 byte long file as described above.
|
||||
|
||||
```
|
||||
fluxengine write amiga -i amiga.adf:b=528
|
||||
```
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
|
||||
|
||||
@@ -14,10 +14,12 @@ Apparently about 20% of Brother word processors have alignment issues which
|
||||
means that the disks can't be read by FluxEngine (because the tracks on the
|
||||
disk don't line up with the position of the head in a PC drive). The word
|
||||
processors themselves solved this by microstepping until they found where the
|
||||
real track is, but normal PC drives aren't capable of doing this.
|
||||
Particularly with the 120kB disks, you might want to fiddle with the start
|
||||
track (e.g. `:t=0-79x2`) to get a clean read. Keep an eye on the bad sector
|
||||
map that's dumped at the end of a read.
|
||||
real track is, but normal PC drives aren't capable of doing this. Particularly
|
||||
with the 120kB disks, you might want to fiddle with the start track (e.g.
|
||||
`:t=0-79x2`) to get a clean read. Keep an eye on the bad sector map that's
|
||||
dumped at the end of a read. My word processor likes to put logical track 0 on
|
||||
physical track 3, which means that logical track 77 is on physical track 80;
|
||||
luckily my PC drive can access track 80.
|
||||
|
||||
Using FluxEngine to *write* disks isn't a problem, so the
|
||||
simplest solution is to use FluxEngine to create a new disk, with the tracks
|
||||
@@ -30,7 +32,7 @@ If you find one of these misaligned disks then *please* [get in
|
||||
touch](https://github.com/davidgiven/fluxengine/issues/new); I want to
|
||||
investigate.
|
||||
|
||||
Reading discs
|
||||
Reading disks
|
||||
-------------
|
||||
|
||||
Just do:
|
||||
@@ -41,7 +43,7 @@ fluxengine read brother
|
||||
|
||||
You should end up with a `brother.img` which is 239616 bytes long.
|
||||
|
||||
Writing discs
|
||||
Writing disks
|
||||
-------------
|
||||
|
||||
Just do:
|
||||
@@ -53,6 +55,27 @@ fluxengine write brother
|
||||
...and it'll write a `brother.img` file which is 239616 bytes long to the
|
||||
disk. (Use `-i` to specify a different input filename.)
|
||||
|
||||
Dealing with misaligned disks
|
||||
-----------------------------
|
||||
|
||||
While FluxEngine can't read misaligned disks directly, Brother word processors
|
||||
_can_. If you have access to a compatible word processor, there's a fairly
|
||||
simple workaround to allow you to extract the data:
|
||||
|
||||
1. Format a disk using FluxEngine (by simply writing a blank filesystem image
|
||||
to a disk). This will have the correct alignment to work on a PC drive.
|
||||
|
||||
2. Use a word processor to copy the misaligned disk to the newly formatted
|
||||
disk. The machine will happily adjust itself to both sets of alignments.
|
||||
|
||||
3. Use FluxEngine to read the data off the correctly aligned disk.
|
||||
|
||||
I realise this is rather unsatisfactory, as the Brother hardware is becoming
|
||||
rarer and they cope rather badly with damaged disks, but this is a limitation
|
||||
of the hardware of normal PC drives. (It _is_ possible to deliberately misalign
|
||||
a drive to make it match up with a bad disk, but this is for experts only --- I
|
||||
wouldn't dare.)
|
||||
|
||||
Low level format
|
||||
----------------
|
||||
|
||||
@@ -60,14 +83,6 @@ The drive is a single-sided 3.5" drive spinning at not 300 rpm (I don't know
|
||||
the precise speed yet but FluxEngine doesn't care). The 240kB disks have 78
|
||||
tracks and the 120kB disks have 39.
|
||||
|
||||
The Brother drive alignment is kinda variable; when you put the disk in the
|
||||
drive it seeks all the way to physical track 0 and then starts searching for
|
||||
something which looks like data. My machine likes to put logical track 0 on
|
||||
physical track 3. FluxEngine puts logical track 0 on physical track 0 for
|
||||
simplicity, which works fine (at least on my machine). If this doesn't work
|
||||
for you, [get in touch](https://github.com/davidgiven/fluxengine/issues/new);
|
||||
there are potential workarounds.
|
||||
|
||||
Each track has 12 256-byte sectors. The drive ignores the index hole so they're
|
||||
lined up all anyhow. As FluxEngine can only read from index to index, it
|
||||
actually reads two complete revolutions and reassembles the sectors from that.
|
||||
@@ -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
|
||||
|
||||
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
|
||||
-------------
|
||||
@@ -37,22 +45,34 @@ Reading discs
|
||||
Just do:
|
||||
|
||||
```
|
||||
fluxengine read mac
|
||||
fluxengine read mac -o mac.diskcopy
|
||||
```
|
||||
|
||||
You should end up with an `mac.img` which is 1001888 bytes long (for a normal
|
||||
DD disk). If you want the single-sided variety, use `-s :s=0`.
|
||||
You should end up with a `mac.diskcopy` file which is compatible with DiskCopy
|
||||
4.2, which most Mac emulators support.
|
||||
|
||||
**Big warning!** The image may not work in an emulator. Mac 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 524 x 12 x 2 x 80 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).
|
||||
**Big warning!** Mac disk images are complicated due to the way the tracks are
|
||||
different sizes and the odd sector size. If you use a normal `.img` file, then
|
||||
FluxEngine will store them in a simple 524 x 12 x 2 x 80 layout, with holes
|
||||
where missing sectors should be; this was easiest, but is unlikely to work with
|
||||
most Mac emulators and other software. In these files, 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`.
|
||||
|
||||
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`.
|
||||
Writing discs
|
||||
-------------
|
||||
|
||||
Just do:
|
||||
|
||||
```
|
||||
fluxengine write mac -i mac.diskcopy
|
||||
```
|
||||
|
||||
It'll read the DiskCopy 4.2 file and write it out.
|
||||
|
||||
The same warning as above applies --- you can use normal `.img` files but it's
|
||||
problematic. Use DiskCopy 4.2 files instead.
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
@@ -66,3 +86,7 @@ Useful references
|
||||
|
||||
- [Les Disquettes et le drive Disk II](http://www.hackzapple.com/DISKII/DISKIITECH.HTM), an
|
||||
epicly detailed writeup of the Apple II disk format (which is closely related)
|
||||
|
||||
- [The DiskCopy 4.2
|
||||
format](https://www.discferret.com/wiki/Apple_DiskCopy_4.2), described on
|
||||
the DiskFerret website.
|
||||
|
||||
55
doc/disk-micropolis.md
Normal file
55
doc/disk-micropolis.md
Normal file
@@ -0,0 +1,55 @@
|
||||
Disk: Micropolis
|
||||
================
|
||||
|
||||
Micropolis MetaFloppy disks use MFM and hard sectors. They were 100 TPI and
|
||||
stored 315k per side. Each of the 16 sectors contains 266 bytes of "user data,"
|
||||
allowing 10 bytes of metadata for use by the operating system. Micropolis DOS
|
||||
(MDOS) used the metadata bytes, but CP/M did not.
|
||||
|
||||
Some later systems were Micropolis-compatible and so were also 100 TPI, like
|
||||
the Vector Graphic Dual-Mode Disk Controller which was paired with a Tandon
|
||||
drive.
|
||||
|
||||
**Important note:** You _cannot_ read these disks with a normal PC drive, as
|
||||
these drives are 96tpi.The track spacing is determined by the physical geometry
|
||||
of the drive and can't be changed in software. You'll need to get hold of a
|
||||
100tpi Micropolis drive. Luckily these seem to use the same connector and
|
||||
pinout as a 96tpi PC 5.25" drive. In use they should be identical.
|
||||
|
||||
Reading disks
|
||||
-------------
|
||||
|
||||
Just do:
|
||||
|
||||
```
|
||||
fluxengine read micropolis
|
||||
```
|
||||
|
||||
You should end up with a `micropolis.img` which is 630784 bytes long (for a
|
||||
normal DD disk). The image is written in CHS order, but HCS is generally used
|
||||
by CP/M tools so the image needs to be post-processed. For only half-full disks
|
||||
or single-sided disks, you can use `-s :s=0` to read only one side of the disk
|
||||
which works around the problem.
|
||||
|
||||
The [CP/M BIOS](https://www.seasip.info/Cpm/bios.html) defined SELDSK, SETTRK,
|
||||
and SETSEC, but no function to select the head/side. Double-sided floppies
|
||||
could be represented as having either twice the number of sectors, for CHS, or
|
||||
twice the number of tracks, HCS; the second side's tracks logically followed
|
||||
the first side (e.g., tracks 77-153). Micropolis disks tended to be the latter.
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
|
||||
- [Micropolis 1040/1050 S-100 Floppy Disk Subsystems User's Manual][micropolis1040/1050].
|
||||
Section 6, pages 261-266. Documents pre-ECC sector format. Note that the
|
||||
entire record, starting at the sync byte, is controlled by software
|
||||
|
||||
- [Vector Graphic Dual-Mode Disk Controller Board Engineering Documentation][vectordualmode].
|
||||
Section 1.6.2. Documents ECC sector format
|
||||
|
||||
- [AltairZ80 Simulator Usage Manual][altairz80]. Section 10.6. Documents ECC
|
||||
sector format and VGI file format
|
||||
|
||||
[micropolis1040/1050]: http://www.bitsavers.org/pdf/micropolis/metafloppy/1084-01_1040_1050_Users_Manual_Apr79.pdf
|
||||
[vectordualmode]: http://bitsavers.org/pdf/vectorGraphic/hardware/7200-1200-02-1_Dual-Mode_Disk_Controller_Board_Engineering_Documentation_Feb81.pdf
|
||||
[altairz80]: http://www.bitsavers.org/simh.trailing-edge.com_201206/pdf/altairz80_doc.pdf
|
||||
46
doc/disk-tids990.md
Normal file
46
doc/disk-tids990.md
Normal file
@@ -0,0 +1,46 @@
|
||||
Disk: TI DS990 FD1000
|
||||
=====================
|
||||
|
||||
The Texas Instruments DS990 was a multiuser modular computing system from 1998,
|
||||
based around the TMS-9900 processor (as used by the TI-99). It had an 8" floppy
|
||||
drive module, the FD1000, which was a 77-track, 288-byte sector FM/MFM system
|
||||
with 26 sectors per track. The encoding scheme was very similar to a simplified
|
||||
version of the IBM scheme, but of course not compatible. A double-sided disk
|
||||
would store a very satisfactory 1126kB of data; here's one at <a
|
||||
href="https://www.old-computers.com/museum/computer.asp?st=1&c=1025">old-computers.com</a>:
|
||||
|
||||
<div style="text-align: center">
|
||||
<a href="https://www.old-computers.com/museum/computer.asp?st=1&c=1025">
|
||||
<img src="tids990.jpg" style="max-width: 60%" alt="A DS990 at old-computers.com"></a>
|
||||
</div>
|
||||
|
||||
FluxEngine will read and write these (but only the DSDD MFM variant).
|
||||
|
||||
Reading discs
|
||||
-------------
|
||||
|
||||
Just do:
|
||||
|
||||
```
|
||||
fluxengine read tids990
|
||||
```
|
||||
|
||||
You should end up with an `tids990.img` which is 1153152 bytes long.
|
||||
|
||||
Writing discs
|
||||
-------------
|
||||
|
||||
Just do:
|
||||
|
||||
```
|
||||
fluxengine write tids990 -i tids990.img
|
||||
```
|
||||
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
|
||||
- [The FD1000 Depot Maintenance
|
||||
Manual](http://www.bitsavers.org/pdf/ti/990/disk/2261885-9701_FD1000depotVo1_Jan81.pdf)
|
||||
|
||||
|
||||
@@ -32,3 +32,11 @@ If you've got a 40-track disk, use `-s :t=0-79x2`.
|
||||
|
||||
If you've got a single density disk, use `--read-fm=true`. (Double density is
|
||||
the default.)
|
||||
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
|
||||
- [The JV3 file format](https://www.tim-mann.org/trs80/dskspec.html):
|
||||
documents the most popular emulator disk image.
|
||||
|
||||
|
||||
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?
|
||||
|
||||
|
||||
71
doc/greaseweazle.md
Normal file
71
doc/greaseweazle.md
Normal file
@@ -0,0 +1,71 @@
|
||||
Using the FluxEngine client software with GreaseWeazle hardware
|
||||
===============================================================
|
||||
|
||||
The FluxEngine isn't the only project which does this; another one is the
|
||||
[GreaseWeazle](https://github.com/keirf/Greaseweazle/wiki), a Blue Pill based
|
||||
completely open source solution. This requires more work to set up (or you can
|
||||
buy a prebuilt GreaseWeazle board), but provides completely open source
|
||||
hardware which doesn't require the use of the Cypress Windows-based tools that
|
||||
the FluxEngine does. Luckily, the FluxEngine software supports it
|
||||
out-of-the-box --- just plug it in and nearly everything should work.
|
||||
|
||||
I am aware that having _software_ called FluxEngine and _hardware_ called
|
||||
FluxEngine makes things complicated when you're not using the FluxEngine client
|
||||
software with a FluxEngine board, but I'm afraid it's too late to change that
|
||||
now. Sorry.
|
||||
|
||||
If you're using Windows
|
||||
-----------------------
|
||||
|
||||
In order to access the GreaseWeazle from Windows, you need to install a WinUSB
|
||||
driver for it. You can do this with the [Zadig](https://zadig.akeo.ie/)
|
||||
program. Download it, plug in the GreaseWeazle, and run it; select Options,
|
||||
List All Devices, and then open the big dropdown box and select the
|
||||
GreaseWeazle. You should see something like this.
|
||||
|
||||
<div style="text-align: center">
|
||||
<img src="zadig.png" style="width:80%" alt="Zadig screenshot"></a>
|
||||
</div>
|
||||
|
||||
Ensure that the Driver boxes say `usbser` and `WinUSB`. Then press 'Replace
|
||||
Driver'. Once done, the GreaseWeazle will be visible to the FluxEngine client.
|
||||
|
||||
**Important note!** Unfortunately, now, the original GreaseWeazle client won't
|
||||
work --- you can't use both drivers at once. I'm working on this. To switch
|
||||
back to the original driver, for using the GreaseWeazle client software
|
||||
instead, open up Zadig again, go through the same process, but make sure the left Driver box says `WinUSB` and the right one says `USB Serial (CDC)`. Now, when you press 'Replace Driver' the original driver will be restored.
|
||||
|
||||
What works
|
||||
----------
|
||||
|
||||
Supported features with the GreaseWeazle include:
|
||||
|
||||
- simple reading and writing of disks, seeking etc
|
||||
- erasing disks
|
||||
- determining disk rotation speed
|
||||
|
||||
What doesn't work
|
||||
-----------------
|
||||
|
||||
(I'm still working on this. If you have an urgent need for anything, please
|
||||
[file an issue](https://github.com/davidgiven/fluxengine/issues/new) and I'll
|
||||
see what I can do.)
|
||||
|
||||
- voltage measurement
|
||||
- hard sectored disks (you can still read these, but you can't use
|
||||
`--hard-sector-count`).
|
||||
|
||||
Who to contact
|
||||
--------------
|
||||
|
||||
I want to make it clear that the FluxEngine code is _not_ supported by the
|
||||
GreaseWeazle team. If you have any problems, please [contact
|
||||
me](https://github.com/davidgiven/fluxengine/issues/new) and not them.
|
||||
|
||||
In addition, the GreaseWeazle release cycle is not synchronised to the
|
||||
FluxEngine release cycle, so it's possible you'll have a version of the
|
||||
GreaseWeazle firmware which is not supported by FluxEngine. Hopefully, it'll
|
||||
detect this and complain. Again, [file an
|
||||
issue](https://github.com/davidgiven/fluxengine/issues/new) and I'll look into
|
||||
it.
|
||||
|
||||
@@ -22,19 +22,17 @@ FluxEngine, where a different datapath state machine thingy (the PSoC5LP has
|
||||
24, all independently programmable) to interpret the bytecodes, generate a
|
||||
stream of pulses to the disk.
|
||||
|
||||
The bytecode format represents an interval between pulses as a byte, a pulse
|
||||
as a byte, and the index hole as a byte. Timer overflows are handled by
|
||||
sending multiple intervals in a row. However, the USB transport applies a
|
||||
simple compression system to this in order to get the USB bandwidth down to
|
||||
something manageable.
|
||||
The bytecode format is very simple with a six-bit interval since the previous
|
||||
event in the lower six bits and the top two bits are set of a pulse or an index
|
||||
hole (or both, or neither).
|
||||
|
||||
An HD floppy has a nominal pulse frequency of 500kHz, and we use a sample
|
||||
clock of 12MHz (every 83ns). This means that our 500kHz pulses will have an
|
||||
average interval of 24. This gives us more than enough resolution. At this
|
||||
speed, in the 200ms that a 3.5" disk takes to rotate, we will see about
|
||||
100,000 pulses. Each one is encoded as two bytes, one for the interval and
|
||||
one to generate the pulse; so that revolution generates 200kB of data.
|
||||
(Extremely approximately. The actual figure is less.)
|
||||
100,000 pulses. Each one is encoded as a single byte; so that revolution
|
||||
generates 100kB of data. (Extremely approximately. The actual figure varies
|
||||
depending on what data is stored on the disk.)
|
||||
|
||||
(The clock needs to be absolutely rock solid or we get jitter which makes the
|
||||
data difficult to analyse, so 12 was chosen to be derivable from the
|
||||
@@ -59,53 +57,36 @@ 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 or via a prebuilt board. It's supported by
|
||||
the FluxEngine client software, and you should, mostly, be able to use
|
||||
GreaseWeazle hardware interchangeably with FluxEngine hardware. See the
|
||||
[dedicated page](greaseweazle.md) for more information.
|
||||
|
||||
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.
|
||||
|
||||
Speaking of which, the CY8CKIT-059 is $10. (Before shipping, which is
|
||||
admittedly expensive.)
|
||||
|
||||
### Some useful links
|
||||
|
||||
|
||||
BIN
doc/tids990.jpg
Normal file
BIN
doc/tids990.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
239
doc/using.md
239
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,48 @@ 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.
|
||||
|
||||
### Connecting it up
|
||||
|
||||
To use, simply plug your FluxEngine into your computer and run the client. If a
|
||||
single device is plugged in, it will be automatically detected and used.
|
||||
|
||||
If _more_ than one device is plugged in, you need to specify which one to use
|
||||
with the `--device` parameter, which takes the device serial number as a
|
||||
parameter. You can find out the serial numbers by running the command without
|
||||
the `--device` parameter, and if more than one device is attached they will be
|
||||
listed. The serial number is also shown whenever a connection is made.
|
||||
|
||||
You _can_ work with more than one FluxEngine at the same time, using different
|
||||
invocations of the client; but be careful of USB bandwidth. If the devices are
|
||||
connected via the same hub, the bandwidth will be shared.
|
||||
|
||||
### 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 +62,7 @@ fluxengine read ibm -s fakedisk.flux:t=0-79:s=0
|
||||
- To access a real disk, leave out the filename (so `:t=0-79:s=0`).
|
||||
|
||||
- To access only some tracks, use the `t=` modifier. To access only some
|
||||
sides, use the `s=` modifier. To change drives, use `d=`.
|
||||
sides, use the `s=` modifier.
|
||||
|
||||
- Inside a modifier, you can use a comma separated list of ranges. So
|
||||
`:t=0-3` and `:t=0,1,2,3` are equivalent.
|
||||
@@ -110,18 +88,15 @@ If you _don't_ specify a modifier, you'll get the default, which should be
|
||||
sensible for the command you're using.
|
||||
|
||||
**Important note:** FluxEngine _always_ uses zero-based units (even if the
|
||||
*disk format says otherwise).
|
||||
disk format says otherwise).
|
||||
|
||||
### Input and output specifiers
|
||||
|
||||
These use a very similar syntax to the source and destination specifiers
|
||||
(because they're based on the same microformat library!) but are used for
|
||||
input and output _images_: i.e. nicely lined up arrays of sectors which you
|
||||
can actually do something with.
|
||||
|
||||
Use `--input` (`-i`) or `--output` (`-o`) as appropriate to tell FluxEngine
|
||||
where you want to read from or write to. The actual format is autodetected
|
||||
based on the extension:
|
||||
When reading or writing _file system images_, use the `--input` (`-i`) and
|
||||
`--output` (`-o`) options to specify the file and file format. These use a very
|
||||
similar syntax to the source and destination specifiers (because they're based
|
||||
on the same microformat library!) but with different specifiers. Also, the
|
||||
exact format varies according to the extension:
|
||||
|
||||
- `.img` or `.adf`: raw sector images in CHS order. Append
|
||||
`:c=80:h=2:s=9:b=512` to set the geometry; that specifies 80 cylinders, 2
|
||||
@@ -157,6 +132,14 @@ based on the extension:
|
||||
FluxEngine's D64 support is currently limited to write only. It will work
|
||||
with up to 40 logical tracks.
|
||||
|
||||
- `.diskcopy`: a Macintosh DiskCopy 4.2 file. This is a special-purpose
|
||||
format due to the weird layout of Mac GCR disks, but it can also support
|
||||
720kB and 1440kB IBM disks (although there's no real benefit).
|
||||
|
||||
- `.jv3`: a disk image format mainly used by the TRS-80. These images can be
|
||||
read, but not yet written. You only get the data; the density and DAM bits
|
||||
are ignored.
|
||||
|
||||
### High density disks
|
||||
|
||||
High density disks use a different magnetic medium to low and double density
|
||||
@@ -176,6 +159,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.
|
||||
@@ -184,56 +190,57 @@ 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 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.
|
||||
- `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.
|
||||
|
||||
**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.
|
||||
- `fluxengine convert`: converts files from various formats to various other
|
||||
formats. The main use of this is probably `fluxengine convert image`, which
|
||||
will convert a disk image from one format to another.
|
||||
|
||||
There are also subcommands for converting 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.
|
||||
|
||||
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
|
||||
@@ -259,8 +266,10 @@ disk). For a 5.25" disk, use `--visualiser-period=166`.
|
||||
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.
|
||||
|
||||
- `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
|
||||
@@ -269,13 +278,13 @@ 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 --write-svg=brother.svg
|
||||
$ 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 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:
|
||||
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 --write-svg=brother.svg
|
||||
|
||||
BIN
doc/zadig.png
Normal file
BIN
doc/zadig.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
88
lib/bytes.cc
88
lib/bytes.cc
@@ -1,7 +1,6 @@
|
||||
#include "globals.h"
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include "common/crunch.h"
|
||||
#include <fstream>
|
||||
#include <zlib.h>
|
||||
|
||||
@@ -42,6 +41,12 @@ Bytes::Bytes(const uint8_t* ptr, size_t len):
|
||||
_high(len)
|
||||
{}
|
||||
|
||||
Bytes::Bytes(const std::string& s):
|
||||
_data(createVector((const uint8_t*)&s[0], s.size())),
|
||||
_low(0),
|
||||
_high(s.size())
|
||||
{}
|
||||
|
||||
Bytes::Bytes(std::initializer_list<uint8_t> data):
|
||||
_data(createVector(data)),
|
||||
_low(0),
|
||||
@@ -147,6 +152,28 @@ Bytes Bytes::slice(unsigned start, unsigned len) const
|
||||
}
|
||||
}
|
||||
|
||||
Bytes Bytes::slice(unsigned start) const
|
||||
{
|
||||
int len = 0;
|
||||
if (start < size())
|
||||
len = size() - start;
|
||||
return slice(start, len);
|
||||
}
|
||||
|
||||
std::vector<bool> Bytes::toBits() const
|
||||
{
|
||||
std::vector<bool> bits;
|
||||
for (uint8_t byte : *this)
|
||||
{
|
||||
for (int i=0; i<8; i++)
|
||||
{
|
||||
bits.push_back(byte & 0x80);
|
||||
byte <<= 1;
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
uint8_t toByte(
|
||||
std::vector<bool>::const_iterator start,
|
||||
std::vector<bool>::const_iterator end)
|
||||
@@ -227,60 +254,6 @@ Bytes Bytes::decompress() const
|
||||
return output;
|
||||
}
|
||||
|
||||
Bytes Bytes::crunch() const
|
||||
{
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
Bytes outputBuffer(1024*1024);
|
||||
|
||||
crunch_state_t cs = {};
|
||||
cs.inputptr = begin();
|
||||
cs.inputlen = size();
|
||||
|
||||
do
|
||||
{
|
||||
cs.outputptr = outputBuffer.begin();
|
||||
cs.outputlen = outputBuffer.size();
|
||||
|
||||
::crunch(&cs);
|
||||
bw += outputBuffer.slice(0, outputBuffer.size() - cs.outputlen);
|
||||
}
|
||||
while (cs.inputlen != 0);
|
||||
cs.outputptr = outputBuffer.begin();
|
||||
cs.outputlen = outputBuffer.size();
|
||||
donecrunch(&cs);
|
||||
bw += outputBuffer.slice(0, outputBuffer.size() - cs.outputlen);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
Bytes Bytes::uncrunch() const
|
||||
{
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
Bytes outputBuffer(1024*1024);
|
||||
|
||||
crunch_state_t cs = {};
|
||||
cs.inputptr = begin();
|
||||
cs.inputlen = size();
|
||||
|
||||
do
|
||||
{
|
||||
cs.outputptr = outputBuffer.begin();
|
||||
cs.outputlen = outputBuffer.size();
|
||||
|
||||
::uncrunch(&cs);
|
||||
bw += outputBuffer.slice(0, outputBuffer.size() - cs.outputlen);
|
||||
}
|
||||
while (cs.inputlen != 0);
|
||||
cs.outputptr = outputBuffer.begin();
|
||||
cs.outputlen = outputBuffer.size();
|
||||
doneuncrunch(&cs);
|
||||
bw += outputBuffer.slice(0, outputBuffer.size() - cs.outputlen);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void Bytes::writeToFile(const std::string& filename) const
|
||||
{
|
||||
std::ofstream f(filename, std::ios::out | std::ios::binary);
|
||||
@@ -291,6 +264,11 @@ void Bytes::writeToFile(const std::string& filename) const
|
||||
f.close();
|
||||
}
|
||||
|
||||
void Bytes::writeTo(std::ostream& stream) const
|
||||
{
|
||||
stream.write((const char*) cbegin(), size());
|
||||
}
|
||||
|
||||
ByteReader Bytes::reader() const
|
||||
{
|
||||
return ByteReader(*this);
|
||||
|
||||
20
lib/bytes.h
20
lib/bytes.h
@@ -1,6 +1,8 @@
|
||||
#ifndef BYTES_H
|
||||
#define BYTES_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
class ByteReader;
|
||||
class ByteWriter;
|
||||
|
||||
@@ -10,6 +12,7 @@ public:
|
||||
Bytes();
|
||||
Bytes(unsigned size);
|
||||
Bytes(const uint8_t* ptr, size_t len);
|
||||
Bytes(const std::string& data);
|
||||
Bytes(std::initializer_list<uint8_t> data);
|
||||
Bytes(std::shared_ptr<std::vector<uint8_t>> data);
|
||||
Bytes(std::shared_ptr<std::vector<uint8_t>> data, unsigned start, unsigned end);
|
||||
@@ -38,6 +41,8 @@ public:
|
||||
uint8_t* begin() { checkWritable(); return &(*_data)[_low]; }
|
||||
uint8_t* end() { checkWritable(); return &(*_data)[_high]; }
|
||||
|
||||
operator const std::string () const { return std::string(cbegin(), cend()); }
|
||||
|
||||
void boundsCheck(unsigned pos) const;
|
||||
void checkWritable();
|
||||
void adjustBounds(unsigned pos);
|
||||
@@ -47,16 +52,17 @@ public:
|
||||
{ resize(0); return *this; }
|
||||
|
||||
Bytes slice(unsigned start, unsigned len) const;
|
||||
Bytes slice(unsigned start) const;
|
||||
Bytes swab() const;
|
||||
Bytes compress() const;
|
||||
Bytes decompress() const;
|
||||
Bytes crunch() const;
|
||||
Bytes uncrunch() const;
|
||||
std::vector<bool> toBits() const;
|
||||
|
||||
ByteReader reader() const;
|
||||
ByteWriter writer();
|
||||
|
||||
void writeToFile(const std::string& filename) const;
|
||||
void writeTo(std::ostream& stream) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::vector<uint8_t>> _data;
|
||||
@@ -270,6 +276,16 @@ public:
|
||||
|
||||
ByteWriter& operator += (std::istream& stream);
|
||||
|
||||
ByteWriter& append(const char* data)
|
||||
{
|
||||
return *this += Bytes((const uint8_t*)data, strlen(data));
|
||||
}
|
||||
|
||||
ByteWriter& append(const std::string& data)
|
||||
{
|
||||
return *this += Bytes(data);
|
||||
}
|
||||
|
||||
ByteWriter& append(const Bytes data)
|
||||
{
|
||||
return *this += data;
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "crunch.h"
|
||||
|
||||
void crunch(crunch_state_t* state)
|
||||
{
|
||||
while (state->inputlen && state->outputlen)
|
||||
{
|
||||
uint8_t data = *state->inputptr++;
|
||||
state->inputlen--;
|
||||
|
||||
if (data & 0x80)
|
||||
{
|
||||
state->fifo = (state->fifo << 2) | 2 | (data & 1);
|
||||
state->fifolen += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->fifo = (state->fifo << 8) | data;
|
||||
state->fifolen += 8;
|
||||
}
|
||||
|
||||
if (state->fifolen >= 8)
|
||||
{
|
||||
data = state->fifo >> (state->fifolen - 8);
|
||||
*state->outputptr++ = data;
|
||||
state->outputlen--;
|
||||
state->fifolen -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void donecrunch(crunch_state_t* state)
|
||||
{
|
||||
if (state->fifolen > 0)
|
||||
{
|
||||
uint8_t b = 0;
|
||||
state->inputptr = &b;
|
||||
state->inputlen = 1;
|
||||
crunch(state);
|
||||
}
|
||||
}
|
||||
|
||||
void uncrunch(crunch_state_t* state)
|
||||
{
|
||||
while (state->inputlen && state->outputlen)
|
||||
{
|
||||
if (state->fifolen < 8)
|
||||
{
|
||||
if (state->inputlen)
|
||||
{
|
||||
state->fifo = (state->fifo << 8) | *state->inputptr++;
|
||||
state->inputlen--;
|
||||
state->fifolen += 8;
|
||||
}
|
||||
else
|
||||
state->fifo <<= 8;
|
||||
}
|
||||
|
||||
uint8_t data = state->fifo >> (state->fifolen - 8);
|
||||
if (data & 0x80)
|
||||
{
|
||||
data = ((data >> 6) & 0x01) | 0x80;
|
||||
state->fifolen -= 2;
|
||||
}
|
||||
else
|
||||
state->fifolen -= 8;
|
||||
|
||||
if (data)
|
||||
{
|
||||
*state->outputptr++ = data;
|
||||
state->outputlen--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void doneuncrunch(crunch_state_t* state)
|
||||
{
|
||||
if (state->fifolen > 0)
|
||||
{
|
||||
uint8_t b = 0;
|
||||
state->inputptr = &b;
|
||||
state->inputlen = 1;
|
||||
uncrunch(state);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#ifndef CRUNCH_H
|
||||
#define CRUNCH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* To save bandwidth, we compress the byte stream from the sampler when
|
||||
* sending it over USB. The encoding used is:
|
||||
*
|
||||
* 0nnn.nnnn: value 0x00..0x7f
|
||||
* 1n : value 0x80|n
|
||||
*
|
||||
* The end of the buffer is terminated with zeroes, which are ignored
|
||||
* (not written to the output).
|
||||
*
|
||||
* This saves ~40%, which gets us in under the bandwidth cap.
|
||||
*/
|
||||
|
||||
typedef struct crunch_state_t
|
||||
{
|
||||
const uint8_t* inputptr;
|
||||
uint32_t inputlen;
|
||||
uint8_t* outputptr;
|
||||
uint32_t outputlen;
|
||||
uint16_t fifo;
|
||||
uint8_t fifolen;
|
||||
}
|
||||
crunch_state_t;
|
||||
|
||||
/* Crunches as much as possible and then stops. */
|
||||
extern void crunch(crunch_state_t* state);
|
||||
extern void donecrunch(crunch_state_t* state);
|
||||
|
||||
/* Uncrunches as much as possible and then stops. */
|
||||
extern void uncrunch(crunch_state_t* state);
|
||||
extern void doneuncrunch(crunch_state_t* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -15,34 +15,31 @@ std::vector<std::string> DataSpec::split(
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
size_t len = 0;
|
||||
do
|
||||
{
|
||||
end = s.find(delimiter,start);
|
||||
len = end - start;
|
||||
std::string token = s.substr(start, len);
|
||||
ret.emplace_back( token );
|
||||
start += len + delimiter.length();
|
||||
}
|
||||
while (end != std::string::npos);
|
||||
if (!s.empty())
|
||||
{
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
size_t len = 0;
|
||||
do
|
||||
{
|
||||
end = s.find(delimiter,start);
|
||||
len = end - start;
|
||||
std::string token = s.substr(start, len);
|
||||
ret.emplace_back( token );
|
||||
start += len + delimiter.length();
|
||||
}
|
||||
while (end != std::string::npos);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DataSpec::Modifier DataSpec::parseMod(const std::string& spec)
|
||||
std::set<unsigned> DataSpec::parseRange(const std::string& data)
|
||||
{
|
||||
static const std::regex MOD_REGEX("([a-z]*)=([-x+0-9,]*)");
|
||||
static const std::regex DATA_REGEX("([0-9]+)(?:(?:-([0-9]+))|(?:\\+([0-9]+)))?(?:x([0-9]+))?");
|
||||
static const std::regex DATA_REGEX("([0-9]+)(?:(?:-([0-9]+))|(?:\\+([0-9]+)))?(?:x([0-9]+))?");
|
||||
|
||||
std::smatch match;
|
||||
if (!std::regex_match(spec, match, MOD_REGEX))
|
||||
Error() << "invalid data modifier syntax '" << spec << "'";
|
||||
|
||||
Modifier m;
|
||||
m.name = match[1];
|
||||
m.source = spec;
|
||||
for (auto& data : split(match[2], ","))
|
||||
std::set<unsigned> result;
|
||||
for (auto& data : split(data, ","))
|
||||
{
|
||||
int start = 0;
|
||||
int count = 1;
|
||||
@@ -64,9 +61,24 @@ DataSpec::Modifier DataSpec::parseMod(const std::string& spec)
|
||||
Error() << "mod '" << data << "' specifies an illegal quantity";
|
||||
|
||||
for (int i = start; i < (start+count); i += step)
|
||||
m.data.insert(i);
|
||||
result.insert(i);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DataSpec::Modifier DataSpec::parseMod(const std::string& spec)
|
||||
{
|
||||
static const std::regex MOD_REGEX("([a-z]*)=([-x+0-9,]*)");
|
||||
|
||||
std::smatch match;
|
||||
if (!std::regex_match(spec, match, MOD_REGEX))
|
||||
Error() << "invalid data modifier syntax '" << spec << "'";
|
||||
|
||||
Modifier m;
|
||||
m.name = match[1];
|
||||
m.source = spec;
|
||||
m.data = parseRange(match[2]);
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -74,7 +86,7 @@ void DataSpec::set(const std::string& spec)
|
||||
{
|
||||
std::vector<std::string> words = split(spec, ":");
|
||||
if (words.size() == 0)
|
||||
Error() << "empty data specification (you have to specify *something*)";
|
||||
return;
|
||||
|
||||
filename = words[0];
|
||||
if (words.size() > 1)
|
||||
|
||||
@@ -34,6 +34,8 @@ public:
|
||||
public:
|
||||
static std::vector<std::string> split(
|
||||
const std::string& s, const std::string& delimiter);
|
||||
static std::set<unsigned> parseRange(const std::string& spec);
|
||||
|
||||
static Modifier parseMod(const std::string& spec);
|
||||
|
||||
public:
|
||||
@@ -117,4 +119,34 @@ private:
|
||||
DataSpec _value;
|
||||
};
|
||||
|
||||
class RangeFlag : public Flag
|
||||
{
|
||||
public:
|
||||
RangeFlag(const std::vector<std::string>& names, const std::string helptext,
|
||||
const std::string& defaultValue):
|
||||
Flag(names, helptext),
|
||||
_stringValue(defaultValue),
|
||||
_value(DataSpec::parseRange(defaultValue))
|
||||
{}
|
||||
|
||||
const std::set<unsigned>& get() const
|
||||
{ checkInitialised(); return _value; }
|
||||
|
||||
operator const std::set<unsigned>& () const
|
||||
{ return get(); }
|
||||
|
||||
bool hasArgument() const { return true; }
|
||||
const std::string defaultValueAsString() const { return _stringValue; }
|
||||
|
||||
void set(const std::string& value)
|
||||
{
|
||||
_stringValue = value;
|
||||
_value = DataSpec::parseRange(value);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _stringValue;
|
||||
std::set<unsigned> _value;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -25,7 +25,7 @@ void AbstractDecoder::decodeToSectors(Track& track)
|
||||
beginTrack();
|
||||
for (;;)
|
||||
{
|
||||
Fluxmap::Position recordStart = sector.position = fmr.tell();
|
||||
Fluxmap::Position recordStart = fmr.tell();
|
||||
sector.clock = 0;
|
||||
sector.status = Sector::MISSING;
|
||||
sector.data.clear();
|
||||
@@ -35,13 +35,13 @@ void AbstractDecoder::decodeToSectors(Track& track)
|
||||
return;
|
||||
if ((r == UNKNOWN_RECORD) || (r == DATA_RECORD))
|
||||
{
|
||||
fmr.readNextMatchingOpcode(F_OP_PULSE);
|
||||
fmr.findEvent(F_BIT_PULSE);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Read the sector record. */
|
||||
|
||||
recordStart = fmr.tell();
|
||||
sector.position = recordStart = fmr.tell();
|
||||
decodeSectorRecord();
|
||||
Fluxmap::Position recordEnd = fmr.tell();
|
||||
pushRecord(recordStart, recordEnd);
|
||||
@@ -51,14 +51,19 @@ void AbstractDecoder::decodeToSectors(Track& track)
|
||||
|
||||
sector.headerStartTime = recordStart.ns();
|
||||
sector.headerEndTime = recordEnd.ns();
|
||||
r = advanceToNextRecord();
|
||||
for (;;)
|
||||
{
|
||||
r = advanceToNextRecord();
|
||||
if (r != UNKNOWN_RECORD)
|
||||
break;
|
||||
if (fmr.findEvent(F_BIT_PULSE) == 0)
|
||||
break;
|
||||
}
|
||||
recordStart = fmr.tell();
|
||||
if (r == DATA_RECORD)
|
||||
{
|
||||
recordStart = fmr.tell();
|
||||
decodeDataRecord();
|
||||
recordEnd = fmr.tell();
|
||||
pushRecord(recordStart, recordEnd);
|
||||
}
|
||||
recordEnd = fmr.tell();
|
||||
pushRecord(recordStart, recordEnd);
|
||||
}
|
||||
sector.dataStartTime = recordStart.ns();
|
||||
sector.dataEndTime = recordEnd.ns();
|
||||
@@ -83,3 +88,10 @@ void AbstractDecoder::pushRecord(const Fluxmap::Position& start, const Fluxmap::
|
||||
_track->rawrecords.push_back(record);
|
||||
_fmr->seek(here);
|
||||
}
|
||||
|
||||
std::set<unsigned> AbstractDecoder::requiredSectors(Track& track) const
|
||||
{
|
||||
static std::set<unsigned> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ extern void setDecoderManualClockRate(double clockrate_us);
|
||||
|
||||
extern Bytes decodeFmMfm(std::vector<bool>::const_iterator start,
|
||||
std::vector<bool>::const_iterator end);
|
||||
extern void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input, bool& lastBit);
|
||||
extern void encodeFm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input);
|
||||
|
||||
static inline Bytes decodeFmMfm(const std::vector<bool> bits)
|
||||
{ return decodeFmMfm(bits.begin(), bits.end()); }
|
||||
@@ -50,6 +52,11 @@ public:
|
||||
void seek(const Fluxmap::Position& pos)
|
||||
{ return _fmr->seek(pos); }
|
||||
|
||||
/* Returns a set of sectors required to exist on this track. If the reader
|
||||
* sees any missing, it will consider this to be an error and will retry
|
||||
* the read. */
|
||||
virtual std::set<unsigned> requiredSectors(Track& track) const;
|
||||
|
||||
protected:
|
||||
virtual void beginTrack() {};
|
||||
virtual RecordType advanceToNextRecord() = 0;
|
||||
|
||||
@@ -18,23 +18,27 @@ DoubleFlag pulseDebounceThreshold(
|
||||
static DoubleFlag clockDecodeThreshold(
|
||||
{ "--bit-error-threshold" },
|
||||
"Amount of error to tolerate in pulse timing, in fractions of a clock.",
|
||||
0.20);
|
||||
0.40);
|
||||
|
||||
static DoubleFlag clockIntervalBias(
|
||||
{ "--clock-interval-bias" },
|
||||
"Adjust intervals between pulses by this many clocks before decoding.",
|
||||
-0.02);
|
||||
|
||||
int FluxmapReader::readOpcode(unsigned& ticks)
|
||||
static DoubleFlag minimumClockUs(
|
||||
{ "--minimum-clock-us" },
|
||||
"Refuse to detect clocks shorter than this, to avoid false positives.",
|
||||
0.75);
|
||||
|
||||
uint8_t FluxmapReader::getNextEvent(unsigned& ticks)
|
||||
{
|
||||
ticks = 0;
|
||||
|
||||
while (!eof())
|
||||
{
|
||||
uint8_t b = _bytes[_pos.bytes++];
|
||||
if (b < 0x80)
|
||||
ticks += b;
|
||||
else
|
||||
ticks += b & 0x3f;
|
||||
if (b & (F_BIT_PULSE|F_BIT_INDEX))
|
||||
{
|
||||
_pos.ticks += ticks;
|
||||
return b;
|
||||
@@ -42,21 +46,21 @@ int FluxmapReader::readOpcode(unsigned& ticks)
|
||||
}
|
||||
|
||||
_pos.ticks += ticks;
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned FluxmapReader::readNextMatchingOpcode(uint8_t opcode)
|
||||
unsigned FluxmapReader::findEvent(uint8_t target)
|
||||
{
|
||||
unsigned ticks = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
unsigned thisTicks;
|
||||
int op = readOpcode(thisTicks);
|
||||
uint8_t bits = getNextEvent(thisTicks);
|
||||
ticks += thisTicks;
|
||||
if (op == -1)
|
||||
if (eof())
|
||||
return 0;
|
||||
if (op == opcode)
|
||||
if (bits & target)
|
||||
return ticks;
|
||||
}
|
||||
}
|
||||
@@ -68,7 +72,7 @@ unsigned FluxmapReader::readInterval(nanoseconds_t clock)
|
||||
|
||||
while (ticks < thresholdTicks)
|
||||
{
|
||||
unsigned thisTicks = readNextMatchingOpcode(F_OP_PULSE);
|
||||
unsigned thisTicks = findEvent(F_BIT_PULSE);
|
||||
if (!thisTicks)
|
||||
break;
|
||||
ticks += thisTicks;
|
||||
@@ -191,7 +195,7 @@ void FluxmapReader::seek(nanoseconds_t ns)
|
||||
while (!eof() && (_pos.ticks < ticks))
|
||||
{
|
||||
unsigned t;
|
||||
readOpcode(t);
|
||||
getNextEvent(t);
|
||||
}
|
||||
_pos.zeroes = 0;
|
||||
}
|
||||
@@ -222,7 +226,9 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu
|
||||
seek(positions[intervalCount-match.intervals]);
|
||||
_pos.zeroes = match.zeroes;
|
||||
matching = match.matcher;
|
||||
return match.clock * NS_PER_TICK;
|
||||
nanoseconds_t detectedClock = match.clock * NS_PER_TICK;
|
||||
if (detectedClock > (minimumClockUs*1000))
|
||||
return match.clock * NS_PER_TICK;
|
||||
}
|
||||
|
||||
for (unsigned i=0; i<intervalCount; i++)
|
||||
@@ -230,7 +236,7 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu
|
||||
positions[i] = positions[i+1];
|
||||
candidates[i] = candidates[i+1];
|
||||
}
|
||||
candidates[intervalCount] = readNextMatchingOpcode(F_OP_PULSE);
|
||||
candidates[intervalCount] = findEvent(F_BIT_PULSE);
|
||||
positions[intervalCount] = tell();
|
||||
|
||||
}
|
||||
@@ -241,7 +247,7 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu
|
||||
|
||||
void FluxmapReader::seekToIndexMark()
|
||||
{
|
||||
readNextMatchingOpcode(F_OP_INDEX);
|
||||
findEvent(F_BIT_INDEX);
|
||||
_pos.zeroes = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,8 +96,8 @@ public:
|
||||
_pos = pos;
|
||||
}
|
||||
|
||||
int readOpcode(unsigned& ticks);
|
||||
unsigned readNextMatchingOpcode(uint8_t opcode);
|
||||
uint8_t getNextEvent(unsigned& ticks);
|
||||
unsigned findEvent(uint8_t bits);
|
||||
unsigned readInterval(nanoseconds_t clock); /* with debounce support */
|
||||
|
||||
/* Important! You can only reliably seek to 1 bits. */
|
||||
|
||||
@@ -51,3 +51,48 @@ Bytes decodeFmMfm(
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void encodeFm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input)
|
||||
{
|
||||
if (bits.size() == 0)
|
||||
return;
|
||||
unsigned len = bits.size()-1;
|
||||
|
||||
for (uint8_t b : input)
|
||||
{
|
||||
for (int i=0; i<8; i++)
|
||||
{
|
||||
bool bit = b & 0x80;
|
||||
b <<= 1;
|
||||
|
||||
if (cursor >= len)
|
||||
return;
|
||||
|
||||
bits[cursor++] = true;
|
||||
bits[cursor++] = bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input, bool& lastBit)
|
||||
{
|
||||
if (bits.size() == 0)
|
||||
return;
|
||||
unsigned len = bits.size()-1;
|
||||
|
||||
for (uint8_t b : input)
|
||||
{
|
||||
for (int i=0; i<8; i++)
|
||||
{
|
||||
bool bit = b & 0x80;
|
||||
b <<= 1;
|
||||
|
||||
if (cursor >= len)
|
||||
return;
|
||||
|
||||
bits[cursor++] = !lastBit && !bit;
|
||||
bits[cursor++] = bit;
|
||||
lastBit = bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
static FlagGroup* currentFlagGroup;
|
||||
static std::vector<Flag*> all_flags;
|
||||
@@ -157,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);
|
||||
}
|
||||
|
||||
@@ -170,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;
|
||||
|
||||
13
lib/flags.h
13
lib/flags.h
@@ -135,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:
|
||||
@@ -147,7 +158,7 @@ public:
|
||||
void set(const std::string& value) { _value = std::stod(value); }
|
||||
};
|
||||
|
||||
class BoolFlag : public ValueFlag<double>
|
||||
class BoolFlag : public ValueFlag<bool>
|
||||
{
|
||||
public:
|
||||
BoolFlag(const std::vector<std::string>& names, const std::string helptext,
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
Fluxmap& Fluxmap::appendBytes(const Bytes& bytes)
|
||||
{
|
||||
if (bytes.size() == 0)
|
||||
return *this;
|
||||
|
||||
return appendBytes(&bytes[0], bytes.size());
|
||||
}
|
||||
|
||||
@@ -15,8 +18,7 @@ Fluxmap& Fluxmap::appendBytes(const uint8_t* ptr, size_t len)
|
||||
while (len--)
|
||||
{
|
||||
uint8_t byte = *ptr++;
|
||||
if (byte < 0x80)
|
||||
_ticks += byte;
|
||||
_ticks += byte & 0x3f;
|
||||
bw.write_8(byte);
|
||||
}
|
||||
|
||||
@@ -24,12 +26,19 @@ Fluxmap& Fluxmap::appendBytes(const uint8_t* ptr, size_t len)
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint8_t& Fluxmap::findLastByte()
|
||||
{
|
||||
if (_bytes.empty())
|
||||
appendByte(0x00);
|
||||
return *(_bytes.end() - 1);
|
||||
}
|
||||
|
||||
Fluxmap& Fluxmap::appendInterval(uint32_t ticks)
|
||||
{
|
||||
while (ticks >= 0x7f)
|
||||
while (ticks >= 0x3f)
|
||||
{
|
||||
appendByte(0x7f);
|
||||
ticks -= 0x7f;
|
||||
appendByte(0x3f);
|
||||
ticks -= 0x3f;
|
||||
}
|
||||
appendByte((uint8_t)ticks);
|
||||
return *this;
|
||||
@@ -37,13 +46,13 @@ Fluxmap& Fluxmap::appendInterval(uint32_t ticks)
|
||||
|
||||
Fluxmap& Fluxmap::appendPulse()
|
||||
{
|
||||
appendByte(0x80);
|
||||
findLastByte() |= 0x80;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Fluxmap& Fluxmap::appendIndex()
|
||||
{
|
||||
appendByte(0x81);
|
||||
findLastByte() |= 0x40;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -54,25 +63,26 @@ void Fluxmap::precompensate(int threshold_ticks, int amount_ticks)
|
||||
for (unsigned i=0; i<_bytes.size(); i++)
|
||||
{
|
||||
uint8_t& prev = (i == 0) ? junk : _bytes[i-1];
|
||||
uint8_t curr = _bytes[i];
|
||||
uint8_t prevticks = prev & 0x3f;
|
||||
uint8_t currticks = _bytes[i] & 0x3f;
|
||||
|
||||
if (curr < (3*threshold_ticks))
|
||||
if (currticks < (3*threshold_ticks))
|
||||
{
|
||||
if ((prev <= threshold_ticks) && (curr > threshold_ticks))
|
||||
if ((prevticks <= threshold_ticks) && (currticks > threshold_ticks))
|
||||
{
|
||||
/* 01001; move the previous bit backwards. */
|
||||
if (prev >= (1+amount_ticks))
|
||||
if (prevticks >= (1+amount_ticks))
|
||||
prev -= amount_ticks;
|
||||
if (curr <= (0x7f-amount_ticks))
|
||||
curr += amount_ticks;
|
||||
if (currticks <= (0x7f-amount_ticks))
|
||||
currticks += amount_ticks;
|
||||
}
|
||||
else if ((prev > threshold_ticks) && (curr <= threshold_ticks))
|
||||
else if ((prevticks > threshold_ticks) && (currticks <= threshold_ticks))
|
||||
{
|
||||
/* 00101; move the current bit forwards. */
|
||||
if (prev <= (0x7f-amount_ticks))
|
||||
if (prevticks <= (0x7f-amount_ticks))
|
||||
prev += amount_ticks;
|
||||
if (curr >= (1+amount_ticks))
|
||||
curr -= amount_ticks;
|
||||
if (currticks >= (1+amount_ticks))
|
||||
currticks -= amount_ticks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,9 @@ public:
|
||||
|
||||
void precompensate(int threshold_ticks, int amount_ticks);
|
||||
|
||||
private:
|
||||
uint8_t& findLastByte();
|
||||
|
||||
private:
|
||||
nanoseconds_t _duration = 0;
|
||||
int _ticks = 0;
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
#ifndef FLUXSINK_H
|
||||
#define FLUXSINK_H
|
||||
|
||||
#include "flags.h"
|
||||
|
||||
extern FlagGroup hardwareFluxSinkFlags;
|
||||
extern FlagGroup sqliteFluxSinkFlags;
|
||||
|
||||
class Fluxmap;
|
||||
class FluxSpec;
|
||||
|
||||
@@ -9,11 +14,9 @@ class FluxSink
|
||||
public:
|
||||
virtual ~FluxSink() {}
|
||||
|
||||
private:
|
||||
static std::unique_ptr<FluxSink> createSqliteFluxSink(const std::string& filename);
|
||||
static std::unique_ptr<FluxSink> createHardwareFluxSink(unsigned drive);
|
||||
|
||||
public:
|
||||
static std::unique_ptr<FluxSink> create(const FluxSpec& spec);
|
||||
|
||||
public:
|
||||
@@ -21,6 +24,7 @@ public:
|
||||
};
|
||||
|
||||
extern void setHardwareFluxSinkDensity(bool high_density);
|
||||
extern void setHardwareFluxSinkHardSectorCount(int sectorCount);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,22 +1,52 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "fluxmap.h"
|
||||
#include "usb.h"
|
||||
#include "usb/usb.h"
|
||||
#include "fluxsink/fluxsink.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
FlagGroup hardwareFluxSinkFlags = {
|
||||
&usbFlags,
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
static IntFlag hardSectorCount(
|
||||
{ "--write-hard-sector-count" },
|
||||
"number of hard sectors on the disk (0=soft sectors)",
|
||||
0);
|
||||
|
||||
void setHardwareFluxSinkDensity(bool high_density)
|
||||
{
|
||||
::high_density = high_density;
|
||||
}
|
||||
|
||||
void setHardwareFluxSinkHardSectorCount(int sectorCount)
|
||||
{
|
||||
::hardSectorCount.setDefaultValue(sectorCount);
|
||||
}
|
||||
|
||||
class HardwareFluxSink : public FluxSink
|
||||
{
|
||||
public:
|
||||
HardwareFluxSink(unsigned drive):
|
||||
_drive(drive)
|
||||
{
|
||||
if (hardSectorCount != 0)
|
||||
{
|
||||
usbSetDrive(_drive, high_density, indexMode);
|
||||
std::cerr << "Measuring rotational speed... " << std::flush;
|
||||
nanoseconds_t oneRevolution = usbGetRotationalPeriod(hardSectorCount);
|
||||
_hardSectorThreshold = oneRevolution * 3 / (4 * hardSectorCount);
|
||||
std::cerr << fmt::format("{}ms\n", oneRevolution / 1e6);
|
||||
}
|
||||
else
|
||||
_hardSectorThreshold = 0;
|
||||
}
|
||||
|
||||
~HardwareFluxSink()
|
||||
@@ -26,15 +56,15 @@ public:
|
||||
public:
|
||||
void writeFlux(int track, int side, Fluxmap& fluxmap)
|
||||
{
|
||||
usbSetDrive(_drive, high_density);
|
||||
usbSetDrive(_drive, high_density, indexMode);
|
||||
usbSeek(track);
|
||||
|
||||
Bytes crunched = fluxmap.rawBytes().crunch();
|
||||
return usbWrite(side, crunched);
|
||||
return usbWrite(side, fluxmap.rawBytes(), _hardSectorThreshold);
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned _drive;
|
||||
nanoseconds_t _hardSectorThreshold;
|
||||
};
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::createHardwareFluxSink(unsigned drive)
|
||||
|
||||
@@ -2,14 +2,37 @@
|
||||
#include "fluxmap.h"
|
||||
#include "sql.h"
|
||||
#include "fluxsink/fluxsink.h"
|
||||
#include "flags.h"
|
||||
#include "fmt/format.h"
|
||||
#include <unistd.h>
|
||||
|
||||
FlagGroup sqliteFluxSinkFlags;
|
||||
|
||||
static SettableFlag mergeFlag(
|
||||
{ "--merge" },
|
||||
"merge new data into existing flux file");
|
||||
|
||||
static SettableFlag overwriteFlag(
|
||||
{ "--overwrite" },
|
||||
"overwrite existing flux file");
|
||||
|
||||
class SqliteFluxSink : public FluxSink
|
||||
{
|
||||
public:
|
||||
SqliteFluxSink(const std::string& filename)
|
||||
{
|
||||
if (mergeFlag && overwriteFlag)
|
||||
Error() << "you can't specify --merge and --overwrite";
|
||||
|
||||
if (!mergeFlag)
|
||||
{
|
||||
if (!overwriteFlag && (access(filename.c_str(), F_OK) == 0))
|
||||
Error() << "cowardly refusing to overwrite flux file without --merge or --overwrite specified";
|
||||
if ((access(filename.c_str(), F_OK) == 0) && (remove(filename.c_str()) != 0))
|
||||
Error() << fmt::format("failed to overwrite flux file");
|
||||
}
|
||||
_outdb = sqlOpen(filename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
|
||||
|
||||
int oldVersion = sqlReadIntProperty(_outdb, "version");
|
||||
if ((oldVersion != 0) && (oldVersion != FLUX_VERSION_CURRENT))
|
||||
Error() << fmt::format("that flux file is version {}, but this client is for version {}",
|
||||
|
||||
@@ -27,8 +27,10 @@ public:
|
||||
virtual bool retryable() { return false; }
|
||||
};
|
||||
|
||||
extern void setHardwareFluxSourceRevolutions(int revolutions);
|
||||
extern void setHardwareFluxSourceRevolutions(double revolutions);
|
||||
extern void setHardwareFluxSourceDensity(bool high_density);
|
||||
extern void setHardwareFluxSourceSynced(bool synced);
|
||||
extern void setHardwareFluxSourceHardSectorCount(int sectorCount);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,15 +1,33 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "fluxmap.h"
|
||||
#include "usb.h"
|
||||
#include "usb/usb.h"
|
||||
#include "fluxsource/fluxsource.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
FlagGroup hardwareFluxSourceFlags;
|
||||
FlagGroup hardwareFluxSourceFlags = {
|
||||
&usbFlags
|
||||
};
|
||||
|
||||
static IntFlag revolutions(
|
||||
static DoubleFlag revolutions(
|
||||
{ "--revolutions" },
|
||||
"read this many revolutions of the disk",
|
||||
1);
|
||||
1.25);
|
||||
|
||||
static BoolFlag synced(
|
||||
{ "--sync-with-index" },
|
||||
"whether to wait for an index pulse before started to read",
|
||||
false);
|
||||
|
||||
static IntFlag indexMode(
|
||||
{ "--index-mode" },
|
||||
"index pulse source (0=drive, 1=300 RPM fake source, 2=360 RPM fake source",
|
||||
0);
|
||||
|
||||
static IntFlag hardSectorCount(
|
||||
{ "--hard-sector-count" },
|
||||
"number of hard sectors on the disk (0=soft sectors)",
|
||||
0);
|
||||
|
||||
static bool high_density = false;
|
||||
|
||||
@@ -24,6 +42,14 @@ public:
|
||||
HardwareFluxSource(unsigned drive):
|
||||
_drive(drive)
|
||||
{
|
||||
usbSetDrive(_drive, high_density, indexMode);
|
||||
std::cerr << "Measuring rotational speed... " << std::flush;
|
||||
_oneRevolution = usbGetRotationalPeriod(hardSectorCount);
|
||||
if (hardSectorCount != 0)
|
||||
_hardSectorThreshold = _oneRevolution * 3 / (4 * hardSectorCount);
|
||||
else
|
||||
_hardSectorThreshold = 0;
|
||||
std::cerr << fmt::format("{}ms\n", _oneRevolution / 1e6);
|
||||
}
|
||||
|
||||
~HardwareFluxSource()
|
||||
@@ -33,11 +59,12 @@ public:
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> readFlux(int track, int side)
|
||||
{
|
||||
usbSetDrive(_drive, high_density);
|
||||
usbSetDrive(_drive, high_density, indexMode);
|
||||
usbSeek(track);
|
||||
Bytes crunched = usbRead(side, revolutions);
|
||||
Bytes data = usbRead(
|
||||
side, synced, revolutions * _oneRevolution, _hardSectorThreshold);
|
||||
auto fluxmap = std::make_unique<Fluxmap>();
|
||||
fluxmap->appendBytes(crunched.uncrunch());
|
||||
fluxmap->appendBytes(data);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
@@ -54,13 +81,25 @@ public:
|
||||
private:
|
||||
unsigned _drive;
|
||||
unsigned _revolutions;
|
||||
nanoseconds_t _oneRevolution;
|
||||
nanoseconds_t _hardSectorThreshold;
|
||||
};
|
||||
|
||||
void setHardwareFluxSourceRevolutions(int revolutions)
|
||||
void setHardwareFluxSourceRevolutions(double revolutions)
|
||||
{
|
||||
::revolutions.setDefaultValue(revolutions);
|
||||
}
|
||||
|
||||
void setHardwareFluxSourceSynced(bool synced)
|
||||
{
|
||||
::synced.setDefaultValue(synced);
|
||||
}
|
||||
|
||||
void setHardwareFluxSourceHardSectorCount(int sectorCount)
|
||||
{
|
||||
::hardSectorCount.setDefaultValue(sectorCount);
|
||||
}
|
||||
|
||||
std::unique_ptr<FluxSource> FluxSource::createHardwareFluxSource(unsigned drive)
|
||||
{
|
||||
return std::unique_ptr<FluxSource>(new HardwareFluxSource(drive));
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <set>
|
||||
#include <cassert>
|
||||
|
||||
typedef int nanoseconds_t;
|
||||
typedef double nanoseconds_t;
|
||||
class Bytes;
|
||||
|
||||
extern double getCurrentTime();
|
||||
|
||||
25
lib/image.cc
25
lib/image.cc
@@ -1,25 +0,0 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagereader/imagereader.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
SectorSet readSectorsFromFile(const ImageSpec& spec)
|
||||
{
|
||||
return ImageReader::create(spec)->readImage();
|
||||
}
|
||||
|
||||
void writeSectorsToFile(const SectorSet& sectors, const ImageSpec& spec)
|
||||
{
|
||||
std::unique_ptr<ImageWriter> writer(ImageWriter::create(sectors, spec));
|
||||
writer->adjustGeometry();
|
||||
writer->printMap();
|
||||
writer->writeImage();
|
||||
}
|
||||
14
lib/image.h
14
lib/image.h
@@ -1,14 +0,0 @@
|
||||
#ifndef IMAGE_H
|
||||
#define IMAGE_H
|
||||
|
||||
class SectorSet;
|
||||
class ImageSpec;
|
||||
|
||||
extern SectorSet readSectorsFromFile(
|
||||
const ImageSpec& filename);
|
||||
|
||||
extern void writeSectorsToFile(
|
||||
const SectorSet& sectors,
|
||||
const ImageSpec& filename);
|
||||
|
||||
#endif
|
||||
128
lib/imagereader/diskcopyimagereader.cc
Normal file
128
lib/imagereader/diskcopyimagereader.cc
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagereader/imagereader.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
class DiskCopyImageReader : public ImageReader
|
||||
{
|
||||
public:
|
||||
DiskCopyImageReader(const ImageSpec& spec):
|
||||
ImageReader(spec)
|
||||
{}
|
||||
|
||||
SectorSet readImage()
|
||||
{
|
||||
std::ifstream inputFile(spec.filename, std::ios::in | std::ios::binary);
|
||||
if (!inputFile.is_open())
|
||||
Error() << "cannot open input file";
|
||||
|
||||
Bytes data;
|
||||
data.writer() += inputFile;
|
||||
ByteReader br(data);
|
||||
|
||||
br.seek(1);
|
||||
std::string label = br.read(data[0]);
|
||||
|
||||
br.seek(0x40);
|
||||
uint32_t dataSize = br.read_be32();
|
||||
|
||||
br.seek(0x50);
|
||||
uint8_t encoding = br.read_8();
|
||||
uint8_t formatByte = br.read_8();
|
||||
|
||||
unsigned numCylinders = 80;
|
||||
unsigned numHeads = 2;
|
||||
unsigned numSectors = 0;
|
||||
bool mfm = false;
|
||||
|
||||
switch (encoding)
|
||||
{
|
||||
case 0: /* GCR CLV 400kB */
|
||||
numHeads = 1;
|
||||
break;
|
||||
|
||||
case 1: /* GCR CLV 800kB */
|
||||
break;
|
||||
|
||||
case 2: /* MFM CAV 720kB */
|
||||
numSectors = 9;
|
||||
mfm = true;
|
||||
break;
|
||||
|
||||
case 3: /* MFM CAV 1440kB */
|
||||
numSectors = 18;
|
||||
mfm = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
Error() << fmt::format("don't understant DiskCopy disks of type {}", encoding);
|
||||
}
|
||||
|
||||
std::cout << "reading DiskCopy 4.2 image\n"
|
||||
<< fmt::format("{} cylinders, {} heads; {}; {}\n",
|
||||
numCylinders, numHeads,
|
||||
mfm ? "MFM" : "GCR",
|
||||
label);
|
||||
|
||||
auto sectorsPerTrack = [&](int track) -> int
|
||||
{
|
||||
if (mfm)
|
||||
return numSectors;
|
||||
|
||||
if (track < 16)
|
||||
return 12;
|
||||
if (track < 32)
|
||||
return 11;
|
||||
if (track < 48)
|
||||
return 10;
|
||||
if (track < 64)
|
||||
return 9;
|
||||
return 8;
|
||||
};
|
||||
|
||||
uint32_t dataPtr = 0x54;
|
||||
uint32_t tagPtr = dataPtr + dataSize;
|
||||
|
||||
SectorSet sectors;
|
||||
for (int track = 0; track < numCylinders; track++)
|
||||
{
|
||||
int numSectors = sectorsPerTrack(track);
|
||||
for (int head = 0; head < numHeads; head++)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < numSectors; sectorId++)
|
||||
{
|
||||
br.seek(dataPtr);
|
||||
Bytes payload = br.read(512);
|
||||
dataPtr += 512;
|
||||
|
||||
br.seek(tagPtr);
|
||||
Bytes tag = br.read(12);
|
||||
tagPtr += 12;
|
||||
|
||||
std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId);
|
||||
sector.reset(new Sector);
|
||||
sector->status = Sector::OK;
|
||||
sector->logicalTrack = sector->physicalTrack = track;
|
||||
sector->logicalSide = sector->physicalSide = head;
|
||||
sector->logicalSector = sectorId;
|
||||
sector->data.writer().append(payload).append(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ImageReader> ImageReader::createDiskCopyImageReader(
|
||||
const ImageSpec& spec)
|
||||
{
|
||||
return std::unique_ptr<ImageReader>(new DiskCopyImageReader(spec));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagereader/imagereader.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <ctype.h>
|
||||
|
||||
std::map<std::string, ImageReader::Constructor> ImageReader::formats =
|
||||
{
|
||||
{".adf", ImageReader::createImgImageReader},
|
||||
{".d81", ImageReader::createImgImageReader},
|
||||
{".diskcopy", ImageReader::createDiskCopyImageReader},
|
||||
{".img", ImageReader::createImgImageReader},
|
||||
{".ima", ImageReader::createImgImageReader},
|
||||
{".jv1", ImageReader::createImgImageReader},
|
||||
{".jv3", ImageReader::createJv3ImageReader},
|
||||
};
|
||||
|
||||
static bool ends_with(const std::string& value, const std::string& ending)
|
||||
@@ -43,7 +48,7 @@ std::unique_ptr<ImageReader> ImageReader::create(const ImageSpec& spec)
|
||||
void ImageReader::verifyImageSpec(const ImageSpec& spec)
|
||||
{
|
||||
if (!findConstructor(spec))
|
||||
Error() << "unrecognised image filename extension";
|
||||
Error() << "unrecognised input image filename extension";
|
||||
}
|
||||
|
||||
ImageReader::ImageReader(const ImageSpec& spec):
|
||||
|
||||
@@ -23,7 +23,9 @@ private:
|
||||
|
||||
static std::map<std::string, Constructor> formats;
|
||||
|
||||
static std::unique_ptr<ImageReader> createDiskCopyImageReader(const ImageSpec& spec);
|
||||
static std::unique_ptr<ImageReader> createImgImageReader(const ImageSpec& spec);
|
||||
static std::unique_ptr<ImageReader> createJv3ImageReader(const ImageSpec& spec);
|
||||
|
||||
static Constructor findConstructor(const ImageSpec& spec);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
|
||||
141
lib/imagereader/jv3imagereader.cc
Normal file
141
lib/imagereader/jv3imagereader.cc
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagereader/imagereader.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
/* JV3 files are kinda weird. There's a fixed layout for up to 2901 sectors, which may appear
|
||||
* in any order, followed by the same again for more sectors. To find the second data block
|
||||
* you need to know the size of the first data block, which requires parsing it.
|
||||
*
|
||||
* https://www.tim-mann.org/trs80/dskspec.html
|
||||
*
|
||||
* typedef struct {
|
||||
* SectorHeader headers1[2901];
|
||||
* unsigned char writeprot;
|
||||
* unsigned char data1[];
|
||||
* SectorHeader headers2[2901];
|
||||
* unsigned char padding;
|
||||
* unsigned char data2[];
|
||||
* } JV3;
|
||||
*
|
||||
* typedef struct {
|
||||
* unsigned char track;
|
||||
* unsigned char sector;
|
||||
* unsigned char flags;
|
||||
* } SectorHeader;
|
||||
*/
|
||||
|
||||
struct SectorHeader
|
||||
{
|
||||
uint8_t track;
|
||||
uint8_t sector;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
#define JV3_DENSITY 0x80 /* 1=dden, 0=sden */
|
||||
#define JV3_DAM 0x60 /* data address mark code; see below */
|
||||
#define JV3_SIDE 0x10 /* 0=side 0, 1=side 1 */
|
||||
#define JV3_ERROR 0x08 /* 0=ok, 1=CRC error */
|
||||
#define JV3_NONIBM 0x04 /* 0=normal, 1=short */
|
||||
#define JV3_SIZE 0x03 /* in used sectors: 0=256,1=128,2=1024,3=512
|
||||
in free sectors: 0=512,1=1024,2=128,3=256 */
|
||||
|
||||
#define JV3_FREE 0xFF /* in track and sector fields of free sectors */
|
||||
#define JV3_FREEF 0xFC /* in flags field, or'd with size code */
|
||||
|
||||
static unsigned getSectorSize(uint8_t flags)
|
||||
{
|
||||
if ((flags & JV3_FREEF) == JV3_FREEF)
|
||||
{
|
||||
switch (flags & JV3_SIZE)
|
||||
{
|
||||
case 0: return 512;
|
||||
case 1: return 1024;
|
||||
case 2: return 128;
|
||||
case 3: return 256;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (flags & JV3_SIZE)
|
||||
{
|
||||
case 0: return 256;
|
||||
case 1: return 128;
|
||||
case 2: return 1024;
|
||||
case 3: return 512;
|
||||
}
|
||||
}
|
||||
Error() << "not reachable";
|
||||
}
|
||||
|
||||
class Jv3ImageReader : public ImageReader
|
||||
{
|
||||
public:
|
||||
Jv3ImageReader(const ImageSpec& spec):
|
||||
ImageReader(spec)
|
||||
{}
|
||||
|
||||
SectorSet readImage()
|
||||
{
|
||||
std::ifstream inputFile(spec.filename, std::ios::in | std::ios::binary);
|
||||
if (!inputFile.is_open())
|
||||
Error() << "cannot open input file";
|
||||
|
||||
inputFile.seekg( 0, std::ios::end);
|
||||
unsigned inputFileSize = inputFile.tellg();
|
||||
unsigned headerPtr = 0;
|
||||
SectorSet sectors;
|
||||
for (;;)
|
||||
{
|
||||
unsigned dataPtr = headerPtr + 2901*3 + 1;
|
||||
if (dataPtr >= inputFileSize)
|
||||
break;
|
||||
|
||||
for (unsigned i=0; i<2901; i++)
|
||||
{
|
||||
SectorHeader header = {0, 0, 0xff};
|
||||
inputFile.seekg(headerPtr);
|
||||
inputFile.read((char*) &header, 3);
|
||||
unsigned sectorSize = getSectorSize(header.flags);
|
||||
if ((header.flags & JV3_FREEF) != JV3_FREEF)
|
||||
{
|
||||
Bytes data(sectorSize);
|
||||
inputFile.seekg(dataPtr);
|
||||
inputFile.read((char*) data.begin(), sectorSize);
|
||||
|
||||
unsigned head = !!(header.flags & JV3_SIDE);
|
||||
std::unique_ptr<Sector>& sector = sectors.get(header.track, head, header.sector);
|
||||
sector.reset(new Sector);
|
||||
sector->status = Sector::OK;
|
||||
sector->logicalTrack = sector->physicalTrack = header.track;
|
||||
sector->logicalSide = sector->physicalSide = head;
|
||||
sector->logicalSector = header.sector;
|
||||
sector->data = data;
|
||||
}
|
||||
|
||||
headerPtr += 3;
|
||||
dataPtr += sectorSize;
|
||||
}
|
||||
|
||||
/* dataPtr is now pointing at the beginning of the next chunk. */
|
||||
|
||||
headerPtr = dataPtr;
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ImageReader> ImageReader::createJv3ImageReader(
|
||||
const ImageSpec& spec)
|
||||
{
|
||||
return std::unique_ptr<ImageReader>(new Jv3ImageReader(spec));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
|
||||
168
lib/imagewriter/diskcopyimagewriter.cc
Normal file
168
lib/imagewriter/diskcopyimagewriter.cc
Normal file
@@ -0,0 +1,168 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
#include "ldbs.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
static const char LABEL[] = "FluxEngine image";
|
||||
|
||||
static void write_and_update_checksum(ByteWriter& bw, uint32_t& checksum, const Bytes& data)
|
||||
{
|
||||
ByteReader br(data);
|
||||
while (!br.eof())
|
||||
{
|
||||
uint32_t i = br.read_be16();
|
||||
checksum += i;
|
||||
checksum = (checksum >> 1) | (checksum << 31);
|
||||
bw.write_be16(i);
|
||||
}
|
||||
}
|
||||
|
||||
class DiskCopyImageWriter : public ImageWriter
|
||||
{
|
||||
public:
|
||||
DiskCopyImageWriter(const SectorSet& sectors, const ImageSpec& spec):
|
||||
ImageWriter(sectors, spec)
|
||||
{}
|
||||
|
||||
void writeImage()
|
||||
{
|
||||
bool mfm = false;
|
||||
|
||||
if (spec.bytes == 524)
|
||||
{
|
||||
/* GCR disk */
|
||||
}
|
||||
else if (spec.bytes == 512)
|
||||
{
|
||||
/* MFM disk */
|
||||
mfm = true;
|
||||
}
|
||||
else
|
||||
Error() << "this image is not compatible with the DiskCopy 4.2 format";
|
||||
|
||||
std::cout << "writing DiskCopy 4.2 image\n"
|
||||
<< fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector; {}\n",
|
||||
spec.cylinders, spec.heads, spec.sectors, spec.bytes,
|
||||
mfm ? "MFM" : "GCR");
|
||||
|
||||
auto sectors_per_track = [&](int track) -> int
|
||||
{
|
||||
if (mfm)
|
||||
return spec.sectors;
|
||||
|
||||
if (track < 16)
|
||||
return 12;
|
||||
if (track < 32)
|
||||
return 11;
|
||||
if (track < 48)
|
||||
return 10;
|
||||
if (track < 64)
|
||||
return 9;
|
||||
return 8;
|
||||
};
|
||||
|
||||
Bytes image;
|
||||
ByteWriter bw(image);
|
||||
|
||||
/* Write the actual sectr data. */
|
||||
|
||||
uint32_t dataChecksum = 0;
|
||||
uint32_t tagChecksum = 0;
|
||||
uint32_t offset = 0x54;
|
||||
uint32_t sectorDataStart = offset;
|
||||
for (int track = 0; track < spec.cylinders; track++)
|
||||
{
|
||||
for (int head = 0; head < spec.heads; head++)
|
||||
{
|
||||
int sectorCount = sectors_per_track(track);
|
||||
for (int sectorId = 0; sectorId < sectorCount; sectorId++)
|
||||
{
|
||||
const auto& sector = sectors.get(track, head, sectorId);
|
||||
if (sector)
|
||||
{
|
||||
bw.seek(offset);
|
||||
write_and_update_checksum(bw, dataChecksum, sector->data.slice(0, 512));
|
||||
}
|
||||
offset += 512;
|
||||
}
|
||||
}
|
||||
}
|
||||
uint32_t sectorDataEnd = offset;
|
||||
if (!mfm)
|
||||
{
|
||||
for (int track = 0; track < spec.cylinders; track++)
|
||||
{
|
||||
for (int head = 0; head < spec.heads; head++)
|
||||
{
|
||||
int sectorCount = sectors_per_track(track);
|
||||
for (int sectorId = 0; sectorId < sectorCount; sectorId++)
|
||||
{
|
||||
const auto& sector = sectors.get(track, head, sectorId);
|
||||
if (sector)
|
||||
{
|
||||
bw.seek(offset);
|
||||
write_and_update_checksum(bw, tagChecksum, sector->data.slice(512, 12));
|
||||
}
|
||||
offset += 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
uint32_t tagDataEnd = offset;
|
||||
|
||||
/* Write the header. */
|
||||
|
||||
uint8_t encoding;
|
||||
uint8_t format;
|
||||
if (mfm)
|
||||
{
|
||||
format = 0x22;
|
||||
if (spec.sectors == 18)
|
||||
encoding = 3;
|
||||
else
|
||||
encoding = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spec.heads == 2)
|
||||
{
|
||||
encoding = 1;
|
||||
format = 0x22;
|
||||
}
|
||||
else
|
||||
{
|
||||
encoding = 0;
|
||||
format = 0x02;
|
||||
}
|
||||
}
|
||||
|
||||
bw.seek(0);
|
||||
bw.write_8(sizeof(LABEL));
|
||||
bw.append(LABEL);
|
||||
bw.seek(0x40);
|
||||
bw.write_be32(sectorDataEnd - sectorDataStart); /* data size */
|
||||
bw.write_be32(tagDataEnd - sectorDataEnd); /* tag size */
|
||||
bw.write_be32(dataChecksum); /* data checksum */
|
||||
bw.write_be32(tagChecksum); /* tag checksum */
|
||||
bw.write_8(encoding); /* encoding */
|
||||
bw.write_8(format); /* format byte */
|
||||
bw.write_be16(0x0100); /* magic number */
|
||||
|
||||
image.writeToFile(spec.filename);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ImageWriter> ImageWriter::createDiskCopyImageWriter(
|
||||
const SectorSet& sectors, const ImageSpec& spec)
|
||||
{
|
||||
return std::unique_ptr<ImageWriter>(new DiskCopyImageWriter(sectors, spec));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
std::map<std::string, ImageWriter::Constructor> ImageWriter::formats =
|
||||
{
|
||||
{".adf", ImageWriter::createImgImageWriter},
|
||||
{".d64", ImageWriter::createD64ImageWriter},
|
||||
{".d81", ImageWriter::createImgImageWriter},
|
||||
{".diskcopy", ImageWriter::createDiskCopyImageWriter},
|
||||
{".img", ImageWriter::createImgImageWriter},
|
||||
{".ldbs", ImageWriter::createLDBSImageWriter},
|
||||
};
|
||||
@@ -45,7 +47,7 @@ std::unique_ptr<ImageWriter> ImageWriter::create(const SectorSet& sectors, const
|
||||
void ImageWriter::verifyImageSpec(const ImageSpec& spec)
|
||||
{
|
||||
if (!findConstructor(spec))
|
||||
Error() << "unrecognised image filename extension";
|
||||
Error() << "unrecognised output image filename extension";
|
||||
}
|
||||
|
||||
ImageWriter::ImageWriter(const SectorSet& sectors, const ImageSpec& spec):
|
||||
@@ -63,6 +65,56 @@ void ImageWriter::adjustGeometry()
|
||||
}
|
||||
}
|
||||
|
||||
void ImageWriter::writeCsv(const std::string& filename)
|
||||
{
|
||||
std::ofstream f(filename, std::ios::out);
|
||||
if (!f.is_open())
|
||||
Error() << "cannot open CSV report file";
|
||||
|
||||
f << "\"Physical track\","
|
||||
"\"Physical side\","
|
||||
"\"Logical track\","
|
||||
"\"Logical side\","
|
||||
"\"Logical sector\","
|
||||
"\"Clock (ns)\","
|
||||
"\"Header start (ns)\","
|
||||
"\"Header end (ns)\","
|
||||
"\"Data start (ns)\","
|
||||
"\"Data end (ns)\","
|
||||
"\"Raw data address (bytes)\","
|
||||
"\"User payload length (bytes)\","
|
||||
"\"Status\""
|
||||
"\n";
|
||||
|
||||
for (int track = 0; track < spec.cylinders; track++)
|
||||
{
|
||||
for (int head = 0; head < spec.heads; head++)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < spec.sectors; sectorId++)
|
||||
{
|
||||
f << fmt::format("{},{},", track, head);
|
||||
const auto& sector = sectors.get(track, head, sectorId);
|
||||
if (!sector)
|
||||
f << fmt::format(",,{},,,,,,,,MISSING\n", sectorId);
|
||||
else
|
||||
f << fmt::format("{},{},{},{},{},{},{},{},{},{},{}\n",
|
||||
sector->logicalTrack,
|
||||
sector->logicalSide,
|
||||
sector->logicalSector,
|
||||
sector->clock,
|
||||
sector->headerStartTime,
|
||||
sector->headerEndTime,
|
||||
sector->dataStartTime,
|
||||
sector->dataEndTime,
|
||||
sector->position.bytes,
|
||||
sector->data.size(),
|
||||
Sector::statusToString(sector->status)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImageWriter::printMap()
|
||||
{
|
||||
int badSectors = 0;
|
||||
|
||||
@@ -29,12 +29,15 @@ private:
|
||||
const SectorSet& sectors, const ImageSpec& spec);
|
||||
static std::unique_ptr<ImageWriter> createD64ImageWriter(
|
||||
const SectorSet& sectors, const ImageSpec& spec);
|
||||
static std::unique_ptr<ImageWriter> createDiskCopyImageWriter(
|
||||
const SectorSet& sectors, const ImageSpec& spec);
|
||||
|
||||
static Constructor findConstructor(const ImageSpec& spec);
|
||||
|
||||
public:
|
||||
virtual void adjustGeometry();
|
||||
void printMap();
|
||||
void writeCsv(const std::string& filename);
|
||||
virtual void writeImage() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
@@ -47,7 +46,7 @@ public:
|
||||
if (sector)
|
||||
{
|
||||
outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*numBytes, std::ios::beg);
|
||||
outputFile.write((const char*) sector->data.cbegin(), sector->data.size());
|
||||
sector->data.slice(0, numBytes).writeTo(outputFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "usb.h"
|
||||
#include "usb/usb.h"
|
||||
#include "fluxsource/fluxsource.h"
|
||||
#include "fluxsink/fluxsink.h"
|
||||
#include "reader.h"
|
||||
#include "fluxmap.h"
|
||||
#include "sql.h"
|
||||
@@ -11,14 +12,21 @@
|
||||
#include "sectorset.h"
|
||||
#include "visualiser.h"
|
||||
#include "record.h"
|
||||
#include "image.h"
|
||||
#include "bytes.h"
|
||||
#include "decoders/rawbits.h"
|
||||
#include "track.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
FlagGroup readerFlags { &hardwareFluxSourceFlags, &fluxmapReaderFlags, &visualiserFlags };
|
||||
FlagGroup readerFlags
|
||||
{
|
||||
&hardwareFluxSourceFlags,
|
||||
&sqliteFluxSinkFlags,
|
||||
&fluxmapReaderFlags,
|
||||
&visualiserFlags
|
||||
};
|
||||
|
||||
static DataSpecFlag source(
|
||||
{ "--source", "-s" },
|
||||
@@ -46,7 +54,11 @@ static SettableFlag justRead(
|
||||
|
||||
static SettableFlag dumpRecords(
|
||||
{ "--dump-records" },
|
||||
"Dump the parsed records.");
|
||||
"Dump the parsed but undecoded records.");
|
||||
|
||||
static SettableFlag dumpSectors(
|
||||
{ "--dump-sectors" },
|
||||
"Dump the decoded sectors.");
|
||||
|
||||
static IntFlag retries(
|
||||
{ "--retries" },
|
||||
@@ -57,7 +69,12 @@ static SettableFlag highDensityFlag(
|
||||
{ "--high-density", "--hd" },
|
||||
"set the drive to high density mode");
|
||||
|
||||
static sqlite3* outdb;
|
||||
static StringFlag csvFile(
|
||||
{ "--write-csv" },
|
||||
"write a CSV report of the disk state",
|
||||
"");
|
||||
|
||||
static std::unique_ptr<FluxSink> outputFluxSink;
|
||||
|
||||
void setReaderDefaultSource(const std::string& source)
|
||||
{
|
||||
@@ -74,16 +91,31 @@ void setReaderRevolutions(int revolutions)
|
||||
setHardwareFluxSourceRevolutions(revolutions);
|
||||
}
|
||||
|
||||
void setReaderHardSectorCount(int sectorCount)
|
||||
{
|
||||
setHardwareFluxSourceHardSectorCount(sectorCount);
|
||||
}
|
||||
|
||||
static void writeSectorsToFile(const SectorSet& sectors, const ImageSpec& spec)
|
||||
{
|
||||
std::unique_ptr<ImageWriter> writer(ImageWriter::create(sectors, spec));
|
||||
writer->adjustGeometry();
|
||||
writer->printMap();
|
||||
if (!csvFile.get().empty())
|
||||
writer->writeCsv(csvFile.get());
|
||||
writer->writeImage();
|
||||
}
|
||||
|
||||
void Track::readFluxmap()
|
||||
{
|
||||
std::cout << fmt::format("{0:>3}.{1}: ", physicalTrack, physicalSide) << std::flush;
|
||||
fluxmap = fluxsource->readFlux(physicalTrack, physicalSide);
|
||||
std::cout << fmt::format(
|
||||
"{0} ms in {1} bytes\n",
|
||||
int(fluxmap->duration()/1e6),
|
||||
fluxmap->duration()/1e6,
|
||||
fluxmap->bytes());
|
||||
if (outdb)
|
||||
sqlWriteFlux(outdb, physicalTrack, physicalSide, *fluxmap);
|
||||
if (outputFluxSink)
|
||||
outputFluxSink->writeFlux(physicalTrack, physicalSide, *fluxmap);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Track>> readTracks()
|
||||
@@ -96,17 +128,8 @@ std::vector<std::unique_ptr<Track>> readTracks()
|
||||
|
||||
if (!destination.get().empty())
|
||||
{
|
||||
outdb = sqlOpen(destination, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
|
||||
std::cout << "Writing a copy of the flux to " << destination.get() << std::endl;
|
||||
sqlPrepareFlux(outdb);
|
||||
sqlStmt(outdb, "BEGIN;");
|
||||
sqlWriteIntProperty(outdb, "version", FLUX_VERSION_CURRENT);
|
||||
atexit([]()
|
||||
{
|
||||
sqlStmt(outdb, "COMMIT;");
|
||||
sqlClose(outdb);
|
||||
}
|
||||
);
|
||||
outputFluxSink = FluxSink::createSqliteFluxSink(destination.get());
|
||||
}
|
||||
|
||||
std::shared_ptr<FluxSource> fluxSource = FluxSource::create(spec);
|
||||
@@ -174,8 +197,6 @@ void readDiskCommand(AbstractDecoder& decoder)
|
||||
decoder.decodeToSectors(*track);
|
||||
|
||||
std::cout << " ";
|
||||
if (!track->sectors.empty())
|
||||
{
|
||||
std::cout << fmt::format("{} records, {} sectors; ",
|
||||
track->rawrecords.size(),
|
||||
track->sectors.size());
|
||||
@@ -191,9 +212,12 @@ void readDiskCommand(AbstractDecoder& decoder)
|
||||
}
|
||||
|
||||
bool hasBadSectors = false;
|
||||
std::set<unsigned> requiredSectors = decoder.requiredSectors(*track);
|
||||
for (const auto& i : readSectors)
|
||||
{
|
||||
const auto& sector = i.second;
|
||||
requiredSectors.erase(sector->logicalSector);
|
||||
|
||||
if (sector->status != Sector::OK)
|
||||
{
|
||||
std::cout << std::endl
|
||||
@@ -202,6 +226,12 @@ void readDiskCommand(AbstractDecoder& decoder)
|
||||
hasBadSectors = true;
|
||||
}
|
||||
}
|
||||
for (unsigned logicalSector : requiredSectors)
|
||||
{
|
||||
std::cout << "\n"
|
||||
<< " Required sector " << logicalSector << " missing; ";
|
||||
hasBadSectors = true;
|
||||
}
|
||||
|
||||
if (hasBadSectors)
|
||||
failures = false;
|
||||
@@ -211,7 +241,6 @@ void readDiskCommand(AbstractDecoder& decoder)
|
||||
|
||||
if (!hasBadSectors)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!track->fluxsource->retryable())
|
||||
break;
|
||||
@@ -219,10 +248,7 @@ void readDiskCommand(AbstractDecoder& decoder)
|
||||
std::cout << "giving up" << std::endl
|
||||
<< " ";
|
||||
else
|
||||
{
|
||||
std::cout << retry << " retries remaining" << std::endl;
|
||||
track->fluxsource->recalibrate();
|
||||
}
|
||||
}
|
||||
|
||||
if (dumpRecords)
|
||||
@@ -230,13 +256,27 @@ void readDiskCommand(AbstractDecoder& decoder)
|
||||
std::cout << "\nRaw (undecoded) records follow:\n\n";
|
||||
for (auto& record : track->rawrecords)
|
||||
{
|
||||
std::cout << fmt::format("I+{:.2f}us", record.position.ns() / 1000.0)
|
||||
<< std::endl;
|
||||
std::cout << fmt::format("I+{:.2f}us with {:.2f}us clock\n",
|
||||
record.position.ns() / 1000.0, record.clock / 1000.0);
|
||||
hexdump(std::cout, record.data);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (dumpSectors)
|
||||
{
|
||||
std::cout << "\nDecoded sectors follow:\n\n";
|
||||
for (auto& sector : track->sectors)
|
||||
{
|
||||
std::cout << fmt::format("{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock: status {}\n",
|
||||
sector.logicalTrack, sector.logicalSide, sector.logicalSector,
|
||||
sector.position.ns() / 1000.0, sector.clock / 1000.0,
|
||||
sector.status);
|
||||
hexdump(std::cout, sector.data);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int size = 0;
|
||||
bool printedTrack = false;
|
||||
for (auto& i : readSectors)
|
||||
@@ -261,7 +301,7 @@ void readDiskCommand(AbstractDecoder& decoder)
|
||||
|
||||
if (!visualise.get().empty())
|
||||
visualiseSectorsToFile(allSectors, visualise.get());
|
||||
|
||||
|
||||
writeSectorsToFile(allSectors, outputSpec);
|
||||
if (failures)
|
||||
std::cerr << "Warning: some sectors could not be decoded." << std::endl;
|
||||
|
||||
@@ -13,6 +13,7 @@ extern FlagGroup readerFlags;
|
||||
extern void setReaderDefaultSource(const std::string& source);
|
||||
extern void setReaderDefaultOutput(const std::string& output);
|
||||
extern void setReaderRevolutions(int revolutions);
|
||||
extern void setReaderHardSectorCount(int sectorCount);
|
||||
|
||||
extern std::vector<std::unique_ptr<Track>> readTracks();
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
|
||||
|
||||
@@ -10,8 +10,9 @@ enum
|
||||
FLUX_VERSION_0, /* without properties table */
|
||||
FLUX_VERSION_1,
|
||||
FLUX_VERSION_2, /* new bytecode with index marks */
|
||||
FLUX_VERSION_3, /* simplified bytecode with six-bit timer */
|
||||
|
||||
FLUX_VERSION_CURRENT = 2,
|
||||
FLUX_VERSION_CURRENT = FLUX_VERSION_3,
|
||||
};
|
||||
|
||||
extern void sqlCheck(sqlite3* db, int i);
|
||||
|
||||
267
lib/usb.cc
267
lib/usb.cc
@@ -1,267 +0,0 @@
|
||||
#include "globals.h"
|
||||
#include "usb.h"
|
||||
#include "protocol.h"
|
||||
#include "fluxmap.h"
|
||||
#include "bytes.h"
|
||||
#include "common/crunch.h"
|
||||
#include <libusb.h>
|
||||
#include "fmt/format.h"
|
||||
|
||||
#define TIMEOUT 5000
|
||||
|
||||
static libusb_device_handle* device;
|
||||
|
||||
static uint8_t buffer[FRAME_SIZE];
|
||||
|
||||
static std::string usberror(int i)
|
||||
{
|
||||
return libusb_strerror((libusb_error) i);
|
||||
}
|
||||
|
||||
static void usb_init()
|
||||
{
|
||||
if (device)
|
||||
return;
|
||||
|
||||
int i = libusb_init(NULL);
|
||||
if (i < 0)
|
||||
Error() << "could not start libusb: " << usberror(i);
|
||||
|
||||
device = libusb_open_device_with_vid_pid(NULL, FLUXENGINE_VID, FLUXENGINE_PID);
|
||||
if (!device)
|
||||
Error() << "cannot find the FluxEngine (is it plugged in?)";
|
||||
|
||||
int cfg = -1;
|
||||
libusb_get_configuration(device, &cfg);
|
||||
if (cfg != 1)
|
||||
{
|
||||
i = libusb_set_configuration(device, 1);
|
||||
if (i < 0)
|
||||
Error() << "the FluxEngine would not accept configuration: " << usberror(i);
|
||||
}
|
||||
|
||||
i = libusb_claim_interface(device, 0);
|
||||
if (i < 0)
|
||||
Error() << "could not claim interface: " << usberror(i);
|
||||
|
||||
int version = usbGetVersion();
|
||||
if (version != FLUXENGINE_VERSION)
|
||||
Error() << "your FluxEngine firmware is at version " << version
|
||||
<< " but the client is for version " << FLUXENGINE_VERSION
|
||||
<< "; please upgrade";
|
||||
}
|
||||
|
||||
static int usb_cmd_send(void* ptr, int len)
|
||||
{
|
||||
//std::cerr << "send:\n";
|
||||
//hexdump(std::cerr, Bytes((const uint8_t*)ptr, len));
|
||||
int i = libusb_interrupt_transfer(device, FLUXENGINE_CMD_OUT_EP,
|
||||
(uint8_t*) ptr, len, &len, TIMEOUT);
|
||||
if (i < 0)
|
||||
Error() << "failed to send command: " << usberror(i);
|
||||
return len;
|
||||
}
|
||||
|
||||
void usb_cmd_recv(void* ptr, int len)
|
||||
{
|
||||
int i = libusb_interrupt_transfer(device, FLUXENGINE_CMD_IN_EP,
|
||||
(uint8_t*) ptr, len, &len, TIMEOUT);
|
||||
if (i < 0)
|
||||
Error() << "failed to receive command reply: " << usberror(i);
|
||||
//std::cerr << "recv:\n";
|
||||
//hexdump(std::cerr, Bytes((const uint8_t*)ptr, len));
|
||||
}
|
||||
|
||||
static void bad_reply(void)
|
||||
{
|
||||
struct error_frame* f = (struct error_frame*) buffer;
|
||||
if (f->f.type != F_FRAME_ERROR)
|
||||
Error() << fmt::format("bad USB reply 0x{:2x}", f->f.type);
|
||||
switch (f->error)
|
||||
{
|
||||
case F_ERROR_BAD_COMMAND:
|
||||
Error() << "device did not understand command";
|
||||
|
||||
case F_ERROR_UNDERRUN:
|
||||
Error() << "USB underrun (not enough bandwidth)";
|
||||
|
||||
default:
|
||||
Error() << fmt::format("unknown device error {}", f->error);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T* await_reply(int desired)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
usb_cmd_recv(buffer, sizeof(buffer));
|
||||
struct any_frame* r = (struct any_frame*) buffer;
|
||||
if (r->f.type == F_FRAME_DEBUG)
|
||||
{
|
||||
std::cout << "dev: " << ((struct debug_frame*)r)->payload << std::endl;
|
||||
continue;
|
||||
}
|
||||
if (r->f.type != desired)
|
||||
bad_reply();
|
||||
return (T*) r;
|
||||
}
|
||||
}
|
||||
|
||||
int usbGetVersion(void)
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct any_frame f = { .f = {.type = F_FRAME_GET_VERSION_CMD, .size = sizeof(f)} };
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
auto r = await_reply<struct version_frame>(F_FRAME_GET_VERSION_REPLY);
|
||||
return r->version;
|
||||
}
|
||||
|
||||
void usbSeek(int track)
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct seek_frame f = {
|
||||
{ .type = F_FRAME_SEEK_CMD, .size = sizeof(f) },
|
||||
.track = (uint8_t) track
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
await_reply<struct any_frame>(F_FRAME_SEEK_REPLY);
|
||||
}
|
||||
|
||||
void usbRecalibrate()
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct any_frame f = {
|
||||
{ .type = F_FRAME_RECALIBRATE_CMD, .size = sizeof(f) },
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
await_reply<struct any_frame>(F_FRAME_RECALIBRATE_REPLY);
|
||||
}
|
||||
|
||||
nanoseconds_t usbGetRotationalPeriod(void)
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct any_frame f = { .f = {.type = F_FRAME_MEASURE_SPEED_CMD, .size = sizeof(f)} };
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
auto r = await_reply<struct speed_frame>(F_FRAME_MEASURE_SPEED_REPLY);
|
||||
return r->period_ms * 1000;
|
||||
}
|
||||
|
||||
static int large_bulk_transfer(int ep, Bytes& bytes)
|
||||
{
|
||||
int len;
|
||||
int i = libusb_bulk_transfer(device, ep, bytes.begin(), bytes.size(), &len, TIMEOUT);
|
||||
if (i < 0)
|
||||
Error() << "data transfer failed: " << usberror(i);
|
||||
return len;
|
||||
}
|
||||
|
||||
void usbTestBulkTransport()
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct any_frame f = { .f = {.type = F_FRAME_BULK_TEST_CMD, .size = sizeof(f)} };
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
/* These must match the device. */
|
||||
const int XSIZE = 64;
|
||||
const int YSIZE = 256;
|
||||
const int ZSIZE = 64;
|
||||
|
||||
Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
|
||||
double start_time = getCurrentTime();
|
||||
large_bulk_transfer(FLUXENGINE_DATA_IN_EP, bulk_buffer);
|
||||
double elapsed_time = getCurrentTime() - start_time;
|
||||
|
||||
std::cout << "Transferred "
|
||||
<< bulk_buffer.size()
|
||||
<< " bytes in "
|
||||
<< int(elapsed_time * 1000.0)
|
||||
<< " ("
|
||||
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
|
||||
<< " kB/s)"
|
||||
<< std::endl;
|
||||
|
||||
for (int x=0; x<XSIZE; x++)
|
||||
for (int y=0; y<YSIZE; y++)
|
||||
for (int z=0; z<ZSIZE; z++)
|
||||
{
|
||||
int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
|
||||
if (bulk_buffer[offset] != uint8_t(x+y+z))
|
||||
Error() << "data transfer corrupted at 0x"
|
||||
<< std::hex << offset << std::dec
|
||||
<< " "
|
||||
<< x << '.' << y << '.' << z << '.';
|
||||
}
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_BULK_TEST_REPLY);
|
||||
}
|
||||
|
||||
Bytes usbRead(int side, int revolutions)
|
||||
{
|
||||
struct read_frame f = {
|
||||
.f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) },
|
||||
.side = (uint8_t) side,
|
||||
.revolutions = (uint8_t) revolutions
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
auto fluxmap = std::unique_ptr<Fluxmap>(new Fluxmap);
|
||||
|
||||
Bytes buffer(1024*1024);
|
||||
int len = large_bulk_transfer(FLUXENGINE_DATA_IN_EP, buffer);
|
||||
buffer.resize(len);
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_READ_REPLY);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void usbWrite(int side, const Bytes& bytes)
|
||||
{
|
||||
unsigned safelen = bytes.size() & ~(FRAME_SIZE-1);
|
||||
Bytes safeBytes = bytes.slice(0, safelen);
|
||||
|
||||
struct write_frame f = {
|
||||
.f = { .type = F_FRAME_WRITE_CMD, .size = sizeof(f) },
|
||||
.side = (uint8_t) side,
|
||||
};
|
||||
((uint8_t*)&f.bytes_to_write)[0] = safelen;
|
||||
((uint8_t*)&f.bytes_to_write)[1] = safelen >> 8;
|
||||
((uint8_t*)&f.bytes_to_write)[2] = safelen >> 16;
|
||||
((uint8_t*)&f.bytes_to_write)[3] = safelen >> 24;
|
||||
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, safeBytes);
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_WRITE_REPLY);
|
||||
}
|
||||
|
||||
void usbErase(int side)
|
||||
{
|
||||
struct erase_frame f = {
|
||||
.f = { .type = F_FRAME_ERASE_CMD, .size = sizeof(f) },
|
||||
.side = (uint8_t) side,
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_ERASE_REPLY);
|
||||
}
|
||||
|
||||
void usbSetDrive(int drive, bool high_density)
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct set_drive_frame f = {
|
||||
{ .type = F_FRAME_SET_DRIVE_CMD, .size = sizeof(f) },
|
||||
.drive_flags = (uint8_t)((drive ? DRIVE_1 : DRIVE_0) | (high_density ? DRIVE_HD : DRIVE_DD)),
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
await_reply<struct any_frame>(F_FRAME_SET_DRIVE_REPLY);
|
||||
}
|
||||
|
||||
17
lib/usb.h
17
lib/usb.h
@@ -1,17 +0,0 @@
|
||||
#ifndef USB_H
|
||||
#define USB_H
|
||||
|
||||
class Fluxmap;
|
||||
class Bytes;
|
||||
|
||||
extern int usbGetVersion();
|
||||
extern void usbRecalibrate();
|
||||
extern void usbSeek(int track);
|
||||
extern nanoseconds_t usbGetRotationalPeriod();
|
||||
extern void usbTestBulkTransport();
|
||||
extern Bytes usbRead(int side, int revolutions);
|
||||
extern void usbWrite(int side, const Bytes& bytes);
|
||||
extern void usbErase(int side);
|
||||
extern void usbSetDrive(int drive, bool high_density);
|
||||
|
||||
#endif
|
||||
333
lib/usb/fluxengineusb.cc
Normal file
333
lib/usb/fluxengineusb.cc
Normal file
@@ -0,0 +1,333 @@
|
||||
#include "globals.h"
|
||||
#include "usb.h"
|
||||
#include "protocol.h"
|
||||
#include "fluxmap.h"
|
||||
#include "bytes.h"
|
||||
#include <libusb.h>
|
||||
#include "fmt/format.h"
|
||||
|
||||
#define TIMEOUT 5000
|
||||
|
||||
/* Hacky: the board always operates in little-endian mode. */
|
||||
static uint16_t read_short_from_usb(uint16_t usb)
|
||||
{
|
||||
uint8_t* p = (uint8_t*)&usb;
|
||||
return p[0] | (p[1] << 8);
|
||||
}
|
||||
|
||||
class FluxEngineUsb : public USB
|
||||
{
|
||||
private:
|
||||
uint8_t _buffer[FRAME_SIZE];
|
||||
|
||||
int usb_cmd_send(void* ptr, int len)
|
||||
{
|
||||
//std::cerr << "send:\n";
|
||||
//hexdump(std::cerr, Bytes((const uint8_t*)ptr, len));
|
||||
int i = libusb_interrupt_transfer(_device, FLUXENGINE_CMD_OUT_EP,
|
||||
(uint8_t*) ptr, len, &len, TIMEOUT);
|
||||
if (i < 0)
|
||||
Error() << "failed to send command: " << usberror(i);
|
||||
return len;
|
||||
}
|
||||
|
||||
void usb_cmd_recv(void* ptr, int len)
|
||||
{
|
||||
int i = libusb_interrupt_transfer(_device, FLUXENGINE_CMD_IN_EP,
|
||||
(uint8_t*) ptr, len, &len, TIMEOUT);
|
||||
if (i < 0)
|
||||
Error() << "failed to receive command reply: " << usberror(i);
|
||||
//std::cerr << "recv:\n";
|
||||
//hexdump(std::cerr, Bytes((const uint8_t*)ptr, len));
|
||||
}
|
||||
|
||||
int large_bulk_transfer(int ep, Bytes& bytes)
|
||||
{
|
||||
if (bytes.size() == 0)
|
||||
return 0;
|
||||
|
||||
int len;
|
||||
int i = libusb_bulk_transfer(_device, ep, bytes.begin(), bytes.size(), &len, TIMEOUT);
|
||||
if (i < 0)
|
||||
Error() << fmt::format("data transfer failed at {} bytes: {}", len, usberror(i));
|
||||
return len;
|
||||
}
|
||||
|
||||
public:
|
||||
FluxEngineUsb(libusb_device_handle* device)
|
||||
{
|
||||
_device = device;
|
||||
|
||||
int i;
|
||||
int cfg = -1;
|
||||
libusb_get_configuration(_device, &cfg);
|
||||
if (cfg != 1)
|
||||
{
|
||||
i = libusb_set_configuration(_device, 1);
|
||||
if (i < 0)
|
||||
Error() << "the FluxEngine would not accept configuration: " << usberror(i);
|
||||
}
|
||||
|
||||
i = libusb_claim_interface(_device, 0);
|
||||
if (i < 0)
|
||||
Error() << "could not claim interface: " << usberror(i);
|
||||
|
||||
int version = getVersion();
|
||||
if (version != FLUXENGINE_VERSION)
|
||||
Error() << "your FluxEngine firmware is at version " << version
|
||||
<< " but the client is for version " << FLUXENGINE_VERSION
|
||||
<< "; please upgrade";
|
||||
}
|
||||
|
||||
private:
|
||||
void bad_reply(void)
|
||||
{
|
||||
struct error_frame* f = (struct error_frame*) _buffer;
|
||||
if (f->f.type != F_FRAME_ERROR)
|
||||
Error() << fmt::format("bad USB reply 0x{:2x}", f->f.type);
|
||||
switch (f->error)
|
||||
{
|
||||
case F_ERROR_BAD_COMMAND:
|
||||
Error() << "device did not understand command";
|
||||
|
||||
case F_ERROR_UNDERRUN:
|
||||
Error() << "USB underrun (not enough bandwidth)";
|
||||
|
||||
default:
|
||||
Error() << fmt::format("unknown device error {}", f->error);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* await_reply(int desired)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
usb_cmd_recv(_buffer, sizeof(_buffer));
|
||||
struct any_frame* r = (struct any_frame*) _buffer;
|
||||
if (r->f.type == F_FRAME_DEBUG)
|
||||
{
|
||||
std::cout << "dev: " << ((struct debug_frame*)r)->payload << std::endl;
|
||||
continue;
|
||||
}
|
||||
if (r->f.type != desired)
|
||||
bad_reply();
|
||||
return (T*) r;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
int getVersion()
|
||||
{
|
||||
struct any_frame f = { .f = {.type = F_FRAME_GET_VERSION_CMD, .size = sizeof(f)} };
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
auto r = await_reply<struct version_frame>(F_FRAME_GET_VERSION_REPLY);
|
||||
return r->version;
|
||||
}
|
||||
|
||||
void seek(int track)
|
||||
{
|
||||
struct seek_frame f = {
|
||||
{ .type = F_FRAME_SEEK_CMD, .size = sizeof(f) },
|
||||
.track = (uint8_t) track
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
await_reply<struct any_frame>(F_FRAME_SEEK_REPLY);
|
||||
}
|
||||
|
||||
void recalibrate()
|
||||
{
|
||||
struct any_frame f = {
|
||||
{ .type = F_FRAME_RECALIBRATE_CMD, .size = sizeof(f) },
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
await_reply<struct any_frame>(F_FRAME_RECALIBRATE_REPLY);
|
||||
}
|
||||
|
||||
nanoseconds_t getRotationalPeriod(int hardSectorCount)
|
||||
{
|
||||
struct measurespeed_frame f = {
|
||||
.f = {.type = F_FRAME_MEASURE_SPEED_CMD, .size = sizeof(f)},
|
||||
.hard_sector_count = (uint8_t) hardSectorCount,
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
auto r = await_reply<struct speed_frame>(F_FRAME_MEASURE_SPEED_REPLY);
|
||||
return r->period_ms * 1000000;
|
||||
}
|
||||
|
||||
void testBulkWrite()
|
||||
{
|
||||
struct any_frame f = { .f = {.type = F_FRAME_BULK_WRITE_TEST_CMD, .size = sizeof(f)} };
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
/* These must match the device. */
|
||||
const int XSIZE = 64;
|
||||
const int YSIZE = 256;
|
||||
const int ZSIZE = 64;
|
||||
|
||||
Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
|
||||
double start_time = getCurrentTime();
|
||||
large_bulk_transfer(FLUXENGINE_DATA_IN_EP, bulk_buffer);
|
||||
double elapsed_time = getCurrentTime() - start_time;
|
||||
|
||||
std::cout << "Transferred "
|
||||
<< bulk_buffer.size()
|
||||
<< " bytes from FluxEngine -> PC in "
|
||||
<< int(elapsed_time * 1000.0)
|
||||
<< " ms ("
|
||||
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
|
||||
<< " kB/s)"
|
||||
<< std::endl;
|
||||
|
||||
for (int x=0; x<XSIZE; x++)
|
||||
for (int y=0; y<YSIZE; y++)
|
||||
for (int z=0; z<ZSIZE; z++)
|
||||
{
|
||||
int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
|
||||
if (bulk_buffer[offset] != uint8_t(x+y+z))
|
||||
Error() << "data transfer corrupted at 0x"
|
||||
<< std::hex << offset << std::dec
|
||||
<< " "
|
||||
<< x << '.' << y << '.' << z << '.';
|
||||
}
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_BULK_WRITE_TEST_REPLY);
|
||||
}
|
||||
|
||||
void testBulkRead()
|
||||
{
|
||||
struct any_frame f = { .f = {.type = F_FRAME_BULK_READ_TEST_CMD, .size = sizeof(f)} };
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
/* These must match the device. */
|
||||
const int XSIZE = 64;
|
||||
const int YSIZE = 256;
|
||||
const int ZSIZE = 64;
|
||||
|
||||
Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
|
||||
for (int x=0; x<XSIZE; x++)
|
||||
for (int y=0; y<YSIZE; y++)
|
||||
for (int z=0; z<ZSIZE; z++)
|
||||
{
|
||||
int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
|
||||
bulk_buffer[offset] = uint8_t(x+y+z);
|
||||
}
|
||||
|
||||
double start_time = getCurrentTime();
|
||||
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, bulk_buffer);
|
||||
double elapsed_time = getCurrentTime() - start_time;
|
||||
|
||||
std::cout << "Transferred "
|
||||
<< bulk_buffer.size()
|
||||
<< " bytes from PC -> FluxEngine in "
|
||||
<< int(elapsed_time * 1000.0)
|
||||
<< " ms ("
|
||||
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
|
||||
<< " kB/s)"
|
||||
<< std::endl;
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_BULK_READ_TEST_REPLY);
|
||||
}
|
||||
|
||||
Bytes read(int side, bool synced, nanoseconds_t readTime,
|
||||
nanoseconds_t hardSectorThreshold)
|
||||
{
|
||||
struct read_frame f = {
|
||||
.f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) },
|
||||
.side = (uint8_t) side,
|
||||
.synced = (uint8_t) synced,
|
||||
};
|
||||
f.hardsec_threshold_ms = (hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
|
||||
uint16_t milliseconds = readTime / 1e6;
|
||||
((uint8_t*)&f.milliseconds)[0] = milliseconds;
|
||||
((uint8_t*)&f.milliseconds)[1] = milliseconds >> 8;
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
auto fluxmap = std::unique_ptr<Fluxmap>(new Fluxmap);
|
||||
|
||||
Bytes buffer(1024*1024);
|
||||
int len = large_bulk_transfer(FLUXENGINE_DATA_IN_EP, buffer);
|
||||
buffer.resize(len);
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_READ_REPLY);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void write(int side, const Bytes& bytes, nanoseconds_t hardSectorThreshold)
|
||||
{
|
||||
unsigned safelen = bytes.size() & ~(FRAME_SIZE-1);
|
||||
Bytes safeBytes = bytes.slice(0, safelen);
|
||||
|
||||
struct write_frame f = {
|
||||
.f = { .type = F_FRAME_WRITE_CMD, .size = sizeof(f) },
|
||||
.side = (uint8_t) side,
|
||||
};
|
||||
f.hardsec_threshold_ms = (hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
|
||||
((uint8_t*)&f.bytes_to_write)[0] = safelen;
|
||||
((uint8_t*)&f.bytes_to_write)[1] = safelen >> 8;
|
||||
((uint8_t*)&f.bytes_to_write)[2] = safelen >> 16;
|
||||
((uint8_t*)&f.bytes_to_write)[3] = safelen >> 24;
|
||||
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, safeBytes);
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_WRITE_REPLY);
|
||||
}
|
||||
|
||||
void erase(int side, nanoseconds_t hardSectorThreshold)
|
||||
{
|
||||
struct erase_frame f = {
|
||||
.f = { .type = F_FRAME_ERASE_CMD, .size = sizeof(f) },
|
||||
.side = (uint8_t) side,
|
||||
};
|
||||
f.hardsec_threshold_ms = (hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_ERASE_REPLY);
|
||||
}
|
||||
|
||||
void setDrive(int drive, bool high_density, int index_mode)
|
||||
{
|
||||
struct set_drive_frame f = {
|
||||
{ .type = F_FRAME_SET_DRIVE_CMD, .size = sizeof(f) },
|
||||
.drive = (uint8_t) drive,
|
||||
.high_density = high_density,
|
||||
.index_mode = (uint8_t) index_mode
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
await_reply<struct any_frame>(F_FRAME_SET_DRIVE_REPLY);
|
||||
}
|
||||
|
||||
void measureVoltages(struct voltages_frame* voltages)
|
||||
{
|
||||
struct any_frame f = {
|
||||
{ .type = F_FRAME_MEASURE_VOLTAGES_CMD, .size = sizeof(f) },
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
auto convert_voltages_from_usb = [&](const struct voltages& vin, struct voltages& vout)
|
||||
{
|
||||
vout.logic0_mv = read_short_from_usb(vin.logic0_mv);
|
||||
vout.logic1_mv = read_short_from_usb(vin.logic1_mv);
|
||||
};
|
||||
|
||||
struct voltages_frame* r = await_reply<struct voltages_frame>(F_FRAME_MEASURE_VOLTAGES_REPLY);
|
||||
convert_voltages_from_usb(r->input_both_off, voltages->input_both_off);
|
||||
convert_voltages_from_usb(r->input_drive_0_selected, voltages->input_drive_0_selected);
|
||||
convert_voltages_from_usb(r->input_drive_1_selected, voltages->input_drive_1_selected);
|
||||
convert_voltages_from_usb(r->input_drive_0_running, voltages->input_drive_0_running);
|
||||
convert_voltages_from_usb(r->input_drive_1_running, voltages->input_drive_1_running);
|
||||
convert_voltages_from_usb(r->output_both_off, voltages->output_both_off);
|
||||
convert_voltages_from_usb(r->output_drive_0_selected, voltages->output_drive_0_selected);
|
||||
convert_voltages_from_usb(r->output_drive_1_selected, voltages->output_drive_1_selected);
|
||||
convert_voltages_from_usb(r->output_drive_0_running, voltages->output_drive_0_running);
|
||||
convert_voltages_from_usb(r->output_drive_1_running, voltages->output_drive_1_running);
|
||||
}
|
||||
};
|
||||
|
||||
USB* createFluxengineUsb(libusb_device_handle* device)
|
||||
{
|
||||
return new FluxEngineUsb(device);
|
||||
}
|
||||
|
||||
156
lib/usb/greaseweazle.cc
Normal file
156
lib/usb/greaseweazle.cc
Normal file
@@ -0,0 +1,156 @@
|
||||
#include "globals.h"
|
||||
#include "usb.h"
|
||||
#include "protocol.h"
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include "greaseweazle.h"
|
||||
|
||||
Bytes fluxEngineToGreaseWeazle(const Bytes& fldata, nanoseconds_t clock)
|
||||
{
|
||||
Bytes gwdata;
|
||||
ByteWriter bw(gwdata);
|
||||
ByteReader br(fldata);
|
||||
uint32_t ticks_fl = 0;
|
||||
uint32_t ticks_gw = 0;
|
||||
|
||||
auto write_28 = [&](uint32_t val) {
|
||||
bw.write_8(1 | (val<<1) & 0xff);
|
||||
bw.write_8(1 | (val>>6) & 0xff);
|
||||
bw.write_8(1 | (val>>13) & 0xff);
|
||||
bw.write_8(1 | (val>>20) & 0xff);
|
||||
};
|
||||
|
||||
while (!br.eof())
|
||||
{
|
||||
uint8_t b = br.read_8();
|
||||
ticks_fl += b & 0x3f;
|
||||
if (b & F_BIT_PULSE)
|
||||
{
|
||||
uint32_t newticks_gw = ticks_fl * NS_PER_TICK / clock;
|
||||
uint32_t delta = newticks_gw - ticks_gw;
|
||||
if (delta < 250)
|
||||
bw.write_8(delta);
|
||||
else
|
||||
{
|
||||
int high = (delta-250) / 255;
|
||||
if (high < 5)
|
||||
{
|
||||
bw.write_8(250 + high);
|
||||
bw.write_8(1 + (delta-250) % 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
bw.write_8(255);
|
||||
bw.write_8(FLUXOP_SPACE);
|
||||
write_28(delta - 249);
|
||||
bw.write_8(249);
|
||||
}
|
||||
}
|
||||
ticks_gw = newticks_gw;
|
||||
}
|
||||
}
|
||||
bw.write_8(0); /* end of stream */
|
||||
return gwdata;
|
||||
}
|
||||
|
||||
Bytes greaseWeazleToFluxEngine(const Bytes& gwdata, nanoseconds_t clock)
|
||||
{
|
||||
Bytes fldata;
|
||||
ByteReader br(gwdata);
|
||||
ByteWriter bw(fldata);
|
||||
|
||||
auto read_28 = [&]() {
|
||||
return ((br.read_8() & 0xfe) >> 1)
|
||||
| ((br.read_8() & 0xfe) << 6)
|
||||
| ((br.read_8() & 0xfe) << 13)
|
||||
| ((br.read_8() & 0xfe) << 20);
|
||||
};
|
||||
|
||||
uint32_t ticks_gw = 0;
|
||||
uint32_t lastevent_fl = 0;
|
||||
uint32_t index_gw = ~0;
|
||||
|
||||
while (!br.eof())
|
||||
{
|
||||
uint8_t b = br.read_8();
|
||||
if (!b)
|
||||
break;
|
||||
|
||||
uint8_t event = 0;
|
||||
if (b == 255)
|
||||
{
|
||||
switch (br.read_8())
|
||||
{
|
||||
case FLUXOP_INDEX:
|
||||
index_gw = ticks_gw + read_28();
|
||||
break;
|
||||
|
||||
case FLUXOP_SPACE:
|
||||
ticks_gw += read_28();
|
||||
break;
|
||||
|
||||
default:
|
||||
Error() << "bad opcode in GreaseWeazle stream";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b < 250)
|
||||
ticks_gw += b;
|
||||
else
|
||||
{
|
||||
int delta = 250 + (b-250)*255 + br.read_8() - 1;
|
||||
ticks_gw += delta;
|
||||
}
|
||||
event = F_BIT_PULSE;
|
||||
}
|
||||
|
||||
if (event)
|
||||
{
|
||||
uint32_t index_fl = (index_gw * clock) / NS_PER_TICK;
|
||||
uint32_t ticks_fl = (ticks_gw * clock) / NS_PER_TICK;
|
||||
if (index_gw != ~0)
|
||||
{
|
||||
if (index_fl < ticks_fl)
|
||||
{
|
||||
uint32_t delta_fl = index_fl - lastevent_fl;
|
||||
while (delta_fl > 0x3f)
|
||||
{
|
||||
bw.write_8(0x3f);
|
||||
delta_fl -= 0x3f;
|
||||
}
|
||||
bw.write_8(delta_fl | F_BIT_INDEX);
|
||||
lastevent_fl = index_fl;
|
||||
index_gw = ~0;
|
||||
}
|
||||
else if (index_fl == ticks_fl)
|
||||
event |= F_BIT_INDEX;
|
||||
}
|
||||
|
||||
uint32_t delta_fl = ticks_fl - lastevent_fl;
|
||||
while (delta_fl > 0x3f)
|
||||
{
|
||||
bw.write_8(0x3f);
|
||||
delta_fl -= 0x3f;
|
||||
}
|
||||
bw.write_8(delta_fl | event);
|
||||
lastevent_fl = ticks_fl;
|
||||
}
|
||||
}
|
||||
|
||||
return fldata;
|
||||
}
|
||||
|
||||
/* Left-truncates at the first index mark, so the resulting data as aligned at
|
||||
* the index. */
|
||||
Bytes stripPartialRotation(const Bytes& fldata)
|
||||
{
|
||||
for (unsigned i=0; i<fldata.size(); i++)
|
||||
{
|
||||
uint8_t b = fldata[i];
|
||||
if (b & F_BIT_INDEX)
|
||||
return fldata.slice(i);
|
||||
}
|
||||
return fldata;
|
||||
}
|
||||
|
||||
203
lib/usb/greaseweazle.h
Normal file
203
lib/usb/greaseweazle.h
Normal file
@@ -0,0 +1,203 @@
|
||||
#ifndef GREASEWEAZLE_H
|
||||
#define GREASEWEAZLE_H
|
||||
|
||||
#define GREASEWEAZLE_VID 0x1209
|
||||
#define GREASEWEAZLE_PID 0x4d69
|
||||
|
||||
#define EP_OUT 0x02
|
||||
#define EP_IN 0x83
|
||||
|
||||
#define GREASEWEAZLE_VERSION 22
|
||||
|
||||
extern Bytes fluxEngineToGreaseWeazle(const Bytes& fldata, nanoseconds_t clock);
|
||||
extern Bytes greaseWeazleToFluxEngine(const Bytes& gwdata, nanoseconds_t clock);
|
||||
extern Bytes stripPartialRotation(const Bytes& fldata);
|
||||
|
||||
/* Copied from https://github.com/keirf/Greaseweazle/blob/master/inc/cdc_acm_protocol.h.
|
||||
*
|
||||
* WANING: these headers were originally defined with 'packed', which is a gccism so it's
|
||||
* been dummied out. Don't use them expecting wire protocol structures. */
|
||||
|
||||
#define packed /* */
|
||||
|
||||
/*
|
||||
* GREASEWEAZLE COMMAND SET
|
||||
*/
|
||||
|
||||
/* CMD_GET_INFO, length=3, idx. Returns 32 bytes after ACK. */
|
||||
#define CMD_GET_INFO 0
|
||||
/* [BOOTLOADER] CMD_UPDATE, length=6, <update_len>.
|
||||
* Host follows with <update_len> bytes.
|
||||
* Bootloader finally returns a status byte, 0 on success. */
|
||||
/* [MAIN FIRMWARE] CMD_UPDATE, length=10, <update_len>, 0xdeafbee3.
|
||||
* Host follows with <update_len> bytes.
|
||||
* Main firmware finally returns a status byte, 0 on success. */
|
||||
#define CMD_UPDATE 1
|
||||
/* CMD_SEEK, length=3, cyl#. Seek to cyl# on selected drive. */
|
||||
#define CMD_SEEK 2
|
||||
/* CMD_HEAD, length=3, head# (0=bottom) */
|
||||
#define CMD_HEAD 3
|
||||
/* CMD_SET_PARAMS, length=3+nr, idx, <nr bytes> */
|
||||
#define CMD_SET_PARAMS 4
|
||||
/* CMD_GET_PARAMS, length=4, idx, nr_bytes. Returns nr_bytes after ACK. */
|
||||
#define CMD_GET_PARAMS 5
|
||||
/* CMD_MOTOR, length=4, drive#, on/off. Turn on/off a drive motor. */
|
||||
#define CMD_MOTOR 6
|
||||
/* CMD_READ_FLUX, length=2-8. Argument is gw_read_flux.
|
||||
* Returns flux readings until EOStream. */
|
||||
#define CMD_READ_FLUX 7
|
||||
/* CMD_WRITE_FLUX, length=2-4. Argument is gw_write_flux.
|
||||
* Host follows with flux readings until EOStream. */
|
||||
#define CMD_WRITE_FLUX 8
|
||||
/* CMD_GET_FLUX_STATUS, length=2. Last read/write status returned in ACK. */
|
||||
#define CMD_GET_FLUX_STATUS 9
|
||||
/* CMD_SWITCH_FW_MODE, length=3, <mode> */
|
||||
#define CMD_SWITCH_FW_MODE 11
|
||||
/* CMD_SELECT, length=3, drive#. Select drive# as current unit. */
|
||||
#define CMD_SELECT 12
|
||||
/* CMD_DESELECT, length=2. Deselect current unit (if any). */
|
||||
#define CMD_DESELECT 13
|
||||
/* CMD_SET_BUS_TYPE, length=3, bus_type. Set the bus type. */
|
||||
#define CMD_SET_BUS_TYPE 14
|
||||
/* CMD_SET_PIN, length=4, pin#, level. */
|
||||
#define CMD_SET_PIN 15
|
||||
/* CMD_RESET, length=2. Reset all state to initial (power on) values. */
|
||||
#define CMD_RESET 16
|
||||
/* CMD_ERASE_FLUX, length=6. Argument is gw_erase_flux. */
|
||||
#define CMD_ERASE_FLUX 17
|
||||
/* CMD_SOURCE_BYTES, length=6. Argument is gw_sink_source_bytes. */
|
||||
#define CMD_SOURCE_BYTES 18
|
||||
/* CMD_SINK_BYTES, length=6. Argument is gw_sink_source_bytes. */
|
||||
#define CMD_SINK_BYTES 19
|
||||
#define CMD_MAX 19
|
||||
|
||||
|
||||
/*
|
||||
* CMD_SET_BUS CODES
|
||||
*/
|
||||
#define BUS_NONE 0
|
||||
#define BUS_IBMPC 1
|
||||
#define BUS_SHUGART 2
|
||||
|
||||
|
||||
/*
|
||||
* ACK RETURN CODES
|
||||
*/
|
||||
#define ACK_OKAY 0
|
||||
#define ACK_BAD_COMMAND 1
|
||||
#define ACK_NO_INDEX 2
|
||||
#define ACK_NO_TRK0 3
|
||||
#define ACK_FLUX_OVERFLOW 4
|
||||
#define ACK_FLUX_UNDERFLOW 5
|
||||
#define ACK_WRPROT 6
|
||||
#define ACK_NO_UNIT 7
|
||||
#define ACK_NO_BUS 8
|
||||
#define ACK_BAD_UNIT 9
|
||||
#define ACK_BAD_PIN 10
|
||||
#define ACK_BAD_CYLINDER 11
|
||||
|
||||
|
||||
/*
|
||||
* CONTROL-CHANNEL COMMAND SET:
|
||||
* We abuse SET_LINE_CODING requests over endpoint 0, stashing a command
|
||||
* in the baud-rate field.
|
||||
*/
|
||||
#define BAUD_NORMAL 9600
|
||||
#define BAUD_CLEAR_COMMS 10000
|
||||
|
||||
/*
|
||||
* Flux stream opcodes. Preceded by 0xFF byte.
|
||||
*
|
||||
* Argument types:
|
||||
* N28: 28-bit non-negative integer N, encoded as 4 bytes b0,b1,b2,b3:
|
||||
* b0 = (uint8_t)(1 | (N << 1))
|
||||
* b1 = (uint8_t)(1 | (N >> 6))
|
||||
* b2 = (uint8_t)(1 | (N >> 13))
|
||||
* b3 = (uint8_t)(1 | (N >> 20))
|
||||
*/
|
||||
/* FLUXOP_INDEX [CMD_READ_FLUX]
|
||||
* Args:
|
||||
* +4 [N28]: ticks to index, relative to sample cursor.
|
||||
* Signals an index pulse in the read stream. Sample cursor is unaffected. */
|
||||
#define FLUXOP_INDEX 1
|
||||
/* FLUXOP_SPACE [CMD_READ_FLUX, CMD_WRITE_FLUX]
|
||||
* Args:
|
||||
* +4 [N28]: ticks to increment the sample cursor.
|
||||
* Increments the sample cursor with no intervening flux transitions. */
|
||||
#define FLUXOP_SPACE 2
|
||||
/* FLUXOP_ASTABLE [CMD_WRITE_FLUX]
|
||||
* Args:
|
||||
* +4 [N28]: astable period.
|
||||
* Generate regular flux transitions at specified astable period.
|
||||
* Duration is specified by immediately preceding FLUXOP_SPACE opcode(s). */
|
||||
#define FLUXOP_ASTABLE 3
|
||||
|
||||
|
||||
/*
|
||||
* COMMAND PACKETS
|
||||
*/
|
||||
|
||||
/* CMD_GET_INFO, index 0 */
|
||||
#define GETINFO_FIRMWARE 0
|
||||
struct packed gw_info {
|
||||
uint8_t fw_major;
|
||||
uint8_t fw_minor;
|
||||
uint8_t is_main_firmware; /* == 0 -> update bootloader */
|
||||
uint8_t max_cmd;
|
||||
uint32_t sample_freq;
|
||||
uint8_t hw_model, hw_submodel;
|
||||
uint8_t usb_speed;
|
||||
};
|
||||
extern struct gw_info gw_info;
|
||||
|
||||
/* CMD_GET_INFO, index 1 */
|
||||
#define GETINFO_BW_STATS 1
|
||||
struct packed gw_bw_stats {
|
||||
struct packed {
|
||||
uint32_t bytes;
|
||||
uint32_t usecs;
|
||||
} min_bw, max_bw;
|
||||
};
|
||||
|
||||
/* CMD_READ_FLUX */
|
||||
struct packed gw_read_flux {
|
||||
/* Maximum ticks to read for (or 0, for no limit). */
|
||||
uint32_t ticks;
|
||||
/* Maximum index pulses to read (or 0, for no limit). */
|
||||
uint16_t max_index;
|
||||
};
|
||||
|
||||
/* CMD_WRITE_FLUX */
|
||||
struct packed gw_write_flux {
|
||||
/* If non-zero, start the write at the index pulse. */
|
||||
uint8_t cue_at_index;
|
||||
/* If non-zero, terminate the write at the next index pulse. */
|
||||
uint8_t terminate_at_index;
|
||||
};
|
||||
|
||||
/* CMD_ERASE_FLUX */
|
||||
struct packed gw_erase_flux {
|
||||
uint32_t ticks;
|
||||
};
|
||||
|
||||
/* CMD_SINK_SOURCE_BYTES */
|
||||
struct packed gw_sink_source_bytes {
|
||||
uint32_t nr_bytes;
|
||||
};
|
||||
|
||||
/* CMD_{GET,SET}_PARAMS, index 0 */
|
||||
#define PARAMS_DELAYS 0
|
||||
struct packed gw_delay {
|
||||
uint16_t select_delay; /* usec */
|
||||
uint16_t step_delay; /* usec */
|
||||
uint16_t seek_settle; /* msec */
|
||||
uint16_t motor_delay; /* msec */
|
||||
uint16_t auto_off; /* msec */
|
||||
};
|
||||
|
||||
/* CMD_SWITCH_FW_MODE */
|
||||
#define FW_MODE_BOOTLOADER 0
|
||||
#define FW_MODE_NORMAL 1
|
||||
|
||||
#endif
|
||||
|
||||
390
lib/usb/greaseweazleusb.cc
Normal file
390
lib/usb/greaseweazleusb.cc
Normal file
@@ -0,0 +1,390 @@
|
||||
#include "globals.h"
|
||||
#include "usb.h"
|
||||
#include "protocol.h"
|
||||
#include "fluxmap.h"
|
||||
#include "bytes.h"
|
||||
#include <libusb.h>
|
||||
#include "fmt/format.h"
|
||||
#include "greaseweazle.h"
|
||||
|
||||
#define TIMEOUT 5000
|
||||
|
||||
static const char* gw_error(int e)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case ACK_OKAY: return "OK";
|
||||
case ACK_BAD_COMMAND: return "Bad command";
|
||||
case ACK_NO_INDEX: return "No index";
|
||||
case ACK_NO_TRK0: return "No track 0";
|
||||
case ACK_FLUX_OVERFLOW: return "Overflow";
|
||||
case ACK_FLUX_UNDERFLOW: return "Underflow";
|
||||
case ACK_WRPROT: return "Write protected";
|
||||
case ACK_NO_UNIT: return "No unit";
|
||||
case ACK_NO_BUS: return "No bus";
|
||||
case ACK_BAD_UNIT: return "Invalid unit";
|
||||
case ACK_BAD_PIN: return "Invalid pin";
|
||||
case ACK_BAD_CYLINDER: return "Invalid cylinder";
|
||||
default: return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
class GreaseWeazleUsb : public USB
|
||||
{
|
||||
private:
|
||||
uint8_t _readbuffer[4096];
|
||||
int _readbuffer_ptr = 0;
|
||||
int _readbuffer_fill = 0;
|
||||
|
||||
void read_bytes(uint8_t* buffer, int len)
|
||||
{
|
||||
while (len > 0)
|
||||
{
|
||||
if (_readbuffer_ptr < _readbuffer_fill)
|
||||
{
|
||||
int buffered = std::min(len, _readbuffer_fill - _readbuffer_ptr);
|
||||
memcpy(buffer, _readbuffer + _readbuffer_ptr, buffered);
|
||||
_readbuffer_ptr += buffered;
|
||||
buffer += buffered;
|
||||
len -= buffered;
|
||||
}
|
||||
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
int actual;
|
||||
int rc = libusb_bulk_transfer(_device, EP_IN,
|
||||
_readbuffer, sizeof(_readbuffer),
|
||||
&actual, TIMEOUT);
|
||||
if (rc < 0)
|
||||
Error() << "failed to receive command reply: " << usberror(rc);
|
||||
|
||||
_readbuffer_fill = actual;
|
||||
_readbuffer_ptr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void read_bytes(Bytes& bytes)
|
||||
{
|
||||
read_bytes(bytes.begin(), bytes.size());
|
||||
}
|
||||
|
||||
Bytes read_bytes(unsigned len)
|
||||
{
|
||||
Bytes b(len);
|
||||
read_bytes(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
uint8_t read_byte()
|
||||
{
|
||||
uint8_t b;
|
||||
read_bytes(&b, 1);
|
||||
return b;
|
||||
}
|
||||
|
||||
uint32_t read_28()
|
||||
{
|
||||
return ((read_byte() & 0xfe) >> 1)
|
||||
| ((read_byte() & 0xfe) << 6)
|
||||
| ((read_byte() & 0xfe) << 13)
|
||||
| ((read_byte() & 0xfe) << 20);
|
||||
}
|
||||
|
||||
void write_bytes(const uint8_t* buffer, int len)
|
||||
{
|
||||
while (len > 0)
|
||||
{
|
||||
int actual;
|
||||
int rc = libusb_bulk_transfer(_device, EP_OUT, (uint8_t*)buffer, len, &actual, 0);
|
||||
if (rc < 0)
|
||||
Error() << "failed to send command: " << usberror(rc);
|
||||
|
||||
buffer += actual;
|
||||
len -= actual;
|
||||
}
|
||||
}
|
||||
|
||||
void write_bytes(const Bytes& bytes)
|
||||
{
|
||||
write_bytes(bytes.cbegin(), bytes.size());
|
||||
}
|
||||
|
||||
void do_command(const Bytes& command)
|
||||
{
|
||||
write_bytes(command);
|
||||
|
||||
uint8_t buffer[2];
|
||||
read_bytes(buffer, sizeof(buffer));
|
||||
|
||||
if (buffer[0] != command[0])
|
||||
Error() << fmt::format("command returned garbage (0x{:x} != 0x{:x} with status 0x{:x})",
|
||||
buffer[0], command[0], buffer[1]);
|
||||
if (buffer[1])
|
||||
Error() << fmt::format("GreaseWeazle error: {}", gw_error(buffer[1]));
|
||||
}
|
||||
|
||||
public:
|
||||
GreaseWeazleUsb(libusb_device_handle* device)
|
||||
{
|
||||
_device = device;
|
||||
|
||||
/* Configure the device. */
|
||||
|
||||
int i;
|
||||
int cfg = -1;
|
||||
libusb_get_configuration(_device, &cfg);
|
||||
if (cfg != 1)
|
||||
{
|
||||
i = libusb_set_configuration(_device, 1);
|
||||
if (i < 0)
|
||||
Error() << "the GreaseWeazle would not accept configuration: " << usberror(i);
|
||||
}
|
||||
|
||||
/* Detach the existing kernel serial port driver, if there is one, and claim it ourselves. */
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (libusb_kernel_driver_active(_device, i))
|
||||
libusb_detach_kernel_driver(_device, i);
|
||||
int rc = libusb_claim_interface(_device, i);
|
||||
if (rc < 0)
|
||||
Error() << "unable to claim interface: " << libusb_error_name(rc);
|
||||
}
|
||||
|
||||
int version = getVersion();
|
||||
if (version != GREASEWEAZLE_VERSION)
|
||||
Error() << "your GreaseWeazle firmware is at version " << version
|
||||
<< " but the client is for version " << GREASEWEAZLE_VERSION
|
||||
<< "; please upgrade";
|
||||
|
||||
/* Configure the hardware. */
|
||||
|
||||
do_command({ CMD_SET_BUS_TYPE, 3, BUS_IBMPC });
|
||||
}
|
||||
|
||||
int getVersion()
|
||||
{
|
||||
do_command({ CMD_GET_INFO, 3, GETINFO_FIRMWARE });
|
||||
|
||||
Bytes response = read_bytes(32);
|
||||
ByteReader br(response);
|
||||
|
||||
br.seek(4);
|
||||
nanoseconds_t freq = br.read_le32();
|
||||
_clock = 1000000000 / freq;
|
||||
|
||||
br.seek(0);
|
||||
return br.read_be16();
|
||||
}
|
||||
|
||||
void recalibrate()
|
||||
{
|
||||
seek(0);
|
||||
}
|
||||
|
||||
void seek(int track)
|
||||
{
|
||||
do_command({ CMD_SEEK, 3, (uint8_t)track });
|
||||
}
|
||||
|
||||
nanoseconds_t getRotationalPeriod(int hardSectorCount)
|
||||
{
|
||||
if (hardSectorCount != 0)
|
||||
Error() << "hard sectors are currently unsupported on the GreaseWeazel";
|
||||
|
||||
/* The GreaseWeazle doesn't have a command to fetch the period directly,
|
||||
* so we have to do a flux read. */
|
||||
|
||||
do_command({ CMD_READ_FLUX, 2 });
|
||||
|
||||
uint32_t ticks_gw = 0;
|
||||
uint32_t firstindex = ~0;
|
||||
uint32_t secondindex = ~0;
|
||||
for (;;)
|
||||
{
|
||||
uint8_t b = read_byte();
|
||||
if (!b)
|
||||
break;
|
||||
|
||||
if (b == 255)
|
||||
{
|
||||
switch (read_byte())
|
||||
{
|
||||
case FLUXOP_INDEX:
|
||||
{
|
||||
uint32_t index = read_28() + ticks_gw;
|
||||
if (firstindex == ~0)
|
||||
firstindex = index;
|
||||
else if (secondindex == ~0)
|
||||
secondindex = index;
|
||||
break;
|
||||
}
|
||||
|
||||
case FLUXOP_SPACE:
|
||||
read_bytes(4);
|
||||
break;
|
||||
|
||||
default:
|
||||
Error() << "bad opcode in GreaseWeazle stream";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b < 250)
|
||||
ticks_gw += b;
|
||||
else
|
||||
{
|
||||
int delta = 250 + (b-250)*255 + read_byte() - 1;
|
||||
ticks_gw += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (secondindex == ~0)
|
||||
Error() << "unable to determine disk rotational period (is a disk in the drive?)";
|
||||
do_command({ CMD_GET_FLUX_STATUS, 2 });
|
||||
|
||||
_revolutions = (nanoseconds_t)(secondindex - firstindex) * _clock;
|
||||
return _revolutions;
|
||||
}
|
||||
|
||||
void testBulkWrite()
|
||||
{
|
||||
const int LEN = 10*1024*1024;
|
||||
Bytes cmd(6);
|
||||
ByteWriter bw(cmd);
|
||||
bw.write_8(CMD_SINK_BYTES);
|
||||
bw.write_8(cmd.size());
|
||||
bw.write_le32(LEN);
|
||||
do_command(cmd);
|
||||
|
||||
Bytes junk(LEN);
|
||||
double start_time = getCurrentTime();
|
||||
write_bytes(LEN);
|
||||
read_bytes(1);
|
||||
double elapsed_time = getCurrentTime() - start_time;
|
||||
|
||||
std::cout << "Transferred "
|
||||
<< LEN
|
||||
<< " bytes from PC -> GreaseWeazle in "
|
||||
<< int(elapsed_time * 1000.0)
|
||||
<< " ms ("
|
||||
<< int((LEN / 1024.0) / elapsed_time)
|
||||
<< " kB/s)"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void testBulkRead()
|
||||
{
|
||||
const int LEN = 10*1024*1024;
|
||||
Bytes cmd(6);
|
||||
ByteWriter bw(cmd);
|
||||
bw.write_8(CMD_SOURCE_BYTES);
|
||||
bw.write_8(cmd.size());
|
||||
bw.write_le32(LEN);
|
||||
do_command(cmd);
|
||||
|
||||
double start_time = getCurrentTime();
|
||||
read_bytes(LEN);
|
||||
double elapsed_time = getCurrentTime() - start_time;
|
||||
|
||||
std::cout << "Transferred "
|
||||
<< LEN
|
||||
<< " bytes from GreaseWeazle -> PC in "
|
||||
<< int(elapsed_time * 1000.0)
|
||||
<< " ms ("
|
||||
<< int((LEN / 1024.0) / elapsed_time)
|
||||
<< " kB/s)"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
Bytes read(int side, bool synced, nanoseconds_t readTime, nanoseconds_t hardSectorThreshold)
|
||||
{
|
||||
if (hardSectorThreshold != 0)
|
||||
Error() << "hard sectors are currently unsupported on the GreaseWeazel";
|
||||
|
||||
int revolutions = (readTime+_revolutions-1) / _revolutions;
|
||||
|
||||
do_command({ CMD_HEAD, 3, (uint8_t)side });
|
||||
|
||||
{
|
||||
Bytes cmd(4);
|
||||
cmd.writer()
|
||||
.write_8(CMD_READ_FLUX)
|
||||
.write_8(cmd.size())
|
||||
.write_le32(revolutions + (synced ? 1 : 0));
|
||||
do_command(cmd);
|
||||
}
|
||||
|
||||
Bytes buffer;
|
||||
ByteWriter bw(buffer);
|
||||
for (;;)
|
||||
{
|
||||
uint8_t b = read_byte();
|
||||
if (!b)
|
||||
break;
|
||||
bw.write_8(b);
|
||||
}
|
||||
|
||||
do_command({ CMD_GET_FLUX_STATUS, 2 });
|
||||
|
||||
Bytes fldata = greaseWeazleToFluxEngine(buffer, _clock);
|
||||
if (synced)
|
||||
fldata = stripPartialRotation(fldata);
|
||||
return fldata;
|
||||
}
|
||||
|
||||
void write(int side, const Bytes& fldata, nanoseconds_t hardSectorThreshold)
|
||||
{
|
||||
if (hardSectorThreshold != 0)
|
||||
Error() << "hard sectors are currently unsupported on the GreaseWeazel";
|
||||
|
||||
do_command({ CMD_HEAD, 3, (uint8_t)side });
|
||||
do_command({ CMD_WRITE_FLUX, 3, 1 });
|
||||
write_bytes(fluxEngineToGreaseWeazle(fldata, _clock));
|
||||
read_byte(); /* synchronise */
|
||||
|
||||
do_command({ CMD_GET_FLUX_STATUS, 2 });
|
||||
}
|
||||
|
||||
void erase(int side, nanoseconds_t hardSectorThreshold)
|
||||
{
|
||||
if (hardSectorThreshold != 0)
|
||||
Error() << "hard sectors are currently unsupported on the GreaseWeazel";
|
||||
|
||||
do_command({ CMD_HEAD, 3, (uint8_t)side });
|
||||
|
||||
Bytes cmd(6);
|
||||
ByteWriter bw(cmd);
|
||||
bw.write_8(CMD_ERASE_FLUX);
|
||||
bw.write_8(cmd.size());
|
||||
bw.write_le32(200e6 / _clock);
|
||||
do_command(cmd);
|
||||
read_byte(); /* synchronise */
|
||||
|
||||
do_command({ CMD_GET_FLUX_STATUS, 2 });
|
||||
}
|
||||
|
||||
void setDrive(int drive, bool high_density, int index_mode)
|
||||
{
|
||||
do_command({ CMD_SELECT, 3, (uint8_t)drive });
|
||||
do_command({ CMD_MOTOR, 4, (uint8_t)drive, 1 });
|
||||
do_command({ CMD_SET_PIN, 4, 2, (uint8_t)(high_density ? 0 : 1) });
|
||||
}
|
||||
|
||||
void measureVoltages(struct voltages_frame* voltages)
|
||||
{ Error() << "unsupported operation on the GreaseWeazle"; }
|
||||
|
||||
private:
|
||||
nanoseconds_t _clock;
|
||||
nanoseconds_t _revolutions;
|
||||
};
|
||||
|
||||
USB* createGreaseWeazleUsb(libusb_device_handle* device)
|
||||
{
|
||||
return new GreaseWeazleUsb(device);
|
||||
}
|
||||
|
||||
// vim: sw=4 ts=4 et
|
||||
|
||||
170
lib/usb/usb.cc
Normal file
170
lib/usb/usb.cc
Normal file
@@ -0,0 +1,170 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "usb.h"
|
||||
#include "protocol.h"
|
||||
#include "fluxmap.h"
|
||||
#include "bytes.h"
|
||||
#include <libusb.h>
|
||||
#include "fmt/format.h"
|
||||
#include "greaseweazle.h"
|
||||
|
||||
FlagGroup usbFlags;
|
||||
|
||||
static StringFlag device(
|
||||
{ "--device" },
|
||||
"serial number of hardware device to use",
|
||||
"");
|
||||
|
||||
static USB* usb = NULL;
|
||||
|
||||
enum
|
||||
{
|
||||
DEV_FLUXENGINE,
|
||||
DEV_GREASEWEAZLE,
|
||||
};
|
||||
|
||||
struct CandidateDevice
|
||||
{
|
||||
libusb_device* device;
|
||||
libusb_device_descriptor desc;
|
||||
int type;
|
||||
std::string serial;
|
||||
};
|
||||
|
||||
USB::~USB()
|
||||
{}
|
||||
|
||||
std::string USB::usberror(int i)
|
||||
{
|
||||
return libusb_strerror((libusb_error) i);
|
||||
}
|
||||
|
||||
static const char* device_type(int i)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case DEV_FLUXENGINE: return "FluxEngine";
|
||||
case DEV_GREASEWEAZLE: return "GreaseWeazle";
|
||||
default: assert(false);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const std::string get_serial_number(libusb_device* device, libusb_device_descriptor* desc)
|
||||
{
|
||||
std::string serial;
|
||||
|
||||
libusb_device_handle* handle;
|
||||
if (libusb_open(device, &handle) == 0)
|
||||
{
|
||||
unsigned char buffer[64];
|
||||
libusb_get_string_descriptor_ascii(handle, desc->iSerialNumber, buffer, sizeof(buffer));
|
||||
serial = (const char*) buffer;
|
||||
libusb_close(handle);
|
||||
}
|
||||
|
||||
return serial;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::unique_ptr<CandidateDevice>> get_candidates(libusb_device** devices, int numdevices)
|
||||
{
|
||||
std::map<std::string, std::unique_ptr<CandidateDevice>> candidates;
|
||||
for (int i=0; i<numdevices; i++)
|
||||
{
|
||||
std::unique_ptr<CandidateDevice> candidate(new CandidateDevice());
|
||||
candidate->device = devices[i];
|
||||
(void) libusb_get_device_descriptor(devices[i], &candidate->desc);
|
||||
|
||||
uint32_t id = (candidate->desc.idVendor << 16) | candidate->desc.idProduct;
|
||||
switch (id)
|
||||
{
|
||||
case (FLUXENGINE_VID<<16) | FLUXENGINE_PID:
|
||||
{
|
||||
candidate->type = DEV_FLUXENGINE;
|
||||
candidate->serial = get_serial_number(candidate->device, &candidate->desc);
|
||||
candidates[candidate->serial] = std::move(candidate);
|
||||
break;
|
||||
}
|
||||
|
||||
case (GREASEWEAZLE_VID<<16) | GREASEWEAZLE_PID:
|
||||
{
|
||||
candidate->type = DEV_GREASEWEAZLE;
|
||||
candidate->serial = get_serial_number(candidate->device, &candidate->desc);
|
||||
candidates[candidate->serial] = std::move(candidate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
static void open_device(CandidateDevice& candidate)
|
||||
{
|
||||
libusb_device_handle* handle;
|
||||
int i = libusb_open(candidate.device, &handle);
|
||||
if (i < 0)
|
||||
Error() << "cannot open USB device: " << libusb_strerror((libusb_error) i);
|
||||
|
||||
std::cout << "Using " << device_type(candidate.type) << " with serial number " << candidate.serial << '\n';
|
||||
switch (candidate.type)
|
||||
{
|
||||
case DEV_FLUXENGINE:
|
||||
usb = createFluxengineUsb(handle);
|
||||
break;
|
||||
|
||||
case DEV_GREASEWEAZLE:
|
||||
usb = createGreaseWeazleUsb(handle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static CandidateDevice& select_candidate(const std::map<std::string, std::unique_ptr<CandidateDevice>>& devices)
|
||||
{
|
||||
if (devices.size() == 0)
|
||||
Error() << "no USB devices found (is one plugged in? Do you have permission to access USB devices?)";
|
||||
|
||||
if (device.get() == "")
|
||||
{
|
||||
if (devices.size() == 1)
|
||||
return *devices.begin()->second;
|
||||
|
||||
std::cout << "More than one USB device detected. Use --device to specify which one to use:\n";
|
||||
for (auto& i : devices)
|
||||
std::cout << " " << device_type(i.second->type) << ": " << i.first << '\n';
|
||||
Error() << "specify USB device";
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& i = devices.find(device);
|
||||
if (i != devices.end())
|
||||
return *i->second;
|
||||
|
||||
Error() << "device with serial number '" << device.get() << "' not found";
|
||||
}
|
||||
}
|
||||
|
||||
USB& getUsb()
|
||||
{
|
||||
if (!usb)
|
||||
{
|
||||
int i = libusb_init(NULL);
|
||||
if (i < 0)
|
||||
Error() << "could not start libusb: " << libusb_strerror((libusb_error) i);
|
||||
|
||||
libusb_device** devices;
|
||||
int numdevices = libusb_get_device_list(NULL, &devices);
|
||||
if (numdevices < 0)
|
||||
Error() << "could not enumerate USB bus: " << libusb_strerror((libusb_error) numdevices);
|
||||
|
||||
auto candidates = get_candidates(devices, numdevices);
|
||||
auto candidate = select_candidate(candidates);
|
||||
open_device(candidate);
|
||||
|
||||
libusb_free_device_list(devices, true);
|
||||
|
||||
}
|
||||
|
||||
return *usb;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user