Compare commits
346 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6cf86a4797 | |||
| feb5eac02e | |||
|
|
00d30fe26b | ||
|
|
f2083ed5e9 | ||
|
|
6ac98d02a7 | ||
|
|
8baf4ffd2f | ||
|
|
632357ff9d | ||
|
|
074714180b | ||
|
|
bd62b7a9bf | ||
|
|
49723d05cf | ||
|
|
f75422a412 | ||
| 5e8f35c94e | |||
|
|
15eb88e922 | ||
|
|
9a299b758a | ||
|
|
21afc26b68 | ||
|
|
5c68b47a29 | ||
|
|
adff739a5d | ||
|
|
0da3d8b231 | ||
|
|
3c17e74f6d | ||
|
|
bf35983ebb | ||
|
|
cc31b325ea | ||
|
|
7c0eb464c1 | ||
|
|
8884ca09fa | ||
|
|
287f0d8909 | ||
|
|
26c20e2262 | ||
|
|
b062582d15 | ||
|
|
fb66e49a1f | ||
|
|
79e37f2c18 | ||
|
|
9ab1dae553 | ||
|
|
c5ad0b4bec | ||
|
|
606d1012d3 | ||
|
|
45f2d98f3c | ||
|
|
5cf15a9b11 | ||
|
|
178aa9d32f | ||
|
|
29f181f9bf | ||
|
|
86c5cccb08 | ||
|
|
ea8af83d61 | ||
|
|
d303067deb | ||
| 6e817e2d7c | |||
| 16277f803c | |||
| ef55e10ff2 | |||
|
|
aaccd648b3 | ||
|
|
9596cbd85a | ||
|
|
5b6320b61a | ||
|
|
4de4fdc0d1 | ||
|
|
77a0c9f341 | ||
|
|
fc7859dc27 | ||
|
|
39d6b0525f | ||
|
|
51e091ded6 | ||
|
|
52407848c1 | ||
|
|
a6e2511e6b | ||
|
|
422f3ba8c8 | ||
|
|
276282e847 | ||
|
|
7782e27cc5 | ||
|
|
4f0a178984 | ||
|
|
28ddda4635 | ||
|
|
36e20ec396 | ||
|
|
22e65227fb | ||
|
|
9ea68b66f7 | ||
|
|
7d93692468 | ||
|
|
93121275ae | ||
|
|
07b48b2e0d | ||
|
|
294672d946 | ||
|
|
ba3f806616 | ||
|
|
9e4d99faca | ||
|
|
e89648200b | ||
| 3c305e8a37 | |||
| 6cfe69634c | |||
| 61be3714a5 | |||
| 0aed615ee5 | |||
|
|
6797037bdb | ||
|
|
39f8b25fd8 | ||
|
|
96214bf3fd | ||
|
|
400cd87802 | ||
|
|
00c458db1e | ||
|
|
1454e200db | ||
|
|
752875061c | ||
|
|
78186d8a45 | ||
|
|
a4ef434f11 | ||
|
|
9842c9945d | ||
|
|
6dcd97dedf | ||
|
|
549a984eab | ||
|
|
aa805f81e0 | ||
|
|
93a67dadf6 | ||
|
|
e9286f6ae9 | ||
|
|
a31fcdb753 | ||
|
|
0edca836f0 | ||
|
|
8537f291b7 | ||
|
|
46611ec720 | ||
|
|
cbc3db8100 | ||
|
|
7d5b07bf37 | ||
|
|
5bd633d5bb | ||
|
|
17fdad1d6e | ||
|
|
1ad26671b0 | ||
|
|
2dc5064409 | ||
| 79eec41bcd | |||
| 386d22a45e | |||
| d90fcbf7ad | |||
| c4e4520058 | |||
|
|
dd49f01499 | ||
| 2c698cee71 | |||
| 87cb4b6d18 | |||
|
|
2b7c747209 | ||
| 707308b490 | |||
| ed1012bf07 | |||
|
|
cc5b2bc27b | ||
|
|
a1a7cfa735 | ||
|
|
4624ff92df | ||
|
|
47dde98728 | ||
|
|
6f1031e95b | ||
|
|
d5245e3784 | ||
|
|
d97e72edb6 | ||
|
|
23b9e9ef5f | ||
|
|
02c7b86f85 | ||
|
|
f796b6b40d | ||
|
|
528454c361 | ||
| 9ae3f7e61d | |||
|
|
dd33922810 | ||
|
|
b52fdb3155 | ||
| edf9f9e714 | |||
|
|
38eda6ed3c | ||
|
|
32f0c5dc09 | ||
|
|
b45b45b1b3 | ||
|
|
f1c60b7177 | ||
|
|
2d8ff826f3 | ||
|
|
d028db1ba3 | ||
|
|
2d4e2b87bc | ||
|
|
1d7a75c7b3 | ||
|
|
65584a953d | ||
|
|
4cbd19d2e5 | ||
|
|
a59b59fea4 | ||
|
|
5c063a9de3 | ||
|
|
3666a7d7bd | ||
|
|
8250112c7f | ||
|
|
30957f4105 | ||
|
|
eade2e279e | ||
| b4489b9402 | |||
|
|
a67b7c80c1 | ||
|
|
dfc0cdd0fa | ||
|
|
d6faf5b074 | ||
|
|
44ffa6a3b0 | ||
|
|
168b78d9d7 | ||
|
|
bd8f313ccb | ||
|
|
4a0fc3d566 | ||
|
|
8d04d17e39 | ||
|
|
9f44b1e783 | ||
|
|
1b15271fe2 | ||
|
|
f451d3203c | ||
|
|
c713d38c19 | ||
|
|
4d51f9d097 | ||
|
|
8750341862 | ||
|
|
3a4fe086ea | ||
|
|
212e457c4c | ||
|
|
790e2b534f | ||
|
|
48414f0ce9 | ||
|
|
b5c3e75f10 | ||
|
|
548e07ce17 | ||
|
|
0aa0c6866c | ||
|
|
3d4cf7df26 | ||
|
|
c4ba180a0c | ||
|
|
bd392b91b7 | ||
|
|
042f7b0502 | ||
|
|
0fc5f0ee7d | ||
|
|
8cbef669b1 | ||
|
|
f9004fb14c | ||
|
|
40a42c65c1 | ||
|
|
e14030e369 | ||
|
|
c6cef191a7 | ||
|
|
6ca9f83bfe | ||
|
|
b3fcdc5f40 | ||
|
|
c6591cc11a | ||
|
|
e31ba479b2 | ||
|
|
659b668012 | ||
|
|
d69944dd8c | ||
|
|
a25111e411 | ||
|
|
6866d5d3fa | ||
|
|
21b3d1c521 | ||
|
|
f2bdd1cc49 | ||
|
|
9f09794ae6 | ||
|
|
6a1fb3d829 | ||
|
|
631b9768af | ||
|
|
a2d9db85cb | ||
|
|
4cb9d0b50a | ||
|
|
73f37ef289 | ||
|
|
dbda19a209 | ||
|
|
0d7de7bbc0 | ||
|
|
649b78611c | ||
|
|
22a21d76a4 | ||
|
|
5668a74aef | ||
|
|
482638601a | ||
|
|
1bfe518f74 | ||
|
|
eeb5ba40fb | ||
| 33bd912476 | |||
|
|
33f1084f0a | ||
|
|
e8a9b7cae3 | ||
|
|
f6b1d9c493 | ||
|
|
624c34b378 | ||
|
|
bc6753e5bf | ||
|
|
c539debc84 | ||
|
|
830f4cec0f | ||
|
|
03dd9e6e83 | ||
|
|
e8d1c90182 | ||
|
|
0933dc1afa | ||
|
|
610b7fe95c | ||
| a6bf6f901f | |||
| ca2e37e852 | |||
|
|
d1ffaaa327 | ||
|
|
8b7f551505 | ||
|
|
759b3f8fcd | ||
|
|
9ec56f1dfa | ||
|
|
84e997949f | ||
|
|
f82bd45fec | ||
|
|
3863724007 | ||
|
|
7e0ed9fe93 | ||
|
|
945e3f3c5f | ||
|
|
6f100219f7 | ||
|
|
89688394f8 | ||
|
|
091ef6d972 | ||
|
|
e501c44985 | ||
|
|
bc9b761903 | ||
|
|
dfb461b05c | ||
|
|
86f6a2f722 | ||
|
|
77d6d0d5be | ||
|
|
4946909c6d | ||
|
|
2439736cb4 | ||
|
|
8fb2ad1986 | ||
|
|
1d7bbcec66 | ||
|
|
6ac9d34aac | ||
|
|
3369029e9a | ||
|
|
11f411b745 | ||
|
|
9bb6e15900 | ||
|
|
d2a6b37f5f | ||
|
|
0c1f6163f7 | ||
|
|
6860317a58 | ||
|
|
77257b0989 | ||
|
|
8f35da5163 | ||
|
|
82d8c40b5f | ||
|
|
dbfc0f98c3 | ||
|
|
ca15bf4a92 | ||
|
|
3e4d4cc002 | ||
|
|
4257544402 | ||
|
|
83d44bd03e | ||
|
|
745e0685a4 | ||
|
|
d6db2128df | ||
|
|
6e83ed5654 | ||
|
|
a4f44933ec | ||
|
|
fe080e1d90 | ||
|
|
dd462873d2 | ||
|
|
628fec2c92 | ||
|
|
c4700f3cb8 | ||
|
|
fac606c475 | ||
|
|
5a0aadabc1 | ||
|
|
283b9871cb | ||
|
|
f9d88cc2a0 | ||
|
|
c751810b84 | ||
|
|
2dbc9c39b1 | ||
|
|
a9933c7764 | ||
|
|
ffcac3a12d | ||
|
|
a4130e5f78 | ||
|
|
de8f8b7d91 | ||
|
|
b8c58b12fd | ||
|
|
4af355e1f9 | ||
| 0e7e9d5643 | |||
| fafadc3333 | |||
|
|
752c92bb21 | ||
|
|
45d7b284e3 | ||
|
|
085fca7e31 | ||
|
|
8744114790 | ||
|
|
c658a764d0 | ||
|
|
6a07ba850c | ||
|
|
c4e29e74b2 | ||
|
|
40bca8b38c | ||
|
|
18af881fe5 | ||
|
|
dfd97d9fc5 | ||
|
|
ebd2c329ff | ||
|
|
265b169d43 | ||
|
|
d154b1464f | ||
|
|
a32ea6e5f8 | ||
|
|
d7b21bf07e | ||
|
|
d120790da7 | ||
|
|
bd854d29a4 | ||
|
|
d06e3db90b | ||
|
|
5ada533b06 | ||
|
|
6f5e648751 | ||
|
|
9ee5b3eaf3 | ||
|
|
0560da246b | ||
|
|
8b6b3ee3b8 | ||
|
|
45fe83951a | ||
|
|
57e5d8911d | ||
|
|
5768a766b8 | ||
|
|
051e9e38f3 | ||
|
|
63a5954dfa | ||
|
|
897931f273 | ||
|
|
03e80b2f1c | ||
|
|
53c8ec864c | ||
|
|
5d3002f118 | ||
|
|
5eeb52660c | ||
|
|
3dfafaa278 | ||
|
|
462bd9ae5e | ||
|
|
dab0fcc7c0 | ||
|
|
b8a3e8085e | ||
|
|
c9d1d72ba3 | ||
|
|
ccf5d513d2 | ||
|
|
d157b7b05d | ||
|
|
05981ac46c | ||
|
|
08615a5954 | ||
|
|
b6dbe26c7e | ||
|
|
4f0d61b379 | ||
|
|
9e5e494f88 | ||
|
|
b15fd05e8d | ||
|
|
faabc28d1b | ||
|
|
4b815846ee | ||
|
|
fe8be18c4c | ||
|
|
519c30321d | ||
|
|
77be88a5bb | ||
|
|
8d04931f9f | ||
|
|
3d1ee7a43e | ||
|
|
2584a25527 | ||
|
|
0647a82d38 | ||
|
|
c56459c98c | ||
|
|
7b0a4a057d | ||
|
|
3c3e520594 | ||
|
|
ce1daf6a2b | ||
|
|
68314cd44d | ||
|
|
17787b97d4 | ||
|
|
da84297b2c | ||
|
|
e3e3bb770d | ||
|
|
9d4f180741 | ||
|
|
316836d9f6 | ||
|
|
aa3b0a117a | ||
|
|
0f7df3281f | ||
|
|
4a91a35799 | ||
|
|
36c2263675 | ||
|
|
28068d8d31 | ||
|
|
0c3c08db36 | ||
|
|
bb5d62fe69 | ||
|
|
376270dd53 | ||
|
|
9056b23eaa | ||
|
|
634be4ae51 | ||
|
|
422620f4fe | ||
|
|
ebb5c17be4 | ||
|
|
1e99fe5b04 | ||
|
|
66f82f764d | ||
|
|
08c1671f21 | ||
|
|
3b95e56418 | ||
|
|
ce5fcaf172 |
364 changed files with 27866 additions and 5892 deletions
42
.clang-format
Normal file
42
.clang-format
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignArrayOfStructures: Left
|
||||
AlignEscapedNewlines: Left
|
||||
AllowAllArgumentsOnNextLine: 'true'
|
||||
AllowAllConstructorInitializersOnNextLine: 'false'
|
||||
AllowAllParametersOfDeclarationOnNextLine: 'true'
|
||||
AllowShortBlocksOnASingleLine: 'true'
|
||||
AllowShortCaseLabelsOnASingleLine: 'false'
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLambdasOnASingleLine: None
|
||||
AllowShortLoopsOnASingleLine: 'false'
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: 'true'
|
||||
AlwaysBreakTemplateDeclarations: 'Yes'
|
||||
BinPackArguments: 'false'
|
||||
BinPackParameters: 'false'
|
||||
BreakConstructorInitializers: 'AfterColon'
|
||||
BreakBeforeBraces: Allman
|
||||
BreakInheritanceList: AfterColon
|
||||
BreakStringLiterals: 'true'
|
||||
IndentCaseLabels: 'true'
|
||||
IndentWidth: '4'
|
||||
ColumnLimit: '80'
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
|
||||
IncludeBlocks: Preserve
|
||||
IndentWrappedFunctionNames: 'false'
|
||||
KeepEmptyLinesAtTheStartOfBlocks: 'true'
|
||||
PointerAlignment: Left
|
||||
ReflowComments: 'true'
|
||||
SortIncludes: 'false'
|
||||
SortUsingDeclarations: 'true'
|
||||
SpaceAfterTemplateKeyword: 'true'
|
||||
SpaceBeforeAssignmentOperators: 'true'
|
||||
SpaceBeforeCtorInitializerColon: 'false'
|
||||
SpaceBeforeInheritanceColon: 'true'
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: 'false'
|
||||
...
|
||||
15
.github/workflows/ccpp.yml
vendored
15
.github/workflows/ccpp.yml
vendored
|
|
@ -10,7 +10,7 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 1
|
||||
- name: apt
|
||||
run: sudo apt update && sudo apt install libusb-1.0-0-dev libsqlite3-dev ninja-build protobuf-compiler
|
||||
run: sudo apt update && sudo apt install libudev-dev libsqlite3-dev ninja-build protobuf-compiler libwxgtk3.0-gtk3-dev
|
||||
- name: make
|
||||
run: make
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 1
|
||||
- name: brew
|
||||
run: brew install sqlite pkg-config libusb ninja protobuf truncate
|
||||
run: brew install sqlite pkg-config libusb ninja protobuf truncate wxwidgets
|
||||
- name: make
|
||||
run: make
|
||||
|
||||
|
|
@ -45,6 +45,8 @@ jobs:
|
|||
zip
|
||||
mingw-w64-i686-protobuf
|
||||
vim
|
||||
diffutils
|
||||
mingw-w64-i686-wxWidgets
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
|
@ -52,3 +54,12 @@ jobs:
|
|||
run: |
|
||||
make
|
||||
|
||||
- name: zip
|
||||
run: |
|
||||
zip -9 fluxengine.zip fluxengine.exe fluxengine-debug.exe fluxengine-gui.exe fluxengine-gui-debug.exe upgrade-flux-file.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ github.event.repository.name }}.${{ github.sha }}
|
||||
path: fluxengine.zip
|
||||
|
|
|
|||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
|
@ -26,6 +26,8 @@ jobs:
|
|||
zip
|
||||
mingw-w64-i686-protobuf
|
||||
vim
|
||||
diffutils
|
||||
mingw-w64-i686-wxWidgets
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
|
@ -34,7 +36,7 @@ jobs:
|
|||
make
|
||||
- name: zip
|
||||
run: |
|
||||
zip -9 fluxengine.zip fluxengine.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
|
||||
zip -9 fluxengine.zip fluxengine.exe fluxengine-gui.exe upgrade-flux-file.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
|
||||
- name: date
|
||||
run: |
|
||||
echo "RELEASE_DATE=$(date --rfc-3339=date)" >> ${GITHUB_ENV}
|
||||
|
|
|
|||
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -1,2 +1,9 @@
|
|||
.obj
|
||||
.project
|
||||
/.ninja*
|
||||
/brother120tool
|
||||
/brother240tool
|
||||
/fluxengine
|
||||
/brother120tool-*
|
||||
/brother240tool-*
|
||||
/fluxengine-*
|
||||
|
|
|
|||
|
|
@ -1,249 +1,249 @@
|
|||
:4000000000800020110000004110000041100000064A08B5136843F020031360044B1A6803F53F5302331A6001F028F8E8460040FA46004010B5054C237833B9044B13B163
|
||||
:400040000448AFF300800123237010BD6881FF1F00000000C8380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF0000000041
|
||||
:400080006C81FF1FC8380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8881FF1FBC
|
||||
:4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8881FF1F3F000080114BDA68196919B9FD
|
||||
:4001000001221A75597514E09969521A19698A4294BF587D00201875187D094908B1002204E0086982428CBF002201224A75DA689A611B7D13B1002002F046B9704700BF70
|
||||
:400140008881FF1F10B5C4B2204601F059F90128FAD110BD08B572B60F4B0F49DA680132DA60DA690132C82A08BF0022DA611A6AD8690132A72A08BF00220A621B6A002B13
|
||||
:400180000CBF02230023002814BF184643F0010002F082FE62B608BD8881FF1F38B50446C5B2284602F0B2F8062002F0CFFA44F00200C0B202F0AAF8062002F0C7FA28460D
|
||||
:4001C00002F0A4F8BDE83840062002F0A9BA10B5642402F095F828B9FFF7E0FF013CF8D1204610BD012010BD70B5C4B2054620460E4601F005F9012805D0204601F01EFA34
|
||||
:400200002846FFF79FFF204601F002F9314605460246204601F0BEF9204601F0F1F80028FAD1284670BD000038B5044D0024285D013402F03BFA402CF9D138BDAC81FF1FCB
|
||||
:4002400008B502F055FC002002F05EFC02F070FC02F07AFC80B208BD10B50446012002F06DF8642002F05CFAFFF7EAFF2080002002F064F8642002F053FAFFF7E1FF60809C
|
||||
:4002800010BD08B502F060FD002002F069FD02F07BFD02F085FD80B208BD10B50446FFF796FF322002F03CFAFFF7EBFF20800120FFF774FF322002F033FAFFF7E2FF6080D2
|
||||
:4002C00010BD0FB400B593B014AB53F8042B402102A8019302F0E0FE02A802F07CF802F086F813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF723FF627816
|
||||
:400300002146BDE81040042001F0D2B8DC38000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8643043B1002001F0DAFFEE
|
||||
:40034000002002F06DFD002384F8643010BD00BF8881FF1F38B5104D837895F8672004469A4204D0FFF7E4FF002385F86A302368C5F865302279094B1A71A378002B14BF45
|
||||
:400380000220012002F04CFDE07802F043FD2079BDE8384002F07ABD8881FF1FED81FF1F38B50D4C94F8645065B904F16500FFF7D1FF012001F09EFF4FF47A7002F0B0F968
|
||||
:4003C00084F86A50E368E366012384F86430BDE8384002F0E3B900BF8881FF1FF8B5214C0546FFF7DDFF94F86A3003B15DB91E48FFF767FFFFF7EBFE0120002384F86A007C
|
||||
:40040000236702F0A3F92A46216F1848FFF759FF144E0027236F9D4216D001F071FF00B13767236F9D4205DD0120FFF7B7FE336F013305E005DA0020FFF7B0FE336F013B0B
|
||||
:40044000336702F0ABF9E5E7322002F069F92A2DCCBF0020012002F025FDBDE8F8400448FFF72FBF8881FF1FE9380000F03800000D3900002DE9F04F99B062B602F0F8F947
|
||||
:400480009E49042002F01CFA9D4801F045FF9D4802F0E8FC9C4801F079FF02F0C9FB02F09BFA002002F0BCFC01F094FF0221002000F05CFF954C012001F0D4F8002384F890
|
||||
:4004C0006730FFF76DFFFFF782FE84F87400FFF72FFF012384F86730FFF762FFFFF777FE84F87500FFF724FF894B94F87400894994F875202546002A14BF0A461A4600286F
|
||||
:4005000008BF19468448FFF7DCFE0321084602F025F9264602F042F994F8643043B1EA6EEB689B1A41F28832934201D9FFF700FF00F054FF18B97948FFF7C3FE04E000F079
|
||||
:4005400053FF0028F7D10BE000F048FF10B902F025F9F9E77248FFF7B4FE032001F06EF8032000F04DFF0128D4D16E48FFF7F2FE6D490320FFF738FE94F876106B48FFF7F9
|
||||
:40058000A0FE94F87630023B142B00F2D683DFE813F01500D4031E00D4032400D4035000D4037600D403D900D403C101D4030803D4032C03D4033303D4034D0303238DF851
|
||||
:4005C00020308DF821300F238DF822302AE394F87800FFF703FF564B21E340F2DC57FFF7DFFE00232375E068227D02F0FF012AB9EB681B1ABB42F7DD0B4611E083B100227E
|
||||
:40060000174696F87810F068277594F814E0BEF1000F02D1EB681B1AF7E701329142F3DA07228DF8202004228DF82120ADF82230F8E20220FFF786FD4FF000080DF1200A54
|
||||
:4006400002F0ACF84FF480790027C9EB0803DA1907F80A200137402FF9D10220FFF772FD3A465146022000F023FFB9F10109EBD108F10108B8F1400FE2D12E4B38E04FF076
|
||||
:40068000010A4FF000080DF1200B02F087F84FF0000959460120FFF7A7FD08EB090300270493049B1BF807203B44DBB29A4209D08DE80C0041463B464A461F48FFF701FEE0
|
||||
:4006C0004FF0000A0137402FEBD109F10109B9F5807FDED108F10108B8F1400FD5D151461648FFF7EEFDBAF1000F00F01B81144B1B8807A8ADF81C3095E200BF5501000004
|
||||
:40070000F900000091000000C50000008881FF1F1F3900001B390000223900003A3900004D390000ED81FF1FFE81FF1F57390000CC380000CE380000663900008239000026
|
||||
:40074000D0380000206FFFF749FE94F8780001F0F5FD94F8780001F0D9FD02F009FCB94BDFF8FC821A78002702F0FB021A701A7842F001021A701A7802F0FE021A701A7880
|
||||
:4007800002F0FE021A7002F0F7FB0220FFF7DAFC012141F6FF734FF48042084602F046FB84F8B60001F068FF08F807000137402FF8D1DFF8B0A200270AF195091FFA89F816
|
||||
:4007C0000137402F14BF3A4600221AF8010F2244062392F82420402101F082FF424646F240419AF8000001F08DFF08F14008402F1FFA88F8E4D196F8793053B196F87C30C8
|
||||
:40080000336100233375237D002BFCD000233375336100234FF0FF32236062602372236894F8B600234493F8241001F0DDFE94F8B60001F09BFE012194F8B60001F06EFEAA
|
||||
:400840002368002BFCD0002398467360D6F80CA0012701F0A3FFE368B4F87A20CAEB030393420DD367B1042195F8B60001F0C8FE94F8B60001F0D4FE0028F9D107463072CF
|
||||
:40088000237AFBB96A682B689A4202D1002FE0D118E00220FFF756FC6968402209EB8111022000F005FE6A68674B01321340002BBEBF03F1FF3363F03F03013308F1010820
|
||||
:4008C0006360C6E70220277AFFF73CFC00221146022000F0EDFD0220FFF734FCFFB2FFF7A3FC002001F012FD37B15848FFF7E9FC0220FFF70DFD06E0554B08A81B88ADF871
|
||||
:400900002030FFF7F3FC227D4146237A5148FFF7D8FC15E25048FFF7D4FCD4F87A7017F03F0701D0032009E2286FFFF757FD95F8780001F003FD95F8780001F0E7FC012054
|
||||
:4009400001F002FD02F014FB444BDFF814811A7842F004021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F003FB01214FF4804341F6FF72084601F0CB
|
||||
:40098000E9FC85F8B60001F077FE08F807000137402FF8D1DFF8CC90002709F195031FFA83F804930137402F14BF3A46002219F8010F2244052392F82420402101F090FEB2
|
||||
:4009C000414646F2475299F8000001F09BFE08F14008402F1FFA88F8E4D100274FF0FF33376098467360BB463B46D6F87A9037725FEA99190CBF4FF0010A4FF0000A2168C1
|
||||
:400A0000114A01310A40002ABCBF02F1FF3262F03F026068B8BF013282426FD02BB1227A002A7AD12A7D002A77D12068049A059302EB8010BAF1000F16D040223F2102F079
|
||||
:400A4000F7FA1CE0906400403F0000808C390000D2380000A6390000B939000097650040AC81FF1FAB81FF1F014601370120FFF7BBFBC7EB0903D3F1000A4AEB030A216882
|
||||
:400A8000B34A01310A40002ABEBF02F1FF3262F03F02013222606268059B01322ED12A683F2A2BD14FF00008C5F8048001F080FC85F808806B6895F8B6002B4493F82410D9
|
||||
:400AC00001F092FD95F8B60001F050FD012195F8B60001F023FD95F87E302B6185F81480237D002BFCD04FF00008012086F8148001F06AFC404601F027FC00E023B1237A54
|
||||
:400B00005BB92B7D4BB90123626842453FF477AF0BF1010BD5F8048071E701F04FFC012001F012FC002001F04FFC042194F8B60001F066FD94F8B60001F072FD8046002873
|
||||
:400B4000F8D196F8B60001F0FFFC337D327A0293012303920193CDF800A05B463A4649467C48FFF7AEFBC6F81080BAF1000F0BD0FFF75AFB002001F0C9FB237A63B1764854
|
||||
:400B8000FFF79FFB0220D9E0B945F1D073490120FFF72AFB0137F7E77148FFF792FB714B3DE094F8780001F0C9FB206FFFF716FC6D48FFF786FB94F87930236100232375F8
|
||||
:400BC000237D002BFCD0012001F0FEFB00233375237D002BFCD0002001F0F6FB002363483361FFF76EFB624B19E0002084F86A00FFF7F4FB5F4B12E094F8743023B195F870
|
||||
:400C000075200AB985F8782094F875201AB113B9012385F878305848FFF79CFB574B1B88ADF8203008A8FFF761FB89E0FFF780FB02F07CF8002002F01FF82A2701F04AFFE9
|
||||
:400C4000002001F0EDFE3A46002108A802F0F0F917238DF820308DF8217001F09FFD002001F048FB002002F0DBF8C82001F058FD0DF12200FFF7F0FA0DF13600FFF70DFB01
|
||||
:400C800001F08CFD012002F0CBF8322001F048FD0DF12600FFF7E0FA0DF13A00FFF7FDFA012001F027FB4FF4967001F039FD01F075FD0DF12E00FFF7CFFA0DF14200FFF71B
|
||||
:400CC000ECFA002001F016FB4FF4967001F028FD01F064FD022002F0A3F8322001F020FD0DEB0700FFF7B8FA0DF13E00FFF7D5FA012001F0FFFA4FF4967001F011FD01F040
|
||||
:400D00004DFD0DF13200FFF7A7FA0DF14600FFF7C4FA002001F0EEFA4FF4967001F000FD01F03CFD002002F07BF8002384F86A3001F07EFF01F050FE74E70120FFF7E8FA91
|
||||
:400D4000032000F07BFC0E48FFF7BBFAFFF7E2BB3F000080C3390000F33900004092FF1FFD390000D4380000053A0000133A0000D6380000D8380000FE81FF1FDA380000E3
|
||||
:400D8000203A00002DE9F04172B6884B61221A70A3F5F06301221A801924854A9C7092E803008033062283F8002283E80300522203F580731A707F4B7F4A1B787F4EDBB2FE
|
||||
:400DC000137040F618027E4B00251A8041F2512223F8022C33784FF4F07003F0010343EA450502F0BDF8013C05F003052ED0032DF0D1744B4FF480721A8007221A70724A20
|
||||
:400E0000002548211570917002221D705D7103F8032C0422DA716D4A6D4C13786D4E43F00103137012F8013C062743F0030302F8013C2378012243F0800323705B4B1A70F9
|
||||
:400E4000654A137843F02003137000E0FEE707FB056300219A881868013502F0E9F8072DF5D15E485E4E002550F8041F05F1105303F1480221F0FF074933C9B20B44520042
|
||||
:400E80005B0002329A4206D012F802EC12F801CC0EF807C0F5E7B0420D44E5D1514A002313609360136193614F4B504F1A68504BDFF888811A604F4B1A684F4B1A604F4A7B
|
||||
:400EC000137843F002031370137C43F0020313742378A2F5863243F040032370413A137843F010031370464A464B07CA03C31A80454A2833106843F8250C127903F8212CA9
|
||||
:400F0000424A07CA03C31A80414AE83B07CA03C31A80404A083307CA03C31A803E4A3F4BA2F5616203CBC2F8100EC2F8141E1378042043F008031370394B02F5AA521B78A7
|
||||
:400F40003D78DBB298F80060EDB203F007010C321B091170F6B2537045F003033B7046F0030388F800302F4B48221A702E4A402313702E49937013729372082382F81F32BF
|
||||
:400F800020220A7048710A72294A0A20137001F0DDFB284B88F8006044223D70264D1A7094E80F0007C52B80BDE8F081004800404C0A00480F010049A1460040254200408F
|
||||
:400FC000224200400440004006400040A2430040A0430040253A0000E8460040FCFFFF478C00004800760040540A0048F846004020760040580A004828760040035001404D
|
||||
:401000000C0A0048C0510040180A0048200A00482C0A0048380A004832510040440A0048CF0100491D51004001590040235B0040585B004076580040B0430040F9460040F2
|
||||
:4010400008B501F0C9FF03680C2B00D1FEE7FEE7084908B50B68084A1844821A802A01DC086005E001F0B8FF0C2303604FF0FF33184608BDCC80FF1F9093FF1F80B5114817
|
||||
:40108000114B0025C0B1A3F1100192C922460439161BB74204D051F8046F42F8046BF7E7114653F8046C8C1AA64202D041F8045BF9E701381033E5E701F094FFFFF7DAF9D9
|
||||
:4010C000FEE700BF01000000F43B0000124A134B10B51A60124A134C1368134843F4007313600023032B98BF54F823204FEA830188BF0E4A0133302B4250F3D10C4B1A7814
|
||||
:401100000C4B1A700C4B084A1A60FFF73BFEBDE8104001F0EDB900BF0004FA050CED00E014ED00E0000000000080FF1F41100000BC760040C080FF1F08ED00E0F8B501F042
|
||||
:4011400017FF4B4A01271378022643F001031370137C484C43F001031374474B02F5E3521F700B3203F8946C1378054603F07F031370002001F0EAFA2378404A03F0F903F2
|
||||
:4011800023701378384603F0DF03137023783B43237001F0DBFA282001F0D8FA384B30461A7802F07F021A701A7802F0BF021A7023783343237001F0C9FA2378314A43F0C6
|
||||
:4011C000040323700023137053702F4AFF2199540133092BFBD1284601F0CEFE0721172001F0FCFA2949172001F0EAFA0721182001F0F4FA2649182001F0E2FA0721152033
|
||||
:4012000001F0ECFA2349152001F0DAFA0721052001F0E4FA2049052001F0D2FA0721062001F0DCFA1D49062001F0CAFA0721084601F0D4FA1A49072001F0C2FA07210820F8
|
||||
:4012400001F0CCFA1749082001F0BAFA0021162001F0C4FA1449162001F0B2FA07210C2001F0BCFABDE8F84010490C2001F0A8BAA5430040944300409D6000401260004076
|
||||
:40128000F851004084600040B592FF1F0B1B000045190000091B00003D1A0000691A0000991A0000D11A0000111B0000851B0000214B224A10B5187000231370204A40209B
|
||||
:4012C0001370204A0F2413701F4A13701F4A13701F4A13701F4A13701F4B4FF400021A604FF080721A604FF400121A6020221A601860802018604FF480701860174804702E
|
||||
:401300004FF480001860164B1A70933B19B91A7802F0FE0202E01A7842F001021A70114B03221A70802203F8202C012001F018FE0D4B04221A7010BDD092FF1FD692FF1F39
|
||||
:40134000D492FF1FD592FF1FD192FF1FC092FF1FD392FF1F4893FF1F00E100E09E6000409C600040286000401260004070B5074C054623780E461BB9FFF7E0FE0123237031
|
||||
:4013800031462846BDE87040FFF792BF8092FF1F0A4A002313700A4A13700A4A13700A4A13700A4A13700A4A13700A4A13700A4B03221A70802203F8202C7047D692FF1F4E
|
||||
:4013C000D492FF1FD592FF1FD192FF1FC092FF1FD392FF1F4893FF1F28600040014B1878704700BFD592FF1F044B1A7802F0FF001AB118780022C0B21A707047D492FF1F52
|
||||
:40140000024A0C2303FB002040787047DC92FF1F431E072B0CD8074A064B00010344805C5B7800F00F0043EA0020023880B2704700207047FC5F00401A4A38B50C2303FBA9
|
||||
:4014400000231B79090C13F0800F00F1FF35044619BF8AB24FF480438BB24FF48042032D18D8DFE805F002070C110021084601F01BF80DE00021084600F0FAFF08E0002180
|
||||
:40148000084600F0D9FF03E00021084600F0B8FF054B1855EDB2072D03D801F0EDF8034B185538BDDC92FF1FAC92FF1FB592FF1F431E072B2DE9F0470446894615465CD857
|
||||
:4014C0002F4F0C2202FB0072D388DFF8B8A09BB2C3F500739D424FF00C0303FB007388BFD588DB7884BFC5F50075ADB2254A43EA15230601B354B244EBB28AF80130224BD4
|
||||
:401500001A5C9846FF2A01D1FFF796FF0C2303FB047200215170B9F1000F28D03DB31B4F385D01F011F811232946FE2218F8040001F0D6F806F5C04278321FFA89F118F8D2
|
||||
:40154000040001F0DFF8124D18F80410385D01F04BF80121385D00F0E1FF735D43F002037355735D03F0FD037355BDE8F08703FB04746379DBB28AF80230BDE8F08700BFE7
|
||||
:40158000DC92FF1FFC5F0040B592FF1FAC92FF1F706000402DE9F047044615468846002940D0431E072B3FD8FFF732FFA84203D22046FFF72DFF05461D4E335DFF2B03D1DE
|
||||
:4015C00041462046FFF738FFDFF868A027011AF8040000F0B9FF1223FE222946305D01F07FF807F5C0411FFA88F27831305D01F089F8DFF84490315D1AF8040000F0F4FFE9
|
||||
:4016000001211AF8040000F089FF17F8093043F0020307F8093017F8093003F0FD0307F8093002E00D4600E000252846BDE8F087B592FF1FAC92FF1F70600040431E072BA7
|
||||
:401640000AD8064A0C2303FB002300225A705A79034BD2B200011A54704700BFDC92FF1FFE5F0040431E072B9FBF024B000108221A547047FE5F004030B51A4A1A491B4D0A
|
||||
:401680000878138803449BB21380194A00231488D8B2A4B27CB1082B0CD050680078C0B2E85450680133013050601088013880B21080ECE718460B780E4C082B0E4A00D003
|
||||
:4016C00040B10E4D2B7883F080032B700F232370022301E0022323701370094B1870087030BD00BF4C93FF1F4893FF1F00600040C492FF1FC192FF1FD692FF1FD292FF1FE1
|
||||
:401700004993FF1F074B02221A70074B80221A70064B0F221A70064A00231370054A012013707047D692FF1FD292FF1FC192FF1F4893FF1F4993FF1F30B5164B16491B78E1
|
||||
:401740000A8803F00F03023BDBB21A4492B20A80124C134A0020118889B279B173B15568215C013BC9B229705168DBB20131516011880130013989B21180ECE7094A1370B3
|
||||
:40178000094A137883F080031370084B0B221A7030BD00BF296000404C93FF1F00600040C492FF1F4993FF1FD292FF1FC192FF1F064A06231370064A01201370054B802273
|
||||
:4017C0001A70054B00221A70704700BFD692FF1FC192FF1FD292FF1F4993FF1F054B9A683AB19A68044910709A680988518000229A607047C492FF1F4C93FF1F08B5124BAC
|
||||
:401800001A78D2B21A701B78DBB21A0602D50F4A137008BD0220FFF7E1FF0D4B1B7803F06003202B05D0402B06D043B900F012FC04E001F0A5FB01E000F046FD10B9034B1C
|
||||
:4018400003221A7008BD00BF28600040C192FF1F0060004008B5084A084B0120197813880B449BB21380064B00221A70FFF7B6FF044B03221A7008BD4C93FF1F4893FF1F1D
|
||||
:40188000D692FF1FC192FF1F08B50C4B1B78DBB2042B07D0062B09D0022B0DD1BDE80840FFF7D8BFBDE80840FFF746BF0320FFF795FF034B03221A7008BD00BFD692FF1FCC
|
||||
:4018C000C192FF1F08B5054B002201201A70FFF785FF034B03221A7008BD00BFD692FF1FC192FF1F08B50A4B1A7832B11A78094942F080020A7000221A70074B00220120D1
|
||||
:401900001A70FFF76BFF054B03221A7008BD00BFC092FF1F08600040D692FF1FC192FF1F074B1B78DBB2042B05D0062B05D0022B05D1FFF7A1BEFFF7C5BFFFF7D3BF70479E
|
||||
:40194000D692FF1F38B51D4C2378DBB2DD0634D518060AD503F00F03012B2ED1FFF74EFF174B1B78190609D538BD5A0602D5FFF7D7FF03E09D0620D5FFF786FF23781B066B
|
||||
:401980001BD4104B1A78104B1B7813430F4A13701278934211D10A4A0849154613782078DBB2000605D41378DBB20B700B7803F00F0328788342F1D138BD38BD2860004067
|
||||
:4019C000C192FF1FD292FF1F4993FF1F29600040054A00231380054A916819B191680B7092685380704700BF4C93FF1FC492FF1F0E4808B503889BB213B9FFF783FE13E0D1
|
||||
:401A00000B4B02221A700B4B00221A70FFF7E0FF094AD1799379028843EA012392B2934238BF0380FFF728FE012008BDC492FF1FD692FF1FD292FF1F00600040084B012220
|
||||
:401A40001A700F3B9B7C074B1A7B02F00302012A1EBFDA7B82F08002DA7301225A7370470B600040DC92FF1F094B02221A700F3B93F82230074B1A7E02F00302012A1EBF0F
|
||||
:401A8000DA7E82F08002DA7601225A76704700BF0B600040DC92FF1F0B4B04221A700F3B93F83230094B93F8242002F00302012A1EBF93F8272082F0800283F827200122E0
|
||||
:401AC00083F82520704700BF0B600040DC92FF1F0B4B08221A700F3B93F84230094B93F8302002F00302012A1EBF93F8332082F0800283F83320012283F83120704700BFF5
|
||||
:401B00000B600040DC92FF1F7047FFF741BC0000F0B5184B184E19780C27C9B201234FF0000C31B3CA0720D5144A4FEA031E7244947850782040C5070DD507FB03652C799F
|
||||
:401B4000240608D5147804F0FE0414706D790C4CEDB204F80E50840706D507FB036425792D0658BF84F801C090700133DBB24908D7E7F0BD9F600040DC92FF1F70600040D5
|
||||
:401B8000FE5F004000F0ACBC70B50446184B88B003AA03F11006154618685968083303C5B3422A46F7D11B782B70FCB12223237001AD03232846637000F08AFE00222046DB
|
||||
:401BC0001146AB5C08AC04EB131414F8144C03F00F03847008AC234413F8143C0132082AC1700371417100F10400EAD108B070BD4F3A00002DE9F0431C4D01222E460C2093
|
||||
:401C00001F274FF0800E4FF0080C194B00FB02581401234418705F70164998F805902144B9F1000F07D098F8044024064CBF887081F802C001E081F802E000FB0261CC880F
|
||||
:401C40000132E4B29C71CC88092AC4F30724DC71CC88E4B21C71C988C1F307215971D4D1054BFF221A70BDE8F08300BFDC92FF1F70600040FC5F00400A600040064B074A70
|
||||
:401C80001B7802EBC30253681A7C824286BF03EBC003586900207047D092FF1FB03A00002DE9F84F424B1A78002A7ED01878414D0138C0B2FFF7E2FFA8463F4AC368147810
|
||||
:401CC000007ADFF800C1E4B203EBC0000C2600274FF0010E834268D01A78A24263D11CF80420597891425ED19A7893F8039002F07F0206FB02FA05EB0A01CF7093F802B01E
|
||||
:401D000009F0030981F804B093F803B005F80AB0B3F804A0A1F808A093F902A0BAF1000F0BDAB9F1010F0CBF4FF007094FF00D0981F8059081F801E009E0B9F1010F0CBF20
|
||||
:401D40004FF005094FF0090981F805904F704FEA02191A4906FB0282494481F802E0B2F808A0CAF3072A81F800A0B2F808A05FFA8AFA81F801A0B2F806A011495FFA8AFAAB
|
||||
:401D8000494481F806A0B2F80690C9F3072981F80790B2F806905FFA89F981F80490D288C2F307224A71083394E7BDE8F88F00BFD592FF1FDC92FF1FD192FF1FFC5F004052
|
||||
:401DC00070600040C292FF1F08B5064B18780138C0B2FFF753FF20B143681B7900EBC300406908BDD592FF1F00212DE9F84F0B464E4E0C2707FB01F4013132190929335553
|
||||
:401E00004FF000059370494CD3701381937253705371EFD118B1464B1D70464B1D70464B1A78002A7FD0187801250138C0B2FFF725FFA8464368DFF8F8E0DB790C2713F0B5
|
||||
:401E4000400F3E4B4FF0000C1A7814BF42F0010202F0FE021A70027AD20007FB0541C36803EB02094B4531D093F802A00AF07F06AE4229D10E89B3F804B0B6B25E4538BF50
|
||||
:401E8000A1F808B01E7893F801B01EF80660B3451AD181F804A0DE780E7093F902A0DE78BAF1000F06F0030607DA012E0CBF07260D264E7181F8018006E0012E0CBF052673
|
||||
:401EC00009264E7181F801C00833CBE70135092DC3D1C1680A328B1C0A440C200833934209D013F8081C13F80A5C01F07F0100FB01418D72F2E7FFF767FF114B01211860E6
|
||||
:401F000000230C2000FB0142D3801289013113449BB203F00102134409299BB2F2D1BDE8F84FFFF767BEBDE8F88F00BFDC92FF1FC292FF1F4A93FF1FD592FF1FD392FF1FCE
|
||||
:401F4000D892FF1F114B1B7903F07F035A1E072A19D80F490C2202FB031291781B0141F0010191700021D170517841F002015170127912F0800F074A1A4414BF8D238923CF
|
||||
:401F80009370FFF715BC0020704700BF00600040DC92FF1FFC5F004030B4194B1A7902F07F02531E072B27D8164B0C2404FB02339978154D01F0FE0199700021D970294600
|
||||
:401FC0001201505D114400F07F0050555A7802F0FD025A701A795B78120605D5012B01D18C7006E00D2303E0012B0CBF082309238B7030BCFFF7DCBB002030BC704700BF9C
|
||||
:4020000000600040DC92FF1FFC5F004010B50D4B0D4C21791878C9B20138C0B2FFF72EFE43681B798B4201D2012909D8074A0848535CDBB24354A3780120DBB2535410BD56
|
||||
:40204000002010BDD592FF1F00600040C292FF1F4A93FF1F38B58A4A8A4C13780021DBB221801806517840F18D800A2900F20581DFE811F05D000301030103010301030167
|
||||
:402080000B0003017E0003018200D3787C49012B09D17D4B1A787D4B03EBC2035B685B686360122310E0CB78022B12D18878FFF7E5FD002800F0E180436863606368DA7885
|
||||
:4020C00063689B7843EA02232380BDE83840FFF78FBCCB78032B26D16D4B00228878D5B2854209D3664A91786A4AEE2908BF1346634A917881B106E0187801320028F1D025
|
||||
:4021000018780344EAE764499278097C914203D16248FFF739FD614B1A78002A00F0AD801A78228018E0BDE8384000F029BF13F0030313D0022B40F0A0802380504B0C21C4
|
||||
:402140001B7903F07F02564B01FB02339A78554BD2B21A7000225A706360B6E702222280514A11784F4AC9B2117053706260ACE7012323804D4BEFE70123238013794C4AC4
|
||||
:402180001344E9E701390A2977D8DFE801F037764F76067676760A7620009378454ADBB25AE0937803F0FF0153B9404B1A7891425FD01970404B01201870FFF715FE58E082
|
||||
:4021C000481EC0B2FFF75AFD0028EED155E0FFF71DFF002851D02A4A384913791279DBB2D2B20A70364A3249D25CCB5C9A4240D0314B01221A70FFF753FD3AE003F00303EE
|
||||
:40220000012B2BD009D3022B37D11D4B9B78002B33D1BDE83840FFF7BFBE194B9B78012B2BD1214A137803F0FD0315E003F00303012B13D008D3022B1FD1114B9B78E3B9A4
|
||||
:40224000BDE83840FFF77EBE0D4B9B78012B14D1154A137843F0020313700AE0084B1A795AB998781B791749DBB2CA5C22EA0002CA54BDE83840FFF79BBA002038BD00BFEC
|
||||
:4022800000600040C492FF1FD092FF1FB03A0000143B00009C3A0000873B00006893FF1FDC92FF1F8192FF1FD392FF1FD592FF1FC292FF1FC092FF1FD492FF1FD192FF1FF4
|
||||
:4022C0004A93FF1FD792FF1F074B1A78120609D55B78012B06D1054B054A5A6012781A80FFF786BB0020704700600040C492FF1F743A0000014B1870704700BF76650040FA
|
||||
:40230000014B1878704700BF67640040014B1870704700BF77640040064A0123136002F688321268E0211064034A1170A2F540721360704780E100E000E400E0014B187039
|
||||
:40234000704700BF74640040014B1870704700BF7565004073B515461E460B4C05230022019200920A4601461846237000F064F932462946207800F01FF90221207800F0B7
|
||||
:4023800009F9207802B070BDD080FF1F064A0423136002F688321268E0219064034A1170A2F202321360704780E100E002E400E0014B04221A60704700E100E0014B04228E
|
||||
:4023C0001A60704780E100E0014B1870704700BF74650040704738B505460078012428B100F066FD285D0134E4B2F8E738BD08B50D2000F05DFDBDE808400A2000F058BDDC
|
||||
:40240000F7B516461F460B4C00230325019300930A4601462846257000F00EF93A463146207800F0C9F80221207800F0B3F8207803B0F0BDE080FF1FF7B516461F460B4C05
|
||||
:4024400000230225019300930A4601462846257000F0F2F83A463146207800F0ADF82946207800F097F8207803B0F0BDE180FF1FF7B516461F460B4C002301250193009322
|
||||
:402480000A4601462846257000F0D6F83A463146207800F091F80221207800F07BF8207803B0F0BDE280FF1F73B515461E460B4C0023019300930A4601461846237000F026
|
||||
:4024C000BBF832462946207800F076F80221207800F060F8207802B070BD00BFE380FF1F024B1878C0F38010704700BF8F450040074A7F23802113705170064A013BDBB2F7
|
||||
:4025000002F80839002BF9D1034A1370704700BFE480FF1FF87B00400078004017280FD8084B0001C25C11B142F0200201E002F0DF02C254C25C42F00102C2540020704780
|
||||
:40254000012070471070004017280BD8064B0001C25C02F0FE02C254C25C02F0DF02C25400207047012070471070004017280DD8074900010B4603441A7942F004021A7150
|
||||
:40258000435C43F00103435400207047012070471070004017280BD8064A0001835C490003F0F10301F00E011943815400207047012070471070004041F6FF73994208BFD2
|
||||
:4025C0004FF400519A4208BF4FF4005217289FBFC00000F1804000F5EC4081809ABFC280002001207047000017289FBF034B00011954002088BF0120704700BF197000402C
|
||||
:4026000017289FBF054B00011A5C01F007019DBF1143195400200120704700BF1470004017289FBF034B0001185C00F0070088BFFF20704714700040172810B51AD8C000B7
|
||||
:4026400001F07F0100F1804441EAC21204F5EC44D2B222709DF8082003F00F0343EA0213DBB263709DF80C30002003F00F03A370E07010BD012010BD10B500F079FC0A4A00
|
||||
:402680005378182B0AD91478013B5370E30003F1804303F5F0431B78137000E0FF2400F06BFC204610BD00BFE480FF1F030610B5044611D400F05CFC084AE300117803F1FE
|
||||
:4026C000804303F5F04319705378147001335370BDE8104000F050BC10BD00BFE480FF1F30B504060CD411F4704509D1C40004F1804404F5F0442180A270E370284630BDB0
|
||||
:40270000012030BD03065FBFC00000F1804000F5F04081805ABFC280002001207047000038B50446084DB4F5004F05D9286800F017FCA4F50044F6E7034B58686043BDE875
|
||||
:40274000384000F00DBC00BFEC80FF1F024B1B7A584300F005BC00BFEC80FF1F0E4B00F003001A78490102F0FC02104318701A7801F0600142F080021A701A7802F07F024E
|
||||
:402780001A701A7802F09F020A431A701A7842F010021A70704700BF83430040014B01221A70704784430040044B00F00F021B6853F8220043F82210704700BF08ED00E008
|
||||
:4027C000054A00F01F00126800F1100352F8230042F82310704700BF08ED00E000F01F0000F16040490100F56440C9B2017070470F4B10B50F4900240F205C609C60DC60F4
|
||||
:402800001C615C61FFF7D0FF0B4A136843F0040313600A4B4FF47A72DB68B3FBF2F3084A1360084B4FF400421C60C3F8E82010BD8492FF1F9D28000010E000E0EC80FF1FC6
|
||||
:4028400014E000E018E000E0024A136843F002031360704710E000E008B5FFF7F5FF034A136843F00103136008BD00BF10E000E010B5054CA3691BB9FFF7BAFF0123A36179
|
||||
:40288000BDE81040FFF7E8BF8492FF1F024B1868C0F30040704700BF10E000E038B5FFF7F5FF012808D1054D002455F8243003B198470134052CF8D138BD00BF8892FF1FAE
|
||||
:4028C000024B03EB80035868596070478492FF1F134B144A1B78DBB20360127843EA0223114A0360127843EA0243104A0360127843EA026303600E4B0E4A1B78DBB2436031
|
||||
:40290000127843EA02230C4A4360127843EA02430A4A4360127843EA02634360704700BF0301004904010049EC46004002010049010100490001004905010049060100490D
|
||||
:4029400010B500F015FB204A044613780A2043F002031370137C43F00203137412F80A3C43F0010302F80A3C937943F00103937102F5AB52137843F003031370134B18223F
|
||||
:402980001A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222183B1A70094A137843F008031370FFF7CAFE064B10222046BDE8DE
|
||||
:4029C00010401A6000F0D8BAAB4300400E5900402F5B004080E200E008B500F0C9FA0F4A137803F0FE031370A2F5AA521D3A137803F0FD031370137C03F0FD03137412F8E1
|
||||
:402A00000A3C03F0FE0302F80A3C937903F0FE039371BDE8084000F0AFBA00BF08590040044A137803F03F0343EA8010C0B21070704700BF08590040082804D00A280CBF9D
|
||||
:402A40008223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B33
|
||||
:402A80001A8070470A590040603A00005293FF1F5493FF1F5893FF1F08B5102000F0A6F907210420FFF79AFE07490420FFF788FE064A0C20137843F006031370FFF7BCFFBA
|
||||
:402AC000034B00221A8008BD912B0000095900405093FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF72ABFA092FF1F044B1A7802F0FB021A701A7842F0010256
|
||||
:402B00001A7070470859004010B5084B1C7814F0010403D10028F9D0002404E02046FFF715FE024B1B78204610BD00BF09590040034A044B1B881088181A00B2704700BF1D
|
||||
:402B40005893FF1FA25B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F0C1
|
||||
:402B800000B270475293FF1F5493FF1F5093FF1F7047000010B500F0EBF9214A044613780A2043F001031370137C43F00103137412F80A3C43F0020302F80A3C937943F0DB
|
||||
:402BC0000203937102F5AA521832137843F003031370144B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222123B1A7067
|
||||
:402C0000094A137843F008031370FFF79FFD074B08222046BDE810401A6000F0ADB900BFAB43004006590040275B004080E200E008B500F09DF90F4A137803F0FE0313708E
|
||||
:402C4000A2F5AA52153A137803F0FE031370137C03F0FE03137412F80A3C03F0FD0302F80A3C937903F0FD039371BDE8084000F083B900BF00590040044A137803F03F0361
|
||||
:402C800043EA8010C0B21070704700BF00590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A80AE
|
||||
:402CC00042F210734B4341F2883103F6C41393FBF1F305490B60054B1A807047025900406A3A00005E93FF1F6493FF1F5C93FF1F08B5102000F084F807210320FFF76EFD92
|
||||
:402D000007490320FFF75CFD064A0C20137843F006031370FFF7BCFF034B00221A8008BDE92D0000015900406093FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040C8
|
||||
:402D4000FFF728BFA192FF1F044B1A7802F0FB021A701A7842F001021A7070470059004010B5084B1C7814F0010403D10028F9D0002404E02046FFF7E9FC024B1B78204621
|
||||
:402D800010BD00BF01590040034A044B1B881088181A00B2704700BF5C93FF1FA05B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107381
|
||||
:402DC00000B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475E93FF1F6493FF1F6093FF1F70470000034A00F0F800137803431370704700BF02410040A1
|
||||
:402E0000034A00F0F800137803431370704700BF06410040014B1870704700BF72640040014B1870704700BF7864004073B515461E460B4C04230022019200920A46014603
|
||||
:402E400018462370FFF7F8FB324629462078FFF7B3FB02212078FFF79DFB207802B070BDFC80FF1F074A0223136002F688321268E0215064044A11706FF440710A4413608D
|
||||
:402E8000704700BF80E100E001E400E0014B1870704700BF75640040014B1870704700BF76640040014B1870704700BF79640040FEB5494652465B460EB407462449096800
|
||||
:402EC0008A46244A12682448022100F071F8030020480068C018204900F06AF8143883460121C9430C460125002600F041F8814651460B7823400B705846013000F030F81C
|
||||
:402F00003800F04028400B78234003430B70584600F026F80136072EF2D9002001300138013001200B78234003430B705846043000F016F8484600F01FF800BF00BF00BF12
|
||||
:402F40000EBC894692469B46FEBD00BFAFF30080D480FF1FF880FF1F00C20100000000000230800803D000BF01380046FCD17047EFF3108072B6704780F31088704700BF77
|
||||
:402F8000094A137803F00303012B0AD0022B09D113790C2103F07F02044B01FB02339B7A00E013790020704700600040DC92FF1F002902D0B0FBF1F0704708B14FF0FF3097
|
||||
:402FC00000F008B80029F8D00246B0FBF1F000FB11217047704700BF014B1868704700BF6081FF1F0E4B70B51E460E4C0025E41AA410A54204D056F8253098470135F8E75C
|
||||
:4030000000F0DEFD084B094C1E46E41AA4100025A54204D056F8253098470135F8E770BDCC3B0000CC3B0000CC3B0000D43B000003460244934202D003F8011BFAE770475A
|
||||
:4030400030B5141E05469BB0184604DA8B232B604FF0FF301DE04FF40273ADF80C300CBF234604F1FF33029305934FF6FF7300910491ADF80E3002461E9B6946284600F0D4
|
||||
:4030800073F8431CBCBF8B232B6014B1009B00221A701BB030BD000007B5009313460A46014603480068FFF7CBFF03B05DF804FB6081FF1F2DE9F0478E6882469E420C46D9
|
||||
:4030C000914698463ED88A8912F4906F3AD02568096902236F1A656905EB450595FBF3F57B1C43449D4238BF1D4653050FD5294600F04AFB064698B13A46216900F0D2FA24
|
||||
:40310000A38923F4906343F08003A38113E02A4600F098FB064670B92169504600F0E8FA0C23CAF80030A3894FF0FF3043F04003A381BDE8F08726613E44266046466561E0
|
||||
:40314000ED1BA560464528BF464649463246206800F0B3FAA36800209B1BA36023681E442660BDE8F08700002DE9F04F9DB003938B8980461C060D4616460DD50B695BB958
|
||||
:40318000402100F001FB2860286118B90C23C8F80030CDE040236B610023099320238DF82930DFF89CB130238DF82A3037463C4614F8013B1BB9B7EB060910D003E0252BD0
|
||||
:4031C000F9D02746F3E74B46324629464046FFF771FF013000F0A780099B4B4409933B78002B00F0A08000234FF0FF3204930793059206938DF853301A93012605222178C6
|
||||
:403200004E4800F041FA671C049B38B14B4A3C46801A06FA00F018430490EFE7D90644BF20228DF853201A0744BF2B228DF8532022782A2A03D0079A00210A200BE0039AA2
|
||||
:40324000111D12680391002A10DA524243F00200079204900BE027463B780134303B092B03D800FB02320121F5E701B107923B782E2B1ED17B782A2B0AD1039B02371A1DAA
|
||||
:403280001B680392002BB8BF4FF0FF33059310E0002319460593781C0A2407463A780130303A092A03D804FB01210123F5E703B1059103223978224800F0E6F940B1402309
|
||||
:4032C000CBEB000003FA00F0049B013718430490397806221B487E1C8DF8281000F0D4F988B1194B33B9039B073323F007030833039314E003AB00932A46144B04A940468F
|
||||
:40330000AFF3008007E003AB00932A460F4B04A9404600F093F8B0F1FF3F824603D0099B5344099342E7AB895B0601D4099801E04FF0FF301DB0BDE8F08F00BF9B3B000071
|
||||
:40334000A13B0000A53B000000000000B53000002DE9F04791461F460A698B6806469342B8BF1346C9F8003091F843200C46DDF8208012B10133C9F800302368990642BF15
|
||||
:40338000D9F800300233C9F80030256815F0060510D104F1190A07E00123524639463046C04701301AD00135E368D9F800209B1A9D42F1DB94F843302268003318BF01230D
|
||||
:4033C00092060FD5E118302081F843005A1C94F845102244023382F8431003E04FF0FF30BDE8F08704F1430239463046C0470130F4D02268D9F80050E36802F00602042AD7
|
||||
:4034000008BF5D1B2269A3680CBF25EAE57500259342C4BF9B1AED184FF000091A344D4509D00123224639463046C0470130D5D009F10109F3E70020BDE8F0872DE9F0438A
|
||||
:4034400017460A7E85B06E2A984606460C460C9B01F1430E00F0AE8011D8632A22D009D8002A00F0BB80582A40F0CA8081F84520834955E0642A1ED0692A1CD0C0E0732A65
|
||||
:4034800000F0B08009D86F2A2ED0702A40F0B8800A6842F020020A603EE0752A24D0782A3AD0ADE01A6801F14205111D1960136884F84230A8E021681A6811F0800F02D098
|
||||
:4034C000111D196008E011F0400F02F10401196002D0B2F9003000E01368002B3CDA2D225B4284F8432037E021681A6811F0800F02D0111D196007E011F0400F02F104010E
|
||||
:40350000196001D0138800E01368227E5C496F2A14BF0A2208221BE078225A4984F845202268186812F0800F00F104051D6003D1550601D5038800E00368D00744BF42F032
|
||||
:40354000200222601BB9226822F0200222601022002084F8430001E049490A226568002DA56008DB206820F0040020602BB9002D7DD175460CE0002B79D07546B3FBF2F020
|
||||
:4035800002FB1033CB5C05F8013D03460028F5D1082A0BD12368DA0708D5236962689A42DEBF302305F8013C05F1FF35C5EB0E0323612EE008681A6810F0800F496903D0FC
|
||||
:4035C000101D1860136808E010F0400F02F104001860136801D0198000E0196000232361754616E01A68111D1960156800216268284600F049F808B1401B6060636804E0BC
|
||||
:4036000004F1420584F8422001232361002384F84330CDF800803B4603AA21463046FFF797FE013002D14FF0FF3026E023692A4639463046C0470130F5D023689B0710D563
|
||||
:40364000002504F1190907E001234A4639463046C0470130E7D00135E368039A9B1A9D42F2DBE068039B9842B8BF184605E00B7804F1420584F842308AE705B0BDE8F083AB
|
||||
:403680004F3A0000AC3B000010B5C9B202449042034605D01C7801308C42F8D1184610BD002010BD10B5431E0A44914204D011F8014B03F8014FF8E710BD884210B501EBCE
|
||||
:4036C000020301D8421E0BE09842FBD28118D21AD34204D013F8014D01F8014DF8E710BD994204D011F8014B02F8014FF8E710BD38B50546002944D051F8043C0C1F002BF2
|
||||
:40370000B8BFE41800F0D4F81E4A1368114613B96360146030E0A3420DD92268A018834201BF18685B681218226063600C6023E0A24203D813465A68002AF9D118681918DB
|
||||
:40374000A1420BD12168014458188242196013D110685268014419605A600DE002D90C232B6009E021686018824201BF106852680918216062605C602846BDE8384000F0C4
|
||||
:4037800098B838BDA892FF1F70B5CD1C25F0030508350C2D38BF0C25002D064601DBA94202D90C23336046E000F082F8234B1C681A462146A1B10B685B1B0ED40B2B03D94E
|
||||
:4037C0000B60CC18CD501EE08C420BBF63684B681360636018BF0C4615E00C464968E9E7174C23681BB9304600F052F820602946304600F04DF8431C18D0C41C24F00304D4
|
||||
:40380000A0420DD12560304600F053F804F10B00231D20F00700C31A0ED05A42E25070BD211A304600F034F80130EBD10C233360304600F03EF8002070BD00BFA892FF1F09
|
||||
:40384000A492FF1FF8B5074615460E4621B91146BDE8F840FFF798BF1AB9FFF749FF2846F8BD00F027F885420ED929463846FFF78BFF044650B131462A46FFF713FF3146E1
|
||||
:403880003846FFF735FF01E03046F8BD2046F8BD38B5064C0023054608462360FDF7D8FB431C02D1236803B12B6038BD8C93FF1F7047704751F8040C0028BEBF091851F8F0
|
||||
:4038C000043CC0180438704700000000050209020B020D020F021102130215027265706C792030782530327800686F6D696E6700626567696E6E696E67207365656B2066CB
|
||||
:40390000726F6D20256420746F2025640066696E6973686564207365656B00796573006E6F00647269766520303A20257320647269766520313A2025730057616974696E5F
|
||||
:403940006720666F72205553422E2E2E0055534220726561647900636F6D6D616E6420307825303278006661696C2025642B25642B2564203D3D2025642C206E6F7420254E
|
||||
:4039800064007061737365643D256400756E64657272756E206166746572202564207061636B65747300636F756E743D256420693D256420643D256400636D645F77726997
|
||||
:4039C000746500703D25642063723D25642063773D256420663D256420773D256420696E6465783D256420756E64657272756E3D256400756E64657272756E21007375635E
|
||||
:403A0000636573730073746172742065726173696E670073746F702065726173696E670069646C6500005100401000405100403000000001400010001401400008004001A3
|
||||
:403A400040000A004C0140000200500140200030313233343536373839414243444546000001000000040000001000010000000400000010280000000001040001000000C2
|
||||
:403A800000000000000157494E5553420000303030303100000000000000000012034D005300460054003100300030000100000001000000B83A000001000000873B0000A5
|
||||
:403AC000000000000000000001000000D03A000001000000593B000004000000F23A0000000000000000000000000000F03A0000FF00000001024000FF00000082024000C7
|
||||
:403B0000FF00000003034000FF00000084034000FF00020304030904160346006C007500780045006E00670069006E0065002A0343006F0077006C00610072006B00200034
|
||||
:403B400054006500630068006E006F006C006F0067006900650073000009022E0001010080320904000004FF00000107050102400000070582024000000705030340000AE8
|
||||
:403B80000705840340000A12010002FF0001080912006E0100020180014300232D302B2000686C4C0065666745464700303132333435363738396162636465660000000069
|
||||
:403BC000F8B500BFF8BC08BC9E46704759000000CD100000F8B500BFF8BC08BC9E46704735000000F83B0000C880FF1FA00000002812000000000000000000009093FF1FA8
|
||||
:403C0000FF000000675000400C00000007000000FFFFFFFF7F8000003F0000000000007D00FA0000400000000090D003FF0000000000000000000000000000000000000028
|
||||
:403C400000000000000000000000000000000000993B0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070
|
||||
:403C8000000000000000000000000000000000000081FF1F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000065
|
||||
:403CC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C4
|
||||
:403D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083
|
||||
:403D40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043
|
||||
:4000000000800020110000009910000099100000064A08B5136843F020031360044B1A6803F53F5302331A6001F054F8E8460040FA46004010B5054C237833B9044B13B187
|
||||
:400040000448AFF300800123237010BD6081FF1F00000000E8380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF0000000029
|
||||
:400080006481FF1FE8380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8081FF1FAC
|
||||
:4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8081FF1F3F000080114BDA68196919B905
|
||||
:4001000001221A75597514E09969521A19698A4294BF587D00201875187D094908B1002204E0086982428CBF002201224A75DA689A611B7D13B1002002F07EB9704700BF38
|
||||
:400140008081FF1F10B5C4B2204601F085F90128FAD110BD08B572B60F4B0F49DA680132DA60DA690132C82A08BF0022DA611A6AD8690132A72A08BF00220A621B6A002BEF
|
||||
:400180000CBF02230023002814BF184643F0010002F0D6FB62B608BD8081FF1F38B50446C5B2284602F0C0F8062002F095FD44F00200C0B202F0B8F8062002F08DFD284616
|
||||
:4001C00002F0B2F8BDE83840062002F06FBD10B5642402F0A3F828B9FFF7E0FF013CF8D1204610BD012010BD70B5C4B2054620460E4601F031F9012805D0204601F04AFAF7
|
||||
:400200002846FFF79FFF204601F02EF9314605460246204601F0EAF9204601F01DF90028FAD1284670BD000038B5044D0024285D013402F001FD402CF9D138BDA481FF1F85
|
||||
:4002400008B502F0F9F9002002F002FA02F014FA02F01EFA80B208BD10B50446012002F07BF8642002F022FDFFF7EAFF2080002002F072F8642002F019FDFFF7E1FF608067
|
||||
:4002800010BD08B502F004FB002002F00DFB02F01FFB02F029FB80B208BD10B50446FFF796FF322002F002FDFFF7EBFF20800120FFF774FF322002F0F9FCFFF7E2FF6080B9
|
||||
:4002C00010BD0FB400B593B014AB53F8042B402102A8019302F0F0FE02A802F0BAF802F0C4F813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF723FF62788A
|
||||
:400300002146BDE81040042001F0FEB8043A000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8643043B1002001F0E8FF8A
|
||||
:40034000002002F07FF8002384F8643010BD00BF8081FF1F38B5124D837895F8672004469A4204D0FFF7E4FF002385F86A302368C5F8653022790B4B1A71A378002B14BF3C
|
||||
:400380000220012002F05EF8E078B0FA80F0400902F0D0FA2079BDE8384002F0D7BA00BF8081FF1FE581FF1F38B50D4C94F8645065B904F16500FFF7CDFF012001F0A8FF27
|
||||
:4003C0004FF47A7002F072FC84F86A50E368E366012384F86430BDE8384002F0A5BC00BF8081FF1FF8B5214C0546FFF7DDFF94F86A3003B15DB91E48FFF763FFFFF7E7FE64
|
||||
:400400000120002384F86A00236702F065FC2A46216F1848FFF755FF144E0027236F9D4216D001F07BFF00B13767236F9D4205DD0120FFF7B3FE336F013305E005DA00209C
|
||||
:40044000FFF7ACFE336F013B336702F06DFCE5E7322002F02BFC2A2DCCBF0020012002F07BFABDE8F8400448FFF72BBF8081FF1F113A0000183A0000353A00002DE9F04FBF
|
||||
:4004800099B062B602F0BAFC9E49042002F0DEFC9D4801F04FFF9D4801F080FF9C4801F0ADFF02F069F902F03BF8002001F0CAFF01F0CEFF0221002000F084FF954C012099
|
||||
:4004C00001F0FCF8002384F86730FFF76DFFFFF77EFE84F87400FFF72BFF012384F86730FFF762FFFFF773FE84F87500FFF720FF894B94F87400894994F875202546002AA6
|
||||
:4005000014BF0A461A46002808BF19468448FFF7D8FE0321084602F0E7FB264602F004FC94F8643043B1EA6EEB689B1A41F28832934201D9FFF7FCFE00F07CFF18B97948B1
|
||||
:40054000FFF7BFFE04E000F07BFF0028F7D10BE000F070FF10B902F0E7FBF9E77248FFF7B0FE032001F096F8032000F075FF0128D4D16E48FFF7EEFE6D490320FFF734FED5
|
||||
:4005800094F876106B48FFF79CFE94F87630023B142B00F2D683DFE813F01500D4031E00D4032400D4035000D4037600D403D900D403C101D4030803D4032C03D40333036C
|
||||
:4005C000D4034D0303238DF820308DF8213011238DF822302AE394F87800FFF703FF564B21E340F2DC57FFF7DFFE00232375E068227D02F0FF012AB9EB681B1ABB42F7DD42
|
||||
:400600000B4611E083B10022174696F87810F068277594F814E0BEF1000F02D1EB681B1AF7E701329142F3DA07228DF8202004228DF82120ADF82230F8E20220FFF782FD2F
|
||||
:400640004FF000080DF1200A02F06EFB4FF480790027C9EB0803DA1907F80A200137402FF9D10220FFF76EFD3A465146022000F04BFFB9F10109EBD108F10108B8F1400FA1
|
||||
:40068000E2D12E4B38E04FF0010A4FF000080DF1200B02F049FB4FF0000959460120FFF7A3FD08EB090300270493049B1BF807203B44DBB29A4209D08DE80C0041463B4688
|
||||
:4006C0004A461F48FFF7FDFD4FF0000A0137402FEBD109F10109B9F5807FDED108F10108B8F1400FD5D151461648FFF7EAFDBAF1000F00F01B81144B1B8807A8ADF81C30AD
|
||||
:4007000095E200BF55010000F900000091000000C50000008081FF1F473A0000433A00004A3A0000623A0000753A0000E581FF1FF681FF1F7F3A0000EC380000EE380000D6
|
||||
:400740008E3A0000AA3A0000F0380000206FFFF749FE94F8780001F0FFFD94F8780001F0E3FD02F015FCB94BDFF8FC821A78002702F0FB021A701A7842F001021A701A78A2
|
||||
:4007800002F0FE021A701A7802F0FE021A7002F003FC0220FFF7D6FC012141F6FF734FF48042084601F0DEFD84F8B60002F02AFA08F807000137402FF8D1DFF8B0A20027DB
|
||||
:4007C0000AF195091FFA89F80137402F14BF3A4600221AF8010F2244062392F82420402102F044FA424646F24E419AF8000002F04FFA08F14008402F1FFA88F8E4D196F8F2
|
||||
:40080000793053B196F87C30336100233375237D002BFCD000233375336100234FF0FF32236062602372236894F8B600234493F8241002F09FF994F8B60002F05DF90121E6
|
||||
:4008400094F8B60002F030F92368002BFCD0002398467360D6F80CA0012702F065FAE368B4F87A20CAEB030393420DD367B1042195F8B60002F08AF994F8B60002F096F919
|
||||
:400880000028F9D107463072237AFBB96A682B689A4202D1002FE0D118E00220FFF752FC6968402209EB8111022000F02DFE6A68674B01321340002BBEBF03F1FF3363F093
|
||||
:4008C0003F03013308F101086360C6E70220277AFFF738FC00221146022000F015FE0220FFF730FCFFB2FFF79FFC002001F01CFD37B15848FFF7E5FC0220FFF709FD06E072
|
||||
:40090000554B08A81B88ADF82030FFF7EFFC227D4146237A5148FFF7D4FC15E25048FFF7D0FCD4F87A7017F03F0701D0032009E2286FFFF757FD95F8780001F00DFD95F82B
|
||||
:40094000780001F0F1FC012001F098FD02F020FB444BDFF814811A7842F004021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F00FFB01214FF480438D
|
||||
:4009800041F6FF72084601F01DFD85F8B60002F039F908F807000137402FF8D1DFF8CC90002709F195031FFA83F804930137402F14BF3A46002219F8010F2244052392F8FC
|
||||
:4009C0002420402102F052F9414646F2484299F8000002F05DF908F14008402F1FFA88F8E4D100274FF0FF33376098467360BB463B46D6F87A9037725FEA99190CBF4FF00D
|
||||
:400A0000010A4FF0000A2168114A01310A40002ABCBF02F1FF3262F03F026068B8BF013282426FD02BB1227A002A7AD12A7D002A77D12068049A059302EB8010BAF1000F36
|
||||
:400A400016D040223F2102F003FB1CE09E6400403F000080B43A0000F2380000CE3A0000E13A000098640040A481FF1FA381FF1F014601370120FFF7B7FBC7EB0903D3F11B
|
||||
:400A8000000A4AEB030A2168B34A01310A40002ABEBF02F1FF3262F03F02013222606268059B01322ED12A683F2A2BD14FF00008C5F8048001F0B4FC85F808806B6895F8B4
|
||||
:400AC000B6002B4493F8241002F054F895F8B60002F012F8012195F8B60001F0E5FF95F87E302B6185F81480237D002BFCD04FF00008012086F8148001F09EFC404601F070
|
||||
:400B0000BDFC00E023B1237A5BB92B7D4BB90123626842453FF477AF0BF1010BD5F8048071E701F083FC012001F0A8FC002001F083FC042194F8B60002F028F894F8B600FB
|
||||
:400B400002F034F880460028F8D196F8B60001F0C1FF337D327A0293012303920193CDF800A05B463A4649467C48FFF7AAFBC6F81080BAF1000F0BD0FFF756FB002001F0BE
|
||||
:400B8000D3FB237A63B17648FFF79BFB0220D9E0B945F1D073490120FFF726FB0137F7E77148FFF78EFB714B3DE094F8780001F0D3FB206FFFF716FC6D48FFF782FB94F8A9
|
||||
:400BC0007930236100232375237D002BFCD0012001F032FC00233375237D002BFCD0002001F02AFC002363483361FFF76AFB624B19E0002084F86A00FFF7F4FB5F4B12E0B3
|
||||
:400C000094F8743023B195F875200AB985F8782094F875201AB113B9012385F878305848FFF798FB574B1B88ADF8203008A8FFF75DFB89E0FFF77CFB01F01CFE002001F062
|
||||
:400C4000BFFD2A2701F0EAFC002001F08DFC3A46002108A802F0FCF917238DF820308DF8217002F061F8002001F052FB002001F0E9FBC82002F01AF80DF12200FFF7ECFA13
|
||||
:400C80000DF13600FFF709FB02F04EF8012001F0D9FB322002F00AF80DF12600FFF7DCFA0DF13A00FFF7F9FA012001F031FB4FF4967001F0FBFF02F037F80DF12E00FFF7DC
|
||||
:400CC000CBFA0DF14200FFF7E8FA002001F020FB4FF4967001F0EAFF02F026F8022001F0B1FB322001F0E2FF0DEB0700FFF7B4FA0DF13E00FFF7D1FA012001F009FB4FF4DC
|
||||
:400D0000967001F0D3FF02F00FF80DF13200FFF7A3FA0DF14600FFF7C0FA002001F0F8FA4FF4967001F0C2FF01F0FEFF002001F089FB002384F86A3001F01EFD01F0F0FB98
|
||||
:400D400074E70120FFF7E4FA032000F0A3FC0E48FFF7B7FAFFF7E2BB3F000080EB3A00001B3B00003892FF1F253B0000F43800002D3B00003B3B0000F6380000F8380000F7
|
||||
:400D8000F681FF1FFA380000483B00000F4B1A78120616D55878C0B2012814D11B79DBB2042B02D0052B05D07047094A094B5A60282203E0084A074B5A60E0221A8000F054
|
||||
:400DC00043BE002070470120704700BF00600040FC380000BC92FF1F243900002DE9F04172B6884B61221A70A3F5F06301221A801924854A9C7092E803008033062283F805
|
||||
:400E0000002283E80300522203F580731A707F4B7F4A1B787F4EDBB2137040F618027E4B00251A8041F2512223F8022C33784FF4F07003F0010343EA450502F0A1F8013CEB
|
||||
:400E400005F003052ED0032DF0D1744B4FF480721A8007221A70724A002548211570917002221D705D7103F8032C0422DA716D4A6D4C13786D4E43F00103137012F8013CA4
|
||||
:400E8000062743F0030302F8013C2378012243F0800323705B4B1A70654A137843F02003137000E0FEE707FB056300219A881868013502F0CDF8072DF5D15E485E4E0025FB
|
||||
:400EC00050F8041F05F1105303F14A0221F0FF074B33C9B20B4452005B0002329A4206D012F802EC12F801CC0EF807C0F5E7B0420D44E5D1514A00231360936013619361FF
|
||||
:400F00004F4B504F1A68504BDFF888811A604F4B1A684F4B1A604F4A137843F002031370137C43F0020313742378A2F5863243F040032370413A137843F010031370464A52
|
||||
:400F4000464B07CA03C31A80454A2833106843F8250C127903F8212C424A07CA03C31A80414AE83B07CA03C31A80404A083307CA03C31A803E4A3F4BA2F5616203CBC2F888
|
||||
:400F8000100EC2F8141E1378042043F008031370394B02F5AA521B783D78DBB298F80060EDB203F007010C321B091170F6B2537045F003033B7046F0030388F800302F4B47
|
||||
:400FC00048221A702E4A402313702E49937013729372082382F81F3220220A7048710A72294A0A20137001F077FE284B88F8006044223D70264D1A7094E80F0007C52B80C9
|
||||
:40100000BDE8F08100480040680A00480F010049A146004025420040224200400440004006400040A2430040A04300404D3B0000E8460040FCFFFF47900000480076004076
|
||||
:40104000700A0048F846004020760040740A00482876004003500140280A0048C0510040340A00483C0A0048480A0048540A004832510040600A0048CF0100491D510040C7
|
||||
:4010800001590040235B0040585B004076580040B0430040F946004008B501F0ADFF03680C2B00D1FEE7FEE7084908B50B68084A1844821A802A01DC086005E001F09CFF63
|
||||
:4010C0000C2303604FF0FF33184608BDCC80FF1F8893FF1F80B51148114B0025C0B1A3F1100192C922460439161BB74204D051F8046F42F8046BF7E7114653F8046C8C1A09
|
||||
:40110000A64202D041F8045BF9E701381033E5E701F078FFFFF7B2F9FEE700BF01000000E03C0000124A134B10B51A60124A134C1368134843F4007313600023032B98BF1C
|
||||
:4011400054F823204FEA830188BF0E4A0133302B4250F3D10C4B1A780C4B1A700C4B084A1A60FFF73BFEBDE8104001F087BC00BF0004FA050CED00E014ED00E000000000DE
|
||||
:401180000080FF1F99100000BC760040C080FF1F08ED00E0F8B501F0FBFE4B4A01271378022643F001031370137C484C43F001031374474B02F5E3521F700B3203F8946C1C
|
||||
:4011C0001378054603F07F031370002001F084FD2378404A03F0F90323701378384603F0DF03137023783B43237001F075FD282001F072FD384B30461A7802F07F021A7048
|
||||
:401200001A7802F0BF021A7023783343237001F063FD2378314A43F0040323700023137053702F4AFF2199540133092BFBD1284601F0B2FE0721172001F096FD2949172049
|
||||
:4012400001F084FD0721182001F08EFD2649182001F07CFD0721152001F086FD2349152001F074FD0721052001F07EFD2049052001F06CFD0721062001F076FD1D4906205C
|
||||
:4012800001F064FD0721084601F06EFD1A49072001F05CFD0721082001F066FD1749082001F054FD0021162001F05EFD1449162001F04CFD07210C2001F056FDBDE8F840E3
|
||||
:4012C00010490C2001F042BDA5430040944300409D60004012600040F851004084600040AD92FF1F631B00009D190000611B0000951A0000C11A0000F11A0000291B0000B8
|
||||
:40130000691B0000DD1B0000214B224A10B5187000231370204A40201370204A0F2413701F4A13701F4A13701F4A13701F4A13701F4B4FF400021A604FF080721A604FF432
|
||||
:4013400000121A6020221A601860802018604FF480701860174804704FF480001860164B1A70933B19B91A7802F0FE0202E01A7842F001021A70114B03221A70802203F8F2
|
||||
:40138000202C012001F0FCFD0D4B04221A7010BDC892FF1FCE92FF1FCC92FF1FCD92FF1FC992FF1FB892FF1FCB92FF1F4093FF1F00E100E09E6000409C60004028600040C2
|
||||
:4013C0001260004070B5074C054623780E461BB9FFF7E0FE0123237031462846BDE87040FFF792BF7892FF1F0A4A002313700A4A13700A4A13700A4A13700A4A13700A4AD7
|
||||
:4014000013700A4A13700A4B03221A70802203F8202C7047CE92FF1FCC92FF1FCD92FF1FC992FF1FB892FF1FCB92FF1F4093FF1F28600040014B1878704700BFCD92FF1F53
|
||||
:40144000044B1A7802F0FF001AB118780022C0B21A707047CC92FF1F024A0C2303FB002040787047D492FF1F431E072B0CD8074A064B00010344805C5B7800F00F0043EA26
|
||||
:401480000020023880B2704700207047FC5F00401A4A38B50C2303FB00231B79090C13F0800F00F1FF35044619BF8AB24FF480438BB24FF48042032D18D8DFE805F00207EB
|
||||
:4014C0000C110021084601F0A1FA0DE00021084601F080FA08E00021084601F05FFA03E00021084601F03EFA054B1855EDB2072D03D801F087FB034B185538BDD492FF1FDF
|
||||
:40150000A492FF1FAD92FF1F431E072B2DE9F0470446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB2C3F500739D424FF00C0303FB007388BFD588DB7884BFC5F5C3
|
||||
:401540000075ADB2254A43EA15230601B354B244EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FB047200215170B9F1000F28D03DB31B4F385D01F0ABFA112339
|
||||
:401580002946FE2218F8040001F070FB06F5C04278321FFA89F118F8040001F079FB124D18F80410385D01F0E5FA0121385D01F07BFA735D43F002037355735D03F0FD03E1
|
||||
:4015C0007355BDE8F08703FB04746379DBB28AF80230BDE8F08700BFD492FF1FFC5F0040AD92FF1FA492FF1F706000402DE9F047044615468846002940D0431E072B3FD816
|
||||
:40160000FFF732FFA84203D22046FFF72DFF05461D4E335DFF2B03D141462046FFF738FFDFF868A027011AF8040001F053FA1223FE222946305D01F019FB07F5C0411FFA17
|
||||
:4016400088F27831305D01F023FBDFF84490315D1AF8040001F08EFA01211AF8040001F023FA17F8093043F0020307F8093017F8093003F0FD0307F8093002E00D4600E05D
|
||||
:4016800000252846BDE8F087AD92FF1FA492FF1F70600040431E072B0AD8064A0C2303FB002300225A705A79034BD2B200011A54704700BFD492FF1FFE5F0040431E072B7B
|
||||
:4016C0009FBF024B000108221A547047FE5F004030B51A4A1A491B4D0878138803449BB21380194A00231488D8B2A4B27CB1082B0CD050680078C0B2E854506801330130C3
|
||||
:4017000050601088013880B21080ECE718460B780E4C082B0E4A00D040B10E4D2B7883F080032B700F232370022301E0022323701370094B1870087030BD00BF4493FF1F82
|
||||
:401740004093FF1F00600040BC92FF1FB992FF1FCE92FF1FCA92FF1F4193FF1F074B02221A70074B80221A70064B0F221A70064A00231370054A012013707047CE92FF1F71
|
||||
:40178000CA92FF1FB992FF1F4093FF1F4193FF1F30B5164B16491B780A8803F00F03023BDBB21A4492B20A80124C134A0020118889B279B173B15568215C013BC9B2297017
|
||||
:4017C0005168DBB20131516011880130013989B21180ECE7094A1370094A137883F080031370084B0B221A7030BD00BF296000404493FF1F00600040BC92FF1F4193FF1F7E
|
||||
:40180000CA92FF1FB992FF1F064A06231370064A01201370054B80221A70054B00221A70704700BFCE92FF1FB992FF1FCA92FF1F4193FF1F054B9A683AB19A680449107088
|
||||
:401840009A680988518000229A607047BC92FF1F4493FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A137008BD0220FFF7E1FF0D4B1B7803F06003202B05D0402B9A
|
||||
:4018800006D043B900F012FC04E001F089FB01E0FFF77CFA10B9034B03221A7008BD00BF28600040B992FF1F0060004008B5084A084B0120197813880B449BB21380064B68
|
||||
:4018C00000221A70FFF7B6FF044B03221A7008BD4493FF1F4093FF1FCE92FF1FB992FF1F08B50C4B1B78DBB2042B07D0062B09D0022B0DD1BDE80840FFF7D8BFBDE808404B
|
||||
:40190000FFF746BF0320FFF795FF034B03221A7008BD00BFCE92FF1FB992FF1F08B5054B002201201A70FFF785FF034B03221A7008BD00BFCE92FF1FB992FF1F08B50A4BC9
|
||||
:401940001A7832B11A78094942F080020A7000221A70074B002201201A70FFF76BFF054B03221A7008BD00BFB892FF1F08600040CE92FF1FB992FF1F074B1B78DBB2042B9A
|
||||
:4019800005D0062B05D0022B05D1FFF7A1BEFFF7C5BFFFF7D3BF7047CE92FF1F38B51D4C2378DBB2DD0634D518060AD503F00F03012B2ED1FFF74EFF174B1B78190609D5F1
|
||||
:4019C00038BD5A0602D5FFF7D7FF03E09D0620D5FFF786FF23781B061BD4104B1A78104B1B7813430F4A13701278934211D10A4A0849154613782078DBB2000605D41378E6
|
||||
:401A0000DBB20B700B7803F00F0328788342F1D138BD38BD28600040B992FF1FCA92FF1F4193FF1F29600040054A00231380054A916819B191680B7092685380704700BFD1
|
||||
:401A40004493FF1FBC92FF1F0E4808B503889BB213B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FF094AD1799379028843EA012392B2934238BF0380FFF728FED6
|
||||
:401A8000012008BDBC92FF1FCE92FF1FCA92FF1F00600040084B01221A700F3B9B7C074B1A7B02F00302012A1EBFDA7B82F08002DA7301225A7370470B600040D492FF1F89
|
||||
:401AC000094B02221A700F3B93F82230074B1A7E02F00302012A1EBFDA7E82F08002DA7601225A76704700BF0B600040D492FF1F0B4B04221A700F3B93F83230094B93F884
|
||||
:401B0000242002F00302012A1EBF93F8272082F0800283F82720012283F82520704700BF0B600040D492FF1F0B4B08221A700F3B93F84230094B93F8302002F00302012AB0
|
||||
:401B40001EBF93F8332082F0800283F83320012283F83120704700BF0B600040D492FF1F7047FFF741BC0000F0B5184B184E19780C27C9B201234FF0000C31B3CA0720D5E4
|
||||
:401B8000144A4FEA031E7244947850782040C5070DD507FB03652C79240608D5147804F0FE0414706D790C4CEDB204F80E50840706D507FB036425792D0658BF84F801C08E
|
||||
:401BC00090700133DBB24908D7E7F0BD9F600040D492FF1F70600040FE5F004000F032BF70B50446184B88B003AA03F11006154618685968083303C5B3422A46F7D11B78F7
|
||||
:401C00002B70FCB12223237001AD03232846637001F024F9002220461146AB5C08AC04EB131414F8144C03F00F03847008AC234413F8143C0132082AC1700371417100F129
|
||||
:401C40000400EAD108B070BD773B00002DE9F0431C4D01222E460C201F274FF0800E4FF0080C194B00FB02581401234418705F70164998F805902144B9F1000F07D098F89E
|
||||
:401C8000044024064CBF887081F802C001E081F802E000FB0261CC880132E4B29C71CC88092AC4F30724DC71CC88E4B21C71C988C1F307215971D4D1054BFF221A70BDE84B
|
||||
:401CC000F08300BFD492FF1F70600040FC5F00400A600040064B074A1B7802EBC30253681A7C824286BF03EBC003586900207047C892FF1F9C3B00002DE9F84F424B1A7884
|
||||
:401D0000002A7ED01878414D0138C0B2FFF7E2FFA8463F4AC3681478007ADFF800C1E4B203EBC0000C2600274FF0010E834268D01A78A24263D11CF80420597891425ED1AE
|
||||
:401D40009A7893F8039002F07F0206FB02FA05EB0A01CF7093F802B009F0030981F804B093F803B005F80AB0B3F804A0A1F808A093F902A0BAF1000F0BDAB9F1010F0CBF43
|
||||
:401D80004FF007094FF00D0981F8059081F801E009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEA02191A4906FB0282494481F802E0B2F808A0CAF3072A81F861
|
||||
:401DC00000A0B2F808A05FFA8AFA81F801A0B2F806A011495FFA8AFA494481F806A0B2F80690C9F3072981F80790B2F806905FFA89F981F80490D288C2F307224A71083335
|
||||
:401E000094E7BDE8F88F00BFCD92FF1FD492FF1FC992FF1FFC5F004070600040BA92FF1F08B5064B18780138C0B2FFF753FF20B143681B7900EBC300406908BDCD92FF1F73
|
||||
:401E400000212DE9F84F0B464E4E0C2707FB01F401313219092933554FF000059370494CD3701381937253705371EFD118B1464B1D70464B1D70464B1A78002A7FD0187866
|
||||
:401E800001250138C0B2FFF725FFA8464368DFF8F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010202F0FE021A70027AD20007FB0541C36803EB02094B4531D0E1
|
||||
:401EC00093F802A00AF07F06AE4229D10E89B3F804B0B6B25E4538BFA1F808B01E7893F801B01EF80660B3451AD181F804A0DE780E7093F902A0DE78BAF1000F06F00306A4
|
||||
:401F000007DA012E0CBF07260D264E7181F8018006E0012E0CBF052609264E7181F801C00833CBE70135092DC3D1C1680A328B1C0A440C200833934209D013F8081C13F8E4
|
||||
:401F40000A5C01F07F0100FB01418D72F2E7FFF767FF114B0121186000230C2000FB0142D3801289013113449BB203F00102134409299BB2F2D1BDE8F84FFFF767BEBDE897
|
||||
:401F8000F88F00BFD492FF1FBA92FF1F4293FF1FCD92FF1FCB92FF1FD092FF1F114B1B7903F07F035A1E072A19D80F490C2202FB031291781B0141F0010191700021D17030
|
||||
:401FC000517841F002015170127912F0800F074A1A4414BF8D2389239370FFF715BC0020704700BF00600040D492FF1FFC5F004030B4194B1A7902F07F02531E072B27D81A
|
||||
:40200000164B0C2404FB02339978154D01F0FE0199700021D97029461201505D114400F07F0050555A7802F0FD025A701A795B78120605D5012B01D18C7006E00D2303E095
|
||||
:40204000012B0CBF082309238B7030BCFFF7DCBB002030BC704700BF00600040D492FF1FFC5F004010B50D4B0D4C21791878C9B20138C0B2FFF72EFE43681B798B4201D296
|
||||
:40208000012909D8074A0848535CDBB24354A3780120DBB2535410BD002010BDCD92FF1F00600040BA92FF1F4293FF1F38B5874A874C13780021DBB221801806517840F17A
|
||||
:4020C00088800A2900F20081DFE811F05800FE00FE00FE00FE00FE000B00FE007900FE007D00D3787949012B09D17A4B1A787A4B03EBC2035B685B686360122310E0CB788C
|
||||
:40210000022B12D18878FFF7E5FD002800F0DC80436863606368DA7863689B7843EA02232380BDE83840FFF78FBCCB78032B21D16A4B00228878D5B2854203D3634A927872
|
||||
:402140003AB910E0187801320028F7D018780344F0E75E4A62499278097C914203D16148FFF73EFD5F4B1A78002A00F0AD801A78228018E0BDE8384000F012BF13F0030323
|
||||
:4021800013D0022B40F0A0802380504B0C211B7903F07F02544B01FB02339A78534BD2B21A7000225A706360BBE702222280504A11784E4AC9B2117053706260B1E70123AF
|
||||
:4021C00023804C4BEFE70123238013794A4A1344E9E701390A2977D8DFE801F037764F76067676760A7620009378444ADBB25AE0937803F0FF0153B93E4B1A7891425FD04C
|
||||
:4022000019703F4B01201870FFF71AFE58E0481EC0B2FFF75FFD0028EED155E0FFF722FF002851D0294A374913791279DBB2D2B20A70354A3049D25CCB5C9A4240D0304BAD
|
||||
:4022400001221A70FFF758FD3AE003F00303012B2BD009D3022B37D11C4B9B78002B33D1BDE83840FFF7C4BE184B9B78012B2BD11F4A137803F0FD0315E003F00303012B3E
|
||||
:4022800013D008D3022B1FD1104B9B78E3B9BDE83840FFF783BE0D4B9B78012B14D1144A137843F0020313700AE0084B1A795AB998781B791549DBB2CA5C22EA0002CA5401
|
||||
:4022C000BDE83840FFF7A0BA002038BD00600040BC92FF1FC892FF1F9C3B0000003C0000733C00006093FF1FD492FF1F7992FF1FCB92FF1FCD92FF1FBA92FF1FB892FF1F8E
|
||||
:40230000CC92FF1FC992FF1F4293FF1FCF92FF1F014B1870704700BF72640040014B1878704700BF68650040014B1870704700BF7A650040064A0123136002F688321268FB
|
||||
:40234000E0211064034A1170A2F540721360704780E100E000E400E0014B1870704700BF7A64004073B515461E460B4C04230022019200920A4601461846237000F022FCF8
|
||||
:4023800032462946207800F0DDFB0221207800F0C7FB207802B070BDD080FF1F074A0223136002F688321268E0215064044A11706FF440710A441360704700BF80E100E05F
|
||||
:4023C00001E400E073B515461E460B4C05230022019200920A4601461846237000F0F2FB32462946207800F0ADFB0221207800F097FB207802B070BDD180FF1F064A042355
|
||||
:40240000136002F688321268E0219064034A1170A2F202321360704780E100E002E400E0014B04221A60704700E100E0014B04221A60704780E100E0014B1870704700BFAF
|
||||
:402440007B650040014B1870704700BF73640040704738B505460078012428B100F038FD285D0134E4B2F8E738BD08B50D2000F02FFDBDE808400A2000F02ABD014B187065
|
||||
:40248000704700BF7865004010B500F081FD204A044613780A2043F002031370137C43F00203137412F80A3C43F0010302F80A3C937943F00103937102F5AB52137843F024
|
||||
:4024C00003031370134B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222183B1A70094A137843F00803137000F0ECFBF7
|
||||
:40250000064B10222046BDE810401A6000F044BDAB4300400E5900402F5B004080E200E008B500F035FD0F4A137803F0FE031370A2F5AA521D3A137803F0FD031370137CBD
|
||||
:4025400003F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE039371BDE8084000F01BBD00BF08590040044A137803F03F0343EA8010C0B21070704700BF0859004070
|
||||
:40258000082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBE4
|
||||
:4025C000F1F305490B60054B1A8070470A590040883B00004A93FF1F4C93FF1F5093FF1F08B5102000F036FA0721042000F0BCFB0749042000F0AAFB064A0C20137843F0FB
|
||||
:4026000006031370FFF7BCFF034B00221A8008BDD9260000095900404893FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF72ABF7B92FF1F044B1A7802F0FB0218
|
||||
:402640001A701A7842F001021A7070470859004010B5084B1C7814F0010403D10028F9D0002404E0204600F037FB024B1B78204610BD00BF09590040034A044B1B881088D2
|
||||
:40268000181A00B2704700BF5093FF1FA25B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF97
|
||||
:4026C0005B42134493FBF1F000B270474A93FF1F4C93FF1F4893FF1F7047000010B500F057FC214A044613780A2043F001031370137C43F00103137412F80A3C43F0020365
|
||||
:4027000002F80A3C937943F00203937102F5AA521832137843F003031370144B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F591
|
||||
:4027400097530222123B1A70094A137843F00803137000F0C1FA074B08222046BDE810401A6000F019BC00BFAB43004006590040275B004080E200E008B500F009FC0F4A79
|
||||
:40278000137803F0FE031370A2F5AA52153A137803F0FE031370137C03F0FE03137412F80A3C03F0FD0302F80A3C937903F0FD039371BDE8084000F0EFBB00BF00590040C4
|
||||
:4027C000044A137803F03F0343EA8010C0B21070704700BF00590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F91010F2
|
||||
:402800000A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A80704702590040923B00005693FF1F5C93FF1F5493FF1F08B5102000F014F9D3
|
||||
:402840000721032000F090FA0749032000F07EFA064A0C20137843F006031370FFF7BCFF034B00221A8008BD31290000015900405893FF1F10B5054C23781BB9FFF7DCFF1F
|
||||
:4028800001232370BDE81040FFF728BF7C92FF1F044B1A7802F0FB021A701A7842F001021A7070470059004010B5084B1C7814F0010403D10028F9D0002404E0204600F090
|
||||
:4028C0000BFA024B1B78204610BD00BF01590040034A044B1B881088181A00B2704700BF5493FF1FA05B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B37
|
||||
:402900001B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475693FF1F5C93FF1F5893FF1F70470000014B1870704700BF7B64004062
|
||||
:40294000014B1870704700BF7F640040014B1870704700BF7C640040014B1870704700BF7E640040F7B516461F460B4C00230325019300930A4601462846257000F022F939
|
||||
:402980003A463146207800F0DDF80221207800F0C7F8207803B0F0BDE080FF1FF7B516461F460B4C00230225019300930A4601462846257000F006F93A463146207800F0A6
|
||||
:4029C000C1F82946207800F0ABF8207803B0F0BDE180FF1FF7B516461F460B4C00230125019300930A4601462846257000F0EAF83A463146207800F0A5F80221207800F0A6
|
||||
:402A00008FF8207803B0F0BDE280FF1F73B515461E460B4C0023019300930A4601461846237000F0CFF832462946207800F08AF80221207800F074F8207802B070BD00BF92
|
||||
:402A4000E380FF1F024B1878C0F38010704700BF8F450040034A00F0F800137803431370704700BF02410040034A00F0F800137803431370704700BF06410040074A7F2330
|
||||
:402A8000802113705170064A013BDBB202F80839002BF9D1034A1370704700BFE480FF1FF87B00400078004017280FD8084B0001C25C11B142F0200201E002F0DF02C2543D
|
||||
:402AC000C25C42F00102C25400207047012070471070004017280BD8064B0001C25C02F0FE02C254C25C02F0DF02C25400207047012070471070004017280DD80749000179
|
||||
:402B00000B4603441A7942F004021A71435C43F00103435400207047012070471070004017280BD8064A0001835C490003F0F10301F00E0119438154002070470120704769
|
||||
:402B40001070004041F6FF73994208BF4FF400519A4208BF4FF4005217289FBFC00000F1804000F5EC4081809ABFC280002001207047000017289FBF034B00011954002042
|
||||
:402B800088BF0120704700BF1970004017289FBF054B00011A5C01F007019DBF1143195400200120704700BF1470004017289FBF034B0001185C00F0070088BFFF20704705
|
||||
:402BC00014700040172810B51AD8C00001F07F0100F1804441EAC21204F5EC44D2B222709DF8082003F00F0343EA0213DBB263709DF80C30002003F00F03A370E07010BD6D
|
||||
:402C0000012010BD10B500F0C3F90A4A5378182B0AD91478013B5370E30003F1804303F5F0431B78137000E0FF2400F0B5F9204610BD00BFE480FF1F030610B5044611D478
|
||||
:402C400000F0A6F9084AE300117803F1804303F5F04319705378147001335370BDE8104000F09AB910BD00BFE480FF1F30B504060CD411F4704509D1C40004F1804404F537
|
||||
:402C8000F0442180A270E370284630BD012030BD03065FBFC00000F1804000F5F04081805ABFC280002001207047000038B50446084DB4F5004F05D9286800F061F9A4F58B
|
||||
:402CC0000044F6E7034B58686043BDE8384000F057B900BFEC80FF1F024B1B7A584300F04FB900BFEC80FF1F0E4B00F003001A78490102F0FC02104318701A7801F0600107
|
||||
:402D000042F080021A701A7802F07F021A701A7802F09F020A431A701A7842F010021A70704700BF83430040014B01221A70704784430040044B00F00F021B6853F82200F7
|
||||
:402D400043F82210704700BF08ED00E0054A00F01F00126800F1100352F8230042F82310704700BF08ED00E000F01F0000F16040490100F56440C9B2017070470F4B10B555
|
||||
:402D80000F4900240F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040313600A4B4FF47A72DB68B3FBF2F3084A1360084B4FF400421C60C3F8E82010BD8092FF1FC5
|
||||
:402DC000292E000010E000E0EC80FF1F14E000E018E000E0024A136843F002031360704710E000E008B5FFF7F5FF034A136843F00103136008BD00BF10E000E010B5054CFA
|
||||
:402E0000A3691BB9FFF7BAFF0123A361BDE81040FFF7E8BF8092FF1F024B1868C0F30040704700BF10E000E038B5FFF7F5FF012808D1054D002455F8243003B1984701345B
|
||||
:402E4000052CF8D138BD00BF8492FF1F024B03EB80035868596070478092FF1F134B144A1B78DBB20360127843EA0223114A0360127843EA0243104A0360127843EA0263A4
|
||||
:402E800003600E4B0E4A1B78DBB24360127843EA02230C4A4360127843EA02430A4A4360127843EA02634360704700BF0301004904010049EC46004002010049010100499A
|
||||
:402EC00000010049050100490601004900000000FEB5494652465B460EB40746244909688A46244A12682448022100F071F8030020480068C018204900F06AF81438834666
|
||||
:402F00000121C9430C460125002600F041F8814651460B7823400B705846013000F030F83800F04028400B78234003430B70584600F026F80136072EF2D900200130013812
|
||||
:402F4000013001200B78234003430B705846043000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF30080D480FF1FF880FF1F00C2010000000000FD
|
||||
:402F80000230800803D000BF01380046FCD17047EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D113790C2103F07F02044B01FB02339B7A4A
|
||||
:402FC00000E013790020704700600040D492FF1F002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB11217047704700BF014B1868704700BFF4
|
||||
:403000005C81FF1F0E4B70B51E460E4C0025E41AA410A54204D056F8253098470135F8E700F044FE084B094C1E46E41AA4100025A54204D056F8253098470135F8E770BD9C
|
||||
:40304000B83C0000B83C0000B83C0000C03C000003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B604FF0FF301DE04FF40273ADF80C300CBFA2
|
||||
:40308000234604F1FF33029305934FF6FF7300910491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B00221A701BB030BD000007B5009313460A464B
|
||||
:4030C000014603480068FFF7CBFF03B05DF804FB5C81FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD02568096902236F1A656905EB450595FBF3F5BD
|
||||
:403100007B1C43449D4238BF1D4653050FD5294600F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A4600F098FB064670B92169504600F0E8FAA0
|
||||
:403140000C23CAF80030A3894FF0FF3043F04003A381BDE8F08726613E44266046466561ED1BA560464528BF464649463246206800F0B3FAA36800209B1BA36023681E44F5
|
||||
:403180002660BDE8F08700002DE9F04F9DB003938B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F80030CDE040236B610023099320238DF86F
|
||||
:4031C0002930DFF89CB130238DF82A3037463C4614F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF771FF013000F0A780099B4B4409933B7803
|
||||
:40320000002B00F0A08000234FF0FF3204930793059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46801A06FA00F018430490EFE7D90644BFAF
|
||||
:4032400020228DF853201A0744BF2B228DF8532022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F00200079204900BE027463B780134303B092B51
|
||||
:4032800003D800FB02320121F5E701B107923B782E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E0002319460593781C0A2407463A780130D0
|
||||
:4032C000303A092A03D804FB01210123F5E703B1059103223978224800F0E6F940B14023CBEB000003FA00F0049B013718430490397806221B487E1C8DF8281000F0D4F9CF
|
||||
:4033000088B1194B33B9039B073323F007030833039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A9404600F093F8B0F1FF3F824603D0099B27
|
||||
:403340005344099342E7AB895B0601D4099801E04FF0FF301DB0BDE8F08F00BF873C00008D3C0000913C000000000000D53000002DE9F04791461F460A698B680646934279
|
||||
:40338000B8BF1346C9F8003091F843200C46DDF8208012B10133C9F800302368990642BFD9F800300233C9F80030256815F0060510D104F1190A07E0012352463946304631
|
||||
:4033C000C04701301AD00135E368D9F800209B1A9D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F845102244023382F8431003E04FF0FF3091
|
||||
:40340000BDE8F08704F1430239463046C0470130F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500259342C4BF9B1AED184FF000091A344D45BF
|
||||
:4034400009D00123224639463046C0470130D5D009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9B01F1430E00F0AE8011D8632A22D009D833
|
||||
:40348000002A00F0BB80582A40F0CA8081F84520834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B8800A6842F020020A603EE0752A24D0782A87
|
||||
:4034C0003AD0ADE01A6801F14205111D1960136884F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F10401196002D0B2F9003000E01368002B3CDA2D228D
|
||||
:403500005B4284F8432037E021681A6811F0800F02D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A14BF0A2208221BE078225A4984F8452055
|
||||
:403540002268186812F0800F00F104051D6003D1550601D5038800E00368D00744BF42F0200222601BB9226822F0200222601022002084F8430001E049490A226568002DF0
|
||||
:40358000A56008DB206820F0040020602BB9002D7DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D1082A0BD12368DA0708D5236962689A42E0
|
||||
:4035C000DEBF302305F8013C05F1FF35C5EB0E0323612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F104001860136801D0198000E019600023236173
|
||||
:40360000754616E01A68111D1960156800216268284600F049F808B1401B6060636804E004F1420584F8422001232361002384F84330CDF800803B4603AA21463046FFF70C
|
||||
:4036400097FE013002D14FF0FF3026E023692A4639463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046C0470130E7D00135E368039A9B1A9D42D0
|
||||
:40368000F2DBE068039B9842B8BF184605E00B7804F1420584F842308AE705B0BDE8F083773B0000983C000010B5C9B202449042034605D01C7801308C42F8D1184610BD55
|
||||
:4036C000002010BD10B5431E0A44914204D011F8014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21AD34204D013F8014D01F8014DF8E710BD71
|
||||
:40370000994204D011F8014B02F8014FF8E710BD38B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B96360146030E0A3420DD92268A0188342ED
|
||||
:4037400001BF18685B681218226063600C6023E0A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D110685268014419605A600DE002D90C232A
|
||||
:403780002B6009E021686018824201BF106852680918216062605C602846BDE8384000F098B838BDA092FF1F70B5CD1C25F0030508350C2D38BF0C25002D064601DBA942A5
|
||||
:4037C00002D90C23336046E000F082F8234B1C681A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B681360636018BF0C4615E00C464968E9E70D
|
||||
:40380000174C23681BB9304600F052F820602946304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B00231D20F00700C31A0ED05A42E25070BD37
|
||||
:40384000211A304600F034F80130EBD10C233360304600F03EF8002070BD00BFA092FF1F9C92FF1FF8B5074615460E4621B91146BDE8F840FFF798BF1AB9FFF749FF284605
|
||||
:40388000F8BD00F027F885420ED929463846FFF78BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD38B5064C0023054608462360FDF7F4FB48
|
||||
:4038C000431C02D1236803B12B6038BD8493FF1F7047704751F8040C0028BEBF091851F8043CC0180438704700000000050209020B020D020F02110213021502280000001B
|
||||
:40390000000104000100000000000000000157494E55534200003030303031000000000000000000E0000000000105000100D6000000070000002A00440065007600690041
|
||||
:403940006300650049006E0074006500720066006100630065004700550049004400730000009E0000007B00330064003200370035006300660065002D003500340033000D
|
||||
:4039800035002D0034006400640035002D0061006300630061002D003900660062003900390035006500320066003600330038007D0000007B00330064003200370035001F
|
||||
:4039C0006300660065002D0035003400330035002D0034006400640035002D0061006300630061002D003900660062003900390035006500320066003600330038007D0098
|
||||
:403A0000000000007265706C792030782530327800686F6D696E6700626567696E6E696E67207365656B2066726F6D20256420746F2025640066696E69736865642073652D
|
||||
:403A4000656B00796573006E6F00647269766520303A20257320647269766520313A2025730057616974696E6720666F72205553422E2E2E0055534220726561647900631F
|
||||
:403A80006F6D6D616E6420307825303278006661696C2025642B25642B2564203D3D2025642C206E6F74202564007061737365643D256400756E64657272756E2061667479
|
||||
:403AC0006572202564207061636B65747300636F756E743D256420693D256420643D256400636D645F777269746500703D25642063723D25642063773D256420663D2564F8
|
||||
:403B000020773D256420696E6465783D256420756E64657272756E3D256400756E64657272756E2100737563636573730073746172742065726173696E670073746F702027
|
||||
:403B400065726173696E670069646C650000510040100040510040300000000140001000140140000800400140000A004C01400002005001402000303132333435363738CF
|
||||
:403B80003941424344454600000100000004000000100001000000040000001001000000A43B000001000000733C0000000000000000000001000000BC3B00000100000084
|
||||
:403BC000453C000004000000DE3B0000000000000000000000000000DC3B0000FF00000001024000FF00000082024000FF00000003034000FF00000084034000FF000203FC
|
||||
:403C000004030904160346006C007500780045006E00670069006E0065002A0343006F0077006C00610072006B00200054006500630068006E006F006C006F006700690036
|
||||
:403C4000650073000009022E0001010080320904000004FF00000107050102400000070582024000000705030340000A0705840340000A12010002FF0001080912006E016F
|
||||
:403C800000020180014300232D302B2000686C4C00656667454647003031323334353637383961626364656600000000F8B500BFF8BC08BC9E4670475900000025110000DE
|
||||
:403CC000F8B500BFF8BC08BC9E46704735000000E03C0000C880FF1F9800000028120000000000008893FF1FFFFF0000675000400C00000007000000FFFFFFFF7F80000080
|
||||
:403D00003F0000000000007D00FA0000400000000090D0030000000000000000000000000000000000000000000000000000000000000000853C0000000000000000000069
|
||||
:403D400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC80FF1F0000000000000000A9
|
||||
:403D80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003
|
||||
:403DC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C3
|
||||
:403E00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082
|
||||
|
|
@ -4098,48 +4098,48 @@
|
|||
:40FF80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041
|
||||
:40FFC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
|
||||
:0200000480007A
|
||||
:400000000145004009520040015B00400165004047000140380101404E0201404703014046040140440501404C060140490701402C08014031090140040B0140030D014075
|
||||
:400040006C14014057150140591601404B17014011190140071B01400B400140104101400C4201400F43014002440140054501400A4601400B4701400C480140104901404C
|
||||
:40008000174C01400C4D014006500140045101407E02080409021082110218021904601561137C402721240A02E40303040E0520064107C00A840B900E840F901001120693
|
||||
:4000C00013901684179019011A841B481C081DFC1E221F0221C122182324268427902B9C2CE72E10300733E0351F36F83A023B084204460E480449FF4AFF4BFF4F83580B46
|
||||
:40010000590B5A045B045C995D095F01868088028A8190E7921094019686980F9A409C0F9E20A2E0A618A804AA81AE80B207B4F8D80BDC09DF010022022003010524084089
|
||||
:4001400009080A080B400C420D200E2010021108130215801610172119011A011C241E201F0C210422082308284029042B88312032643301382039883A013B804240528017
|
||||
:400180005A215B4A600461016218634278027C028280C06FC2FFC4FDCA07CC0FCE0FD001D60FD80FDE8103140404050806110710081F0A800C010E1E0FE4101F124015102D
|
||||
:4001C00016E0178218021A111B141C1F1E20211023212408261127142B142D1F2F4032F0330F360F37F03A083B80580B590B5B045C995F018203830884618608873088204C
|
||||
:4002000089108A508B828DC78E108F2892109304961C97C49A109B04A061A110A204A341A440A630A704AB04AC7CAE02AF04B01FB30FB460B5F4BB08D80BD90BDB04DC99FF
|
||||
:40024000DF01012A02200301040805400708081009080B8C0C420E200F04104211081504180819221A0C1B011C221D4C1E211F4820042218242025442731285029402B8839
|
||||
:400280002C022E042F883204331134103508380439813A103D8C3E016230680269806A806D4078027C029308942096E897809C029D249F61A403A50CA620A781AA01AB0889
|
||||
:4002C000C0EFC2FEC42DCAFFCC67CEDFDE81E208021004400620080C0C010E0210081101140818051A081C04200828202AC02CE02F0130603280341E3501360138203E40ED
|
||||
:400300005608580459045B045C995D905F0181608209842487108A128B108F10903F93039407950197089B1C9D40A036A209A310A710A82DA901AA12AB04AC40AD7CAF0220
|
||||
:40034000B11FB238B440B540B607B720BE10D608D80BD90BDB04DC99DD90DF01000201280302040A070409980B800C020E20104212081550170119281A401B401E201FA0A5
|
||||
:4003800022102662274828022C022E052F043302364237283C203D803F0558805D80600267016B026D106F017C0287208B848F4091409205930496409B209C029F48A0124A
|
||||
:4003C000A140A208A620A780AD01AE08C07FC23FC4DBCAF8CCF1CEF0D618D818DE80E208E408EA040104020F0440068008700A800C800E200F0110011106130114081602E9
|
||||
:40040000170218011C0F1EF0200124072608288029052A102B022C082E04310736FF3E403F01580459045B045F018001811C820283E18402852087C0880489828B218C0248
|
||||
:400440008D088F10900491FF940495089710980599029C049D04A002A1FCA402A520A740A802AC03AE04AF02B007B3FCB503BB20BE01D804D904DB04DC90DF010081012440
|
||||
:4004800004450520084009080A080B810C080D010E020F041008120113861544172219011C0220042114222C23282740280229012B202D1030083180321033023705396892
|
||||
:4004C0003A013D82588068066C016D806F048004810184048F0191109220940A9780980299209B019C409F04A401A598A702A880A980AF02C0FFC2FFC4FFCA2BCCCFCE9F8E
|
||||
:40050000D608E24CE402E605EA04EC02EE0C06010B0410041304160119021C022101240829042C013001310232043304340236083701380239083E553F45560858045904A9
|
||||
:400540005B045D905F0183018A029501A401AC02B401B501B602B880B920BE50BF10D804D904DB04DF01030804080D800F50100212101410154019101A021E2A1F022210BD
|
||||
:40058000250226882C802F083308372138023F10584059105DA45E01645266106A406C076D406F09800282C085808608890293409980AF40C022C2B0C4A5CAA0CCA2CE2157
|
||||
:4005C000D6FCD8F0EA04EE0C9980E208E448EA049980B180E208018002200608070108280B100C020D210E2C1028110F1434152117081828192F1C101E021F2F202821210D
|
||||
:40060000230224022740282829212B042C202D212E012F0E321F336035803620371F39083E443F1040524720482049FF4AFF4BFF4D204EF05110580459045A045B045C9038
|
||||
:400640005D095F01610862406340648066406740800F810183028503861087FC88208A018B048C208DFF8E0290039302942395809608970499049A409B209D049E2F9FF9AF
|
||||
:40068000A023A104A20CA308A423A504A604A740A82FA904AB10AF02B060B3FFB460B61FB822BF04D804D904DB04DC09DF01012202200301040205040608070209880A04D5
|
||||
:4006C0000B800C080D800E48118012081305144015051610171019881C401D401E041F602118228023112410284129082A102D202E402F6030083240331234013680372A63
|
||||
:40070000381039423A083C063D803F1041104980510159025A0269806C016D806F0285018D409004914592C2930494429604979199249A089B3B9D40A009A180A22CA548B8
|
||||
:40074000A722AC02B704C0FFC2FFC47FCAFFCCFFCEFFD004D208E402E808EE1200FF01880403054606FC07B8080C09880A100C0C0EF10F0112081510160417A0180C198849
|
||||
:400780001A401C011D421E021F04218822022480259A260C2720280C29882A202E022F0431C132FF333F35C137C139A23E043F04580459045B045C905F01800282058521EF
|
||||
:4007C000870288028C0291279403950896049B389D049F01A118A340A706AC02AF01B006B107B310B401B560B708BE10BF40C006C5ECC803C9FFCAFFCBFFD004D601D8046C
|
||||
:40080000D904DA04DB04DC99DD09DF01E2C00020010203020406056007100A200C010D800E040F1013801542172819131D042003218022D5240425102605278128102901DB
|
||||
:400840002A802D202E402F603180321433023680372838043C043D803F11481049104B0264016701684869476A406C02728873068410860194419546978799209B2C9F827A
|
||||
:40088000A228A448A598A601A722AB40B002B481C0FBC2F4C4F1CAFBCC7FCEF2D204E220E604EA4082108A018E048F029222960197109D209F20A214A302B080B104B6017C
|
||||
:4008C000E220EA10EC808F209F20AF10B211B520E412EE021B011F083240330836843B408140C630CCF0CE10E62030803204358037083A023D408480914096809740A6046A
|
||||
:40090000AE80AF01CCF0CE60E210508057208580968897409D809E02A604AB08D460E210EA20832284808E02968097409E02A480A620A720AA24AE04E230E620EC80EE30D9
|
||||
:400940001501C4045D828740B101D605EE011B04850287108D8097809D82A480A880C608E4020B880C800F108A109798A480A740AB44AF04C20FE404262080018A01974061
|
||||
:40098000A280A302A620AA40B680C820E620EE8052805302551070017E0190029202A110A280A302AF40B510D4E0DC80DE20EE40052008040E020F801F1053805610588014
|
||||
:4009C00063028E208F809A109D20A740B120C001C20DC601D407D602E002E402EA04762084809A209C80AF10B004B301B601DE04E801EA08EE01010109010B010D011101C4
|
||||
:400A00001B0100FF01AB020211050000BF0000A09F001F000000000000000000100000004000000000000000C0000000FF0000B847004700000100008000000282008200DC
|
||||
:400A400000000000000707000700000027001801270018010004000000050000000000000000000000000000000000000000000000000000000000000000000000000000D8
|
||||
:400000000145004008520040015B004001650040020101402C0201402D0301400F0501400B0701406308014056090140570A01404A0B0140500C01404A0D0140480E014042
|
||||
:40004000390F01400415014004170140671801404B190140361A0140471B01400D40014010410140134201400D43014004440140064501400A460140084701400B48014097
|
||||
:4000800010490140144C01400C4D014006500140045101407E02080109441180180219016009610A7C402721290AE204E6080F011001150126012E012F013001310138026A
|
||||
:4000C00039023E013F01580459045B045F01810883048C018D0E910194029701A404AD04AE04AF0AB004B102B301B402B50CB601B802B908BE51BF04D608D804D909DB0401
|
||||
:40010000DC90DD90DF01020208080C02100213081540170818081A801B801C80200821012210231026012A802C02300233043708388039023D405840600268026B0C6D4004
|
||||
:40014000782088028B109840B040C001C214C4A5CA18CC43CE19D608D808DE04E208E68080088201928094809C289E80A201A602B044B380B404B610E604EA01EE44804068
|
||||
:40018000842094809C209E80A602AA40E211E608EA01EE0C00800121020403020403052106FC0704080109210A020B080C040E100F4010FF11011404152F162019201A0495
|
||||
:4001C0001B011C041D211EF91F0E220224042608271028042A402B2F2D0F2E02311F336034FF39083A0C3E104003450C470E481149FF4AFF4BFF50045609581859045A044E
|
||||
:400200005B045C995D995F0162C081028407850186388702880189048C028D048E04902091049208940895029610983F99029C089D049E30A105A503A704A802A902AA0417
|
||||
:40024000AC3FAD02B43FB507B63FBF10D608D804D904DB04DC09DD90DF010010010803010490050909240A420C030E020F24100A15081602172119401A021B501E101F107B
|
||||
:400280002120242025052603272029982B402C022F18300831803202332035983701388039013B183C803D113E0447084F0856085A805F4062806302670269806B026C0881
|
||||
:4002C0006D116E846F21759076467F01814087018E08908094209581971499049C019DC99E449F03A382A408A682A729C0F7C2FFC4F3CA7FCCFFCEFFD040D618D818DE1080
|
||||
:40030000E210EA01EE0C0101041205010624084209800A100B200C040D700F80107011081201130414061508170219071A081B081D801E601F102140221023802404250F16
|
||||
:4003400027F028042B0F2C802D01300F320F348035FF3670380A3B0C3E103F105804590A5B045C995F01824185F186418704888089208A018B118CF18E0291F1924E930218
|
||||
:40038000954096419711980199109A149BE19F0EA1F1A308A580A641A711A801AA28B00FB10FB3F0B4F0BA02BB02D80BD90BDB04DC99DF010108020203080510076208017B
|
||||
:4003C00009200A220D020E1410021240131414081508188819041A221B821C801D121F1021282501277029892B082D022F103008314033213608376238A039013A043E1454
|
||||
:4004000068026D407A107E80800481408D108F019108921495809702980899029D109F72A218A440A509A701A809B340B408B502B640C0F7C27FC46FCACFCCFFCE6FDE8298
|
||||
:40044000E401E8F0EC20EE40020103C4070808050A020B040C380D200F82101011C7120813181602170418081A301B041F302520274128042B042C062E012F043020320715
|
||||
:40048000330F341837F43B083E04580459085B045C995F0182108303840285628604870489208A108B508DFC8E108F0192039420964097809A109B1C9C409E209F10A07C22
|
||||
:4004C000A162A201A308A61CA710A940AA10AB30AC02AE08AF10B01FB11FB360B460B780D808D908DC99DF0100A001800204036004020706081009080A800B010C080E8878
|
||||
:400500001218134215401608180419201A841B201C081EA02105220923112504268429922A242D802E102F20301031083350340136883722380439823B103D0A3E203F4070
|
||||
:40054000611062106D406F047B037F028A08900292509508968297129C019D329F62A018A444A501A690A721C0DFC27FC4CFCA7FCCFECEFFDE18E020E208EA8000020120A4
|
||||
:40058000024803C004C20502062407440A030B900E9011FC129013011690170318201AC01B901E901F9020FC21C222012328269027902A9C2B9C2F90321F331F34E037E015
|
||||
:4005C0003A203B80420647E0482049FF4AFF4BFF4F83580859085A045B045C995D095F0180E28208861088E28A049210942096D09A03A0FCA201A440A630A880AA30AE1C91
|
||||
:40060000B2E0B41FD808DB04DC09DF01009001C6032005260610072009010A140B420D200E100F40121013121548171218101A241B101C201D201E101F0820022204230148
|
||||
:4006400029862B08300131103204335039043B1148054A0252015AAA6120628263206A406C097B037F0282018610C07FC27FC4FECA0FCC0FCE07D204D60FD80FDE188A02E6
|
||||
:40068000A602E604EE02A602B680E401EE020010022003020406063807040802090C0A040BF10C080D0C0F20100813081408150C17101808190C1B401C081F0221802204B3
|
||||
:4006C000230C250327FC281A29012A202B022DFF2E01303F31FF3E013F01580459045B045F0181238280830C842286CC870188308A028B2F8CC88D0F9123924894C8952367
|
||||
:40070000970898D499239B049CC89F10A0C8A120A302A4C8A52FA802AB40AC80AE01B080B261B31FB41FB560B760B808B9A0BE11C046C620C808C9FFCAFFCBFFCD20CEF004
|
||||
:40074000D110D804D904DA04DB04DC99DD09DF01E108E240E340E480E640E7400010010803820490050909080A420B040E020F941002110A120415401721184019181C80B4
|
||||
:40078000218C2314274029182A022D4A2F0830083361350436413720381039413A043D013F9440404A085010584059205F806C026D4082409080910192029410950896C023
|
||||
:4007C000971C99049A069B219D099F08A008A105A2C1A3E6A442A580A708B340B610C0FFC2FFC4DFCAF7CCFFCEFFD001D204D61CEE120008060209020D01100411041202D8
|
||||
:4008000018041F04220428012C063006310132013302340835043A023E143F055608580459045B045C995D905F0181028405881090019236930198019C319E0AAC05AD012B
|
||||
:40084000B00CB102B210B420B501B603BE04BF11D608D804D904DB04DC09DD90DF0101080240051809200A400E820F08100811401280154019231A501C101D101E821F0E2C
|
||||
:4008800020402120220827882F02300232803601382039803F0158205A805F40600862406701680869806F0180408210844887408B018E4090209140941096409708982064
|
||||
:4008C00099089C409D239F88A002A280A304A608A708B020B410C06CC2DAC48BCA10CC89CE8CD61CD81CE001E622EE021B011F083140330836843B4083408540C630CCF07B
|
||||
:40090000CE10E220E61030803204364037043A083F80848088029740A340A604AE80AF01CCF0CE60E2105004570880049002960897409A049E089F04A002A120A644A80182
|
||||
:40094000AA04B520D460E680EA80EE208E08900297409A049E08A002A120A640AA04AB0CAE04EA80EEB014407008C404DC0160808740A408B040D802EA011B0482209780E6
|
||||
:400980009940B008B140B480C608E809EC040B880F4197899940A220AB05AF04C20F24088004900297409A04A002A120AE40C820E480EE4050015120560470027F018301EC
|
||||
:4009C00090029A04A002A120AF40D4E0DC80DE20E620EE40070208080D010E021F10520854085940622095029940A220AF40B101C001C20DC601D407D802EA04800887021E
|
||||
:400A00008E019B02A201A408AF10B008B608E208EA01EC02010109010D010F0111011D0100FF01AB02021105BF0000A09F001F0000000000000000001000000040000000B7
|
||||
:400A400000000000C0000000FF0000B84700470000010000800000008000800000000000000707000700000027001801270018010004000000050000000000000000000052
|
||||
:400A80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036
|
||||
:400AC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F6
|
||||
:400B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B5
|
||||
|
|
@ -4615,12 +4615,12 @@
|
|||
:0200000490105A
|
||||
:04000000BC90ACAF55
|
||||
:0200000490303A
|
||||
:02000000FEBF41
|
||||
:0200000018EFF7
|
||||
:0200000490402A
|
||||
:4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0
|
||||
:400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
|
||||
:400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
|
||||
:4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
:0200000490501A
|
||||
:0C00000000012E16106900002E300F28A1
|
||||
:0C00000000012E16106900002E30295857
|
||||
:00000001FF
|
||||
|
|
@ -816,7 +816,7 @@
|
|||
<Group key="v1">
|
||||
<Data key="cy_boot" value="cy_boot_v6_10" />
|
||||
<Data key="Em_EEPROM_Dynamic" value="Em_EEPROM_Dynamic_v2_20" />
|
||||
<Data key="LIN_Dynamic" value="LIN_Dynamic_v5_0" />
|
||||
<Data key="LIN_Dynamic" value="LIN_Dynamic_v6_0" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Data key="DataVersionKey" value="2" />
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -23,36 +23,49 @@ module Sampler (
|
|||
|
||||
reg [5:0] counter;
|
||||
|
||||
reg index_q;
|
||||
reg rdata_q;
|
||||
|
||||
reg index_edge;
|
||||
reg rdata_edge;
|
||||
|
||||
reg req_toggle;
|
||||
|
||||
reg rdata_toggle;
|
||||
reg old_rdata_toggle;
|
||||
|
||||
reg index_toggle;
|
||||
reg old_index_toggle;
|
||||
|
||||
always @(posedge rdata)
|
||||
begin
|
||||
rdata_toggle <= ~rdata_toggle;
|
||||
end
|
||||
|
||||
always @(posedge index)
|
||||
begin
|
||||
index_toggle <= ~index_toggle;
|
||||
end
|
||||
|
||||
always @(posedge sampleclock)
|
||||
begin
|
||||
if (reset)
|
||||
begin
|
||||
old_rdata_toggle <= 0;
|
||||
old_index_toggle <= 0;
|
||||
|
||||
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.
|
||||
*/
|
||||
/* If data_toggle or index_toggle have changed state, this means that they've
|
||||
* gone high since the last sampleclock. */
|
||||
|
||||
index_edge <= index && !index_q;
|
||||
index_q <= index;
|
||||
index_edge <= index_toggle != old_index_toggle;
|
||||
old_index_toggle <= index_toggle;
|
||||
|
||||
rdata_edge <= rdata && !rdata_q;
|
||||
rdata_q <= rdata;
|
||||
rdata_edge <= rdata_toggle != old_rdata_toggle;
|
||||
old_rdata_toggle <= rdata_toggle;
|
||||
|
||||
if (rdata_edge || index_edge || (counter == 6'h3f)) begin
|
||||
opcode <= { rdata_edge, index_edge, counter };
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -147,7 +147,7 @@ static void set_drive_flags(struct set_drive_frame* flags)
|
|||
|
||||
current_drive_flags = *flags;
|
||||
DRIVESELECT_REG_Write(flags->drive ? 2 : 1); /* select drive 1 or 0 */
|
||||
DENSITY_REG_Write(flags->high_density); /* density bit */
|
||||
DENSITY_REG_Write(!flags->high_density); /* double density bit */
|
||||
INDEX_REG_Write(flags->index_mode);
|
||||
}
|
||||
|
||||
|
|
@ -221,7 +221,7 @@ static int usb_read(int ep, uint8_t buffer[FRAME_SIZE])
|
|||
static void cmd_get_version(struct any_frame* f)
|
||||
{
|
||||
DECLARE_REPLY_FRAME(struct version_frame, F_FRAME_GET_VERSION_REPLY);
|
||||
r.version = FLUXENGINE_VERSION;
|
||||
r.version = FLUXENGINE_PROTOCOL_VERSION;
|
||||
send_reply((struct any_frame*) &r);
|
||||
}
|
||||
|
||||
|
|
@ -937,3 +937,75 @@ int main(void)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t USBFS_MSOS_CONFIGURATION_DESCR[USBFS_MSOS_CONF_DESCR_LENGTH] = {
|
||||
/* Length of the descriptor 4 bytes */ 0x28u, 0x00u, 0x00u, 0x00u,
|
||||
/* Version of the descriptor 2 bytes */ 0x00u, 0x01u,
|
||||
/* wIndex - Fixed:INDEX_CONFIG_DESCRIPTOR */ 0x04u, 0x00u,
|
||||
/* bCount - Count of device functions. */ 0x01u,
|
||||
/* Reserved : 7 bytes */ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
||||
/* bFirstInterfaceNumber */ 0x00u,
|
||||
/* Reserved */ 0x01u,
|
||||
/* compatibleId - "WINUSB\0\0" */ 'W', 'I', 'N', 'U', 'S', 'B', 0, 0,
|
||||
/* subcompatibleID - "00001\0\0" */ '0', '0', '0', '0', '1',
|
||||
0x00u, 0x00u, 0x00u,
|
||||
/* Reserved : 6 bytes */ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u
|
||||
};
|
||||
|
||||
const uint8_t USBFS_MSOS_EXTENDED_PROPERTIES_DESCR[224] = {
|
||||
/* Length; 4 bytes */ 224, 0, 0, 0,
|
||||
/* Version; 2 bytes */ 0x00, 0x01, /* 1.0 */
|
||||
/* wIndex */ 0x05, 0x00,
|
||||
/* Number of sections */ 0x01, 0x00,
|
||||
/* Property section size */ 214, 0, 0, 0,
|
||||
/* Property data type */ 0x07, 0x00, 0x00, 0x00, /* 7 = REG_MULTI_SZ Unicode */
|
||||
/* Property name length */ 42, 0,
|
||||
/* Property name */ 'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0,
|
||||
'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0,
|
||||
'a', 0, 'c', 0, 'e', 0, 'G', 0, 'U', 0, 'I', 0,
|
||||
'D', 0, 's', 0, 0, 0,
|
||||
/* Property data length */ 158, 0, 0, 0,
|
||||
/* GUID #1 data */ '{', 0, '3', 0, 'd', 0, '2', 0, '7', 0, '5', 0,
|
||||
'c', 0, 'f', 0, 'e', 0, '-', 0, '5', 0, '4', 0,
|
||||
'3', 0, '5', 0, '-', 0, '4', 0, 'd', 0, 'd', 0,
|
||||
'5', 0, '-', 0, 'a', 0, 'c', 0, 'c', 0, 'a', 0,
|
||||
'-', 0, '9', 0, 'f', 0, 'b', 0, '9', 0, '9', 0,
|
||||
'5', 0, 'e', 0, '2', 0, 'f', 0, '6', 0, '3', 0,
|
||||
'8', 0, '}', 0, '\0', 0,
|
||||
/* GUID #2 data */ '{', 0, '3', 0, 'd', 0, '2', 0, '7', 0, '5', 0,
|
||||
'c', 0, 'f', 0, 'e', 0, '-', 0, '5', 0, '4', 0,
|
||||
'3', 0, '5', 0, '-', 0, '4', 0, 'd', 0, 'd', 0,
|
||||
'5', 0, '-', 0, 'a', 0, 'c', 0, 'c', 0, 'a', 0,
|
||||
'-', 0, '9', 0, 'f', 0, 'b', 0, '9', 0, '9', 0,
|
||||
'5', 0, 'e', 0, '2', 0, 'f', 0, '6', 0, '3', 0,
|
||||
'8', 0, '}', 0, '\0', 0, '\0', 0
|
||||
};
|
||||
|
||||
uint8 USBFS_HandleVendorRqst(void)
|
||||
{
|
||||
if (!(USBFS_bmRequestTypeReg & USBFS_RQST_DIR_D2H))
|
||||
return false;
|
||||
|
||||
switch (USBFS_bRequestReg)
|
||||
{
|
||||
case USBFS_GET_EXTENDED_CONFIG_DESCRIPTOR:
|
||||
switch (USBFS_wIndexLoReg)
|
||||
{
|
||||
case 4:
|
||||
USBFS_currentTD.pData = (volatile uint8 *) &USBFS_MSOS_CONFIGURATION_DESCR[0u];
|
||||
USBFS_currentTD.count = USBFS_MSOS_CONFIGURATION_DESCR[0u];
|
||||
return USBFS_InitControlRead();
|
||||
|
||||
case 5:
|
||||
USBFS_currentTD.pData = (volatile uint8 *) &USBFS_MSOS_EXTENDED_PROPERTIES_DESCR[0u];
|
||||
USBFS_currentTD.count = USBFS_MSOS_EXTENDED_PROPERTIES_DESCR[0u];
|
||||
return USBFS_InitControlRead();
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
const READ = 1
|
||||
const WRITE = 2
|
||||
|
||||
filename = "Generated_Source\PSoC5\USBFS_descr.c"
|
||||
set fso = CreateObject("Scripting.FileSystemObject")
|
||||
|
||||
set file = fso.OpenTextFile(filename, READ)
|
||||
text = file.ReadAll
|
||||
file.Close
|
||||
|
||||
set r = New RegExp
|
||||
r.MultiLine = True
|
||||
r.Pattern = "/\* +compatibleID.*\n.*"
|
||||
text = r.replace(text, "'W', 'I', 'N', 'U', 'S', 'B', 0, 0,")
|
||||
|
||||
set file = fso.CreateTextFile(filename, True)
|
||||
file.Write text
|
||||
file.Close
|
||||
53
Makefile
53
Makefile
|
|
@ -1,6 +1,9 @@
|
|||
PACKAGES = zlib sqlite3 libusb-1.0 protobuf
|
||||
PACKAGES = zlib sqlite3 protobuf
|
||||
|
||||
export CFLAGS = -x c++ --std=gnu++2a -ffunction-sections -fdata-sections \
|
||||
export CFLAGS = \
|
||||
-ffunction-sections -fdata-sections
|
||||
export CXXFLAGS = $(CFLAGS) \
|
||||
-x c++ --std=gnu++2a \
|
||||
-Wno-deprecated-enum-enum-conversion \
|
||||
-Wno-deprecated-enum-float-conversion
|
||||
export LDFLAGS = -pthread
|
||||
|
|
@ -11,15 +14,31 @@ export LDOPTFLAGS = -Os
|
|||
export CDBGFLAGS = -O0 -g
|
||||
export LDDBGFLAGS = -O0 -g
|
||||
|
||||
ifeq ($(OS), Windows_NT)
|
||||
else
|
||||
ifeq ($(shell uname),Darwin)
|
||||
else
|
||||
PACKAGES += libudev
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(OS), Windows_NT)
|
||||
export PROTOC = /mingw32/bin/protoc
|
||||
export CC = /mingw32/bin/gcc
|
||||
export CXX = /mingw32/bin/g++
|
||||
export AR = /mingw32/bin/ar rc
|
||||
export RANLIB = /mingw32/bin/ranlib
|
||||
export STRIP = /mingw32/bin/strip
|
||||
export CFLAGS += -I/mingw32/include/libusb-1.0 -I/mingw32/include
|
||||
export LDFLAGS +=
|
||||
export LIBS += -L/mingw32/lib -static -lz -lsqlite3 -lusb-1.0 -lprotobuf
|
||||
export CFLAGS += -I/mingw32/include
|
||||
export CXXFLAGS += $(shell wx-config --cxxflags --static=yes)
|
||||
export GUILDFLAGS += -lmingw32
|
||||
export LIBS += -L/mingw32/lib -static\
|
||||
-lsqlite3 -lz \
|
||||
-lsetupapi -lwinusb -lole32 -lprotobuf -luuid
|
||||
export GUILIBS += -L/mingw32/lib -static -lsqlite3 \
|
||||
$(shell wx-config --libs --static=yes core base) -lz \
|
||||
-lcomctl32 -loleaut32 -lspoolss -loleacc -lwinspool \
|
||||
-lsetupapi -lwinusb -lole32 -lprotobuf -luuid
|
||||
export EXTENSION = .exe
|
||||
else
|
||||
|
||||
|
|
@ -28,20 +47,31 @@ 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
|
||||
wx-exist = $(shell wx-config --cflags > /dev/null && echo yes)
|
||||
ifneq ($(wx-exist),yes)
|
||||
$(error You must have these wx-config installed)
|
||||
endif
|
||||
|
||||
export PROTOC = protoc
|
||||
export CC = gcc
|
||||
export CXX = g++
|
||||
export AR = ar rc
|
||||
export RANLIB = ranlib
|
||||
export STRIP = strip
|
||||
export CFLAGS += $(shell pkg-config --cflags $(PACKAGES))
|
||||
export CFLAGS += $(shell pkg-config --cflags $(PACKAGES)) $(shell wx-config --cxxflags)
|
||||
export LDFLAGS +=
|
||||
export LIBS += $(shell pkg-config --libs $(PACKAGES))
|
||||
export GUILIBS += $(shell wx-config --libs core base)
|
||||
export EXTENSION =
|
||||
|
||||
ifeq ($(shell uname),Darwin)
|
||||
AR = ar rcS
|
||||
RANLIB += -c -no_warning_for_no_symbols
|
||||
export CC = clang
|
||||
export CXX = clang++
|
||||
export COBJC = clang
|
||||
export LDFLAGS += -framework IOKit -framework CoreFoundation
|
||||
export CFLAGS += -Wno-deprecated-declarations
|
||||
endif
|
||||
|
||||
endif
|
||||
|
|
@ -51,10 +81,12 @@ CFLAGS += -Ilib -Idep/fmt -Iarch
|
|||
|
||||
export OBJDIR = .obj
|
||||
|
||||
.PHONY: all
|
||||
all: .obj/build.ninja
|
||||
@ninja -f .obj/build.ninja -k 0
|
||||
@ninja -f .obj/build.ninja
|
||||
@if command -v cscope > /dev/null; then cscope -bRq; fi
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@echo CLEAN
|
||||
@rm -rf $(OBJDIR)
|
||||
|
|
@ -64,3 +96,10 @@ clean:
|
|||
@mkdir -p $(OBJDIR)
|
||||
@sh $< > $@
|
||||
|
||||
.PHONY: compdb
|
||||
compdb:
|
||||
@ninja -f .obj/build.ninja -t compdb > compile_commands.json
|
||||
|
||||
.PHONY: fluxengine fluxengine-debug
|
||||
fluxengine fluxengine-debug: .obj/build.ninja
|
||||
@ninja -f .obj/build.ninja "$@"
|
||||
|
|
|
|||
41
README.md
41
README.md
|
|
@ -2,7 +2,7 @@ FluxEngine
|
|||
==========
|
||||
|
||||
(If you're reading this on GitHub, the formatting's a bit messed up. [Try the
|
||||
version on cowlark.com instead.](http://cowlark.com/fluxengine/)
|
||||
version on cowlark.com instead.](http://cowlark.com/fluxengine/))
|
||||
|
||||
**Breaking news!** As of 2021-05-21, the command line environment has changed
|
||||
_substantially_ (to make it more consistent and flexible, and allow some new
|
||||
|
|
@ -37,11 +37,6 @@ 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.
|
||||
|
||||
**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?
|
||||
------
|
||||
|
||||
|
|
@ -66,14 +61,18 @@ following friendly articles:
|
|||
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
|
||||
software](doc/greaseweazle.md) ∾ what works ∾ what doesn't work ∾ where to
|
||||
go for help
|
||||
|
||||
- [Configuring for your drive](doc/drives.md) ∾ but I don't have a 80 track
|
||||
drive! ∾ reading and writing 40 track disks ∾ Shugart and Apple II
|
||||
|
||||
- [Troubleshooting dubious disks](doc/problems.md) ∾ it's not an exact
|
||||
science ∾ the sector map ∾ clock detection and the histogram
|
||||
science ∾ the sector map ∾ clock detection and the histogram
|
||||
|
||||
- [Checking your drive](doc/driveresponse.md) ∾ you can't do that with that ∾
|
||||
measuring your drive's ability to work with exotic formats
|
||||
- [Disk densities](doc/driveresponse.md) ∾ what's the difference between an HD
|
||||
and DD disk? ∾ you can't do that with that ∾ measuring your drive's ability to
|
||||
work with exotic formats ∾ I think my drive is broken
|
||||
|
||||
Which?
|
||||
------
|
||||
|
|
@ -93,16 +92,21 @@ people who've had it work).
|
|||
| Format | Read? | Write? | Notes |
|
||||
|:------------------------------------------|:-----:|:------:|-------|
|
||||
| [IBM PC compatible](doc/disk-ibm.md) | 🦄 | 🦄 | and compatibles (like the Atari ST) |
|
||||
| [Atari ST](doc/disk-atarist.md) | 🦄 | 🦄 | technically the same as IBM, almost |
|
||||
| [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 |
|
||||
| [Agat](doc/disk-agat.md) | 🦖 | | Soviet Union Apple-II-like computer |
|
||||
| [Apple II](doc/disk-apple2.md) | 🦄 | 🦄 | |
|
||||
| [Amiga](doc/disk-amiga.md) | 🦄 | 🦄 | |
|
||||
| [Commodore 64 1541/1581](doc/disk-c64.md) | 🦄 | 🦄 | and probably the other formats |
|
||||
| [Brother 120kB](doc/disk-brother.md) | 🦄 | 🦖 | |
|
||||
| [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 |
|
||||
| [Elektronika BK](doc/disk-bd.md) | 🦄 | 🦄 | Soviet Union PDP-11 clone |
|
||||
| [Macintosh 400kB/800kB](doc/disk-macintosh.md) | 🦄 | 🦄 | |
|
||||
| [NEC PC-98](doc/disk-ibm.md) | 🦄 | 🦄 | trimode drive not required |
|
||||
| [Sharp X68000](doc/disk-ibm.md) | 🦄 | 🦄 | |
|
||||
| [TRS-80](doc/disk-trs80.md) | 🦖 | 🦖* | a minor variation of the IBM scheme |
|
||||
{: .datatable }
|
||||
|
||||
|
|
@ -125,9 +129,9 @@ at least, check the CRC so what data's there is probably good.
|
|||
| [DVK MX](doc/disk-mx.md) | 🦖 | | Soviet PDP-11 clone |
|
||||
| [VDS Eco1](doc/disk-eco1.md) | 🦖 | | 8" mixed format |
|
||||
| [Micropolis](doc/disk-micropolis.md) | 🦄 | | Micropolis 100tpi drives |
|
||||
| [Northstar(doc/disk-northstar.md) | 🦖 | 🦖 | 5.25" hard sectors |
|
||||
| [Northstar](doc/disk-northstar.md) | 🦖 | 🦖 | 5.25" hard sectors |
|
||||
| [TI DS990 FD1000](doc/disk-tids990.md) | 🦄 | 🦄 | 8" |
|
||||
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8" |
|
||||
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 5.25" GCR encoded |
|
||||
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8" _and_ hard sectors |
|
||||
{: .datatable }
|
||||
|
||||
|
|
@ -146,7 +150,7 @@ at least, check the CRC so what data's there is probably good.
|
|||
There hasn't been a lot of demand for this yet; if you have a pressing
|
||||
need to write weird disks, [please
|
||||
ask](https://github.com/davidgiven/fluxengine/issues/new). I haven't
|
||||
implement write support for PC disks because they're boring and I'm lazy,
|
||||
implemented write support for PC disks because they're boring and I'm lazy,
|
||||
and also because they vary so much that figuring out how to specify them
|
||||
is hard.
|
||||
|
||||
|
|
@ -230,3 +234,6 @@ As an exception, `dep/snowhouse` contains the snowhouse assertion library,
|
|||
taken from https://github.com/banditcpp/snowhouse. It is Boost Standard License
|
||||
1.0 licensed. Please see the contents of the directory for the full text.
|
||||
|
||||
As an exception, `dep/libusbp` contains the libusbp library, taken from
|
||||
https://github.com/pololu/libusbp. It is MIT licensed. Please see the contents
|
||||
of the directory for the full text.
|
||||
|
|
|
|||
|
|
@ -30,17 +30,14 @@ public:
|
|||
AbstractDecoder(config)
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
|
||||
if (_fmr->eof() || !_sector->clock)
|
||||
return UNKNOWN_RECORD;
|
||||
return SECTOR_RECORD;
|
||||
return seekToPattern(SECTOR_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
/* Skip ID mark. */
|
||||
/* Skip ID mark (we know it's a AESLANIER_RECORD_SEPARATOR). */
|
||||
|
||||
readRawBits(16);
|
||||
|
||||
|
|
|
|||
22
arch/agat/agat.cc
Normal file
22
arch/agat/agat.cc
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#include "globals.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "agat.h"
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
uint8_t agatChecksum(const Bytes& bytes)
|
||||
{
|
||||
uint16_t checksum = 0;
|
||||
|
||||
for (uint8_t b : bytes)
|
||||
{
|
||||
if (checksum > 0xff)
|
||||
checksum = (checksum + 1) & 0xff;
|
||||
|
||||
checksum += b;
|
||||
}
|
||||
|
||||
return checksum & 0xff;
|
||||
}
|
||||
|
||||
|
||||
11
arch/agat/agat.h
Normal file
11
arch/agat/agat.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef AGAT_H
|
||||
#define AGAT_H
|
||||
|
||||
#define AGAT_SECTOR_SIZE 256
|
||||
|
||||
extern std::unique_ptr<AbstractDecoder> createAgatDecoder(const DecoderProto& config);
|
||||
|
||||
extern uint8_t agatChecksum(const Bytes& bytes);
|
||||
|
||||
#endif
|
||||
|
||||
5
arch/agat/agat.proto
Normal file
5
arch/agat/agat.proto
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
syntax = "proto2";
|
||||
|
||||
message AgatDecoderProto {}
|
||||
|
||||
|
||||
97
arch/agat/decoder.cc
Normal file
97
arch/agat/decoder.cc
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#include "globals.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "agat.h"
|
||||
#include "crc.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "sector.h"
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* data: X X X X X X X X X - - X - X - X - X X - X - X - = 0xff956a
|
||||
* flux: 01 01 01 01 01 01 01 01 01 00 10 01 00 01 00 01 00 01 01 00 01 00 01 00 = 0x555549111444
|
||||
*
|
||||
* data: X X X X X X X X - X X - X - X - X - - X - X - X = 0xff6a95
|
||||
* flux: 01 01 01 01 01 01 01 01 00 01 01 00 01 00 01 00 01 00 10 01 00 01 00 01 = 0x555514444911
|
||||
*
|
||||
* Each pattern is prefixed with this one:
|
||||
*
|
||||
* data: - - - X - - X - = 0x12
|
||||
* flux: (10) 10 10 10 01 00 10 01 00 = 0xa924
|
||||
* magic: (10) 10 00 10 01 00 10 01 00 = 0x8924
|
||||
* ^
|
||||
*
|
||||
* This seems to be generated by emitting A4 in MFM and then a single 0 bit to
|
||||
* shift it out of phase, so the data bits become clock bits and vice versa.
|
||||
*
|
||||
* X - X - - X - - = 0xA4
|
||||
* 0100010010010010 = MFM encoded
|
||||
* 1000100100100100 = with trailing zero
|
||||
* - - - X - - X - = effective bitstream = 0x12
|
||||
*
|
||||
*/
|
||||
|
||||
static const uint64_t SECTOR_ID = 0x8924555549111444;
|
||||
static const FluxPattern SECTOR_PATTERN(64, SECTOR_ID);
|
||||
|
||||
static const uint64_t DATA_ID = 0x8924555514444911;
|
||||
static const FluxPattern DATA_PATTERN(64, DATA_ID);
|
||||
|
||||
static const FluxMatchers ALL_PATTERNS = {
|
||||
&SECTOR_PATTERN,
|
||||
&DATA_PATTERN
|
||||
};
|
||||
|
||||
class AgatDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
AgatDecoder(const DecoderProto& config):
|
||||
AbstractDecoder(config)
|
||||
{}
|
||||
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
return seekToPattern(ALL_PATTERNS);
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
if (readRaw64() != SECTOR_ID)
|
||||
return;
|
||||
|
||||
auto bytes = decodeFmMfm(readRawBits(64)).slice(0, 4);
|
||||
if (bytes[3] != 0x5a)
|
||||
return;
|
||||
|
||||
_sector->logicalTrack = bytes[1] >> 1;
|
||||
_sector->logicalSector = bytes[2];
|
||||
_sector->logicalSide = bytes[1] & 1;
|
||||
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
if (readRaw64() != DATA_ID)
|
||||
return;
|
||||
|
||||
Bytes bytes = decodeFmMfm(readRawBits((AGAT_SECTOR_SIZE+2)*16)).slice(0, AGAT_SECTOR_SIZE+2);
|
||||
|
||||
if (bytes[AGAT_SECTOR_SIZE+1] != 0x5a)
|
||||
return;
|
||||
|
||||
_sector->data = bytes.slice(0, AGAT_SECTOR_SIZE);
|
||||
uint8_t wantChecksum = bytes[AGAT_SECTOR_SIZE];
|
||||
uint8_t gotChecksum = agatChecksum(_sector->data);
|
||||
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractDecoder> createAgatDecoder(const DecoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractDecoder>(new AgatDecoder(config));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#define AMIGA_TRACKS_PER_DISK 80
|
||||
#define AMIGA_SECTORS_PER_TRACK 11
|
||||
#define AMIGA_RECORD_SIZE 0x21f
|
||||
#define AMIGA_RECORD_SIZE 0x21c
|
||||
|
||||
extern std::unique_ptr<AbstractDecoder> createAmigaDecoder(const DecoderProto& config);
|
||||
extern std::unique_ptr<AbstractEncoder> createAmigaEncoder(const EncoderProto& config);
|
||||
|
|
|
|||
|
|
@ -29,23 +29,23 @@ public:
|
|||
_config(config.amiga())
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord() override
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
|
||||
if (_fmr->eof() || !_sector->clock)
|
||||
return UNKNOWN_RECORD;
|
||||
return SECTOR_RECORD;
|
||||
return seekToPattern(SECTOR_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
if (readRaw48() != AMIGA_SECTOR_RECORD)
|
||||
return;
|
||||
|
||||
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;
|
||||
const uint8_t* ptr = bytes.begin();
|
||||
|
||||
Bytes header = amigaDeinterleave(ptr, 4);
|
||||
Bytes recoveryinfo = amigaDeinterleave(ptr, 16);
|
||||
|
|
@ -55,12 +55,12 @@ public:
|
|||
_sector->logicalSector = header[2];
|
||||
|
||||
uint32_t wantedheaderchecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(6, 40));
|
||||
uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(0, 40));
|
||||
if (gotheaderchecksum != wantedheaderchecksum)
|
||||
return;
|
||||
|
||||
uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(62, 1024));
|
||||
uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(56, 1024));
|
||||
|
||||
Bytes data;
|
||||
data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo);
|
||||
|
|
@ -68,7 +68,7 @@ public:
|
|||
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
|
||||
std::set<unsigned> requiredSectors(const Location&) const override
|
||||
{
|
||||
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
||||
return sectors;
|
||||
|
|
@ -76,6 +76,7 @@ public:
|
|||
|
||||
private:
|
||||
const AmigaDecoderProto& _config;
|
||||
nanoseconds_t _clock;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractDecoder> createAmigaDecoder(const DecoderProto& config)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
#include "encoders/encoders.h"
|
||||
#include "amiga.h"
|
||||
#include "crc.h"
|
||||
#include "writer.h"
|
||||
#include "readerwriter.h"
|
||||
#include "image.h"
|
||||
#include "mapper.h"
|
||||
#include "arch/amiga/amiga.pb.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
|
||||
|
|
@ -12,151 +13,159 @@ static bool lastBit;
|
|||
|
||||
static int charToInt(char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
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)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||
{
|
||||
for (bool bit : src)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = bit;
|
||||
}
|
||||
for (bool bit : src)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = bit;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, uint64_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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
{
|
||||
ByteReader br(bytes);
|
||||
BitReader bitr(br);
|
||||
ByteReader br(bytes);
|
||||
BitReader bitr(br);
|
||||
|
||||
while (!bitr.eof())
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bitr.get();
|
||||
}
|
||||
while (!bitr.eof())
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bitr.get();
|
||||
}
|
||||
}
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<Sector>& sector)
|
||||
static void write_sector(std::vector<bool>& bits,
|
||||
unsigned& cursor,
|
||||
const std::shared_ptr<const Sector>& sector)
|
||||
{
|
||||
if ((sector->data.size() != 512) && (sector->data.size() != 528))
|
||||
Error() << "unsupported sector size --- you must pick 512 or 528";
|
||||
if ((sector->data.size() != 512) && (sector->data.size() != 528))
|
||||
Error() << "unsupported sector size --- you must pick 512 or 528";
|
||||
|
||||
uint32_t checksum = 0;
|
||||
uint32_t checksum = 0;
|
||||
|
||||
auto write_interleaved_bytes = [&](const Bytes& bytes)
|
||||
{
|
||||
Bytes interleaved = amigaInterleave(bytes);
|
||||
Bytes mfm = encodeMfm(interleaved, lastBit);
|
||||
checksum ^= amigaChecksum(mfm);
|
||||
checksum &= 0x55555555;
|
||||
write_bits(bits, cursor, mfm);
|
||||
};
|
||||
auto write_interleaved_bytes = [&](const Bytes& bytes)
|
||||
{
|
||||
Bytes interleaved = amigaInterleave(bytes);
|
||||
Bytes mfm = encodeMfm(interleaved, lastBit);
|
||||
checksum ^= amigaChecksum(mfm);
|
||||
checksum &= 0x55555555;
|
||||
write_bits(bits, cursor, mfm);
|
||||
};
|
||||
|
||||
auto write_interleaved_word = [&](uint32_t word)
|
||||
{
|
||||
Bytes b(4);
|
||||
b.writer().write_be32(word);
|
||||
write_interleaved_bytes(b);
|
||||
};
|
||||
auto write_interleaved_word = [&](uint32_t word)
|
||||
{
|
||||
Bytes b(4);
|
||||
b.writer().write_be32(word);
|
||||
write_interleaved_bytes(b);
|
||||
};
|
||||
|
||||
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6*8);
|
||||
write_bits(bits, cursor, 0xaaaa, 2 * 8);
|
||||
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6 * 8);
|
||||
|
||||
checksum = 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(header);
|
||||
Bytes recoveryInfo(16);
|
||||
if (sector->data.size() == 528)
|
||||
recoveryInfo = sector->data.slice(512, 16);
|
||||
write_interleaved_bytes(recoveryInfo);
|
||||
write_interleaved_word(checksum);
|
||||
checksum = 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(header);
|
||||
Bytes recoveryInfo(16);
|
||||
if (sector->data.size() == 528)
|
||||
recoveryInfo = sector->data.slice(512, 16);
|
||||
write_interleaved_bytes(recoveryInfo);
|
||||
write_interleaved_word(checksum);
|
||||
|
||||
Bytes data = sector->data.slice(0, 512);
|
||||
write_interleaved_word(amigaChecksum(encodeMfm(amigaInterleave(data), lastBit)));
|
||||
write_interleaved_bytes(data);
|
||||
Bytes data = sector->data.slice(0, 512);
|
||||
write_interleaved_word(
|
||||
amigaChecksum(encodeMfm(amigaInterleave(data), lastBit)));
|
||||
write_interleaved_bytes(data);
|
||||
}
|
||||
|
||||
class AmigaEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
AmigaEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.amiga()) {}
|
||||
AmigaEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.amiga())
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
if ((physicalTrack >= 0) && (physicalTrack < AMIGA_TRACKS_PER_DISK))
|
||||
{
|
||||
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
if ((location.logicalTrack >= 0) &&
|
||||
(location.logicalTrack < AMIGA_TRACKS_PER_DISK))
|
||||
{
|
||||
for (int sectorId = 0; sectorId < AMIGA_SECTORS_PER_TRACK;
|
||||
sectorId++)
|
||||
{
|
||||
const auto& sector =
|
||||
image.get(location.logicalTrack, location.head, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
/* Number of bits for one nominal revolution of a real 200ms Amiga disk. */
|
||||
int bitsPerRevolution = 200e3 / _config.clock_rate_us();
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
fillBitmapTo(bits,
|
||||
cursor,
|
||||
_config.post_index_gap_ms() * 1000 / _config.clock_rate_us(),
|
||||
{true, false});
|
||||
lastBit = false;
|
||||
|
||||
fillBitmapTo(bits, cursor, _config.post_index_gap_ms() * 1000 / _config.clock_rate_us(), { true, false });
|
||||
lastBit = false;
|
||||
for (const auto& sector : sectors)
|
||||
write_sector(bits, cursor, sector);
|
||||
|
||||
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
|
||||
{
|
||||
const auto& sectorData = image.get(physicalTrack, physicalSide, sectorId);
|
||||
if (sectorData)
|
||||
write_sector(bits, cursor, sectorData);
|
||||
}
|
||||
if (cursor >= bits.size())
|
||||
Error() << "track data overrun";
|
||||
fillBitmapTo(bits, cursor, bits.size(), {true, false});
|
||||
|
||||
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, _config.clock_rate_us()*1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
auto fluxmap = std::make_unique<Fluxmap>();
|
||||
fluxmap->appendBits(bits,
|
||||
Mapper::calculatePhysicalClockPeriod(
|
||||
_config.clock_rate_us() * 1e3, 200e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const AmigaEncoderProto& _config;
|
||||
const AmigaEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createAmigaEncoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new AmigaEncoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new AmigaEncoder(config));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
#ifndef APPLE2_H
|
||||
#define APPLE2_H
|
||||
|
||||
#include <memory.h>
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
|
||||
#define APPLE2_SECTOR_RECORD 0xd5aa96
|
||||
#define APPLE2_DATA_RECORD 0xd5aaad
|
||||
|
||||
#define APPLE2_SECTOR_LENGTH 256
|
||||
#define APPLE2_ENCODED_SECTOR_LENGTH 342
|
||||
|
||||
#define APPLE2_SECTORS 16
|
||||
|
||||
extern std::unique_ptr<AbstractDecoder> createApple2Decoder(const DecoderProto& config);
|
||||
extern std::unique_ptr<AbstractEncoder> createApple2Encoder(const EncoderProto& config);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,16 @@
|
|||
syntax = "proto2";
|
||||
|
||||
import "lib/common.proto";
|
||||
|
||||
message Apple2DecoderProto {}
|
||||
|
||||
message Apple2EncoderProto
|
||||
{
|
||||
/* 245kHz. */
|
||||
optional double clock_period_us = 1
|
||||
[ default = 4, (help) = "clock rate on the real device" ];
|
||||
|
||||
/* Apple II disk drives spin at 300rpm. */
|
||||
optional double rotational_period_ms = 2
|
||||
[ default = 200.0, (help) = "rotational period on the real device" ];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,21 +71,15 @@ public:
|
|||
AbstractDecoder(config)
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
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;
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
/* Skip ID (as we know it's a APPLE2_SECTOR_RECORD). */
|
||||
readRawBits(24);
|
||||
if (readRaw24() != APPLE2_SECTOR_RECORD)
|
||||
return;
|
||||
|
||||
/* Read header. */
|
||||
|
||||
|
|
@ -96,26 +90,68 @@ public:
|
|||
_sector->logicalTrack = combine(br.read_be16());
|
||||
_sector->logicalSector = combine(br.read_be16());
|
||||
uint8_t checksum = combine(br.read_be16());
|
||||
|
||||
// If the checksum is correct, upgrade the sector from MISSING
|
||||
// to DATA_MISSING in anticipation of its data record
|
||||
if (checksum == (volume ^ _sector->logicalTrack ^ _sector->logicalSector))
|
||||
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
void decodeDataRecord()
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
/* Check ID. */
|
||||
|
||||
Bytes bytes = toBytes(readRawBits(3*8)).slice(0, 3);
|
||||
if (bytes.reader().read_be24() != APPLE2_DATA_RECORD)
|
||||
if (readRaw24() != APPLE2_DATA_RECORD)
|
||||
return;
|
||||
|
||||
// Sometimes there's a 1-bit gap between APPLE2_DATA_RECORD and
|
||||
// the data itself. This has been seen on real world disks
|
||||
// such as the Apple II Operating System Kit from Apple2Online.
|
||||
// However, I haven't seen it described in any of the various
|
||||
// references.
|
||||
//
|
||||
// This extra '0' bit would not affect the real disk interface,
|
||||
// as it was a '1' reaching the top bit of a shift register
|
||||
// that triggered a byte to be available, but it affects the
|
||||
// way the data is read here.
|
||||
//
|
||||
// While the floppies tested only seemed to need this applied
|
||||
// to the first byte of the data record, applying it
|
||||
// consistently to all of them doesn't seem to hurt, and
|
||||
// simplifies the code.
|
||||
|
||||
/* Read and decode data. */
|
||||
|
||||
unsigned recordLength = APPLE2_ENCODED_SECTOR_LENGTH + 2;
|
||||
bytes = toBytes(readRawBits(recordLength*8)).slice(0, recordLength);
|
||||
auto readApple8 = [&]() {
|
||||
auto result = 0;
|
||||
while((result & 0x80) == 0) {
|
||||
auto b = readRawBits(1);
|
||||
if(b.empty()) break;
|
||||
result = (result << 1) | b[0];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
constexpr unsigned recordLength = APPLE2_ENCODED_SECTOR_LENGTH+2;
|
||||
uint8_t bytes[recordLength];
|
||||
for(auto &byte : bytes) {
|
||||
byte = readApple8();
|
||||
}
|
||||
|
||||
// Upgrade the sector from MISSING to BAD_CHECKSUM.
|
||||
// If decode_crazy_data succeeds, it upgrades the sector to
|
||||
// OK.
|
||||
_sector->status = Sector::BAD_CHECKSUM;
|
||||
_sector->data = decode_crazy_data(&bytes[0], _sector->status);
|
||||
}
|
||||
|
||||
std::set<unsigned> requiredSectors(const Location& location) const override
|
||||
{
|
||||
std::set<unsigned> sectors;
|
||||
for (int sectorId = 0; sectorId < APPLE2_SECTORS; sectorId++)
|
||||
sectors.insert(sectorId);
|
||||
return sectors;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractDecoder> createApple2Decoder(const DecoderProto& config)
|
||||
|
|
|
|||
210
arch/apple2/encoder.cc
Normal file
210
arch/apple2/encoder.cc
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
#include "globals.h"
|
||||
#include "arch/apple2/apple2.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "sector.h"
|
||||
#include "readerwriter.h"
|
||||
#include "image.h"
|
||||
#include "mapper.h"
|
||||
#include "fmt/format.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
#include <ctype.h>
|
||||
#include "bytes.h"
|
||||
|
||||
static int encode_data_gcr(uint8_t data)
|
||||
{
|
||||
switch (data)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: \
|
||||
return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
class Apple2Encoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
Apple2Encoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.apple2())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const Apple2EncoderProto& _config;
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
if (location.head == 0)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < APPLE2_SECTORS; sectorId++)
|
||||
{
|
||||
const auto& sector =
|
||||
image.get(location.logicalTrack, 0, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution =
|
||||
(_config.rotational_period_ms() * 1e3) / _config.clock_period_us();
|
||||
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
for (const auto& sector : sectors)
|
||||
writeSector(bits, cursor, *sector);
|
||||
|
||||
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,
|
||||
Mapper::calculatePhysicalClockPeriod(
|
||||
_config.clock_period_us() * 1e3,
|
||||
_config.rotational_period_ms() * 1e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t volume_id = 254;
|
||||
|
||||
/* This is extremely inspired by the MESS implementation, written by Nathan
|
||||
* Woods and R. Belmont:
|
||||
* https://github.com/mamedev/mame/blob/7914a6083a3b3a8c243ae6c3b8cb50b023f21e0e/src/lib/formats/ap2_dsk.cpp
|
||||
* as well as Understanding the Apple II (1983) Chapter 9
|
||||
* https://archive.org/details/Understanding_the_Apple_II_1983_Quality_Software/page/n230/mode/1up?view=theater
|
||||
*/
|
||||
|
||||
void writeSector(
|
||||
std::vector<bool>& bits, unsigned& cursor, const Sector& sector) const
|
||||
{
|
||||
if ((sector.status == Sector::OK) or
|
||||
(sector.status == Sector::BAD_CHECKSUM))
|
||||
{
|
||||
auto write_bit = [&](bool val)
|
||||
{
|
||||
if (cursor <= bits.size())
|
||||
{
|
||||
bits[cursor] = val;
|
||||
}
|
||||
cursor++;
|
||||
};
|
||||
|
||||
auto write_bits = [&](uint32_t bits, int width)
|
||||
{
|
||||
for (int i = width; i--;)
|
||||
{
|
||||
write_bit(bits & (1u << i));
|
||||
}
|
||||
};
|
||||
|
||||
auto write_gcr44 = [&](uint8_t value)
|
||||
{
|
||||
write_bits((value << 7) | value | 0xaaaa, 16);
|
||||
};
|
||||
|
||||
auto write_gcr6 = [&](uint8_t value)
|
||||
{
|
||||
write_bits(encode_data_gcr(value), 8);
|
||||
};
|
||||
|
||||
// The special "FF40" sequence is used to synchronize the receiving
|
||||
// shift register. It's written as "1111 1111 00"; FF indicates the
|
||||
// 8 consecutive 1-bits, while "40" indicates the total number of
|
||||
// microseconds.
|
||||
auto write_ff40 = [&](int n = 1)
|
||||
{
|
||||
for (; n--;)
|
||||
{
|
||||
write_bits(0xff << 2, 10);
|
||||
}
|
||||
};
|
||||
|
||||
// There is data to encode to disk.
|
||||
if ((sector.data.size() != APPLE2_SECTOR_LENGTH))
|
||||
Error() << fmt::format(
|
||||
"unsupported sector size {} --- you must pick 256",
|
||||
sector.data.size());
|
||||
|
||||
// Write address syncing leader : A sequence of "FF40"s; 5 of them
|
||||
// are said to suffice to synchronize the decoder.
|
||||
// "FF40" indicates that the actual data written is "1111
|
||||
// 1111 00" i.e., 8 1s and a total of 40 microseconds
|
||||
//
|
||||
// In standard formatting, the first logical sector apparently gets
|
||||
// extra padding.
|
||||
write_ff40(sector.logicalSector == 0 ? 32 : 8);
|
||||
|
||||
// Write address field: APPLE2_SECTOR_RECORD + sector identifier +
|
||||
// DE AA EB
|
||||
write_bits(APPLE2_SECTOR_RECORD, 24);
|
||||
write_gcr44(volume_id);
|
||||
write_gcr44(sector.logicalTrack);
|
||||
write_gcr44(sector.logicalSector);
|
||||
write_gcr44(volume_id ^ sector.logicalTrack ^ sector.logicalSector);
|
||||
write_bits(0xDEAAEB, 24);
|
||||
|
||||
// Write data syncing leader: FF40 + APPLE2_DATA_RECORD + sector
|
||||
// data + sum + DE AA EB (+ mystery bits cut off of the scan?)
|
||||
write_ff40(8);
|
||||
write_bits(APPLE2_DATA_RECORD, 24);
|
||||
|
||||
// Convert the sector data to GCR, append the checksum, and write it
|
||||
// out
|
||||
constexpr auto TWOBIT_COUNT =
|
||||
0x56; // Size of the 'twobit' area at the start of the GCR data
|
||||
uint8_t checksum = 0;
|
||||
for (int i = 0; i < APPLE2_ENCODED_SECTOR_LENGTH; i++)
|
||||
{
|
||||
int value;
|
||||
if (i >= TWOBIT_COUNT)
|
||||
{
|
||||
value = sector.data[i - TWOBIT_COUNT] >> 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t tmp = sector.data[i];
|
||||
value = ((tmp & 1) << 1) | ((tmp & 2) >> 1);
|
||||
|
||||
tmp = sector.data[i + TWOBIT_COUNT];
|
||||
value |= ((tmp & 1) << 3) | ((tmp & 2) << 1);
|
||||
|
||||
if (i + 2 * TWOBIT_COUNT < APPLE2_SECTOR_LENGTH)
|
||||
{
|
||||
tmp = sector.data[i + 2 * TWOBIT_COUNT];
|
||||
value |= ((tmp & 1) << 5) | ((tmp & 2) << 3);
|
||||
}
|
||||
}
|
||||
checksum ^= value;
|
||||
// assert(checksum & ~0x3f == 0);
|
||||
write_gcr6(checksum);
|
||||
checksum = value;
|
||||
}
|
||||
if (sector.status == Sector::BAD_CHECKSUM)
|
||||
checksum ^= 0x3f;
|
||||
write_gcr6(checksum);
|
||||
write_bits(0xDEAAEB, 24);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createApple2Encoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new Apple2Encoder(config));
|
||||
}
|
||||
|
|
@ -15,6 +15,5 @@ message BrotherEncoderProto {
|
|||
optional string sector_skew = 5 [default = "05a3816b4927"];
|
||||
|
||||
optional BrotherFormat format = 6 [default = BROTHER240];
|
||||
optional int32 bias = 7 [default = 0];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include "globals.h"
|
||||
#include "sql.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "decoders/decoders.h"
|
||||
|
|
@ -60,20 +59,16 @@ public:
|
|||
AbstractDecoder(config)
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
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;
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
readRawBits(32);
|
||||
if (readRaw32() != BROTHER_SECTOR_RECORD)
|
||||
return;
|
||||
|
||||
const auto& rawbits = readRawBits(32);
|
||||
const auto& bytes = toBytes(rawbits).slice(0, 4);
|
||||
|
||||
|
|
@ -91,9 +86,10 @@ public:
|
|||
_sector->status = Sector::DATA_MISSING;
|
||||
}
|
||||
|
||||
void decodeDataRecord()
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
readRawBits(32);
|
||||
if (readRaw32() != BROTHER_DATA_RECORD)
|
||||
return;
|
||||
|
||||
const auto& rawbits = readRawBits(BROTHER_DATA_RECORD_ENCODED_SIZE*8);
|
||||
const auto& rawbytes = toBytes(rawbits).slice(0, BROTHER_DATA_RECORD_ENCODED_SIZE);
|
||||
|
|
|
|||
|
|
@ -3,91 +3,95 @@
|
|||
#include "encoders/encoders.h"
|
||||
#include "brother.h"
|
||||
#include "crc.h"
|
||||
#include "writer.h"
|
||||
#include "readerwriter.h"
|
||||
#include "image.h"
|
||||
#include "arch/brother/brother.pb.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
|
||||
static int encode_header_gcr(uint16_t word)
|
||||
{
|
||||
switch (word)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: return gcr;
|
||||
#include "header_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
switch (word)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: \
|
||||
return gcr;
|
||||
#include "header_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int encode_data_gcr(uint8_t data)
|
||||
{
|
||||
switch (data)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
switch (data)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: \
|
||||
return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width)
|
||||
{
|
||||
cursor += width;
|
||||
for (int i=0; i<width; i++)
|
||||
{
|
||||
unsigned pos = cursor - i - 1;
|
||||
if (pos < bits.size())
|
||||
bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
cursor += width;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
unsigned pos = cursor - i - 1;
|
||||
if (pos < bits.size())
|
||||
bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_sector_header(std::vector<bool>& bits, unsigned& cursor,
|
||||
int track, int sector)
|
||||
static void write_sector_header(
|
||||
std::vector<bool>& bits, unsigned& cursor, int track, int sector)
|
||||
{
|
||||
write_bits(bits, cursor, 0xffffffff, 31);
|
||||
write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32);
|
||||
write_bits(bits, cursor, encode_header_gcr(track), 16);
|
||||
write_bits(bits, cursor, encode_header_gcr(sector), 16);
|
||||
write_bits(bits, cursor, encode_header_gcr(0x2f), 16);
|
||||
write_bits(bits, cursor, 0xffffffff, 31);
|
||||
write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32);
|
||||
write_bits(bits, cursor, encode_header_gcr(track), 16);
|
||||
write_bits(bits, cursor, encode_header_gcr(sector), 16);
|
||||
write_bits(bits, cursor, encode_header_gcr(0x2f), 16);
|
||||
}
|
||||
|
||||
static void write_sector_data(std::vector<bool>& bits, unsigned& cursor, const Bytes& data)
|
||||
static void write_sector_data(
|
||||
std::vector<bool>& bits, unsigned& cursor, const Bytes& data)
|
||||
{
|
||||
write_bits(bits, cursor, 0xffffffff, 32);
|
||||
write_bits(bits, cursor, BROTHER_DATA_RECORD, 32);
|
||||
write_bits(bits, cursor, 0xffffffff, 32);
|
||||
write_bits(bits, cursor, BROTHER_DATA_RECORD, 32);
|
||||
|
||||
uint16_t fifo = 0;
|
||||
int width = 0;
|
||||
uint16_t fifo = 0;
|
||||
int width = 0;
|
||||
|
||||
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
|
||||
Error() << "unsupported sector size";
|
||||
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
|
||||
Error() << "unsupported sector size";
|
||||
|
||||
auto write_byte = [&](uint8_t byte)
|
||||
{
|
||||
fifo |= (byte << (8 - width));
|
||||
width += 8;
|
||||
auto write_byte = [&](uint8_t byte)
|
||||
{
|
||||
fifo |= (byte << (8 - width));
|
||||
width += 8;
|
||||
|
||||
while (width >= 5)
|
||||
{
|
||||
uint8_t quintet = fifo >> 11;
|
||||
fifo <<= 5;
|
||||
width -= 5;
|
||||
while (width >= 5)
|
||||
{
|
||||
uint8_t quintet = fifo >> 11;
|
||||
fifo <<= 5;
|
||||
width -= 5;
|
||||
|
||||
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
|
||||
}
|
||||
};
|
||||
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
|
||||
}
|
||||
};
|
||||
|
||||
for (uint8_t byte : data)
|
||||
write_byte(byte);
|
||||
for (uint8_t byte : data)
|
||||
write_byte(byte);
|
||||
|
||||
uint32_t realCrc = crcbrother(data);
|
||||
write_byte(realCrc>>16);
|
||||
write_byte(realCrc>>8);
|
||||
write_byte(realCrc);
|
||||
write_byte(0x58); /* magic */
|
||||
uint32_t realCrc = crcbrother(data);
|
||||
write_byte(realCrc >> 16);
|
||||
write_byte(realCrc >> 8);
|
||||
write_byte(realCrc);
|
||||
write_byte(0x58); /* magic */
|
||||
write_byte(0xd4);
|
||||
while (width != 0)
|
||||
write_byte(0);
|
||||
|
|
@ -95,118 +99,98 @@ static void write_sector_data(std::vector<bool>& bits, unsigned& cursor, const B
|
|||
|
||||
static int charToInt(char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
}
|
||||
|
||||
class BrotherEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
BrotherEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.brother())
|
||||
{}
|
||||
BrotherEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.brother())
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location,
|
||||
const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
int logicalTrack;
|
||||
if (physicalSide != 0)
|
||||
return sectors;
|
||||
physicalTrack -= _config.bias();
|
||||
switch (_config.format())
|
||||
{
|
||||
case BROTHER120:
|
||||
if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2))
|
||||
|| (physicalTrack & 1))
|
||||
return sectors;
|
||||
logicalTrack = physicalTrack/2;
|
||||
break;
|
||||
if (location.head != 0)
|
||||
return sectors;
|
||||
|
||||
case BROTHER240:
|
||||
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
|
||||
return sectors;
|
||||
logicalTrack = physicalTrack;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int sectorId=0; sectorId<BROTHER_SECTORS_PER_TRACK; sectorId++)
|
||||
switch (_config.format())
|
||||
{
|
||||
const auto& sector = image.get(logicalTrack, 0, sectorId);
|
||||
case BROTHER120:
|
||||
if (location.logicalTrack >= BROTHER_TRACKS_PER_120KB_DISK)
|
||||
return sectors;
|
||||
break;
|
||||
|
||||
case BROTHER240:
|
||||
if (location.logicalTrack >= BROTHER_TRACKS_PER_240KB_DISK)
|
||||
return sectors;
|
||||
break;
|
||||
}
|
||||
|
||||
const std::string& skew = _config.sector_skew();
|
||||
for (int sectorCount = 0; sectorCount < BROTHER_SECTORS_PER_TRACK;
|
||||
sectorCount++)
|
||||
{
|
||||
int sectorId = charToInt(skew.at(sectorCount));
|
||||
const auto& sector = image.get(location.logicalTrack, 0, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
int logicalTrack;
|
||||
if (physicalSide != 0)
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
physicalTrack -= _config.bias();
|
||||
switch (_config.format())
|
||||
{
|
||||
case BROTHER120:
|
||||
if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2))
|
||||
|| (physicalTrack & 1))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
logicalTrack = physicalTrack/2;
|
||||
break;
|
||||
std::unique_ptr<Fluxmap> encode(
|
||||
const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
case BROTHER240:
|
||||
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
logicalTrack = physicalTrack;
|
||||
break;
|
||||
}
|
||||
int sectorCount = 0;
|
||||
for (const auto& sectorData : sectors)
|
||||
{
|
||||
double headerMs = _config.post_index_gap_ms() +
|
||||
sectorCount * _config.sector_spacing_ms();
|
||||
unsigned headerCursor = headerMs * 1e3 / _config.clock_rate_us();
|
||||
double dataMs = headerMs + _config.post_header_spacing_ms();
|
||||
unsigned dataCursor = dataMs * 1e3 / _config.clock_rate_us();
|
||||
|
||||
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
|
||||
const std::string& skew = _config.sector_skew();
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
fillBitmapTo(bits, cursor, headerCursor, {true, false});
|
||||
write_sector_header(
|
||||
bits, cursor, sectorData->logicalTrack, sectorData->logicalSector);
|
||||
fillBitmapTo(bits, cursor, dataCursor, {true, false});
|
||||
write_sector_data(bits, cursor, sectorData->data);
|
||||
|
||||
for (int sectorCount=0; sectorCount<BROTHER_SECTORS_PER_TRACK; sectorCount++)
|
||||
{
|
||||
int sectorId = charToInt(skew.at(sectorCount));
|
||||
double headerMs = _config.post_index_gap_ms() + sectorCount*_config.sector_spacing_ms();
|
||||
unsigned headerCursor = headerMs*1e3 / _config.clock_rate_us();
|
||||
double dataMs = headerMs + _config.post_header_spacing_ms();
|
||||
unsigned dataCursor = dataMs*1e3 / _config.clock_rate_us();
|
||||
sectorCount++;
|
||||
}
|
||||
|
||||
const auto& sectorData = image.get(logicalTrack, 0, sectorId);
|
||||
if (cursor >= bits.size())
|
||||
Error() << "track data overrun";
|
||||
fillBitmapTo(bits, cursor, bits.size(), {true, false});
|
||||
|
||||
fillBitmapTo(bits, cursor, headerCursor, { true, false });
|
||||
write_sector_header(bits, cursor, logicalTrack, sectorId);
|
||||
fillBitmapTo(bits, cursor, dataCursor, { true, false });
|
||||
write_sector_data(bits, cursor, sectorData->data);
|
||||
}
|
||||
|
||||
if (cursor >= bits.size())
|
||||
Error() << "track data overrun";
|
||||
fillBitmapTo(bits, cursor, bits.size(), { true, false });
|
||||
|
||||
// The pre-index gap is not normally reported.
|
||||
// std::cerr << "pre-index gap " << 200.0 - (double)cursor*clockRateUs/1e3 << std::endl;
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, _config.clock_rate_us()*1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, _config.clock_rate_us() * 1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const BrotherEncoderProto& _config;
|
||||
|
||||
const BrotherEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createBrotherEncoder(const EncoderProto& config)
|
||||
std::unique_ptr<AbstractEncoder> createBrotherEncoder(
|
||||
const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new BrotherEncoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new BrotherEncoder(config));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
38
arch/c64/c64.cc
Normal file
38
arch/c64/c64.cc
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#include "globals.h"
|
||||
#include "c64.h"
|
||||
|
||||
/*
|
||||
* Track Sectors/track # Sectors Storage in Bytes Clock rate
|
||||
* ----- ------------- --------- ---------------- ----------
|
||||
* 1-17 21 357 7820 3.25
|
||||
* 18-24 19 133 7170 3.5
|
||||
* 25-30 18 108 6300 3.75
|
||||
* 31-40(*) 17 85 6020 4
|
||||
* ---
|
||||
* 683 (for a 35 track image)
|
||||
*
|
||||
* The clock rate is normalised for a 200ms drive.
|
||||
*/
|
||||
unsigned sectorsForC64Track(unsigned track)
|
||||
{
|
||||
if (track < 17)
|
||||
return 21;
|
||||
if (track < 24)
|
||||
return 19;
|
||||
if (track < 30)
|
||||
return 18;
|
||||
return 17;
|
||||
}
|
||||
|
||||
nanoseconds_t clockPeriodForC64Track(unsigned track)
|
||||
{
|
||||
constexpr double BYTE_SIZE = 8.0;
|
||||
|
||||
if (track < 17)
|
||||
return 26.0 / BYTE_SIZE;
|
||||
if (track < 24)
|
||||
return 28.0 / BYTE_SIZE;
|
||||
if (track < 30)
|
||||
return 30.0 / BYTE_SIZE;
|
||||
return 32.0 / BYTE_SIZE;
|
||||
}
|
||||
|
|
@ -30,4 +30,7 @@
|
|||
extern std::unique_ptr<AbstractDecoder> createCommodore64Decoder(const DecoderProto& config);
|
||||
extern std::unique_ptr<AbstractEncoder> createCommodore64Encoder(const EncoderProto& config);
|
||||
|
||||
extern unsigned sectorsForC64Track(unsigned track);
|
||||
extern nanoseconds_t clockPeriodForC64Track(unsigned track);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -7,7 +7,5 @@ message Commodore64DecoderProto {}
|
|||
message Commodore64EncoderProto {
|
||||
optional double post_index_gap_us = 1 [default=0.0,
|
||||
(help) = "post-index gap before first sector header."];
|
||||
optional double clock_compensation_factor = 2 [default=1.0,
|
||||
(help) = "scale the output clock by this much."];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,16 +13,18 @@
|
|||
|
||||
const FluxPattern SECTOR_RECORD_PATTERN(20, C64_SECTOR_RECORD);
|
||||
const FluxPattern DATA_RECORD_PATTERN(20, C64_DATA_RECORD);
|
||||
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
|
||||
const FluxMatchers ANY_RECORD_PATTERN(
|
||||
{&SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN});
|
||||
|
||||
static int decode_data_gcr(uint8_t gcr)
|
||||
{
|
||||
switch (gcr)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case gcr: return data;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case gcr: \
|
||||
return data;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -37,11 +39,11 @@ static Bytes decode(const std::vector<bool>& bits)
|
|||
while (ii != bits.end())
|
||||
{
|
||||
uint8_t inputfifo = 0;
|
||||
for (size_t i=0; i<5; i++)
|
||||
for (size_t i = 0; i < 5; i++)
|
||||
{
|
||||
if (ii == bits.end())
|
||||
break;
|
||||
inputfifo = (inputfifo<<1) | *ii++;
|
||||
inputfifo = (inputfifo << 1) | *ii++;
|
||||
}
|
||||
|
||||
bitw.push(decode_data_gcr(inputfifo), 4);
|
||||
|
|
@ -54,53 +56,57 @@ static Bytes decode(const std::vector<bool>& bits)
|
|||
class Commodore64Decoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
Commodore64Decoder(const DecoderProto& config):
|
||||
AbstractDecoder(config)
|
||||
{}
|
||||
Commodore64Decoder(const DecoderProto& config): AbstractDecoder(config) {}
|
||||
|
||||
RecordType 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;
|
||||
}
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
{
|
||||
readRawBits(20);
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
if (readRaw20() != C64_SECTOR_RECORD)
|
||||
return;
|
||||
|
||||
const auto& bits = readRawBits(5*10);
|
||||
const auto& bytes = decode(bits).slice(0, 5);
|
||||
const auto& bits = readRawBits(5 * 10);
|
||||
const auto& bytes = decode(bits).slice(0, 5);
|
||||
|
||||
uint8_t checksum = bytes[0];
|
||||
_sector->logicalSector = bytes[1];
|
||||
_sector->logicalSide = 0;
|
||||
_sector->logicalTrack = bytes[2] - 1;
|
||||
if (checksum == xorBytes(bytes.slice(1, 4)))
|
||||
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
uint8_t checksum = bytes[0];
|
||||
_sector->logicalSector = bytes[1];
|
||||
_sector->logicalSide = 0;
|
||||
_sector->logicalTrack = bytes[2] - 1;
|
||||
if (checksum == xorBytes(bytes.slice(1, 4)))
|
||||
_sector->status =
|
||||
Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
void decodeDataRecord()
|
||||
{
|
||||
readRawBits(20);
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
if (readRaw20() != C64_DATA_RECORD)
|
||||
return;
|
||||
|
||||
const auto& bits = readRawBits(259*10);
|
||||
const auto& bytes = decode(bits).slice(0, 259);
|
||||
const auto& bits = readRawBits(259 * 10);
|
||||
const auto& bytes = decode(bits).slice(0, 259);
|
||||
|
||||
_sector->data = bytes.slice(0, C64_SECTOR_LENGTH);
|
||||
uint8_t gotChecksum = xorBytes(_sector->data);
|
||||
uint8_t wantChecksum = bytes[256];
|
||||
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
_sector->data = bytes.slice(0, C64_SECTOR_LENGTH);
|
||||
uint8_t gotChecksum = xorBytes(_sector->data);
|
||||
uint8_t wantChecksum = bytes[256];
|
||||
_sector->status =
|
||||
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
std::set<unsigned> requiredSectors(const Location& location) const override
|
||||
{
|
||||
unsigned count = sectorsForC64Track(location.logicalTrack);
|
||||
std::set<unsigned> sectors;
|
||||
for (int sectorId = 0; sectorId < count; sectorId++)
|
||||
sectors.insert(sectorId);
|
||||
return sectors;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractDecoder> createCommodore64Decoder(const DecoderProto& config)
|
||||
std::unique_ptr<AbstractDecoder> createCommodore64Decoder(
|
||||
const DecoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractDecoder>(new Commodore64Decoder(config));
|
||||
return std::unique_ptr<AbstractDecoder>(new Commodore64Decoder(config));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@
|
|||
#include "c64.h"
|
||||
#include "crc.h"
|
||||
#include "sector.h"
|
||||
#include "writer.h"
|
||||
#include "readerwriter.h"
|
||||
#include "image.h"
|
||||
#include "mapper.h"
|
||||
#include "fmt/format.h"
|
||||
#include "arch/c64/c64.pb.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
|
|
@ -14,46 +15,6 @@
|
|||
|
||||
static bool lastBit;
|
||||
|
||||
static double clockRateUsForTrack(unsigned track)
|
||||
{
|
||||
/*
|
||||
* Track # Sectors/Track Speed Zone bits/rotation
|
||||
* 1 – 17 21 3 61,538.4
|
||||
* 18 – 24 19 2 57,142.8
|
||||
* 25 – 30 18 1 53,333.4
|
||||
* 31 – 35 17 0 50,000.0
|
||||
*/
|
||||
if (track < 17)
|
||||
return 200000.0/61538.4;
|
||||
if (track < 24)
|
||||
return 200000.0/57142.8;
|
||||
if (track < 30)
|
||||
return 200000.0/53333.4;
|
||||
return 200000.0/50000.0;
|
||||
|
||||
}
|
||||
|
||||
static unsigned sectorsForTrack(unsigned track)
|
||||
{
|
||||
/*
|
||||
* Track Sectors/track # Sectors Storage in Bytes
|
||||
* ----- ------------- --------- ----------------
|
||||
* 1-17 21 357 7820
|
||||
* 18-24 19 133 7170
|
||||
* 25-30 18 108 6300
|
||||
* 31-40(*) 17 85 6020
|
||||
* ---
|
||||
* 683 (for a 35 track image)
|
||||
*/
|
||||
if (track < 17)
|
||||
return 21;
|
||||
if (track < 24)
|
||||
return 19;
|
||||
if (track < 30)
|
||||
return 18;
|
||||
return 17;
|
||||
}
|
||||
|
||||
static int encode_data_gcr(uint8_t data)
|
||||
{
|
||||
switch (data)
|
||||
|
|
@ -211,17 +172,16 @@ public:
|
|||
{}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
if (physicalSide == 0)
|
||||
if (location.head == 0)
|
||||
{
|
||||
int logicalTrack = physicalTrack / 2;
|
||||
unsigned numSectors = sectorsForTrack(logicalTrack);
|
||||
unsigned numSectors = sectorsForC64Track(location.logicalTrack);
|
||||
for (int sectorId=0; sectorId<numSectors; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(logicalTrack, 0, sectorId);
|
||||
const auto& sector = image.get(location.logicalTrack, 0, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
|
|
@ -230,8 +190,8 @@ public:
|
|||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
/* The format ID Character # 1 and # 2 are in the .d64 image only present
|
||||
* in track 18 sector zero which contains the BAM info in byte 162 and 163.
|
||||
|
|
@ -240,10 +200,7 @@ public:
|
|||
* contains the BAM.
|
||||
*/
|
||||
|
||||
if (physicalSide != 0)
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
|
||||
const auto& sectorData = image.get(C64_BAM_TRACK*2, 0, 0); //Read de BAM to get the DISK ID bytes
|
||||
const auto& sectorData = image.get(C64_BAM_TRACK, 0, 0); //Read de BAM to get the DISK ID bytes
|
||||
if (sectorData)
|
||||
{
|
||||
ByteReader br(sectorData->data);
|
||||
|
|
@ -254,9 +211,7 @@ public:
|
|||
else
|
||||
_formatByte1 = _formatByte2 = 0;
|
||||
|
||||
int logicalTrack = physicalTrack / 2;
|
||||
double clockRateUs = clockRateUsForTrack(logicalTrack) * _config.clock_compensation_factor();
|
||||
|
||||
double clockRateUs = clockPeriodForC64Track(location.logicalTrack);
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
|
|
@ -273,12 +228,13 @@ public:
|
|||
fillBitmapTo(bits, cursor, bits.size(), { true, false });
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs*1e3);
|
||||
fluxmap->appendBits(bits,
|
||||
Mapper::calculatePhysicalClockPeriod(clockRateUs*1e3, 200e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
void writeSector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<Sector>& sector) const
|
||||
void writeSector(std::vector<bool>& bits, unsigned& cursor, std::shared_ptr<const Sector> sector) const
|
||||
{
|
||||
/* Source: http://www.unusedino.de/ec64/technical/formats/g64.html
|
||||
* 1. Header sync FF FF FF FF FF (40 'on' bits, not GCR)
|
||||
|
|
|
|||
|
|
@ -58,22 +58,17 @@ public:
|
|||
AbstractDecoder(config)
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
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;
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
/* Skip sync bits and ID byte. */
|
||||
|
||||
readRawBits(24);
|
||||
if (readRaw24() != F85_SECTOR_RECORD)
|
||||
return;
|
||||
|
||||
/* Read header. */
|
||||
|
||||
|
|
@ -89,11 +84,12 @@ public:
|
|||
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
void decodeDataRecord()
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
/* Skip sync bits ID byte. */
|
||||
|
||||
readRawBits(24);
|
||||
if (readRaw24() != F85_DATA_RECORD)
|
||||
return;
|
||||
|
||||
const auto& bytes = decode(readRawBits((F85_SECTOR_LENGTH+3)*10))
|
||||
.slice(0, F85_SECTOR_LENGTH+3);
|
||||
|
|
|
|||
|
|
@ -104,16 +104,12 @@ public:
|
|||
AbstractDecoder(config)
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
const FluxMatcher* matcher = nullptr;
|
||||
_sector->clock = _fmr->seekToPattern(SECTOR_ID_PATTERN, matcher);
|
||||
if (matcher == &SECTOR_ID_PATTERN)
|
||||
return RecordType::SECTOR_RECORD;
|
||||
return RecordType::UNKNOWN_RECORD;
|
||||
return seekToPattern(SECTOR_ID_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
auto rawbits = readRawBits(FB100_RECORD_SIZE*16);
|
||||
|
||||
|
|
|
|||
|
|
@ -98,88 +98,149 @@ public:
|
|||
_config(config.ibm())
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord() override
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
const FluxMatcher* matcher = nullptr;
|
||||
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
|
||||
|
||||
/* If this is the MFM prefix byte, the the decoder is going to expect three
|
||||
* extra bytes on the front of the header. */
|
||||
_currentHeaderLength = (matcher == &MFM_PATTERN) ? 3 : 0;
|
||||
|
||||
Fluxmap::Position here = tell();
|
||||
resetFluxDecoder();
|
||||
if (_currentHeaderLength > 0)
|
||||
readRawBits(_currentHeaderLength*16);
|
||||
auto idbits = readRawBits(16);
|
||||
const Bytes idbytes = decodeFmMfm(idbits);
|
||||
uint8_t id = idbytes.slice(0, 1)[0];
|
||||
seek(here);
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case IBM_IDAM:
|
||||
return RecordType::SECTOR_RECORD;
|
||||
|
||||
case IBM_DAM1:
|
||||
case IBM_DAM2:
|
||||
case IBM_TRS80DAM1:
|
||||
case IBM_TRS80DAM2:
|
||||
return RecordType::DATA_RECORD;
|
||||
}
|
||||
return RecordType::UNKNOWN_RECORD;
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
unsigned recordSize = _currentHeaderLength + IBM_IDAM_LEN;
|
||||
auto bits = readRawBits(recordSize*16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, recordSize);
|
||||
/* This is really annoying because the IBM record scheme has a
|
||||
* variable-sized header _and_ the checksum covers this header too. So
|
||||
* we have to read and decode a byte at a time until we know where the
|
||||
* record itself starts, saving the bytes for the checksumming later.
|
||||
*/
|
||||
|
||||
Bytes bytes;
|
||||
ByteWriter bw(bytes);
|
||||
|
||||
auto readByte = [&]() {
|
||||
auto bits = readRawBits(16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, 1);
|
||||
uint8_t byte = bytes[0];
|
||||
bw.write_8(byte);
|
||||
return byte;
|
||||
};
|
||||
|
||||
uint8_t id = readByte();
|
||||
if (id == 0xa1)
|
||||
{
|
||||
readByte();
|
||||
readByte();
|
||||
id = readByte();
|
||||
}
|
||||
if (id != IBM_IDAM)
|
||||
return;
|
||||
|
||||
ByteReader br(bytes);
|
||||
br.seek(_currentHeaderLength);
|
||||
br.read_8(); /* skip ID byte */
|
||||
br.seek(bw.pos);
|
||||
|
||||
auto bits = readRawBits(IBM_IDAM_LEN*16);
|
||||
bw += decodeFmMfm(bits).slice(0, IBM_IDAM_LEN);
|
||||
|
||||
IbmDecoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, _sector->physicalTrack, _sector->physicalHead);
|
||||
|
||||
_sector->logicalTrack = br.read_8();
|
||||
_sector->logicalSide = br.read_8();
|
||||
_sector->logicalSector = br.read_8();
|
||||
_currentSectorSize = 1 << (br.read_8() + 7);
|
||||
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, br.pos));
|
||||
uint16_t wantCrc = br.read_be16();
|
||||
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, _currentHeaderLength + 5));
|
||||
if (wantCrc == gotCrc)
|
||||
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
|
||||
|
||||
if (_config.swap_sides())
|
||||
if (trackdata.swap_sides())
|
||||
_sector->logicalSide ^= 1;
|
||||
if (_config.ignore_side_byte())
|
||||
if (trackdata.ignore_side_byte())
|
||||
_sector->logicalSide = _sector->physicalHead;
|
||||
if (_config.ignore_track_byte())
|
||||
_sector->logicalTrack = _sector->physicalCylinder;
|
||||
if (trackdata.ignore_track_byte())
|
||||
_sector->logicalTrack = _sector->physicalTrack;
|
||||
|
||||
for (int sector : trackdata.ignore_sector())
|
||||
if (_sector->logicalSector == sector)
|
||||
{
|
||||
_sector->status = Sector::MISSING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
unsigned recordLength = _currentHeaderLength + _currentSectorSize + 3;
|
||||
auto bits = readRawBits(recordLength*16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, recordLength);
|
||||
/* This is the same deal as the sector record. */
|
||||
|
||||
Bytes bytes;
|
||||
ByteWriter bw(bytes);
|
||||
|
||||
auto readByte = [&]() {
|
||||
auto bits = readRawBits(16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, 1);
|
||||
uint8_t byte = bytes[0];
|
||||
bw.write_8(byte);
|
||||
return byte;
|
||||
};
|
||||
|
||||
uint8_t id = readByte();
|
||||
if (id == 0xa1)
|
||||
{
|
||||
readByte();
|
||||
readByte();
|
||||
id = readByte();
|
||||
}
|
||||
if ((id != IBM_DAM1) && (id != IBM_DAM2)
|
||||
&& (id != IBM_TRS80DAM1) && (id != IBM_TRS80DAM2))
|
||||
return;
|
||||
|
||||
ByteReader br(bytes);
|
||||
br.seek(_currentHeaderLength);
|
||||
br.read_8(); /* skip ID byte */
|
||||
br.seek(bw.pos);
|
||||
|
||||
auto bits = readRawBits((_currentSectorSize + 2) * 16);
|
||||
bw += decodeFmMfm(bits).slice(0, _currentSectorSize+2);
|
||||
|
||||
_sector->data = br.read(_currentSectorSize);
|
||||
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, br.pos));
|
||||
uint16_t wantCrc = br.read_be16();
|
||||
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, recordLength-2));
|
||||
_sector->status = (wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
|
||||
std::set<unsigned> requiredSectors(const Location& location) const override
|
||||
{
|
||||
IbmDecoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, location.logicalTrack, location.head);
|
||||
|
||||
std::set<unsigned> s;
|
||||
for (int sectorId : _config.sectors().sector())
|
||||
s.insert(sectorId);
|
||||
if (trackdata.has_sectors())
|
||||
{
|
||||
for (int sectorId : trackdata.sectors().sector())
|
||||
s.insert(sectorId);
|
||||
}
|
||||
else if (trackdata.has_sector_range())
|
||||
{
|
||||
int sectorId = trackdata.sector_range().min_sector();
|
||||
while (sectorId <= trackdata.sector_range().max_sector())
|
||||
{
|
||||
s.insert(sectorId);
|
||||
sectorId++;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
void getTrackFormat(IbmDecoderProto::TrackdataProto& trackdata, unsigned track, unsigned head) const
|
||||
{
|
||||
trackdata.Clear();
|
||||
for (const auto& f : _config.trackdata())
|
||||
{
|
||||
if (f.has_track() && (f.track() != track))
|
||||
continue;
|
||||
if (f.has_head() && (f.head() != head))
|
||||
continue;
|
||||
|
||||
trackdata.MergeFrom(f);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const IbmDecoderProto& _config;
|
||||
unsigned _currentSectorSize;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
#include "encoders/encoders.h"
|
||||
#include "ibm.h"
|
||||
#include "crc.h"
|
||||
#include "writer.h"
|
||||
#include "readerwriter.h"
|
||||
#include "image.h"
|
||||
#include "mapper.h"
|
||||
#include "arch/ibm/ibm.pb.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
#include "fmt/format.h"
|
||||
|
|
@ -39,9 +40,9 @@
|
|||
* ^^^^^
|
||||
* 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
|
||||
|
|
@ -59,228 +60,258 @@
|
|||
|
||||
static uint8_t decodeUint16(uint16_t raw)
|
||||
{
|
||||
Bytes b;
|
||||
ByteWriter bw(b);
|
||||
bw.write_be16(raw);
|
||||
return decodeFmMfm(b.toBits())[0];
|
||||
Bytes b;
|
||||
ByteWriter bw(b);
|
||||
bw.write_be16(raw);
|
||||
return decodeFmMfm(b.toBits())[0];
|
||||
}
|
||||
|
||||
class IbmEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
IbmEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.ibm())
|
||||
{}
|
||||
IbmEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.ibm())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void 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 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 getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
|
||||
{
|
||||
trackdata.Clear();
|
||||
for (const auto& f : _config.trackdata())
|
||||
{
|
||||
if (f.has_cylinder() && (f.cylinder() != cylinder))
|
||||
continue;
|
||||
if (f.has_head() && (f.head() != head))
|
||||
continue;
|
||||
void getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata,
|
||||
unsigned track,
|
||||
unsigned head)
|
||||
{
|
||||
trackdata.Clear();
|
||||
for (const auto& f : _config.trackdata())
|
||||
{
|
||||
if (f.has_track() && (f.track() != track))
|
||||
continue;
|
||||
if (f.has_head() && (f.head() != head))
|
||||
continue;
|
||||
|
||||
trackdata.MergeFrom(f);
|
||||
}
|
||||
}
|
||||
trackdata.MergeFrom(f);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static std::set<unsigned> getSectorIds(
|
||||
const IbmEncoderProto::TrackdataProto& trackdata)
|
||||
{
|
||||
std::set<unsigned> s;
|
||||
if (trackdata.has_sectors())
|
||||
{
|
||||
for (int sectorId : trackdata.sectors().sector())
|
||||
s.insert(sectorId);
|
||||
}
|
||||
else if (trackdata.has_sector_range())
|
||||
{
|
||||
int sectorId = trackdata.sector_range().min_sector();
|
||||
while (sectorId <= trackdata.sector_range().max_sector())
|
||||
{
|
||||
s.insert(sectorId);
|
||||
sectorId++;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<Sector>> sectors;
|
||||
IbmEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, physicalTrack, physicalSide);
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
IbmEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, location.logicalTrack, location.head);
|
||||
|
||||
int logicalSide = physicalSide ^ trackdata.swap_sides();
|
||||
for (int sectorId : trackdata.sectors().sector())
|
||||
int logicalSide = location.head ^ trackdata.swap_sides();
|
||||
for (int sectorId : getSectorIds(trackdata))
|
||||
{
|
||||
const auto& sector = image.get(physicalTrack, logicalSide, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
const auto& sector =
|
||||
image.get(location.logicalTrack, logicalSide, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
IbmEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, physicalTrack, physicalSide);
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
IbmEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, location.logicalTrack, location.head);
|
||||
|
||||
auto writeBytes = [&](const Bytes& bytes)
|
||||
{
|
||||
if (trackdata.use_fm())
|
||||
encodeFm(_bits, _cursor, bytes);
|
||||
else
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
};
|
||||
auto writeBytes = [&](const Bytes& bytes)
|
||||
{
|
||||
if (trackdata.use_fm())
|
||||
encodeFm(_bits, _cursor, bytes);
|
||||
else
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
};
|
||||
|
||||
auto writeFillerBytes = [&](int count, uint8_t byte)
|
||||
{
|
||||
Bytes bytes = { byte };
|
||||
for (int i=0; i<count; i++)
|
||||
writeBytes(bytes);
|
||||
};
|
||||
auto writeFillerRawBytes = [&](int count, uint16_t byte)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
writeRawBits(byte, 16);
|
||||
};
|
||||
|
||||
double clockRateUs = 1e3 / trackdata.clock_rate_khz();
|
||||
if (!trackdata.use_fm())
|
||||
clockRateUs /= 2.0;
|
||||
int bitsPerRevolution = (trackdata.track_length_ms() * 1000.0) / clockRateUs;
|
||||
_bits.resize(bitsPerRevolution);
|
||||
_cursor = 0;
|
||||
auto writeFillerBytes = [&](int count, uint8_t byte)
|
||||
{
|
||||
Bytes b{byte};
|
||||
for (int i = 0; i < count; i++)
|
||||
writeBytes(b);
|
||||
};
|
||||
|
||||
uint8_t idamUnencoded = decodeUint16(trackdata.idam_byte());
|
||||
uint8_t damUnencoded = decodeUint16(trackdata.dam_byte());
|
||||
double clockRateUs = trackdata.target_clock_period_us();
|
||||
if (!trackdata.use_fm())
|
||||
clockRateUs /= 2.0;
|
||||
int bitsPerRevolution =
|
||||
(trackdata.target_rotational_period_ms() * 1000.0) / clockRateUs;
|
||||
_bits.resize(bitsPerRevolution);
|
||||
_cursor = 0;
|
||||
|
||||
uint8_t sectorSize = 0;
|
||||
{
|
||||
int s = trackdata.sector_size() >> 7;
|
||||
while (s > 1)
|
||||
{
|
||||
s >>= 1;
|
||||
sectorSize += 1;
|
||||
}
|
||||
}
|
||||
uint8_t idamUnencoded = decodeUint16(trackdata.idam_byte());
|
||||
uint8_t damUnencoded = decodeUint16(trackdata.dam_byte());
|
||||
|
||||
uint8_t gapFill = trackdata.use_fm() ? 0x00 : 0x4e;
|
||||
uint8_t sectorSize = 0;
|
||||
{
|
||||
int s = trackdata.sector_size() >> 7;
|
||||
while (s > 1)
|
||||
{
|
||||
s >>= 1;
|
||||
sectorSize += 1;
|
||||
}
|
||||
}
|
||||
|
||||
writeFillerBytes(trackdata.gap0(), gapFill);
|
||||
if (trackdata.emit_iam())
|
||||
{
|
||||
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
|
||||
if (!trackdata.use_fm())
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
writeRawBits(MFM_IAM_SEPARATOR, 16);
|
||||
}
|
||||
writeRawBits(trackdata.use_fm() ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
|
||||
writeFillerBytes(trackdata.gap1(), gapFill);
|
||||
}
|
||||
uint16_t gapFill = trackdata.gap_fill_byte();
|
||||
|
||||
int logicalSide = physicalSide ^ trackdata.swap_sides();
|
||||
bool first = true;
|
||||
for (int sectorId : trackdata.sectors().sector())
|
||||
{
|
||||
if (!first)
|
||||
writeFillerBytes(trackdata.gap3(), gapFill);
|
||||
first = false;
|
||||
writeFillerRawBytes(trackdata.gap0(), gapFill);
|
||||
if (trackdata.emit_iam())
|
||||
{
|
||||
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
|
||||
if (!trackdata.use_fm())
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
writeRawBits(MFM_IAM_SEPARATOR, 16);
|
||||
}
|
||||
writeRawBits(
|
||||
trackdata.use_fm() ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
|
||||
writeFillerRawBytes(trackdata.gap1(), gapFill);
|
||||
}
|
||||
|
||||
const auto& sectorData = image.get(physicalTrack, logicalSide, sectorId);
|
||||
if (!sectorData)
|
||||
continue;
|
||||
bool first = true;
|
||||
for (const auto& sectorData : sectors)
|
||||
{
|
||||
if (!first)
|
||||
writeFillerRawBytes(trackdata.gap3(), gapFill);
|
||||
first = false;
|
||||
|
||||
/* 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. */
|
||||
/* 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);
|
||||
{
|
||||
Bytes header;
|
||||
ByteWriter bw(header);
|
||||
|
||||
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
|
||||
if (!trackdata.use_fm())
|
||||
{
|
||||
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);
|
||||
bw.write_8(sectorSize);
|
||||
uint16_t crc = crc16(CCITT_POLY, header);
|
||||
bw.write_be16(crc);
|
||||
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
|
||||
if (!trackdata.use_fm())
|
||||
{
|
||||
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);
|
||||
bw.write_8(sectorSize);
|
||||
uint16_t crc = crc16(CCITT_POLY, header);
|
||||
bw.write_be16(crc);
|
||||
|
||||
int conventionalHeaderStart = 0;
|
||||
if (!trackdata.use_fm())
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
writeRawBits(MFM_RECORD_SEPARATOR, 16);
|
||||
conventionalHeaderStart += 3;
|
||||
int conventionalHeaderStart = 0;
|
||||
if (!trackdata.use_fm())
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
writeRawBits(MFM_RECORD_SEPARATOR, 16);
|
||||
conventionalHeaderStart += 3;
|
||||
}
|
||||
writeRawBits(trackdata.idam_byte(), 16);
|
||||
conventionalHeaderStart += 1;
|
||||
|
||||
}
|
||||
writeRawBits(trackdata.idam_byte(), 16);
|
||||
conventionalHeaderStart += 1;
|
||||
writeBytes(header.slice(conventionalHeaderStart));
|
||||
}
|
||||
|
||||
writeBytes(header.slice(conventionalHeaderStart));
|
||||
}
|
||||
writeFillerRawBytes(trackdata.gap2(), gapFill);
|
||||
|
||||
writeFillerBytes(trackdata.gap2(), gapFill);
|
||||
{
|
||||
Bytes data;
|
||||
ByteWriter bw(data);
|
||||
|
||||
{
|
||||
Bytes data;
|
||||
ByteWriter bw(data);
|
||||
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
|
||||
if (!trackdata.use_fm())
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
|
||||
}
|
||||
bw.write_8(damUnencoded);
|
||||
|
||||
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
|
||||
if (!trackdata.use_fm())
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
|
||||
}
|
||||
bw.write_8(damUnencoded);
|
||||
Bytes truncatedData =
|
||||
sectorData->data.slice(0, trackdata.sector_size());
|
||||
bw += truncatedData;
|
||||
uint16_t crc = crc16(CCITT_POLY, data);
|
||||
bw.write_be16(crc);
|
||||
|
||||
Bytes truncatedData = sectorData->data.slice(0, trackdata.sector_size());
|
||||
bw += truncatedData;
|
||||
uint16_t crc = crc16(CCITT_POLY, data);
|
||||
bw.write_be16(crc);
|
||||
int conventionalHeaderStart = 0;
|
||||
if (!trackdata.use_fm())
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
writeRawBits(MFM_RECORD_SEPARATOR, 16);
|
||||
conventionalHeaderStart += 3;
|
||||
}
|
||||
writeRawBits(trackdata.dam_byte(), 16);
|
||||
conventionalHeaderStart += 1;
|
||||
|
||||
int conventionalHeaderStart = 0;
|
||||
if (!trackdata.use_fm())
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
writeRawBits(MFM_RECORD_SEPARATOR, 16);
|
||||
conventionalHeaderStart += 3;
|
||||
writeBytes(data.slice(conventionalHeaderStart));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
writeRawBits(trackdata.dam_byte(), 16);
|
||||
conventionalHeaderStart += 1;
|
||||
if (_cursor >= _bits.size())
|
||||
Error() << "track data overrun";
|
||||
while (_cursor < _bits.size())
|
||||
writeFillerRawBytes(1, gapFill);
|
||||
|
||||
writeBytes(data.slice(conventionalHeaderStart));
|
||||
}
|
||||
}
|
||||
|
||||
if (_cursor >= _bits.size())
|
||||
Error() << "track data overrun";
|
||||
while (_cursor < _bits.size())
|
||||
writeFillerBytes(1, gapFill);
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(_bits, clockRateUs*1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(_bits,
|
||||
Mapper::calculatePhysicalClockPeriod(clockRateUs * 1e3,
|
||||
trackdata.target_rotational_period_ms() * 1e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const IbmEncoderProto& _config;
|
||||
std::vector<bool> _bits;
|
||||
unsigned _cursor;
|
||||
bool _lastBit;
|
||||
const IbmEncoderProto& _config;
|
||||
std::vector<bool> _bits;
|
||||
unsigned _cursor;
|
||||
bool _lastBit;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createIbmEncoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new IbmEncoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new IbmEncoder(config));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
struct IbmIdam
|
||||
{
|
||||
uint8_t id;
|
||||
uint8_t cylinder;
|
||||
uint8_t track;
|
||||
uint8_t side;
|
||||
uint8_t sector;
|
||||
uint8_t sectorSize;
|
||||
|
|
|
|||
|
|
@ -2,32 +2,52 @@ syntax = "proto2";
|
|||
|
||||
import "lib/common.proto";
|
||||
|
||||
// Next: 7
|
||||
message IbmDecoderProto {
|
||||
message SectorsProto {
|
||||
repeated int32 sector = 1 [(help) = "require these sectors to exist for a good read"];
|
||||
// Next: 11
|
||||
message TrackdataProto {
|
||||
message SectorsProto {
|
||||
repeated int32 sector = 1 [(help) = "require these sectors to exist for a good read"];
|
||||
}
|
||||
message SectorRangeProto {
|
||||
optional int32 min_sector = 1 [(help) = "require these sectors to exist for a good read"];
|
||||
optional int32 max_sector = 2 [(help) = "require these sectors to exist for a good read"];
|
||||
}
|
||||
|
||||
optional int32 track = 7 [(help) = "if set, the format applies only to this track"];
|
||||
optional int32 head = 8 [(help) = "if set, the format applies only to this head"];
|
||||
|
||||
optional bool ignore_side_byte = 2 [default = false, (help) = "ignore side byte in sector header"];
|
||||
optional bool ignore_track_byte = 6 [default = false, (help) = "ignore track byte in sector header"];
|
||||
optional bool swap_sides = 4 [default = false, (help) = "put logical side 1 on physical side 0"];
|
||||
|
||||
repeated int32 ignore_sector = 10 [(help) = "sectors with these IDs will not be read"];
|
||||
|
||||
oneof required_sectors {
|
||||
SectorsProto sectors = 5 [(help) = "require these sectors to exist for a good read"];
|
||||
SectorRangeProto sector_range = 9 [(help) = "require these sectors to exist for a good read"];
|
||||
}
|
||||
}
|
||||
|
||||
optional bool ignore_side_byte = 2 [default = false, (help) = "ignore side byte in sector header"];
|
||||
optional bool ignore_track_byte = 6 [default = false, (help) = "ignore track byte in sector header"];
|
||||
optional bool swap_sides = 4 [default = false, (help) = "put logical side 1 on physical side 0"];
|
||||
optional SectorsProto sectors = 5 [(help) = "require these sectors to exist for a good read"];
|
||||
repeated TrackdataProto trackdata = 1;
|
||||
}
|
||||
|
||||
message IbmEncoderProto {
|
||||
// Next: 18
|
||||
// Next: 20
|
||||
message TrackdataProto {
|
||||
message SectorsProto {
|
||||
repeated int32 sector = 1 [(help) = "write these sectors (in order) on each track"];
|
||||
}
|
||||
message SectorRangeProto {
|
||||
optional int32 min_sector = 1 [(help) = "write these sectors (in order) on each track"];
|
||||
optional int32 max_sector = 2 [(help) = "write these sectors (in order) on each track"];
|
||||
}
|
||||
|
||||
optional int32 cylinder = 15 [(help) = "if set, the format applies only to this track"];
|
||||
optional int32 track = 15 [(help) = "if set, the format applies only to this track"];
|
||||
optional int32 head = 16 [(help) = "if set, the format applies only to this head"];
|
||||
|
||||
optional double track_length_ms = 1 [(help) = "length of track"];
|
||||
optional int32 sector_size = 2 [default=512, (help) = "number of bytes per sector"];
|
||||
optional bool emit_iam = 3 [default=true, (help) = "whether to emit an IAM record"];
|
||||
optional double clock_rate_khz = 5 [(help) = "data clock rate"];
|
||||
optional double target_clock_period_us = 5 [default=4, (help) = "data clock rate on target disk"];
|
||||
optional bool use_fm = 6 [default=false, (help) = "whether to use FM encoding rather than MFM"];
|
||||
optional int32 idam_byte = 7 [default=0x5554, (help) = "16-bit raw bit pattern of IDAM byte"];
|
||||
optional int32 dam_byte = 8 [default=0x5545, (help) = "16-bit raw bit pattern of DAM byte"];
|
||||
|
|
@ -36,7 +56,13 @@ message IbmEncoderProto {
|
|||
optional int32 gap2 = 11 [default=22, (help) = "size of gap 3 (the pre-data gap)"];
|
||||
optional int32 gap3 = 12 [default=80, (help) = "size of gap 4 (the post-data or format gap)"];
|
||||
optional bool swap_sides = 14 [default=false, (help) = "swap side bytes when writing"];
|
||||
optional SectorsProto sectors = 17 [(help) = "write these sectors (in order) on each track"];
|
||||
optional int32 gap_fill_byte = 18 [default=0x9254, (help) = "16-bit raw bit pattern of gap fill byte"];
|
||||
optional double target_rotational_period_ms = 1 [default=200, (help) = "rotational period of target disk"];
|
||||
|
||||
oneof required_sectors {
|
||||
SectorsProto sectors = 17 [(help) = "require these sectors to exist for a good read"];
|
||||
SectorRangeProto sector_range = 19 [(help) = "require these sectors to exist for a good read"];
|
||||
}
|
||||
}
|
||||
|
||||
repeated TrackdataProto trackdata = 1;
|
||||
|
|
|
|||
|
|
@ -129,28 +129,22 @@ public:
|
|||
AbstractDecoder(config)
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
const FluxMatcher* matcher = nullptr;
|
||||
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
|
||||
if (matcher == &SECTOR_RECORD_PATTERN)
|
||||
return SECTOR_RECORD;
|
||||
if (matcher == &DATA_RECORD_PATTERN)
|
||||
return DATA_RECORD;
|
||||
return UNKNOWN_RECORD;
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
/* Skip ID (as we know it's a MAC_SECTOR_RECORD). */
|
||||
readRawBits(24);
|
||||
if (readRaw24() != MAC_SECTOR_RECORD)
|
||||
return;
|
||||
|
||||
/* Read header. */
|
||||
|
||||
auto header = toBytes(readRawBits(7*8)).slice(0, 7);
|
||||
|
||||
uint8_t encodedTrack = decode_data_gcr(header[0]);
|
||||
if (encodedTrack != (_sector->physicalCylinder & 0x3f))
|
||||
if (encodedTrack != (_sector->physicalTrack & 0x3f))
|
||||
return;
|
||||
|
||||
uint8_t encodedSector = decode_data_gcr(header[1]);
|
||||
|
|
@ -161,7 +155,7 @@ public:
|
|||
if (encodedSector > 11)
|
||||
return;
|
||||
|
||||
_sector->logicalTrack = _sector->physicalCylinder;
|
||||
_sector->logicalTrack = _sector->physicalTrack;
|
||||
_sector->logicalSide = decode_side(encodedSide);
|
||||
_sector->logicalSector = encodedSector;
|
||||
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
|
||||
|
|
@ -169,10 +163,9 @@ public:
|
|||
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
void decodeDataRecord()
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
auto id = toBytes(readRawBits(24)).reader().read_be24();
|
||||
if (id != MAC_DATA_RECORD)
|
||||
if (readRaw24() != MAC_DATA_RECORD)
|
||||
return;
|
||||
|
||||
/* Read data. */
|
||||
|
|
@ -190,16 +183,18 @@ public:
|
|||
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
|
||||
}
|
||||
|
||||
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const
|
||||
std::set<unsigned> requiredSectors(const Location& location) const override
|
||||
{
|
||||
unsigned track = location.logicalTrack;
|
||||
|
||||
int count;
|
||||
if (cylinder < 16)
|
||||
if (track < 16)
|
||||
count = 12;
|
||||
else if (cylinder < 32)
|
||||
else if (track < 32)
|
||||
count = 11;
|
||||
else if (cylinder < 48)
|
||||
else if (track < 48)
|
||||
count = 10;
|
||||
else if (cylinder < 64)
|
||||
else if (track < 64)
|
||||
count = 9;
|
||||
else
|
||||
count = 8;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
#include "encoders/encoders.h"
|
||||
#include "macintosh.h"
|
||||
#include "crc.h"
|
||||
#include "writer.h"
|
||||
#include "readerwriter.h"
|
||||
#include "image.h"
|
||||
#include "mapper.h"
|
||||
#include "fmt/format.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
#include "arch/macintosh/macintosh.pb.h"
|
||||
|
|
@ -14,44 +15,46 @@ 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;
|
||||
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;
|
||||
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
|
||||
#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
|
||||
/* 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)
|
||||
{
|
||||
|
|
@ -59,7 +62,7 @@ static Bytes encode_crazy_data(const Bytes& input)
|
|||
ByteWriter bw(output);
|
||||
ByteReader br(input);
|
||||
|
||||
uint8_t w1, w2, w3, w4;
|
||||
uint8_t w1, w2, w3, w4;
|
||||
|
||||
static const int LOOKUP_LEN = MAC_SECTOR_LENGTH / 3;
|
||||
|
||||
|
|
@ -67,92 +70,94 @@ static Bytes encode_crazy_data(const Bytes& input)
|
|||
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++;
|
||||
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;
|
||||
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;
|
||||
val = br.read_8();
|
||||
c2 += val;
|
||||
if (c3 > 0xff)
|
||||
{
|
||||
c2++;
|
||||
c3 &= 0xff;
|
||||
}
|
||||
b2[j] = (val ^ c3) & 0xff;
|
||||
|
||||
if (br.pos == 524)
|
||||
break;
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
bw.write_8(w4);
|
||||
bw.write_8(w1);
|
||||
bw.write_8(w2);
|
||||
|
||||
if (i != LOOKUP_LEN)
|
||||
bw.write_8(w3);
|
||||
}
|
||||
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);
|
||||
bw.write_8(c4 & 0x3f);
|
||||
bw.write_8(c3 & 0x3f);
|
||||
bw.write_8(c2 & 0x3f);
|
||||
bw.write_8(c1 & 0x3f);
|
||||
|
||||
return output;
|
||||
return output;
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||
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;
|
||||
}
|
||||
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)
|
||||
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;
|
||||
}
|
||||
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)
|
||||
|
|
@ -161,103 +166,116 @@ static uint8_t encode_side(uint8_t track, uint8_t side)
|
|||
* bit 5) and also whether we're above track 0x3f (in bit 0).
|
||||
*/
|
||||
|
||||
return (side ? 0x20 : 0x00) | ((track>0x3f) ? 0x01 : 0x00);
|
||||
return (side ? 0x20 : 0x00) | ((track > 0x3f) ? 0x01 : 0x00);
|
||||
}
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<Sector>& sector)
|
||||
static void write_sector(std::vector<bool>& bits,
|
||||
unsigned& cursor,
|
||||
const std::shared_ptr<const Sector>& sector)
|
||||
{
|
||||
if ((sector->data.size() != 512) && (sector->data.size() != 524))
|
||||
Error() << "unsupported sector size --- you must pick 512 or 524";
|
||||
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);
|
||||
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->logicalTrack & 0x3f;
|
||||
uint8_t encodedSector = sector->logicalSector;
|
||||
uint8_t encodedSide = encode_side(sector->logicalTrack, sector->logicalSide);
|
||||
uint8_t formatByte = MAC_FORMAT_BYTE;
|
||||
uint8_t headerChecksum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
|
||||
uint8_t encodedSector = sector->logicalSector;
|
||||
uint8_t encodedSide =
|
||||
encode_side(sector->logicalTrack, 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, 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);
|
||||
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);
|
||||
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);
|
||||
write_bits(bits, cursor, 0xdeaaff, 3 * 8);
|
||||
}
|
||||
|
||||
class MacintoshEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
MacintoshEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.macintosh())
|
||||
{}
|
||||
MacintoshEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.macintosh())
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
if ((physicalTrack >= 0) && (physicalTrack < MAC_TRACKS_PER_DISK))
|
||||
if ((location.logicalTrack >= 0) &&
|
||||
(location.logicalTrack < MAC_TRACKS_PER_DISK))
|
||||
{
|
||||
unsigned numSectors = sectorsForTrack(physicalTrack);
|
||||
for (int sectorId=0; sectorId<numSectors; sectorId++)
|
||||
unsigned numSectors = sectorsForTrack(location.logicalTrack);
|
||||
for (int sectorId = 0; sectorId < numSectors; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
|
||||
const auto& sector =
|
||||
image.get(location.logicalTrack, location.head, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
if ((physicalTrack < 0) || (physicalTrack >= MAC_TRACKS_PER_DISK))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
double clockRateUs = clockRateUsForTrack(location.logicalTrack);
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
double clockRateUs = clockRateUsForTrack(physicalTrack) * _config.clock_compensation_factor();
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
fillBitmapTo(bits,
|
||||
cursor,
|
||||
_config.post_index_gap_us() / clockRateUs,
|
||||
{true, false});
|
||||
lastBit = false;
|
||||
|
||||
fillBitmapTo(bits, cursor, _config.post_index_gap_us() / clockRateUs, { true, false });
|
||||
lastBit = false;
|
||||
for (const auto& sector : sectors)
|
||||
write_sector(bits, cursor, sector);
|
||||
|
||||
for (const auto& sector : sectors)
|
||||
write_sector(bits, cursor, sector);
|
||||
if (cursor >= bits.size())
|
||||
Error() << fmt::format(
|
||||
"track data overrun by {} bits", cursor - bits.size());
|
||||
fillBitmapTo(bits, cursor, bits.size(), {true, false});
|
||||
|
||||
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;
|
||||
}
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits,
|
||||
Mapper::calculatePhysicalClockPeriod(clockRateUs * 1e3, 200e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const MacintoshEncoderProto& _config;
|
||||
const MacintoshEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createMacintoshEncoder(const EncoderProto& config)
|
||||
std::unique_ptr<AbstractEncoder> createMacintoshEncoder(
|
||||
const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new MacintoshEncoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new MacintoshEncoder(config));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,5 @@ message MacintoshDecoderProto {}
|
|||
message MacintoshEncoderProto {
|
||||
optional double post_index_gap_us = 1 [default = 0.0,
|
||||
(help) = "post-index gap before first sector header (microseconds)."];
|
||||
|
||||
optional double clock_compensation_factor = 2 [default = 1.0,
|
||||
(help) = "scale the output clock by this much."];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,20 @@
|
|||
#include "micropolis.h"
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include "lib/decoders/decoders.pb.h"
|
||||
|
||||
/* The sector has a preamble of MFM 0x00s and uses 0xFF as a sync pattern. */
|
||||
static const FluxPattern SECTOR_SYNC_PATTERN(32, 0xaaaa5555);
|
||||
/* The sector has a preamble of MFM 0x00s and uses 0xFF as a sync pattern.
|
||||
*
|
||||
* 00 00 00 F F
|
||||
* 0000 0000 0000 0000 0000 0000 0101 0101 0101 0101
|
||||
* A A A A A A 5 5 5 5
|
||||
*/
|
||||
static const FluxPattern SECTOR_SYNC_PATTERN(64, 0xAAAAAAAAAAAA5555LL);
|
||||
|
||||
/* Adds all bytes, with carry. */
|
||||
/* Pattern to skip past current SYNC. */
|
||||
static const FluxPattern SECTOR_ADVANCE_PATTERN(64, 0xAAAAAAAAAAAAAAAALL);
|
||||
|
||||
/* Standard Micropolis checksum. Adds all bytes, with carry. */
|
||||
uint8_t micropolisChecksum(const Bytes& bytes) {
|
||||
ByteReader br(bytes);
|
||||
uint16_t sum = 0;
|
||||
|
|
@ -24,48 +33,155 @@ uint8_t micropolisChecksum(const Bytes& bytes) {
|
|||
return sum & 0xFF;
|
||||
}
|
||||
|
||||
/* Vector MZOS does not use the standard Micropolis checksum.
|
||||
* The checksum is initially 0.
|
||||
* For each data byte in the 256-byte payload, rotate left,
|
||||
* carrying bit 7 to bit 0. XOR with the current checksum.
|
||||
*
|
||||
* Unlike the Micropolis checksum, this does not cover the 12-byte
|
||||
* header (track, sector, 10 OS-specific bytes.)
|
||||
*/
|
||||
uint8_t mzosChecksum(const Bytes& bytes) {
|
||||
ByteReader br(bytes);
|
||||
uint8_t checksum = 0;
|
||||
uint8_t databyte;
|
||||
|
||||
while (!br.eof()) {
|
||||
databyte = br.read_8();
|
||||
checksum ^= ((databyte << 1) | (databyte >> 7));
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
class MicropolisDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
MicropolisDecoder(const DecoderProto& config):
|
||||
AbstractDecoder(config)
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord()
|
||||
AbstractDecoder(config),
|
||||
_config(config.micropolis())
|
||||
{
|
||||
_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;
|
||||
_checksumType = _config.checksum_type();
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
nanoseconds_t now = tell().ns();
|
||||
|
||||
/* For all but the first sector, seek to the next sector pulse.
|
||||
* The first sector does not contain the sector pulse in the fluxmap.
|
||||
*/
|
||||
if (now != 0) {
|
||||
seekToIndexMark();
|
||||
now = tell().ns();
|
||||
}
|
||||
|
||||
/* Discard a possible partial sector at the end of the track.
|
||||
* This partial sector could be mistaken for a conflicted sector, if
|
||||
* whatever data read happens to match the checksum of 0, which is
|
||||
* rare, but has been observed on some disks.
|
||||
*/
|
||||
if (now > (getFluxmapDuration() - 12.5e6)) {
|
||||
seekToIndexMark();
|
||||
return 0;
|
||||
}
|
||||
|
||||
nanoseconds_t clock = seekToPattern(SECTOR_SYNC_PATTERN);
|
||||
|
||||
auto syncDelta = tell().ns() - now;
|
||||
/* Due to the weak nature of the Micropolis SYNC patern,
|
||||
* it's possible to detect a false SYNC during the gap
|
||||
* between the sector pulse and the write gate. If the SYNC
|
||||
* is detected less than 100uS after the sector pulse, search
|
||||
* for another valid SYNC.
|
||||
*
|
||||
* Reference: Vector Micropolis Disk Controller Board Technical
|
||||
* Information Manual, pp. 1-16.
|
||||
*/
|
||||
if ((syncDelta > 0) && (syncDelta < 100e3)) {
|
||||
seekToPattern(SECTOR_ADVANCE_PATTERN);
|
||||
clock = seekToPattern(SECTOR_SYNC_PATTERN);
|
||||
}
|
||||
|
||||
_sector->headerStartTime = tell().ns();
|
||||
|
||||
/* seekToPattern() can skip past the index hole, if this happens
|
||||
* too close to the end of the Fluxmap, discard the sector.
|
||||
*/
|
||||
if (_sector->headerStartTime > (getFluxmapDuration() - 12.5e6)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return clock;
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
readRawBits(48);
|
||||
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 */
|
||||
int syncByte = br.read_8(); /* sync */
|
||||
if (syncByte != 0xFF)
|
||||
return;
|
||||
|
||||
_sector->logicalTrack = br.read_8();
|
||||
_sector->logicalSide = _sector->physicalHead;
|
||||
_sector->logicalSector = br.read_8();
|
||||
if (_sector->logicalSector > 15)
|
||||
return;
|
||||
if (_sector->logicalTrack > 77)
|
||||
if (_sector->logicalTrack > 76)
|
||||
return;
|
||||
if (_sector->logicalTrack != _sector->physicalTrack)
|
||||
return;
|
||||
|
||||
br.read(10); /* OS data or padding */
|
||||
_sector->data = br.read(256);
|
||||
auto data = br.read(MICROPOLIS_PAYLOAD_SIZE);
|
||||
uint8_t wantChecksum = br.read_8();
|
||||
uint8_t gotChecksum = micropolisChecksum(bytes.slice(1, 2+266));
|
||||
|
||||
/* If not specified, automatically determine the checksum type.
|
||||
* Once the checksum type is determined, it will be used for the
|
||||
* entire disk.
|
||||
*/
|
||||
if (_checksumType == MicropolisDecoderProto::AUTO) {
|
||||
/* Calculate both standard Micropolis (MDOS, CP/M, OASIS) and MZOS checksums */
|
||||
if (wantChecksum == micropolisChecksum(bytes.slice(1, 2+266))) {
|
||||
_checksumType = MicropolisDecoderProto::MICROPOLIS;
|
||||
} else if (wantChecksum == mzosChecksum(bytes.slice(MICROPOLIS_HEADER_SIZE, MICROPOLIS_PAYLOAD_SIZE))) {
|
||||
_checksumType = MicropolisDecoderProto::MZOS;
|
||||
std::cout << "Note: MZOS checksum detected." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t gotChecksum;
|
||||
|
||||
if (_checksumType == MicropolisDecoderProto::MZOS) {
|
||||
gotChecksum = mzosChecksum(bytes.slice(MICROPOLIS_HEADER_SIZE, MICROPOLIS_PAYLOAD_SIZE));
|
||||
} else {
|
||||
gotChecksum = micropolisChecksum(bytes.slice(1, 2+266));
|
||||
}
|
||||
|
||||
br.read(5); /* 4 byte ECC and ECC-present flag */
|
||||
|
||||
if (_config.sector_output_size() == MICROPOLIS_PAYLOAD_SIZE)
|
||||
_sector->data = data;
|
||||
else if (_config.sector_output_size() == MICROPOLIS_ENCODED_SECTOR_SIZE)
|
||||
_sector->data = bytes;
|
||||
else
|
||||
Error() << "Sector output size may only be 256 or 275";
|
||||
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
std::set<unsigned> requiredSectors(const Location& location) const override
|
||||
{
|
||||
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
return sectors;
|
||||
}
|
||||
|
||||
private:
|
||||
const MicropolisDecoderProto& _config;
|
||||
MicropolisDecoderProto_ChecksumType _checksumType; /* -1 = auto, 1 = Micropolis, 2=MZOS */
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractDecoder> createMicropolisDecoder(const DecoderProto& config)
|
||||
|
|
|
|||
|
|
@ -4,110 +4,129 @@
|
|||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "image.h"
|
||||
#include "mapper.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<Sector>& sector)
|
||||
static void write_sector(std::vector<bool>& bits,
|
||||
unsigned& cursor,
|
||||
const std::shared_ptr<const Sector>& sector)
|
||||
{
|
||||
if ((sector->data.size() != 256) && (sector->data.size() != MICROPOLIS_ENCODED_SECTOR_SIZE))
|
||||
Error() << "unsupported sector size --- you must pick 256 or 275";
|
||||
if ((sector->data.size() != 256) &&
|
||||
(sector->data.size() != MICROPOLIS_ENCODED_SECTOR_SIZE))
|
||||
Error() << "unsupported sector size --- you must pick 256 or 275";
|
||||
|
||||
int fullSectorSize = 40 + MICROPOLIS_ENCODED_SECTOR_SIZE + 40 + 35;
|
||||
auto fullSector = std::make_shared<std::vector<uint8_t>>();
|
||||
fullSector->reserve(fullSectorSize);
|
||||
/* sector preamble */
|
||||
for (int i=0; i<40; i++)
|
||||
fullSector->push_back(0);
|
||||
Bytes sectorData;
|
||||
if (sector->data.size() == MICROPOLIS_ENCODED_SECTOR_SIZE)
|
||||
sectorData = sector->data;
|
||||
else
|
||||
{
|
||||
ByteWriter writer(sectorData);
|
||||
writer.write_8(0xff); /* Sync */
|
||||
writer.write_8(sector->logicalTrack);
|
||||
writer.write_8(sector->logicalSector);
|
||||
for (int i=0; i<10; i++)
|
||||
writer.write_8(0); /* Padding */
|
||||
writer += sector->data;
|
||||
writer.write_8(micropolisChecksum(sectorData.slice(1)));
|
||||
for (int i=0; i<5; i++)
|
||||
writer.write_8(0); /* 4 byte ECC and ECC not present flag */
|
||||
}
|
||||
for (uint8_t b : sectorData)
|
||||
fullSector->push_back(b);
|
||||
/* sector postamble */
|
||||
for (int i=0; i<40; i++)
|
||||
fullSector->push_back(0);
|
||||
/* filler */
|
||||
for (int i=0; i<35; i++)
|
||||
fullSector->push_back(0);
|
||||
int fullSectorSize = 40 + MICROPOLIS_ENCODED_SECTOR_SIZE + 40 + 35;
|
||||
auto fullSector = std::make_shared<std::vector<uint8_t>>();
|
||||
fullSector->reserve(fullSectorSize);
|
||||
/* sector preamble */
|
||||
for (int i = 0; i < 40; i++)
|
||||
fullSector->push_back(0);
|
||||
Bytes sectorData;
|
||||
if (sector->data.size() == MICROPOLIS_ENCODED_SECTOR_SIZE)
|
||||
{
|
||||
if (sector->data[0] != 0xFF)
|
||||
Error() << "275 byte sector doesn't start with sync byte 0xFF. "
|
||||
"Corrupted sector";
|
||||
uint8_t wantChecksum = sector->data[1 + 2 + 266];
|
||||
uint8_t gotChecksum =
|
||||
micropolisChecksum(sector->data.slice(1, 2 + 266));
|
||||
if (wantChecksum != gotChecksum)
|
||||
std::cerr << "Warning: checksum incorrect. Sector: "
|
||||
<< sector->logicalSector << std::endl;
|
||||
sectorData = sector->data;
|
||||
}
|
||||
else
|
||||
{
|
||||
ByteWriter writer(sectorData);
|
||||
writer.write_8(0xff); /* Sync */
|
||||
writer.write_8(sector->logicalTrack);
|
||||
writer.write_8(sector->logicalSector);
|
||||
for (int i = 0; i < 10; i++)
|
||||
writer.write_8(0); /* Padding */
|
||||
writer += sector->data;
|
||||
writer.write_8(micropolisChecksum(sectorData.slice(1)));
|
||||
for (int i = 0; i < 5; i++)
|
||||
writer.write_8(0); /* 4 byte ECC and ECC not present flag */
|
||||
}
|
||||
for (uint8_t b : sectorData)
|
||||
fullSector->push_back(b);
|
||||
/* sector postamble */
|
||||
for (int i = 0; i < 40; i++)
|
||||
fullSector->push_back(0);
|
||||
/* filler */
|
||||
for (int i = 0; i < 35; i++)
|
||||
fullSector->push_back(0);
|
||||
|
||||
if (fullSector->size() != fullSectorSize)
|
||||
Error() << "sector mismatched length";
|
||||
bool lastBit = false;
|
||||
encodeMfm(bits, cursor, fullSector, lastBit);
|
||||
/* filler */
|
||||
for (int i=0; i<5; i++)
|
||||
{
|
||||
bits[cursor++] = 1;
|
||||
bits[cursor++] = 0;
|
||||
}
|
||||
if (fullSector->size() != fullSectorSize)
|
||||
Error() << "sector mismatched length";
|
||||
bool lastBit = false;
|
||||
encodeMfm(bits, cursor, fullSector, lastBit);
|
||||
/* filler */
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
bits[cursor++] = 1;
|
||||
bits[cursor++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class MicropolisEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
MicropolisEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.micropolis())
|
||||
{}
|
||||
MicropolisEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.micropolis())
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
if ((physicalTrack >= 0) && (physicalTrack < 77))
|
||||
{
|
||||
for (int sectorId = 0; sectorId < 16; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
if ((location.logicalTrack >= 0) && (location.logicalTrack < 77))
|
||||
{
|
||||
for (int sectorId = 0; sectorId < 16; sectorId++)
|
||||
{
|
||||
const auto& sector =
|
||||
image.get(location.logicalTrack, location.head, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution = 100000;
|
||||
double clockRateUs = 2.00;
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution =
|
||||
(_config.rotational_period_ms() * 1e3) / _config.clock_period_us();
|
||||
|
||||
if ((physicalTrack < 0) || (physicalTrack >= 77) || sectors.empty())
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
for (const auto& sectorData : sectors)
|
||||
write_sector(bits, cursor, sectorData);
|
||||
|
||||
for (const auto& sectorData : sectors)
|
||||
write_sector(bits, cursor, sectorData);
|
||||
if (cursor != bits.size())
|
||||
Error() << "track data mismatched length";
|
||||
|
||||
if (cursor != bits.size())
|
||||
Error() << "track data mismatched length";
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs * 1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits,
|
||||
Mapper::calculatePhysicalClockPeriod(
|
||||
_config.clock_period_us() * 1e3,
|
||||
_config.rotational_period_ms() * 1e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const MicropolisEncoderProto& _config;
|
||||
const MicropolisEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createMicropolisEncoder(const EncoderProto& config)
|
||||
std::unique_ptr<AbstractEncoder> createMicropolisEncoder(
|
||||
const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new MicropolisEncoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new MicropolisEncoder(config));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#ifndef MICROPOLIS_H
|
||||
#define MICROPOLIS_H
|
||||
|
||||
#define MICROPOLIS_ENCODED_SECTOR_SIZE (1+2+266+6)
|
||||
#define MICROPOLIS_PAYLOAD_SIZE (256)
|
||||
#define MICROPOLIS_HEADER_SIZE (1+2+10)
|
||||
#define MICROPOLIS_ENCODED_SECTOR_SIZE (MICROPOLIS_HEADER_SIZE + MICROPOLIS_PAYLOAD_SIZE + 6)
|
||||
|
||||
class AbstractDecoder;
|
||||
class AbstractEncoder;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,24 @@
|
|||
syntax = "proto2";
|
||||
|
||||
message MicropolisDecoderProto {}
|
||||
message MicropolisEncoderProto {}
|
||||
import "lib/common.proto";
|
||||
|
||||
message MicropolisDecoderProto {
|
||||
enum ChecksumType {
|
||||
AUTO = 0;
|
||||
MICROPOLIS = 1;
|
||||
MZOS = 2;
|
||||
}
|
||||
|
||||
optional int32 sector_output_size = 1 [default = 256,
|
||||
(help) = "How much of the raw sector should be saved. Must be 256 or 275"];
|
||||
optional ChecksumType checksum_type = 2 [default = AUTO,
|
||||
(help) = "Checksum type to use: AUTO, MICROPOLIS, MZOS"];
|
||||
}
|
||||
|
||||
message MicropolisEncoderProto {
|
||||
optional double clock_period_us = 1
|
||||
[ default = 2.0, (help) = "clock rate on the real device" ];
|
||||
optional double rotational_period_ms = 2
|
||||
[ default = 166.0, (help) = "rotational period on the real device" ];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@ const int SECTOR_SIZE = 256;
|
|||
*/
|
||||
|
||||
/* FM beginning of track marker:
|
||||
* 0 0 f 3 decoded nibbles
|
||||
* 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1
|
||||
* 1010 1010 1010 1010 1111 1111 1010 1111
|
||||
* a a a a f f a f
|
||||
* a a a a f f a f encoded nibbles
|
||||
*/
|
||||
const FluxPattern ID_PATTERN(32, 0xaaaaffaf);
|
||||
|
||||
|
|
@ -28,61 +30,52 @@ public:
|
|||
AbstractDecoder(config)
|
||||
{}
|
||||
|
||||
void beginTrack()
|
||||
void beginTrack() override
|
||||
{
|
||||
_currentSector = -1;
|
||||
_clock = 0;
|
||||
_clock = _sector->clock = seekToPattern(ID_PATTERN);
|
||||
_currentSector = 0;
|
||||
}
|
||||
|
||||
RecordType advanceToNextRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
if (_currentSector == -1)
|
||||
{
|
||||
/* First sector in the track: look for the sync marker. */
|
||||
const FluxMatcher* matcher = nullptr;
|
||||
_sector->clock = _clock = _fmr->seekToPattern(ID_PATTERN, matcher);
|
||||
readRawBits(32); /* skip the ID mark */
|
||||
_logicalTrack = decodeFmMfm(readRawBits(32)).slice(0, 32).reader().read_be16();
|
||||
}
|
||||
else if (_currentSector == 10)
|
||||
if (_currentSector == 11)
|
||||
{
|
||||
/* That was the last sector on the disk. */
|
||||
return UNKNOWN_RECORD;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise we assume the clock from the first sector is still valid.
|
||||
* The decoder framwork will automatically stop when we hit the end of
|
||||
* the track. */
|
||||
_sector->clock = _clock;
|
||||
}
|
||||
|
||||
_currentSector++;
|
||||
return SECTOR_RECORD;
|
||||
return _clock;
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
/* Skip the ID pattern and track word, which is only present on the
|
||||
* first sector. We don't trust the track word because some driver
|
||||
* don't write it correctly. */
|
||||
|
||||
if (_currentSector == 0)
|
||||
readRawBits(64);
|
||||
|
||||
auto bits = readRawBits((SECTOR_SIZE+2)*16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE+2).swab();
|
||||
auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE+2);
|
||||
|
||||
uint16_t gotChecksum = 0;
|
||||
ByteReader br(bytes);
|
||||
for (int i=0; i<(SECTOR_SIZE/2); i++)
|
||||
gotChecksum += br.read_le16();
|
||||
uint16_t wantChecksum = br.read_le16();
|
||||
gotChecksum += br.read_be16();
|
||||
uint16_t wantChecksum = br.read_be16();
|
||||
|
||||
_sector->logicalTrack = _logicalTrack;
|
||||
_sector->logicalTrack = _sector->physicalTrack;
|
||||
_sector->logicalSide = _sector->physicalHead;
|
||||
_sector->logicalSector = _currentSector;
|
||||
_sector->data = bytes.slice(0, SECTOR_SIZE);
|
||||
_sector->data = bytes.slice(0, SECTOR_SIZE).swab();
|
||||
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
_currentSector++;
|
||||
}
|
||||
|
||||
private:
|
||||
nanoseconds_t _clock;
|
||||
int _currentSector;
|
||||
int _logicalTrack;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractDecoder> createMxDecoder(const DecoderProto& config)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
#include "lib/decoders/decoders.pb.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
#define MFM_ID 0xaaaaaaaaaaaa5545LL
|
||||
#define FM_ID 0xaaaaaaaaaaaaffefLL
|
||||
/*
|
||||
* MFM sectors have 32 bytes of 00's followed by two sync characters,
|
||||
* specified in the North Star MDS manual as 0xFBFB.
|
||||
|
|
@ -33,14 +35,14 @@
|
|||
* 0000 0000 0000 0000 0000 0000 0101 0101 0100 0101
|
||||
* A A A A A A 5 5 4 5
|
||||
*/
|
||||
static const FluxPattern MFM_PATTERN(64, 0xAAAAAAAAAAAA5545LL);
|
||||
static const FluxPattern MFM_PATTERN(64, MFM_ID);
|
||||
|
||||
/* FM sectors have 16 bytes of 00's followed by 0xFB.
|
||||
* 00 FB
|
||||
* 0000 0000 1111 1111 1110 1111
|
||||
* A A F F E F
|
||||
*/
|
||||
static const FluxPattern FM_PATTERN(64, 0xAAAAAAAAAAAAFFEFLL);
|
||||
static const FluxPattern FM_PATTERN(64, FM_ID);
|
||||
|
||||
const FluxMatchers ANY_SECTOR_PATTERN(
|
||||
{
|
||||
|
|
@ -74,16 +76,16 @@ public:
|
|||
{}
|
||||
|
||||
/* Search for FM or MFM sector record */
|
||||
RecordType advanceToNextRecord() override
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
nanoseconds_t now = _fmr->tell().ns();
|
||||
nanoseconds_t now = tell().ns();
|
||||
|
||||
/* For all but the first sector, seek to the next sector pulse.
|
||||
* The first sector does not contain the sector pulse in the fluxmap.
|
||||
*/
|
||||
if (now != 0) {
|
||||
_fmr->seekToIndexMark();
|
||||
now = _fmr->tell().ns();
|
||||
seekToIndexMark();
|
||||
now = tell().ns();
|
||||
}
|
||||
|
||||
/* Discard a possible partial sector at the end of the track.
|
||||
|
|
@ -91,23 +93,27 @@ public:
|
|||
* whatever data read happens to match the checksum of 0, which is
|
||||
* rare, but has been observed on some disks.
|
||||
*/
|
||||
if (now > (_fmr->getDuration() - 21e6)) {
|
||||
_fmr->seekToIndexMark();
|
||||
return(UNKNOWN_RECORD);
|
||||
if (now > (getFluxmapDuration() - 21e6)) {
|
||||
seekToIndexMark();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msSinceIndex = std::round(now / 1e6);
|
||||
|
||||
const FluxMatcher* matcher = nullptr;
|
||||
|
||||
/* Note that the seekToPattern ignores the sector pulses, so if
|
||||
* a sector is not found for some reason, the seek will advance
|
||||
* past one or more sector pulses. For this reason, calculate
|
||||
* _hardSectorId after the sector header is found.
|
||||
*/
|
||||
_sector->clock = _fmr->seekToPattern(ANY_SECTOR_PATTERN, matcher);
|
||||
nanoseconds_t clock = seekToPattern(ANY_SECTOR_PATTERN);
|
||||
_sector->headerStartTime = tell().ns();
|
||||
|
||||
int sectorFoundTimeRaw = std::round((_fmr->tell().ns()) / 1e6);
|
||||
/* Discard a possible partial sector. */
|
||||
if (_sector->headerStartTime > (getFluxmapDuration() - 21e6)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sectorFoundTimeRaw = std::round(_sector->headerStartTime / 1e6);
|
||||
int sectorFoundTime;
|
||||
|
||||
/* Round time to the nearest 20ms */
|
||||
|
|
@ -121,30 +127,15 @@ public:
|
|||
/* Calculate the sector ID based on time since the index */
|
||||
_hardSectorId = (sectorFoundTime / 20) % 10;
|
||||
|
||||
// std::cout << fmt::format(
|
||||
// "Sector ID {}: hole at {}ms, sector start at {}ms",
|
||||
// _hardSectorId, msSinceIndex, sectorFoundTimeRaw) << std::endl;
|
||||
|
||||
if (matcher == &MFM_PATTERN) {
|
||||
_sectorType = SECTOR_TYPE_MFM;
|
||||
readRawBits(48);
|
||||
return SECTOR_RECORD;
|
||||
}
|
||||
|
||||
if (matcher == &FM_PATTERN) {
|
||||
_sectorType = SECTOR_TYPE_FM;
|
||||
readRawBits(48);
|
||||
return SECTOR_RECORD;
|
||||
}
|
||||
|
||||
return UNKNOWN_RECORD;
|
||||
return clock;
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
uint64_t id = toBytes(readRawBits(64)).reader().read_be64();
|
||||
unsigned recordSize, payloadSize, headerSize;
|
||||
|
||||
if (_sectorType == SECTOR_TYPE_MFM) {
|
||||
if (id == MFM_ID) {
|
||||
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_DD;
|
||||
payloadSize = NORTHSTAR_PAYLOAD_SIZE_DD;
|
||||
headerSize = NORTHSTAR_HEADER_SIZE_DD;
|
||||
|
|
@ -158,26 +149,22 @@ public:
|
|||
auto rawbits = readRawBits(recordSize * 16);
|
||||
auto bytes = decodeFmMfm(rawbits).slice(0, recordSize);
|
||||
ByteReader br(bytes);
|
||||
uint8_t sync_char;
|
||||
|
||||
_sector->logicalSide = _sector->physicalHead;
|
||||
_sector->logicalSector = _hardSectorId;
|
||||
_sector->logicalTrack = _sector->physicalCylinder;
|
||||
_sector->logicalTrack = _sector->physicalTrack;
|
||||
|
||||
sync_char = br.read_8(); /* Sync char: 0xFB */
|
||||
if (_sectorType == SECTOR_TYPE_MFM) {
|
||||
sync_char = br.read_8();/* MFM second Sync char, usually 0xFB */
|
||||
if (headerSize == NORTHSTAR_HEADER_SIZE_DD) {
|
||||
br.read_8(); /* MFM second Sync char, usually 0xFB */
|
||||
}
|
||||
|
||||
_sector->data = br.read(payloadSize);
|
||||
|
||||
uint8_t wantChecksum = br.read_8();
|
||||
uint8_t gotChecksum = northstarChecksum(bytes.slice(headerSize, payloadSize));
|
||||
|
||||
uint8_t gotChecksum = northstarChecksum(bytes.slice(headerSize - 1, payloadSize));
|
||||
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
|
||||
std::set<unsigned> requiredSectors(const Location&) const override
|
||||
{
|
||||
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
return sectors;
|
||||
|
|
@ -185,7 +172,6 @@ public:
|
|||
|
||||
private:
|
||||
const NorthstarDecoderProto& _config;
|
||||
uint8_t _sectorType = SECTOR_TYPE_MFM;
|
||||
uint8_t _hardSectorId;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "image.h"
|
||||
#include "mapper.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
|
||||
#define GAP_FILL_SIZE_SD 30
|
||||
|
|
@ -12,155 +13,176 @@
|
|||
#define GAP_FILL_SIZE_DD 62
|
||||
#define PRE_HEADER_GAP_FILL_SIZE_DD 16
|
||||
|
||||
#define GAP1_FILL_BYTE (0x4F)
|
||||
#define GAP2_FILL_BYTE (0x4F)
|
||||
#define GAP1_FILL_BYTE (0x4F)
|
||||
#define GAP2_FILL_BYTE (0x4F)
|
||||
|
||||
#define TOTAL_SECTOR_BYTES ()
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<Sector>& sector)
|
||||
static void write_sector(std::vector<bool>& bits,
|
||||
unsigned& cursor,
|
||||
const std::shared_ptr<const Sector>& sector)
|
||||
{
|
||||
int preambleSize = 0;
|
||||
int encodedSectorSize = 0;
|
||||
int gapFillSize = 0;
|
||||
int preHeaderGapFillSize = 0;
|
||||
int preambleSize = 0;
|
||||
int encodedSectorSize = 0;
|
||||
int gapFillSize = 0;
|
||||
int preHeaderGapFillSize = 0;
|
||||
|
||||
bool doubleDensity;
|
||||
bool doubleDensity;
|
||||
|
||||
switch (sector->data.size()) {
|
||||
case NORTHSTAR_PAYLOAD_SIZE_SD:
|
||||
preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD;
|
||||
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD + NORTHSTAR_ENCODED_SECTOR_SIZE_SD + GAP_FILL_SIZE_SD;
|
||||
gapFillSize = GAP_FILL_SIZE_SD;
|
||||
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD;
|
||||
doubleDensity = false;
|
||||
break;
|
||||
case NORTHSTAR_PAYLOAD_SIZE_DD:
|
||||
preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD;
|
||||
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD + NORTHSTAR_ENCODED_SECTOR_SIZE_DD + GAP_FILL_SIZE_DD;
|
||||
gapFillSize = GAP_FILL_SIZE_DD;
|
||||
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD;
|
||||
doubleDensity = true;
|
||||
break;
|
||||
default:
|
||||
Error() << "unsupported sector size --- you must pick 256 or 512";
|
||||
break;
|
||||
}
|
||||
switch (sector->data.size())
|
||||
{
|
||||
case NORTHSTAR_PAYLOAD_SIZE_SD:
|
||||
preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD;
|
||||
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD +
|
||||
NORTHSTAR_ENCODED_SECTOR_SIZE_SD +
|
||||
GAP_FILL_SIZE_SD;
|
||||
gapFillSize = GAP_FILL_SIZE_SD;
|
||||
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD;
|
||||
doubleDensity = false;
|
||||
break;
|
||||
case NORTHSTAR_PAYLOAD_SIZE_DD:
|
||||
preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD;
|
||||
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD +
|
||||
NORTHSTAR_ENCODED_SECTOR_SIZE_DD +
|
||||
GAP_FILL_SIZE_DD;
|
||||
gapFillSize = GAP_FILL_SIZE_DD;
|
||||
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD;
|
||||
doubleDensity = true;
|
||||
break;
|
||||
default:
|
||||
Error() << "unsupported sector size --- you must pick 256 or 512";
|
||||
break;
|
||||
}
|
||||
|
||||
int fullSectorSize = preambleSize + encodedSectorSize;
|
||||
auto fullSector = std::make_shared<std::vector<uint8_t>>();
|
||||
fullSector->reserve(fullSectorSize);
|
||||
int fullSectorSize = preambleSize + encodedSectorSize;
|
||||
auto fullSector = std::make_shared<std::vector<uint8_t>>();
|
||||
fullSector->reserve(fullSectorSize);
|
||||
|
||||
/* sector gap after index pulse */
|
||||
for (int i = 0; i < preHeaderGapFillSize; i++)
|
||||
fullSector->push_back(GAP1_FILL_BYTE);
|
||||
/* sector gap after index pulse */
|
||||
for (int i = 0; i < preHeaderGapFillSize; i++)
|
||||
fullSector->push_back(GAP1_FILL_BYTE);
|
||||
|
||||
/* sector preamble */
|
||||
for (int i = 0; i < preambleSize; i++)
|
||||
fullSector->push_back(0);
|
||||
/* sector preamble */
|
||||
for (int i = 0; i < preambleSize; i++)
|
||||
fullSector->push_back(0);
|
||||
|
||||
Bytes sectorData;
|
||||
if (sector->data.size() == encodedSectorSize)
|
||||
sectorData = sector->data;
|
||||
else {
|
||||
ByteWriter writer(sectorData);
|
||||
writer.write_8(0xFB); /* sync character */
|
||||
if (doubleDensity == true) {
|
||||
writer.write_8(0xFB); /* Double-density has two sync characters */
|
||||
}
|
||||
writer += sector->data;
|
||||
if (doubleDensity == true) {
|
||||
writer.write_8(northstarChecksum(sectorData.slice(2)));
|
||||
} else {
|
||||
writer.write_8(northstarChecksum(sectorData.slice(1)));
|
||||
}
|
||||
}
|
||||
for (uint8_t b : sectorData)
|
||||
fullSector->push_back(b);
|
||||
Bytes sectorData;
|
||||
if (sector->data.size() == encodedSectorSize)
|
||||
sectorData = sector->data;
|
||||
else
|
||||
{
|
||||
ByteWriter writer(sectorData);
|
||||
writer.write_8(0xFB); /* sync character */
|
||||
if (doubleDensity == true)
|
||||
{
|
||||
writer.write_8(0xFB); /* Double-density has two sync characters */
|
||||
}
|
||||
writer += sector->data;
|
||||
if (doubleDensity == true)
|
||||
{
|
||||
writer.write_8(northstarChecksum(sectorData.slice(2)));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.write_8(northstarChecksum(sectorData.slice(1)));
|
||||
}
|
||||
}
|
||||
for (uint8_t b : sectorData)
|
||||
fullSector->push_back(b);
|
||||
|
||||
if (sector->logicalSector != 9) {
|
||||
/* sector postamble */
|
||||
for (int i = 0; i < gapFillSize; i++)
|
||||
fullSector->push_back(GAP2_FILL_BYTE);
|
||||
if (sector->logicalSector != 9)
|
||||
{
|
||||
/* sector postamble */
|
||||
for (int i = 0; i < gapFillSize; i++)
|
||||
fullSector->push_back(GAP2_FILL_BYTE);
|
||||
|
||||
if (fullSector->size() != fullSectorSize)
|
||||
Error() << "sector mismatched length (" << sector->data.size() << ") expected: " << fullSector->size() << " got " << fullSectorSize;
|
||||
} else {
|
||||
/* sector postamble */
|
||||
for (int i = 0; i < gapFillSize; i++)
|
||||
fullSector->push_back(GAP2_FILL_BYTE);
|
||||
}
|
||||
if (fullSector->size() != fullSectorSize)
|
||||
Error() << "sector mismatched length (" << sector->data.size()
|
||||
<< ") expected: " << fullSector->size() << " got "
|
||||
<< fullSectorSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* sector postamble */
|
||||
for (int i = 0; i < gapFillSize; i++)
|
||||
fullSector->push_back(GAP2_FILL_BYTE);
|
||||
}
|
||||
|
||||
bool lastBit = false;
|
||||
bool lastBit = false;
|
||||
|
||||
if (doubleDensity == true) {
|
||||
encodeMfm(bits, cursor, fullSector, lastBit);
|
||||
}
|
||||
else {
|
||||
encodeFm(bits, cursor, fullSector);
|
||||
}
|
||||
if (doubleDensity == true)
|
||||
{
|
||||
encodeMfm(bits, cursor, fullSector, lastBit);
|
||||
}
|
||||
else
|
||||
{
|
||||
encodeFm(bits, cursor, fullSector);
|
||||
}
|
||||
}
|
||||
|
||||
class NorthstarEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
NorthstarEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.northstar())
|
||||
{}
|
||||
NorthstarEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.northstar())
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
if ((physicalTrack >= 0) && (physicalTrack < 35))
|
||||
{
|
||||
for (int sectorId = 0; sectorId < 10; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
if ((location.logicalTrack >= 0) && (location.logicalTrack < 35))
|
||||
{
|
||||
for (int sectorId = 0; sectorId < 10; sectorId++)
|
||||
{
|
||||
const auto& sector =
|
||||
image.get(location.logicalTrack, location.head, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution = 100000;
|
||||
double clockRateUs = 4.00;
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution = 100000;
|
||||
double clockRateUs = _config.clock_period_us();
|
||||
|
||||
if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty())
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
const auto& sector = *sectors.begin();
|
||||
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD)
|
||||
bitsPerRevolution /= 2; // FM
|
||||
else
|
||||
clockRateUs /= 2.00;
|
||||
|
||||
const auto& sector = *sectors.begin();
|
||||
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) {
|
||||
bitsPerRevolution /= 2; // FM
|
||||
} else {
|
||||
clockRateUs /= 2.00;
|
||||
}
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
for (const auto& sectorData : sectors)
|
||||
write_sector(bits, cursor, sectorData);
|
||||
|
||||
for (const auto& sectorData : sectors)
|
||||
write_sector(bits, cursor, sectorData);
|
||||
if (cursor > bits.size())
|
||||
Error() << "track data overrun";
|
||||
|
||||
if (cursor > bits.size())
|
||||
Error() << "track data overrun";
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs * 1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits,
|
||||
Mapper::calculatePhysicalClockPeriod(
|
||||
clockRateUs * 1e3, _config.rotational_period_ms() * 1e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const NorthstarEncoderProto& _config;
|
||||
const NorthstarEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createNorthstarEncoder(const EncoderProto& config)
|
||||
std::unique_ptr<AbstractEncoder> createNorthstarEncoder(
|
||||
const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,6 @@
|
|||
#define NORTHSTAR_ENCODED_SECTOR_SIZE_SD (NORTHSTAR_HEADER_SIZE_SD + NORTHSTAR_PAYLOAD_SIZE_SD + NORTHSTAR_CHECKSUM_SIZE)
|
||||
#define NORTHSTAR_ENCODED_SECTOR_SIZE_DD (NORTHSTAR_HEADER_SIZE_DD + NORTHSTAR_PAYLOAD_SIZE_DD + NORTHSTAR_CHECKSUM_SIZE)
|
||||
|
||||
#define SECTOR_TYPE_MFM (0)
|
||||
#define SECTOR_TYPE_FM (1)
|
||||
|
||||
class AbstractDecoder;
|
||||
class AbstractEncoder;
|
||||
class EncoderProto;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
syntax = "proto2";
|
||||
|
||||
message NorthstarDecoderProto {}
|
||||
message NorthstarEncoderProto {}
|
||||
import "lib/common.proto";
|
||||
|
||||
message NorthstarDecoderProto {}
|
||||
|
||||
message NorthstarEncoderProto {
|
||||
optional double clock_period_us = 1
|
||||
[ default = 4.0, (help) = "clock rate on the real device (for FM)" ];
|
||||
optional double rotational_period_ms = 2
|
||||
[ default = 166.0, (help) = "rotational period on the real device" ];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,17 +23,19 @@
|
|||
* 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 uint16_t SECTOR_ID = 0x550a;
|
||||
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
|
||||
* data: 0 1 0 1 0 1 0 1 .0 0 0 0 1 0 1 1 = 0x550b
|
||||
* 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 uint16_t DATA_ID = 0x550b;
|
||||
const FluxPattern DATA_RECORD_PATTERN(32, 0x11112245);
|
||||
|
||||
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
|
||||
|
|
@ -45,26 +47,22 @@ public:
|
|||
AbstractDecoder(config)
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
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;
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE*16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE);
|
||||
|
||||
ByteReader br(bytes);
|
||||
if (br.read_be16() != SECTOR_ID)
|
||||
return;
|
||||
|
||||
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 */
|
||||
|
|
@ -76,15 +74,17 @@ public:
|
|||
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
|
||||
}
|
||||
|
||||
void decodeDataRecord()
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE*16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE);
|
||||
|
||||
ByteReader br(bytes);
|
||||
if (br.read_be16() != DATA_ID)
|
||||
return;
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -3,166 +3,168 @@
|
|||
#include "encoders/encoders.h"
|
||||
#include "tids990.h"
|
||||
#include "crc.h"
|
||||
#include "writer.h"
|
||||
#include "readerwriter.h"
|
||||
#include "image.h"
|
||||
#include "mapper.h"
|
||||
#include "arch/tids990/tids990.pb.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
static int charToInt(char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
}
|
||||
|
||||
static uint8_t decodeUint16(uint16_t raw)
|
||||
{
|
||||
Bytes b;
|
||||
ByteWriter bw(b);
|
||||
bw.write_be16(raw);
|
||||
return decodeFmMfm(b.toBits())[0];
|
||||
Bytes b;
|
||||
ByteWriter bw(b);
|
||||
bw.write_be16(raw);
|
||||
return decodeFmMfm(b.toBits())[0];
|
||||
}
|
||||
|
||||
class Tids990Encoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
Tids990Encoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.tids990())
|
||||
{}
|
||||
Tids990Encoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.tids990())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void 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 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 writeBytes(const Bytes& bytes)
|
||||
{
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
}
|
||||
void writeBytes(const Bytes& bytes)
|
||||
{
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
}
|
||||
|
||||
void writeBytes(int count, uint8_t byte)
|
||||
{
|
||||
Bytes bytes = { byte };
|
||||
for (int i=0; i<count; i++)
|
||||
writeBytes(bytes);
|
||||
}
|
||||
void writeBytes(int count, uint8_t byte)
|
||||
{
|
||||
Bytes bytes = {byte};
|
||||
for (int i = 0; i < count; i++)
|
||||
writeBytes(bytes);
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
for (char sectorChar : _config.sector_skew())
|
||||
for (char sectorChar : _config.sector_skew())
|
||||
{
|
||||
int sectorId = charToInt(sectorChar);
|
||||
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
int sectorId = charToInt(sectorChar);
|
||||
const auto& sector =
|
||||
image.get(location.logicalTrack, location.head, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
double clockRateUs = 1e3 / _config.clock_rate_khz() / 2.0;
|
||||
int bitsPerRevolution = (_config.track_length_ms() * 1000.0) / clockRateUs;
|
||||
_bits.resize(bitsPerRevolution);
|
||||
_cursor = 0;
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
double clockRateUs = _config.clock_period_us() / 2.0;
|
||||
int bitsPerRevolution =
|
||||
(_config.rotational_period_ms() * 1000.0) / clockRateUs;
|
||||
_bits.resize(bitsPerRevolution);
|
||||
_cursor = 0;
|
||||
|
||||
uint8_t am1Unencoded = decodeUint16(_config.am1_byte());
|
||||
uint8_t am2Unencoded = decodeUint16(_config.am2_byte());
|
||||
uint8_t am1Unencoded = decodeUint16(_config.am1_byte());
|
||||
uint8_t am2Unencoded = decodeUint16(_config.am2_byte());
|
||||
|
||||
writeBytes(_config.gap1_bytes(), 0x55);
|
||||
writeBytes(_config.gap1_bytes(), 0x55);
|
||||
|
||||
bool first = true;
|
||||
for (char sectorChar : _config.sector_skew())
|
||||
{
|
||||
int sectorId = charToInt(sectorChar);
|
||||
if (!first)
|
||||
writeBytes(_config.gap3_bytes(), 0x55);
|
||||
first = false;
|
||||
bool first = true;
|
||||
for (const auto& sectorData : sectors)
|
||||
{
|
||||
if (!first)
|
||||
writeBytes(_config.gap3_bytes(), 0x55);
|
||||
first = false;
|
||||
|
||||
const auto& sectorData = image.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. */
|
||||
|
||||
/* 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);
|
||||
|
||||
{
|
||||
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(_config.sector_count());
|
||||
bw.write_8(sectorData->logicalSector);
|
||||
bw.write_be16(sectorData->data.size());
|
||||
uint16_t crc = crc16(CCITT_POLY, header);
|
||||
bw.write_be16(crc);
|
||||
|
||||
writeBytes(12, 0x55);
|
||||
bw.write_8(am1Unencoded);
|
||||
bw.write_8(sectorData->logicalSide << 3);
|
||||
bw.write_8(sectorData->logicalTrack);
|
||||
bw.write_8(_config.sector_count());
|
||||
bw.write_8(sectorData->logicalSector);
|
||||
bw.write_be16(sectorData->data.size());
|
||||
uint16_t crc = crc16(CCITT_POLY, header);
|
||||
bw.write_be16(crc);
|
||||
writeRawBits(_config.am1_byte(), 16);
|
||||
writeBytes(header.slice(1));
|
||||
}
|
||||
|
||||
writeRawBits(_config.am1_byte(), 16);
|
||||
writeBytes(header.slice(1));
|
||||
}
|
||||
writeBytes(_config.gap2_bytes(), 0x55);
|
||||
|
||||
writeBytes(_config.gap2_bytes(), 0x55);
|
||||
{
|
||||
Bytes data;
|
||||
ByteWriter bw(data);
|
||||
|
||||
{
|
||||
Bytes data;
|
||||
ByteWriter bw(data);
|
||||
writeBytes(12, 0x55);
|
||||
bw.write_8(am2Unencoded);
|
||||
|
||||
writeBytes(12, 0x55);
|
||||
bw.write_8(am2Unencoded);
|
||||
bw += sectorData->data;
|
||||
uint16_t crc = crc16(CCITT_POLY, data);
|
||||
bw.write_be16(crc);
|
||||
|
||||
bw += sectorData->data;
|
||||
uint16_t crc = crc16(CCITT_POLY, data);
|
||||
bw.write_be16(crc);
|
||||
writeRawBits(_config.am2_byte(), 16);
|
||||
writeBytes(data.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
writeRawBits(_config.am2_byte(), 16);
|
||||
writeBytes(data.slice(1));
|
||||
}
|
||||
}
|
||||
if (_cursor >= _bits.size())
|
||||
Error() << "track data overrun";
|
||||
while (_cursor < _bits.size())
|
||||
writeBytes(1, 0x55);
|
||||
|
||||
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;
|
||||
}
|
||||
auto fluxmap = std::make_unique<Fluxmap>();
|
||||
fluxmap->appendBits(_bits,
|
||||
Mapper::calculatePhysicalClockPeriod(clockRateUs * 1e3,
|
||||
_config.rotational_period_ms() * 1e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const Tids990EncoderProto& _config;
|
||||
std::vector<bool> _bits;
|
||||
unsigned _cursor;
|
||||
bool _lastBit;
|
||||
const Tids990EncoderProto& _config;
|
||||
std::vector<bool> _bits;
|
||||
unsigned _cursor;
|
||||
bool _lastBit;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createTids990Encoder(const EncoderProto& config)
|
||||
std::unique_ptr<AbstractEncoder> createTids990Encoder(
|
||||
const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new Tids990Encoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new Tids990Encoder(config));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@ syntax = "proto2";
|
|||
import "lib/common.proto";
|
||||
|
||||
message Tids990DecoderProto {}
|
||||
|
||||
message Tids990EncoderProto {
|
||||
optional double track_length_ms = 1 [ default = 166,
|
||||
optional double rotational_period_ms = 1 [ default = 166,
|
||||
(help) = "length of a track" ];
|
||||
optional int32 sector_count = 2 [ default = 26,
|
||||
(help) = "number of sectors per track" ];
|
||||
optional double clock_rate_khz = 3 [ default = 500,
|
||||
optional double clock_period_us = 3 [ default = 2,
|
||||
(help) = "clock rate of data to write" ];
|
||||
optional int32 am1_byte = 4 [ default = 0x2244,
|
||||
(help) = "16-bit RAW bit pattern to use for the AM1 ID byte" ];
|
||||
|
|
|
|||
|
|
@ -59,33 +59,29 @@ public:
|
|||
AbstractDecoder(config)
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
const FluxMatcher* matcher = nullptr;
|
||||
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
|
||||
if (matcher == &SECTOR_RECORD_PATTERN)
|
||||
return SECTOR_RECORD;
|
||||
if (matcher == &DATA_RECORD_PATTERN)
|
||||
return DATA_RECORD;
|
||||
return UNKNOWN_RECORD;
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
/* Skip the sync marker bit. */
|
||||
readRawBits(22);
|
||||
/* Check the ID. */
|
||||
|
||||
if (readRaw32() != VICTOR9K_SECTOR_RECORD)
|
||||
return;
|
||||
|
||||
/* Read header. */
|
||||
|
||||
auto bytes = decode(readRawBits(4*10)).slice(0, 4);
|
||||
auto bytes = decode(readRawBits(3*10)).slice(0, 3);
|
||||
|
||||
uint8_t rawTrack = bytes[1];
|
||||
_sector->logicalSector = bytes[2];
|
||||
uint8_t gotChecksum = bytes[3];
|
||||
uint8_t rawTrack = bytes[0];
|
||||
_sector->logicalSector = bytes[1];
|
||||
uint8_t gotChecksum = bytes[2];
|
||||
|
||||
_sector->logicalTrack = rawTrack & 0x7f;
|
||||
_sector->logicalSide = rawTrack >> 7;
|
||||
uint8_t wantChecksum = bytes[1] + bytes[2];
|
||||
uint8_t wantChecksum = bytes[0] + bytes[1];
|
||||
if ((_sector->logicalSector > 20) || (_sector->logicalTrack > 85) || (_sector->logicalSide > 1))
|
||||
return;
|
||||
|
||||
|
|
@ -93,22 +89,19 @@ public:
|
|||
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
void decodeDataRecord()
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
/* Skip the sync marker bit. */
|
||||
readRawBits(22);
|
||||
/* Check the ID. */
|
||||
|
||||
if (readRaw32() != VICTOR9K_DATA_RECORD)
|
||||
return;
|
||||
|
||||
/* Read data. */
|
||||
|
||||
auto bytes = decode(readRawBits((VICTOR9K_SECTOR_LENGTH+5)*10))
|
||||
.slice(0, VICTOR9K_SECTOR_LENGTH+5);
|
||||
auto bytes = decode(readRawBits((VICTOR9K_SECTOR_LENGTH+4)*10))
|
||||
.slice(0, VICTOR9K_SECTOR_LENGTH+4);
|
||||
ByteReader br(bytes);
|
||||
|
||||
/* Check that this is actually a data record. */
|
||||
|
||||
if (br.read_8() != 8)
|
||||
return;
|
||||
|
||||
_sector->data = br.read(VICTOR9K_SECTOR_LENGTH);
|
||||
uint16_t gotChecksum = sumBytes(_sector->data);
|
||||
uint16_t wantChecksum = br.read_le16();
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@
|
|||
#include "victor9k.h"
|
||||
#include "crc.h"
|
||||
#include "sector.h"
|
||||
#include "writer.h"
|
||||
#include "readerwriter.h"
|
||||
#include "image.h"
|
||||
#include "mapper.h"
|
||||
#include "fmt/format.h"
|
||||
#include "arch/victor9k/victor9k.pb.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
|
|
@ -14,96 +15,115 @@
|
|||
|
||||
static bool lastBit;
|
||||
|
||||
static void write_zero_bits(std::vector<bool>& bits, unsigned& cursor, unsigned count)
|
||||
static void write_zero_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, unsigned count)
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = 0;
|
||||
}
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_one_bits(std::vector<bool>& bits, unsigned& cursor, unsigned count)
|
||||
static void write_one_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, unsigned count)
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = 1;
|
||||
}
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||
{
|
||||
for (bool bit : src)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = bit;
|
||||
}
|
||||
for (bool bit : src)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = bit;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, uint64_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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
{
|
||||
ByteReader br(bytes);
|
||||
BitReader bitr(br);
|
||||
ByteReader br(bytes);
|
||||
BitReader bitr(br);
|
||||
|
||||
while (!bitr.eof())
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bitr.get();
|
||||
}
|
||||
while (!bitr.eof())
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bitr.get();
|
||||
}
|
||||
}
|
||||
|
||||
static int encode_data_gcr(uint8_t data)
|
||||
{
|
||||
switch (data & 0x0f)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: \
|
||||
return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void write_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
static void write_byte(std::vector<bool>& bits, unsigned& cursor, uint8_t b)
|
||||
{
|
||||
for (uint8_t b : bytes)
|
||||
{
|
||||
write_bits(bits, cursor, encode_data_gcr(b>>4), 5);
|
||||
write_bits(bits, cursor, encode_data_gcr(b), 5);
|
||||
}
|
||||
write_bits(bits, cursor, encode_data_gcr(b >> 4), 5);
|
||||
write_bits(bits, cursor, encode_data_gcr(b), 5);
|
||||
}
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor,
|
||||
const Victor9kEncoderProto::TrackdataProto& trackdata,
|
||||
const Sector& sector)
|
||||
static void write_bytes(
|
||||
std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
{
|
||||
for (uint8_t b : bytes)
|
||||
write_byte(bits, cursor, b);
|
||||
}
|
||||
|
||||
static void write_gap(std::vector<bool>& bits, unsigned& cursor, int length)
|
||||
{
|
||||
for (int i = 0; i < length / 10; i++)
|
||||
write_byte(bits, cursor, '0');
|
||||
}
|
||||
|
||||
static void write_sector(std::vector<bool>& bits,
|
||||
unsigned& cursor,
|
||||
const Victor9kEncoderProto::TrackdataProto& trackdata,
|
||||
const Sector& sector)
|
||||
{
|
||||
write_one_bits(bits, cursor, trackdata.pre_header_sync_bits());
|
||||
write_bits(bits, cursor, VICTOR9K_SECTOR_RECORD, 10);
|
||||
|
||||
uint8_t encodedTrack = sector.logicalTrack | (sector.logicalSide<<7);
|
||||
uint8_t encodedTrack = sector.logicalTrack | (sector.logicalSide << 7);
|
||||
uint8_t encodedSector = sector.logicalSector;
|
||||
write_bytes(bits, cursor, Bytes {
|
||||
encodedTrack,
|
||||
encodedSector,
|
||||
(uint8_t)(encodedTrack + encodedSector),
|
||||
});
|
||||
write_bytes(bits,
|
||||
cursor,
|
||||
Bytes{
|
||||
encodedTrack,
|
||||
encodedSector,
|
||||
(uint8_t)(encodedTrack + encodedSector),
|
||||
});
|
||||
|
||||
write_gap(bits, cursor, trackdata.post_header_gap_bits());
|
||||
|
||||
write_zero_bits(bits, cursor, trackdata.post_header_gap_bits());
|
||||
write_one_bits(bits, cursor, trackdata.pre_data_sync_bits());
|
||||
write_bits(bits, cursor, VICTOR9K_DATA_RECORD, 10);
|
||||
|
||||
|
|
@ -112,89 +132,100 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor,
|
|||
Bytes checksum(2);
|
||||
checksum.writer().write_le16(sumBytes(sector.data));
|
||||
write_bytes(bits, cursor, checksum);
|
||||
|
||||
write_zero_bits(bits, cursor, trackdata.post_data_gap_bits());
|
||||
write_gap(bits, cursor, trackdata.post_data_gap_bits());
|
||||
}
|
||||
|
||||
class Victor9kEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
Victor9kEncoder(const EncoderProto& config):
|
||||
Victor9kEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.victor9k())
|
||||
{}
|
||||
_config(config.victor9k())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void getTrackFormat(Victor9kEncoderProto::TrackdataProto& trackdata,
|
||||
unsigned track,
|
||||
unsigned head)
|
||||
{
|
||||
trackdata.Clear();
|
||||
for (const auto& f : _config.trackdata())
|
||||
{
|
||||
if (f.has_min_track() && (track < f.min_track()))
|
||||
continue;
|
||||
if (f.has_max_track() && (track > f.max_track()))
|
||||
continue;
|
||||
if (f.has_head() && (head != f.head()))
|
||||
continue;
|
||||
|
||||
void getTrackFormat(Victor9kEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
|
||||
{
|
||||
trackdata.Clear();
|
||||
for (const auto& f : _config.trackdata())
|
||||
{
|
||||
if (f.has_min_cylinder() && (cylinder < f.min_cylinder()))
|
||||
continue;
|
||||
if (f.has_max_cylinder() && (cylinder > f.max_cylinder()))
|
||||
continue;
|
||||
if (f.has_head() && (head != f.head()))
|
||||
continue;
|
||||
|
||||
trackdata.MergeFrom(f);
|
||||
}
|
||||
}
|
||||
trackdata.MergeFrom(f);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
Victor9kEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, physicalTrack, physicalSide);
|
||||
Victor9kEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, location.logicalTrack, location.head);
|
||||
|
||||
for (int i = 0; i < trackdata.sector_range().sector_count(); i++)
|
||||
{
|
||||
int sectorId = trackdata.sector_range().start_sector() + i;
|
||||
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
const auto& sector =
|
||||
image.get(location.logicalTrack, location.head, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
Victor9kEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, physicalTrack, physicalSide);
|
||||
Victor9kEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, location.logicalTrack, location.head);
|
||||
|
||||
unsigned bitsPerRevolution = trackdata.original_data_rate_khz() * trackdata.original_period_ms();
|
||||
unsigned bitsPerRevolution = (trackdata.rotational_period_ms() * 1e3) /
|
||||
trackdata.clock_period_us();
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
double clockRateUs = 166666.0 / bitsPerRevolution;
|
||||
nanoseconds_t clockPeriod = Mapper::calculatePhysicalClockPeriod(
|
||||
trackdata.clock_period_us() * 1e3,
|
||||
trackdata.rotational_period_ms() * 1e6);
|
||||
unsigned cursor = 0;
|
||||
|
||||
fillBitmapTo(bits, cursor, trackdata.post_index_gap_us() / clockRateUs, { true, false });
|
||||
fillBitmapTo(bits,
|
||||
cursor,
|
||||
trackdata.post_index_gap_us() * 1e3 / clockPeriod,
|
||||
{true, false});
|
||||
lastBit = false;
|
||||
|
||||
for (const auto& sector : sectors)
|
||||
write_sector(bits, cursor, trackdata, *sector);
|
||||
|
||||
if (cursor >= bits.size())
|
||||
Error() << fmt::format("track data overrun by {} bits", cursor - bits.size());
|
||||
fillBitmapTo(bits, cursor, bits.size(), { true, false });
|
||||
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);
|
||||
fluxmap->appendBits(bits, clockPeriod);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const Victor9kEncoderProto& _config;
|
||||
const Victor9kEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createVictor9kEncoder(const EncoderProto& config)
|
||||
std::unique_ptr<AbstractEncoder> createVictor9kEncoder(
|
||||
const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new Victor9kEncoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new Victor9kEncoder(config));
|
||||
}
|
||||
|
||||
// vim: sw=4 ts=4 et
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,13 @@ class DecoderProto;
|
|||
|
||||
/* ... 1101 0101 0111
|
||||
* ^^ ^^^^ ^^^^ ten bit IO byte */
|
||||
#define VICTOR9K_SECTOR_RECORD 0xfffffd57
|
||||
#define VICTOR9K_SECTOR_RECORD 0xfffffd57
|
||||
#define VICTOR9K_HEADER_ID 0x7
|
||||
|
||||
/* ... 1101 0100 1001
|
||||
* ^^ ^^^^ ^^^^ ten bit IO byte */
|
||||
#define VICTOR9K_DATA_RECORD 0xfffffd49
|
||||
#define VICTOR9K_DATA_ID 0x8
|
||||
|
||||
#define VICTOR9K_SECTOR_LENGTH 512
|
||||
|
||||
|
|
|
|||
|
|
@ -5,28 +5,43 @@ import "lib/common.proto";
|
|||
message Victor9kDecoderProto {}
|
||||
|
||||
// NEXT: 12
|
||||
message Victor9kEncoderProto {
|
||||
message TrackdataProto {
|
||||
message SectorRangeProto {
|
||||
optional int32 start_sector = 1 [(help) = "first sector ID on track"];
|
||||
optional int32 sector_count = 2 [(help) = "number of sectors on track"];
|
||||
}
|
||||
message Victor9kEncoderProto
|
||||
{
|
||||
message TrackdataProto
|
||||
{
|
||||
message SectorRangeProto
|
||||
{
|
||||
optional int32 start_sector = 1
|
||||
[ (help) = "first sector ID on track" ];
|
||||
optional int32 sector_count = 2
|
||||
[ (help) = "number of sectors on track" ];
|
||||
}
|
||||
|
||||
optional int32 min_cylinder = 1 [(help) = "minimum cylinder this format applies to"];
|
||||
optional int32 max_cylinder = 2 [(help) = "maximum cylinder this format applies to"];
|
||||
optional int32 head = 3 [(help) = "which head this format applies to"];
|
||||
optional int32 min_track = 1
|
||||
[ (help) = "minimum track this format applies to" ];
|
||||
optional int32 max_track = 2
|
||||
[ (help) = "maximum track this format applies to" ];
|
||||
optional int32 head = 3
|
||||
[ (help) = "which head this format applies to" ];
|
||||
|
||||
optional double original_period_ms = 4 [(help) = "original rotational period of this cylinder"];
|
||||
optional double original_data_rate_khz = 5 [(help) = "original data rate of this cylinder"];
|
||||
optional double post_index_gap_us = 6 [(help) = "size of post-index gap"];
|
||||
optional int32 pre_header_sync_bits = 10 [(help) = "number of sync bits before the sector header"];
|
||||
optional int32 pre_data_sync_bits = 8 [(help) = "number of sync bits before the sector data"];
|
||||
optional int32 post_data_gap_bits = 9 [(help) = "size of gap between data and the next header"];
|
||||
optional int32 post_header_gap_bits = 11 [(help) = "size of gap between header and the data"];
|
||||
optional double rotational_period_ms = 4
|
||||
[ (help) = "original rotational period of this track" ];
|
||||
optional double clock_period_us = 5
|
||||
[ (help) = "original data rate of this track" ];
|
||||
optional double post_index_gap_us = 6
|
||||
[ (help) = "size of post-index gap" ];
|
||||
optional int32 pre_header_sync_bits = 10
|
||||
[ (help) = "number of sync bits before the sector header" ];
|
||||
optional int32 pre_data_sync_bits = 8
|
||||
[ (help) = "number of sync bits before the sector data" ];
|
||||
optional int32 post_data_gap_bits = 9
|
||||
[ (help) = "size of gap between data and the next header" ];
|
||||
optional int32 post_header_gap_bits = 11
|
||||
[ (help) = "size of gap between header and the data" ];
|
||||
|
||||
optional SectorRangeProto sector_range = 7 [(help) = "write these sectors on each track"];
|
||||
}
|
||||
optional SectorRangeProto sector_range = 7
|
||||
[ (help) = "write these sectors on each track" ];
|
||||
}
|
||||
|
||||
repeated TrackdataProto trackdata = 1;
|
||||
repeated TrackdataProto trackdata = 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,17 +20,13 @@ public:
|
|||
AbstractDecoder(config)
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
const FluxMatcher* matcher = nullptr;
|
||||
_fmr->seekToIndexMark();
|
||||
_sector->clock = _fmr->seekToPattern(SECTOR_START_PATTERN, matcher);
|
||||
if (matcher == &SECTOR_START_PATTERN)
|
||||
return SECTOR_RECORD;
|
||||
return UNKNOWN_RECORD;
|
||||
seekToIndexMark();
|
||||
return seekToPattern(SECTOR_START_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
readRawBits(14);
|
||||
|
||||
|
|
|
|||
5
dep/libusbp/.dir-locals.el
Normal file
5
dep/libusbp/.dir-locals.el
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
(
|
||||
(c-default-style . "BSD")
|
||||
(c-mode . ((c-basic-offset . 4) (tab-width . 4) (indent-tabs-mode . nil)))
|
||||
(nil . ((fill-column . 80)))
|
||||
)
|
||||
130
dep/libusbp/CMakeLists.txt
Normal file
130
dep/libusbp/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
cmake_minimum_required (VERSION 2.8.11)
|
||||
|
||||
# Fix behavior of CMAKE_CXX_STANDARD when targeting macOS.
|
||||
if (POLICY CMP0025)
|
||||
cmake_policy(SET CMP0025 NEW)
|
||||
endif ()
|
||||
|
||||
# Fix a warning on macOS.
|
||||
if (POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif ()
|
||||
|
||||
# Don't use -rdynamic since it breaks causes musl static linking.
|
||||
if (POLICY CMP0065)
|
||||
cmake_policy(SET CMP0065 NEW)
|
||||
endif ()
|
||||
|
||||
project (libusbp)
|
||||
|
||||
set (LIBUSBP_VERSION_MAJOR 1)
|
||||
set (LIBUSBP_VERSION_MINOR 2)
|
||||
set (LIBUSBP_VERSION_PATCH 0)
|
||||
|
||||
# Make 'Release' be the default build type, since the debug builds
|
||||
# include exported symbols that might cause name conflicts.
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set (CMAKE_BUILD_TYPE "Release" CACHE STRING
|
||||
"Options are Debug Release RelWithDebInfo MinSizeRel" FORCE)
|
||||
endif ()
|
||||
|
||||
option (BUILD_SHARED_LIBS "Build as shared library" TRUE)
|
||||
if (NOT BUILD_SHARED_LIBS)
|
||||
add_definitions (-DLIBUSBP_STATIC)
|
||||
set (PC_MORE_CFLAGS "-DLIBUSBP_STATIC")
|
||||
endif ()
|
||||
|
||||
set(ENABLE_EXAMPLES FALSE CACHE BOOL
|
||||
"True if you want to build the examples.")
|
||||
|
||||
set(ENABLE_TESTS FALSE CACHE BOOL
|
||||
"True if you want to build the tests.")
|
||||
|
||||
set(LIBUSBP_LOG FALSE CACHE BOOL
|
||||
"Output log messages to stderr for debugging.")
|
||||
|
||||
set(VBOX_LINUX_ON_WINDOWS FALSE CACHE BOOL
|
||||
"Skip tests known to cause problems on a Linux VirtualBox guest on Windows.")
|
||||
|
||||
set(ENABLE_GCOV FALSE CACHE BOOL
|
||||
"Compile with special options needed for gcov.")
|
||||
|
||||
# Our C code uses features from the C99 standard.
|
||||
macro(use_c99)
|
||||
if (CMAKE_VERSION VERSION_LESS "3.1")
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
||||
set (CMAKE_C_FLAGS "--std=gnu99 ${CMAKE_C_FLAGS}")
|
||||
endif ()
|
||||
else ()
|
||||
set (CMAKE_C_STANDARD 99)
|
||||
endif ()
|
||||
endmacro(use_c99)
|
||||
|
||||
# Our C++ code uses features from the C++11 standard.
|
||||
macro(use_cxx11)
|
||||
if (CMAKE_VERSION VERSION_LESS "3.1")
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
||||
# Use --std=gnu++0x instead of --std=gnu++11 in order to support GCC 4.6.
|
||||
set (CMAKE_CXX_FLAGS "--std=gnu++0x ${CMAKE_C_FLAGS}")
|
||||
endif ()
|
||||
else ()
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
endif ()
|
||||
endmacro(use_cxx11)
|
||||
|
||||
set (LIBUSBP_VERSION ${LIBUSBP_VERSION_MAJOR}.${LIBUSBP_VERSION_MINOR}.${LIBUSBP_VERSION_PATCH})
|
||||
|
||||
if (CMAKE_VERSION VERSION_GREATER "2.8.10")
|
||||
string(TIMESTAMP YEAR "%Y")
|
||||
endif ()
|
||||
|
||||
find_package(PkgConfig)
|
||||
|
||||
# Put libraries and executables in the top level of the build directory
|
||||
# so that the executables can find the libraries and it is easy to run
|
||||
# everything.
|
||||
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
# Warn about everything.
|
||||
set (CMAKE_C_FLAGS "-Wall -Wextra -pedantic ${CMAKE_C_FLAGS}")
|
||||
set (CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic ${CMAKE_CXX_FLAGS}")
|
||||
|
||||
if (ENABLE_GCOV)
|
||||
set (CMAKE_C_FLAGS "-fprofile-arcs -ftest-coverage ${CMAKE_C_FLAGS}")
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
|
||||
# Enable correct behavior for the return value of vsnprintf.
|
||||
add_definitions (-D__USE_MINGW_ANSI_STDIO=1)
|
||||
|
||||
# Enable functions only available in Windows Vista and later,
|
||||
# such as StringCompareEx.
|
||||
add_definitions (-D_WIN32_WINNT=0x0600 -DNTDDI_VERSION=0x06000000)
|
||||
|
||||
endif ()
|
||||
|
||||
# Detect Linux.
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set (LINUX 1)
|
||||
endif ()
|
||||
|
||||
# Install the header files into include/
|
||||
install(FILES include/libusbp.h include/libusbp.hpp
|
||||
DESTINATION "include/libusbp-${LIBUSBP_VERSION_MAJOR}")
|
||||
|
||||
add_subdirectory (src)
|
||||
|
||||
if (ENABLE_TESTS)
|
||||
add_subdirectory (test)
|
||||
add_subdirectory (manual_tests)
|
||||
endif ()
|
||||
|
||||
if (ENABLE_EXAMPLES)
|
||||
add_subdirectory (examples)
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
add_subdirectory (install_helper)
|
||||
endif ()
|
||||
74
dep/libusbp/CONTRIBUTING.md
Normal file
74
dep/libusbp/CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Contributing to libusbp
|
||||
|
||||
## Development Tools
|
||||
|
||||
The following tools are used to help build, test, debug, and document this library:
|
||||
|
||||
- USB Test Device A: This is a custom USB device that is used for testing this library. You must have a device like this plugged in if you want to run the full test suite. The `test/firmware/wixel` folder contains firmware that can turn a [Pololu Wixel](https://www.pololu.com/product/1337) into a USB Test Device A, and it should be possible to implement it on other USB-capable boards as well.
|
||||
- [cmake](http://www.cmake.org)
|
||||
- [catch](https://github.com/philsquared/Catch)
|
||||
- [Doxygen](http://www.stack.nl/~dimitri/doxygen/)
|
||||
- Development environments:
|
||||
- Windows: [MSYS2](http://msys2.github.io/)
|
||||
- macOS: [Homebrew](http://brew.sh/)
|
||||
- Memory leak checkers:
|
||||
- Windows: Install Dr. Memory and run `drmemory -leaks_only ./run_test.exe`
|
||||
- Linux: Install Valgrind and run `valgrind ./run_test`
|
||||
- macOS: Run `MallocStackLogging=1 ./run_test -p`, wait for the tests to finish, press Ctrl+Z, run `leaks run_test`, then finally run `fg` to go back to the stopped test process and end it.
|
||||
|
||||
## Underlying API documentation
|
||||
|
||||
This section attempts to organize the documentation and information about the underlying APIs used by libusbp, which should be useful to anyone reviewing or editing the code.
|
||||
|
||||
### Windows APIs
|
||||
|
||||
- [WinUSB](https://msdn.microsoft.com/en-us/library/windows/hardware/ff540196)
|
||||
- [SetupAPI](https://msdn.microsoft.com/en-us/library/cc185682)
|
||||
- [PnP Configuration Manager Reference](https://msdn.microsoft.com/en-us/library/windows/hardware/ff549717)
|
||||
- [Standard USB Identifiers](https://msdn.microsoft.com/en-us/library/windows/hardware/ff553356)
|
||||
- [What characters or bytes are valid in a USB serial number?](https://msdn.microsoft.com/en-us/library/windows/hardware/dn423379#usbsn)
|
||||
- [INFO: Windows Rundll and Rundll32 Interface](https://support.microsoft.com/en-us/kb/164787)
|
||||
- [MSI Custom Action Type 17](https://msdn.microsoft.com/en-us/library/aa368076?f=255&MSPPError=-2147217396)
|
||||
|
||||
### Linux APIs
|
||||
|
||||
- udev
|
||||
- [libudev Reference Manual](https://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/ch01.html)
|
||||
- [Kernel documentation of sysfs-bus-usb](https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-bus-usb)
|
||||
- [sysfs.c](https://github.com/torvalds/linux/blob/master/drivers/usb/core/sysfs.c)
|
||||
- [libudev and Sysfs Tutorial](http://www.signal11.us/oss/udev/)
|
||||
- USB device node
|
||||
- [USB documentation folder in the kernel tree](https://github.com/torvalds/linux/tree/master/Documentation/usb)
|
||||
- [USB error code documentation](https://github.com/torvalds/linux/blob/master/Documentation/usb/error-codes.txt)
|
||||
- [usbdevice_fs.h](https://github.com/torvalds/linux/blob/master/include/uapi/linux/usbdevice_fs.h)
|
||||
- [devio.c](https://github.com/torvalds/linux/blob/master/drivers/usb/core/devio.c)
|
||||
- [Linux USB Drivers presentation](http://free-electrons.com/doc/linux-usb.pdf)
|
||||
- [USB Drivers chapter from a book](http://lwn.net/images/pdf/LDD3/ch13.pdf)
|
||||
- [linux_usbfs.c from libusb](https://github.com/libusb/libusb/blob/master/libusb/os/linux_usbfs.c)
|
||||
- Error numbers
|
||||
- [errno.h](https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno.h)
|
||||
- [errno-base.h](https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno-base.h)
|
||||
|
||||
### macOS APIs
|
||||
|
||||
- [I/O Kit Framework Reference](https://developer.apple.com/library/mac/documentation/Darwin/Reference/IOKit/index.html#//apple_ref/doc/uid/TP30000815)
|
||||
- [IOKitLib.h Reference](https://developer.apple.com/library/mac/documentation/IOKit/Reference/IOKitLib_header_reference/)
|
||||
- [Accessing Hardware From Applications](https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/AccessingHardware)
|
||||
- [USB Device Interface Guide](https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/USBBook/)
|
||||
- [IOUSBInterfaceClass.cpp](https://github.com/opensource-apple/IOUSBFamily/blob/master/IOUSBLib/Classes/IOUSBInterfaceClass.cpp)
|
||||
- [IOUSBInterfaceUserClient.cpp](https://github.com/opensource-apple/IOUSBFamily/blob/master/IOUSBUserClient/Classes/IOUSBInterfaceUserClient.cpp)
|
||||
- [darwin_usb.c from libusb](https://github.com/libusb/libusb/blob/master/libusb/os/darwin_usb.c)
|
||||
- [Mach messaging interface (mach ports)](http://www.gnu.org/software/hurd/gnumach-doc/Messaging-Interface.html)
|
||||
|
||||
## Future development
|
||||
|
||||
Here are some things we might want to work on in future versions of the library:
|
||||
|
||||
- Serial port support. (Even just listing the serial ports of a USB device would be useful.)
|
||||
- Human interface device (HID) support.
|
||||
- Hotplug support: detect when new devices are added and detect when a specific device is removed.
|
||||
- Stronger guarantees about thread safety. (This might not require code changes.)
|
||||
- More options for asynchronous transfers.
|
||||
- Perhaps use asynchronous operations to implement the synchronous ones, like libusb does. This would fix some quirks on various platforms: it would allow us to have timeouts for interrupt endpoints on Mac OS X and it would probably allow long synchronous operations on Linux to be interrupted with Ctrl+C. The main drawback is that it would add complexity.
|
||||
- Sending data on OUT pipes.
|
||||
- A device library template to help people who want to make a cross-platform C or C++ library for a USB device based on libusbp.
|
||||
26
dep/libusbp/Doxyfile
Normal file
26
dep/libusbp/Doxyfile
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Doxygen configuration file for generating documentation.
|
||||
PROJECT_NAME = "libusbp"
|
||||
OUTPUT_DIRECTORY = docs
|
||||
INLINE_INHERITED_MEMB = YES
|
||||
INPUT = README.md PLATFORM_NOTES.md CONTRIBUTING.md THREADS.md include
|
||||
USE_MDFILE_AS_MAINPAGE = README.md
|
||||
RECURSIVE = YES
|
||||
SOURCE_BROWSER = YES
|
||||
USE_MATHJAX = YES
|
||||
GENERATE_LATEX = NO
|
||||
# TYPEDEF_HIDES_STRUCT = YES
|
||||
|
||||
MACRO_EXPANSION = YES
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
PREDEFINED = \
|
||||
LIBUSBP_API= \
|
||||
LIBUSBP_WARN_UNUSED= \
|
||||
_WIN32=1 \
|
||||
__linux__=1 \
|
||||
__APPLE__=1
|
||||
|
||||
EXCLUDE_SYMBOLS = \
|
||||
LIBUSBP_API \
|
||||
LIBUSBP_DLL_EXPORT \
|
||||
LIBUSBP_DLL_IMPORT \
|
||||
LIBUSBP_WARN_UNUSED
|
||||
25
dep/libusbp/LICENSE.txt
Normal file
25
dep/libusbp/LICENSE.txt
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2015-2017 Pololu Corporation. For more information, see
|
||||
|
||||
http://www.pololu.com/
|
||||
http://forum.pololu.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
36
dep/libusbp/PLATFORM_NOTES.md
Normal file
36
dep/libusbp/PLATFORM_NOTES.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Platform notes
|
||||
|
||||
This page documents some notable differences between the platforms that libusbp supports that can affect the library's behavior.
|
||||
|
||||
## Permissions
|
||||
|
||||
On Linux, a udev rule is typically needed in order to grant permissions for non-root users to directly access USB devices. The simplest way to do this would be to add a file named `usb.rules` in the directory `/etc/udev/rules.d` with the following contents, which grants all users permission to access all USB devices:
|
||||
|
||||
SUBSYSTEM=="usb", MODE="0666"
|
||||
|
||||
|
||||
## Multiple handles to the same generic interface
|
||||
|
||||
On Linux, you can have multiple simultaneous handles open to the same generic interface of a device. On Windows and macOS, this is not possible, and you will get an error with the code `LIBUSBP_ERROR_ACCESS_DENIED` when trying to open the second handle with `libusbp_generic_handle_open`.
|
||||
|
||||
|
||||
## Timeouts for interrupt IN endpoints
|
||||
|
||||
On macOS, you cannot specify a timeout for an interrupt IN endpoint. Doing so will result in the following error when you try to read from the endpoint:
|
||||
|
||||
Failed to read from pipe. (iokit/common) invalid argument. Error code 0xe00002c2.
|
||||
|
||||
|
||||
## Serial numbers
|
||||
|
||||
On Windows, calling `libusbp_device_get_serial_number` on a device that does not actually have a serial number will retrieve some other type of identifier that contains andpersands (&) and numbers. On other platforms, an error will be returned with the code `LIBUSBP_ERROR_NO_SERIAL_NUMBER`.
|
||||
|
||||
|
||||
## Synchronous operations and Ctrl+C
|
||||
|
||||
On Linux, a synchronous (blocking) USB transfer cannot be interrupted by pressing Ctrl+C. Other signals will probably not work either.
|
||||
|
||||
|
||||
## Interface-specific control transfers
|
||||
|
||||
Performing control transfers that are directed to a specific interface might not work correctly on all systems, especially if the device is a composite device and the interface you are connected to is not the one addressed in your control transfer. For example, see [this message](https://sourceforge.net/p/libusb/mailman/message/34414447/) from the libusb mailing list.
|
||||
186
dep/libusbp/README.md
Normal file
186
dep/libusbp/README.md
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
# libusbp: Pololu USB Library
|
||||
|
||||
Version: 1.2.0<br/>
|
||||
Release date: 2020-11-16<br/>
|
||||
[www.pololu.com](https://www.pololu.com/)
|
||||
|
||||
The **Pololu USB Library** (also known as **libusbp**) is a cross-platform C library for accessing USB devices.
|
||||
|
||||
## Features
|
||||
|
||||
- Can retrieve the vendor ID, product ID, revision, and serial number for each connected USB device.
|
||||
- Can perform I/O on generic (vendor-defined) USB interfaces:
|
||||
- Synchronous control transfers.
|
||||
- Synchronous and asynchronous bulk/interrupt transfers on IN endpoints.
|
||||
- Synchronous bulk/interrupt transfers on OUT endpoints.
|
||||
- Can retrieve the names of virtual serial ports provided by a specified USB device (e.g. "COM5").
|
||||
- Provides detailed error information to the caller.
|
||||
- Each error includes one or more English sentences describing the error, including error codes from underlying APIs.
|
||||
- Some errors have libusbp-defined error codes that can be used to programmatically decide how to handle the error.
|
||||
- Provides an object-oriented C++ wrapper (using features of C++11).
|
||||
- Provides access to underlying identifiers, handles, and file descriptors.
|
||||
|
||||
|
||||
## Supported platforms
|
||||
|
||||
The library runs on:
|
||||
|
||||
* Microsoft Windows (Windows Vista and later)
|
||||
* Linux
|
||||
* macOS (10.11 and later)
|
||||
|
||||
|
||||
## Supported USB devices and interfaces
|
||||
|
||||
The library only supports certain types of USB devices.
|
||||
|
||||
On Windows, any generic interface that you want to access with this library must use WinUSB as its driver, so you will need to provide a driver package or use some other mechanism to make sure WinUSB is installed. The generic interface must have a registry key named "DeviceInterfaceGUIDs" which is a REG_MULTI_SZ, and the first string in the key must be a valid GUID. The "DeviceInterfaceGUID" key is not supported.
|
||||
|
||||
Both composite and non-composite devices are supported.
|
||||
|
||||
There is no support for switching device configurations or switching between alternate settings of an interface.
|
||||
|
||||
There is no support for accessing a generic interface that is included in an interface association and is not the first interface in the association.
|
||||
|
||||
|
||||
## Platform differences
|
||||
|
||||
This library is a relatively simple wrapper around low-level USB APIs provided by each supported platform. Because these APIs have different capabilities and behavior, you should not assume your program will work on a platform on which it has not been tested. Some notable differences are documented in `PLATFORM_NOTES.md`.
|
||||
|
||||
|
||||
## Comparison to libusb
|
||||
|
||||
This library has a lot in common with [libusb 1.0](http://libusb.info/), which has been around for longer and has many more features. However, libusbp does have some useful features that libusb lacks, such as listing the serial number of a USB device without performing I/O or getting information about a USB device's virtual serial ports.
|
||||
|
||||
|
||||
## Building from source on Windows with MSYS2
|
||||
|
||||
The recommended way to build this library on Windows is to use [MSYS2](http://msys2.github.io/).
|
||||
|
||||
After installing MSYS2, launch it by selecting "MSYS2 MinGW 32-bit" or "MSYS2 MinGW 64-bit" from the Start Menu. Then run this command to install the required packages:
|
||||
|
||||
pacman -S $MINGW_PACKAGE_PREFIX-{toolchain,cmake}
|
||||
|
||||
If pacman prompts you to enter a selection of packages to install, just press enter to install all of the packages.
|
||||
|
||||
Download the source code of this library and navigate to the top-level directory using `cd`. Then run these commands to build the library and install it:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
MSYS2_ARG_CONV_EXCL=- cmake .. -G"MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX
|
||||
make install DESTDIR=/
|
||||
|
||||
We currently do not provide any build files for Visual Studio. You can use CMake to generate Visual Studio build files, and the library and its examples will probably compile, but we have not tested the resulting library.
|
||||
|
||||
|
||||
## Building from source on Linux
|
||||
|
||||
First, you will need to make sure that you have a suitable compiler installed, such as gcc. You can run `gcc -v` in a shell to make sure it is available.
|
||||
|
||||
You will also need to install CMake and libudev. On Ubuntu, Raspbian, and other Debian-based distributions, the command to do this is:
|
||||
|
||||
sudo apt-get install cmake libudev-dev
|
||||
|
||||
On Arch Linux, libudev should already be installed, and you can install CMake by running:
|
||||
|
||||
sudo pacman -S cmake
|
||||
|
||||
Download the source code of this library and navigate to the top-level directory using `cd`. Then run these commands to build the library and install it:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
sudo make install
|
||||
|
||||
|
||||
## Building from source on macOS
|
||||
|
||||
First, install [Homebrew](http://brew.sh/), a package manager for macOS. Then use Homebrew to install CMake by running the following command in a Terminal:
|
||||
|
||||
brew install cmake
|
||||
|
||||
Download the source code of this library and navigate to the top-level directory using `cd`. Then run these commands in a Terminal to build the library and install it:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
sudo make install
|
||||
|
||||
|
||||
## Incorporating libusbp into a C/C++ project
|
||||
|
||||
The first step to incorporating libusbp into another project is to add the libusbp header folder to your project's include search path.
|
||||
The header folder is the folder that contains `libusbp.h` and `libusbp.hpp`, and it will typically be named `libusbp-1`.
|
||||
On systems with `pkg-config`, assuming libusbp has been installed properly, you can run the following command to get the include directory:
|
||||
|
||||
pkg-config --cflags libusbp-1
|
||||
|
||||
The output of that command is formatted so that it can be directly added to the command-line arguments for most compilers.
|
||||
|
||||
Next, you should include the appropriate libusbp header in your source code by adding an include directive at the top of one of your source files.
|
||||
To use the C API from a C or C++ project, you should write:
|
||||
|
||||
#include <libusbp.h>
|
||||
|
||||
To use the C++ API from a C++ project, you should write:
|
||||
|
||||
#include <libusbp.hpp>
|
||||
|
||||
After making the changes above, you should be able compile your project successfully.
|
||||
If the compiler says that the libusbp header file cannot be found, make sure you have specified the include path correctly as described above.
|
||||
|
||||
If you add a call to any of the libusbp functions and rebuild, you will probably get an undefined reference error from the linker. To fix this, you need to add libusbp's linker settings to your project. To get the linker settings, run:
|
||||
|
||||
pkg-config --libs libusbp-1
|
||||
|
||||
If you are using GCC and a shell that supports Bash-like syntax, here is an example command that compiles a single-file C program using the correct compiler and linker settings:
|
||||
|
||||
gcc program.c `pkg-config --cflags --libs libusbp-1`
|
||||
|
||||
Here is an equivalent command for C++. Note that we use the `--std=gnu++11` option because the libusbp C++ API requires features from C++11:
|
||||
|
||||
g++ --std=gnu++11 program.cpp `pkg-config --cflags --libs libusbp-1`
|
||||
|
||||
The order of the arguments above matters: the user program must come before libusbp because it relies on symbols that are defined by libusbp.
|
||||
|
||||
The `examples` folder of this repository contains some example code that uses libusbp. These examples can serve as a starting point for your own project.
|
||||
|
||||
|
||||
## Versioning
|
||||
|
||||
The version numbers used by this library follow the rules of [Semantic Versioning 2.0.0](http://semver.org/).
|
||||
|
||||
A backwards-incompatible version of this library might be released in the future.
|
||||
If that happens, the new version will have a different major version number: its version number will be 2.0.0 and it will be known as libusbp-2 to `pkg-config`.
|
||||
This library was designed to support having different major versions installed side-by-side on the same computer, so you could have both libusbp-1 and libusbp-2 installed at the same time.
|
||||
However, you would not be able to use both versions from a single program.
|
||||
|
||||
If you write software that depends on libusbp, we recommend specifying which major version of libusbp your software uses in both the documentation of your software and in the scripts used to build it. Scripts or instructions for downloading the source code of libusbp should use a branch name to ensure that they downlod the latest version of the code for a given major version number. For example, the branch of this repository named "v1-latest" will always point to the latest release of libusbp-1.
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
For detailed documentation of this library, see the header files `libusb.h` and `libusbp.hpp` in the `include` directory, and see the `.md` files in this directory. You can also use [Doxygen](http://doxygen.org/) to generate documentation from those files.
|
||||
|
||||
|
||||
## Version history
|
||||
|
||||
* 1.2.0 (2020-11-16):
|
||||
* Linux: Made the library work with devices attached to the cp210x driver.
|
||||
* Windows: Made the library work with devices that have lowercase letters in their hardware IDs.
|
||||
* 1.1.0 (2018-11-23):
|
||||
* Added `libusbp_write_pipe`.
|
||||
* 1.0.4 (2017-08-29):
|
||||
* Fixed a compilation error for macOS.
|
||||
* Added the `lsport` example for listing multiple USB serial ports.
|
||||
* 1.0.3 (2017-08-29):
|
||||
* Compiler flags from libudev.pc are now used.
|
||||
* For static builds, libusbp-1.pc now requires libudev.pc instead of copying its flags.
|
||||
* Always build the install helper as a shared library (DLL).
|
||||
* 1.0.2 (2017-06-29):
|
||||
* Fixed some issues with using libusbp as a static library.
|
||||
* Added the `drop_in` example, which shows how to use libusbp as a three-file library.
|
||||
* 1.0.1 (2016-03-20): Fixed the `libusbp_broadcast_setting_change*` functions (a bonus feature for the Windows version).
|
||||
* 1.0.0 (2016-03-02): Original release.
|
||||
18
dep/libusbp/THREADS.md
Normal file
18
dep/libusbp/THREADS.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Thread safety
|
||||
|
||||
If you are using multiple threads in your application, it is important to make sure that your calls to libusbp will be thread safe. This page covers everything you need to know about thread safety and libusbp.
|
||||
|
||||
We will only discuss the C API functions defined in libusbp.h. The C++ API defined in libusbp.hpp is just a simple wrapper around the C API and does not introduce or solve any thread safety issues.
|
||||
|
||||
This library does not create threads, use mutable global variables, use volatile variables, use mutexes, use memory barriers, or use reference counting.
|
||||
|
||||
On this page, two function calls are said to *conflict* with each other if there is no guarantee that executing the function calls concurrently on different threads will work as expected. To characterize the thread-safety of libusbp, we will specify which pairs of function calls conflict with each other. A function call consists of the name of a library function being called along with the values of its arguments.
|
||||
|
||||
Two function calls will not conflict if the memory areas pointed to by their arguments have no overlap. This is because the library does not use any mutable global variables. To determine whether the memory areas overlap, you will need to know which library objects hold pointers to other library objects. The rules for that are defined in the "Pointer rules" section below.
|
||||
|
||||
If there is an overlap in the memory areas pointed to by the arguments of two functions calls, the overlap will not cause a conflict as long as all of the parameters that are responsible for the overlap are marked with the `const` qualifier in the header. We use `const` as an indicator that the function will not modify the memory pointed to by that argument, and it will not call any API functions that might change the state of the underlying handles held by the object.
|
||||
|
||||
## Pointer rules
|
||||
|
||||
* Each ::libusbp_async_in_pipe object may hold a pointer to the ::libusbp_generic_handle that it was created from. Similarly, the ::libusbp_generic_handle may hold pointers to its ::libusbp_async_in_pipe objects.
|
||||
* All other objects contain no pointers to each other.
|
||||
2
dep/libusbp/UPSTREAM.md
Normal file
2
dep/libusbp/UPSTREAM.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
This was taken from https://github.com/pololu/libusbp on 2021-12-11.
|
||||
|
||||
4
dep/libusbp/examples/CMakeLists.txt
Normal file
4
dep/libusbp/examples/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
add_subdirectory(async_in)
|
||||
add_subdirectory(lsport)
|
||||
add_subdirectory(lsusb)
|
||||
add_subdirectory(port_name)
|
||||
9
dep/libusbp/examples/async_in/CMakeLists.txt
Normal file
9
dep/libusbp/examples/async_in/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use_cxx11()
|
||||
|
||||
add_executable(async_in async_in.cpp)
|
||||
|
||||
include_directories (
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
|
||||
target_link_libraries(async_in usbp)
|
||||
91
dep/libusbp/examples/async_in/async_in.cpp
Normal file
91
dep/libusbp/examples/async_in/async_in.cpp
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/* This example shows how to read a constant stream of data from an IN endpoint.
|
||||
* By queueing up a large number of asynchronous transfers, we ensure that the
|
||||
* USB host controller is busy, and that it will retrieve data from the endpoint
|
||||
* as fast as possible for an indefinite period of time.
|
||||
*
|
||||
* This example is designed to connect to Test Device A and read ADC data from
|
||||
* endpoint 0x82. */
|
||||
|
||||
#include <libusbp.hpp>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#ifdef _MSC_VER
|
||||
#define usleep(x) Sleep(((x) + 999) / 1000)
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
const uint16_t vendor_id = 0x1FFB;
|
||||
const uint16_t product_id = 0xDA01;
|
||||
const uint8_t interface_number = 0;
|
||||
const bool composite = true;
|
||||
const uint8_t endpoint_address = 0x82;
|
||||
const size_t transfer_size = 5;
|
||||
const size_t transfer_count = 250;
|
||||
|
||||
// Prints the data in the given buffer to the standard output in HEX.
|
||||
void print_data(uint8_t * buffer, size_t size)
|
||||
{
|
||||
for (uint8_t i = 0; i < size; i++)
|
||||
{
|
||||
printf("%02x", buffer[i]);
|
||||
if (i < size - 1) { putchar(' '); }
|
||||
}
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
int main_with_exceptions()
|
||||
{
|
||||
libusbp::device device = libusbp::find_device_with_vid_pid(vendor_id, product_id);
|
||||
if (!device)
|
||||
{
|
||||
std::cerr << "Device not found." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
libusbp::generic_interface gi(device, interface_number, composite);
|
||||
libusbp::generic_handle handle(gi);
|
||||
libusbp::async_in_pipe pipe = handle.open_async_in_pipe(endpoint_address);
|
||||
pipe.allocate_transfers(transfer_count, transfer_size);
|
||||
|
||||
pipe.start_endless_transfers();
|
||||
|
||||
while(true)
|
||||
{
|
||||
uint8_t buffer[transfer_size];
|
||||
size_t transferred;
|
||||
libusbp::error transfer_error;
|
||||
while(pipe.handle_finished_transfer(buffer, &transferred, &transfer_error))
|
||||
{
|
||||
if (transfer_error) { throw transfer_error; }
|
||||
print_data(buffer, transferred);
|
||||
}
|
||||
|
||||
pipe.handle_events();
|
||||
usleep(500);
|
||||
}
|
||||
|
||||
// Note that closing an async_in_pipe cleanly without causing memory leaks
|
||||
// can be difficult. For more information, see the documentation of
|
||||
// libusbp_async_in_pipe_close in libusbp.h. In this example, we don't
|
||||
// worry about it because we are only ever creating one pipe, so the amount
|
||||
// of the memory leak is bounded.
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
// Suppress unused parameter warnings.
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
try
|
||||
{
|
||||
return main_with_exceptions();
|
||||
}
|
||||
catch(const std::exception & error)
|
||||
{
|
||||
std::cerr << "Error: " << error.what() << std::endl;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
2
dep/libusbp/examples/drop_in/.gitignore
vendored
Normal file
2
dep/libusbp/examples/drop_in/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/libusbp*
|
||||
/lsusb*
|
||||
38
dep/libusbp/examples/drop_in/build.sh
Normal file
38
dep/libusbp/examples/drop_in/build.sh
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# This script converts libusbp into a format that can easily be dropped into
|
||||
# other projects: one source file, one C header file, and one C++ header file.
|
||||
#
|
||||
# This is handy for people who want to use libusbp in their own C/C++ software
|
||||
# projects but don't want to deal with the issues that come with having one more
|
||||
# dependency.
|
||||
#
|
||||
# It also compiles the lsusb example program using the drop-in library.
|
||||
|
||||
set -eou pipefail
|
||||
|
||||
cd `dirname $0`
|
||||
SRC=../../src
|
||||
|
||||
{
|
||||
echo "#if 0"
|
||||
cat $SRC/../LICENSE.txt
|
||||
echo "#endif"
|
||||
cat $SRC/libusbp_internal.h
|
||||
cat $SRC/*.c
|
||||
echo "#ifdef _WIN32"
|
||||
cat $SRC/windows/*.c
|
||||
echo "#endif"
|
||||
echo "#ifdef __linux__"
|
||||
cat $SRC/linux/*.c
|
||||
echo "#endif"
|
||||
echo "#ifdef __APPLE__"
|
||||
cat $SRC/mac/*.c
|
||||
echo "#endif"
|
||||
} | sed 's/#include <libusbp_internal.h>//' > libusbp.c
|
||||
|
||||
cp ../lsusb/lsusb.cpp $SRC/../include/libusbp.h* .
|
||||
|
||||
# TODO: fix the library so we don't need -fpermissive here
|
||||
|
||||
g++ -std=gnu++11 -Wall -fpermissive -g -O2 -I. \
|
||||
-DLIBUSBP_DROP_IN -DLIBUSBP_STATIC \
|
||||
lsusb.cpp libusbp.c -ludev -o lsusb
|
||||
9
dep/libusbp/examples/lsport/CMakeLists.txt
Normal file
9
dep/libusbp/examples/lsport/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use_cxx11()
|
||||
|
||||
add_executable(lsport lsport.cpp)
|
||||
|
||||
include_directories (
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
|
||||
target_link_libraries(lsport usbp)
|
||||
108
dep/libusbp/examples/lsport/lsport.cpp
Normal file
108
dep/libusbp/examples/lsport/lsport.cpp
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// This example prints the names of all USB serial ports along with information
|
||||
// about the USB devices they belong to.
|
||||
//
|
||||
// For each USB device, it prints the USB vendor ID, product ID, and serial
|
||||
// number on a line. Then, on the following lines, it prints any serial port
|
||||
// names it found, sorted by interface number, ascending.
|
||||
//
|
||||
// Note: This example is slow and ugly because libusbp does not yet have
|
||||
// built-in support for listing serial ports; it only has support for finding
|
||||
// a serial port if you already know what USB device it is connected to and what
|
||||
// interface you expect the port to be on. This might be improved in the future.
|
||||
|
||||
#include <libusbp.hpp>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
std::string serial_number_or_default(const libusbp::device & device,
|
||||
const std::string & def)
|
||||
{
|
||||
try
|
||||
{
|
||||
return device.get_serial_number();
|
||||
}
|
||||
catch (const libusbp::error & error)
|
||||
{
|
||||
if (error.has_code(LIBUSBP_ERROR_NO_SERIAL_NUMBER))
|
||||
{
|
||||
return def;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
bool try_print_port_name(const libusbp::device & device,
|
||||
uint8_t interface_number, bool composite)
|
||||
{
|
||||
std::string port_name;
|
||||
try
|
||||
{
|
||||
libusbp::serial_port port(device, interface_number, composite);
|
||||
port_name = port.get_name();
|
||||
}
|
||||
catch (const libusbp::error & error)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::cout << " " << port_name << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
int main_with_exceptions()
|
||||
{
|
||||
auto devices = libusbp::list_connected_devices();
|
||||
for (const libusbp::device & device : devices)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
// Print the USB device info.
|
||||
uint16_t vendor_id = device.get_vendor_id();
|
||||
uint16_t product_id = device.get_product_id();
|
||||
std::string serial_number = serial_number_or_default(device, "-");
|
||||
std::ios::fmtflags flags(std::cout.flags());
|
||||
std::cout
|
||||
<< std::hex << std::setfill('0') << std::right
|
||||
<< std::setw(4) << vendor_id
|
||||
<< ':'
|
||||
<< std::setw(4) << product_id
|
||||
<< ' '
|
||||
<< std::setfill(' ') << std::left << serial_number
|
||||
<< std::endl;
|
||||
std::cout.flags(flags);
|
||||
|
||||
// First, assume the device is composite and try the first 16 interfaces.
|
||||
// Most devices don't have more interfaces than that. Trying all 255 possible
|
||||
// interfaces slows the program down noticeably. This issue could be fixed if
|
||||
// we added better serial port enumeration support to libusbp.
|
||||
for (uint32_t i = 0; i < 16; i++)
|
||||
{
|
||||
success = try_print_port_name(device, i, true) || success;
|
||||
}
|
||||
|
||||
// Try to find a port assuming the device is non-composite. Only do so if
|
||||
// no ports were found earlier, to help avoid printing the same port twice.
|
||||
if (!success)
|
||||
{
|
||||
try_print_port_name(device, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
// Suppress unused parameter warnings.
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
try
|
||||
{
|
||||
return main_with_exceptions();
|
||||
}
|
||||
catch(const std::exception & error)
|
||||
{
|
||||
std::cerr << "Error: " << error.what() << std::endl;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
9
dep/libusbp/examples/lsusb/CMakeLists.txt
Normal file
9
dep/libusbp/examples/lsusb/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use_cxx11()
|
||||
|
||||
add_executable(lsusb lsusb.cpp)
|
||||
|
||||
include_directories (
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
|
||||
target_link_libraries(lsusb usbp)
|
||||
80
dep/libusbp/examples/lsusb/lsusb.cpp
Normal file
80
dep/libusbp/examples/lsusb/lsusb.cpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/* This example shows how to gather information about the USB devices connected
|
||||
* to the system and print it. */
|
||||
|
||||
#include <libusbp.hpp>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
std::string serial_number_or_default(const libusbp::device & device,
|
||||
const std::string & def)
|
||||
{
|
||||
try
|
||||
{
|
||||
return device.get_serial_number();
|
||||
}
|
||||
catch (const libusbp::error & error)
|
||||
{
|
||||
if (error.has_code(LIBUSBP_ERROR_NO_SERIAL_NUMBER))
|
||||
{
|
||||
return def;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void print_device(libusbp::device & device)
|
||||
{
|
||||
uint16_t vendor_id = device.get_vendor_id();
|
||||
uint16_t product_id = device.get_product_id();
|
||||
uint16_t revision = device.get_revision();
|
||||
std::string serial_number = serial_number_or_default(device, "-");
|
||||
std::string os_id = device.get_os_id();
|
||||
|
||||
// Note: The serial number might have spaces in it, so it should be the last
|
||||
// field to avoid confusing programs that are looking for a field after the
|
||||
// serial number.
|
||||
|
||||
std::ios::fmtflags flags(std::cout.flags());
|
||||
std::cout
|
||||
<< std::hex << std::setfill('0') << std::right
|
||||
<< std::setw(4) << vendor_id
|
||||
<< ':'
|
||||
<< std::setw(4) << product_id
|
||||
<< ' '
|
||||
<< std::setfill(' ') << std::setw(2) << (revision >> 8)
|
||||
<< '.'
|
||||
<< std::setfill('0') << std::setw(2) << (revision & 0xFF)
|
||||
<< ' '
|
||||
<< os_id
|
||||
<< ' '
|
||||
<< std::setfill(' ') << std::left << serial_number
|
||||
<< std::endl;
|
||||
std::cout.flags(flags);
|
||||
}
|
||||
|
||||
int main_with_exceptions()
|
||||
{
|
||||
std::vector<libusbp::device> list = libusbp::list_connected_devices();
|
||||
for (auto it = list.begin(); it != list.end(); ++it)
|
||||
{
|
||||
print_device(*it);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
// Suppress unused parameter warnings.
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
try
|
||||
{
|
||||
return main_with_exceptions();
|
||||
}
|
||||
catch(const std::exception & error)
|
||||
{
|
||||
std::cerr << "Error: " << error.what() << std::endl;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
9
dep/libusbp/examples/port_name/CMakeLists.txt
Normal file
9
dep/libusbp/examples/port_name/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use_cxx11()
|
||||
|
||||
add_executable(port_name port_name.cpp)
|
||||
|
||||
include_directories (
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
|
||||
target_link_libraries(port_name usbp)
|
||||
44
dep/libusbp/examples/port_name/port_name.cpp
Normal file
44
dep/libusbp/examples/port_name/port_name.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// This example shows how to get the name of a USB serial port (e.g. "COM6")
|
||||
// for a single USB device.
|
||||
|
||||
#include <libusbp.hpp>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
const uint16_t vendor_id = 0x1FFB;
|
||||
const uint16_t product_id = 0xDA01;
|
||||
const uint8_t interface_number = 2;
|
||||
const bool composite = true;
|
||||
|
||||
int main_with_exceptions()
|
||||
{
|
||||
libusbp::device device = libusbp::find_device_with_vid_pid(vendor_id, product_id);
|
||||
if (!device)
|
||||
{
|
||||
std::cerr << "Device not found." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
libusbp::serial_port port(device, interface_number, composite);
|
||||
std::string port_name = port.get_name();
|
||||
std::cout << port_name << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
// Suppress unused parameter warnings.
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
try
|
||||
{
|
||||
return main_with_exceptions();
|
||||
}
|
||||
catch(const std::exception & error)
|
||||
{
|
||||
std::cerr << "Error: " << error.what() << std::endl;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
737
dep/libusbp/include/libusbp.h
Normal file
737
dep/libusbp/include/libusbp.h
Normal file
|
|
@ -0,0 +1,737 @@
|
|||
// Copyright (C) Pololu Corporation. See www.pololu.com for details.
|
||||
|
||||
/*! \file libusbp.h
|
||||
*
|
||||
* This header file provides the C API for libusbp.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*! The major component of libusbp's version number. In accordance with
|
||||
* semantic versioning, this gets incremented every time there is a breaking
|
||||
* change. */
|
||||
#define LIBUSBP_VERSION_MAJOR 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <wtypesbase.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# define LIBUSBP_DLL_EXPORT __declspec(dllexport)
|
||||
# define LIBUSBP_DLL_IMPORT __declspec(dllimport)
|
||||
#else
|
||||
# define LIBUSBP_DLL_IMPORT __attribute__((visibility ("default")))
|
||||
# define LIBUSBP_DLL_EXPORT __attribute__((visibility ("default")))
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define LIBUSBP_WARN_UNUSED _Check_return_
|
||||
#else
|
||||
#define LIBUSBP_WARN_UNUSED __attribute__((warn_unused_result))
|
||||
#endif
|
||||
|
||||
#ifdef LIBUSBP_STATIC
|
||||
# define LIBUSBP_API
|
||||
#else
|
||||
#error not static
|
||||
# ifdef LIBUSBP_EXPORTS
|
||||
# define LIBUSBP_API LIBUSBP_DLL_EXPORT
|
||||
# else
|
||||
# define LIBUSBP_API LIBUSBP_DLL_IMPORT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*! Some functions in this library return strings to the caller via a char **
|
||||
* argument. If the function call was successful, it is the caller's
|
||||
* responsibility to free those strings by passing them to this function.
|
||||
* Passing the NULL pointer to this function is OK. Do not pass any strings to
|
||||
* this function unless they were previously returned by a call to this
|
||||
* library. Do not free the same non-NULL string twice. */
|
||||
LIBUSBP_API
|
||||
void libusbp_string_free(char *);
|
||||
|
||||
|
||||
/** libusbp_error **************************************************************/
|
||||
|
||||
/*! A libusbp_error object represents an error that occurred in the library.
|
||||
* Many functions return a libusbp_error pointer as a return value. The
|
||||
* convention is that a NULL pointer indicates success. If the pointer is not
|
||||
* NULL, the caller needs to free it at some point by calling
|
||||
* libusbp_error_free().
|
||||
*
|
||||
* NULL is a valid value for a libusbp_error pointer, and can be passed to any
|
||||
* function in this library that takes a libusbp_error pointer. */
|
||||
typedef struct libusbp_error
|
||||
libusbp_error;
|
||||
|
||||
/*! Each ::libusbp_error can have 0 or more error codes that give additional
|
||||
* information about the error that might help the caller take the right action
|
||||
* when the error occurs. This enum defines which error codes are possible. */
|
||||
enum libusbp_error_code
|
||||
{
|
||||
/*! There were problems allocating memory. A memory shortage might be the
|
||||
* root cause of the error, or there might be another error that is masked
|
||||
* by the memory problems. */
|
||||
LIBUSBP_ERROR_MEMORY = 1,
|
||||
|
||||
/*! It is possible that the error was caused by a temporary condition, such
|
||||
* as the operating system taking some time to initialize drivers. Any
|
||||
* function that could return this error will say so explicitly in its
|
||||
* documentation so you do not have to worry about handling it in too many
|
||||
* places. */
|
||||
LIBUSBP_ERROR_NOT_READY = 2,
|
||||
|
||||
/*! Access was denied. A common cause of this error on Windows is that
|
||||
* another application has a handle open to the same device. */
|
||||
LIBUSBP_ERROR_ACCESS_DENIED = 3,
|
||||
|
||||
/*! The device does not have a serial number. */
|
||||
LIBUSBP_ERROR_NO_SERIAL_NUMBER = 4,
|
||||
|
||||
/*! The device took too long to respond to a request or transfer data. */
|
||||
LIBUSBP_ERROR_TIMEOUT = 5,
|
||||
|
||||
/*! The error might have been caused by the device being disconnected, but
|
||||
* it is possible it was caused by something else. */
|
||||
LIBUSBP_ERROR_DEVICE_DISCONNECTED = 6,
|
||||
|
||||
/*! The error might have been caused by the host receiving a STALL packet
|
||||
* from the device, but it is possible it was caused by something else. */
|
||||
LIBUSBP_ERROR_STALL = 7,
|
||||
|
||||
/*! The error might have been caused by the transfer getting cancelled from
|
||||
* the host side. Some data might have been transferred anyway. */
|
||||
LIBUSBP_ERROR_CANCELLED = 8,
|
||||
};
|
||||
|
||||
/*! Attempts to copy an error. If you copy a NULL ::libusbp_error
|
||||
* pointer, the result will also be NULL. If you copy a non-NULL ::libusbp_error
|
||||
* pointer, the result will be non-NULL, but if there are issues allocating
|
||||
* memory, then the copied error might have different properties than the
|
||||
* original error, and it will have the ::LIBUSBP_ERROR_MEMORY code.
|
||||
*
|
||||
* It is the caller's responsibility to free the copied error. */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_error_copy(const libusbp_error *);
|
||||
|
||||
/*! Frees a returned error object. Passing the NULL pointer to this function is
|
||||
* OK. Do not free the same non-NULL error twice. */
|
||||
LIBUSBP_API
|
||||
void libusbp_error_free(libusbp_error *);
|
||||
|
||||
/*! Returns true if the error has specified error code. The error codes are
|
||||
* listed in the ::libusbp_error_code enum. */
|
||||
LIBUSBP_API bool libusbp_error_has_code(const libusbp_error *, uint32_t code);
|
||||
|
||||
/*! Returns an English-language ASCII-encoded string describing the error. The
|
||||
* message consists of one or more sentences. If there are multiple sentences,
|
||||
* the earlier ones will typically explain the context that the error happened
|
||||
* in.
|
||||
*
|
||||
* The returned pointer will be valid until the error is freed, at which point
|
||||
* it might become invalid. Do not pass the returned pointer to
|
||||
* libusbp_string_free(). */
|
||||
LIBUSBP_API const char * libusbp_error_get_message(const libusbp_error *);
|
||||
|
||||
|
||||
/** libusbp_async_in_pipe ******************************************************/
|
||||
|
||||
/*! A libusbp_async_in_pipe is an object that holds the memory and other data
|
||||
* structures for a set of asynchronous USB requests to read data from a
|
||||
* non-zero endpoint. It can be used to read data from a bulk or IN endpoint
|
||||
* with high throughput. */
|
||||
typedef struct libusbp_async_in_pipe
|
||||
libusbp_async_in_pipe;
|
||||
|
||||
/*! Closes the pipe immediately. Note that if the pipe has any
|
||||
* pending transfers, then it is possible that they cannot be freed
|
||||
* by this function. Freeing a pipe with pending transfers could
|
||||
* cause a memory leak, but is otherwise safe. */
|
||||
LIBUSBP_API
|
||||
void libusbp_async_in_pipe_close(libusbp_async_in_pipe *);
|
||||
|
||||
/*! Allocates buffers and other data structures for performing multiple
|
||||
* concurrent transfers on the pipe.
|
||||
*
|
||||
* The @a transfer_count parameter specifies how many transfers to allocate. You
|
||||
* can also think of this as the maximum number of concurrent transfers that can
|
||||
* be active at the same time.
|
||||
*
|
||||
* The @a transfer_size parameter specifies how large each transfer's buffer should
|
||||
* be, and is also the number of bytes that will be requested from the operating
|
||||
* system when the transfer is submitted.
|
||||
*
|
||||
* It is best to set the transfer size to a multiple of the maximum packet size
|
||||
* of the endpoint. Otherwise, you might get an error when the device sends
|
||||
* more data than can fit in the transfer's buffer. This type of error is
|
||||
* called an overflow.
|
||||
*
|
||||
* If you want to be reading the pipe every millisecond without gaps, you should
|
||||
* set the transfer count high enough so that it would take about 100 ms to 250
|
||||
* ms to finish all the transfers. As long as the operating system runs your
|
||||
* process that often, you should be able to keep the USB host controller busy
|
||||
* permanently. (Though we have observed gaps in the transfers when trying to
|
||||
* do this inside a VirtualBox machine.)
|
||||
*
|
||||
* You should not set the transfer count too high, or else it might end up
|
||||
* taking a long time to cancel the transfers when you are closing the pipe. */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_async_in_pipe_allocate_transfers(
|
||||
libusbp_async_in_pipe *,
|
||||
size_t transfer_count,
|
||||
size_t transfer_size);
|
||||
|
||||
/*! Starts reading data from the pipe. */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_async_in_pipe_start_endless_transfers(
|
||||
libusbp_async_in_pipe *);
|
||||
|
||||
/*! Checks for new events, such as a transfer completing. This
|
||||
* function and libusbp_async_in_pipe_handle_finished_transfer() should
|
||||
* be called regularly in order to get data from the pipe. */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_async_in_pipe_handle_events(libusbp_async_in_pipe *);
|
||||
|
||||
/*! Retrieves a boolean saying whether there are any pending
|
||||
* transfers. A pending transfer is a transfer that was submitted to
|
||||
* the operating system, and it may have been completed, but it has
|
||||
* not been passed to the caller yet via
|
||||
* libusbp_async_in_pipe_handle_finished_transfer(). */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED libusbp_error *
|
||||
libusbp_async_in_pipe_has_pending_transfers(
|
||||
libusbp_async_in_pipe *,
|
||||
bool * result);
|
||||
|
||||
/*! Checks to see if there is a finished transfer that can be handled.
|
||||
* If there is one, then this function retrieves the data from the
|
||||
* transfer, the number of bytes transferred, and any error that might
|
||||
* have occurred related to the transfer.
|
||||
*
|
||||
* @param finished An optional output pointer used to return a pointer that
|
||||
* indicates whether a transfer was finished. If the returned value is false,
|
||||
* then no transfer was finished, and there is no transfer_error or data to
|
||||
* handle.
|
||||
*
|
||||
* @param buffer An optional output pointer used to return the data
|
||||
* from the transfer. The buffer must be at least as large as the
|
||||
* transfer size specifed when
|
||||
* libusbp_async_in_pipe_allocate_transfers was called.
|
||||
*
|
||||
* @param transferred An optional output pointer used to return the
|
||||
* number of bytes transferred.
|
||||
*
|
||||
* @param transfer_error An optional pointer used to return an error
|
||||
* related to the transfer, such as a timeout or a cancellation. If
|
||||
* this pointer is provided, and a non-NULL error is returned via it,
|
||||
* then the error must later be freed with libusbp_error_free(). There
|
||||
* will never be a non-NULL transfer error if there is a regular error
|
||||
* returned as the return value of this function. */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED libusbp_error *
|
||||
libusbp_async_in_pipe_handle_finished_transfer(
|
||||
libusbp_async_in_pipe *,
|
||||
bool * finished,
|
||||
void * buffer,
|
||||
size_t * transferred,
|
||||
libusbp_error ** transfer_error);
|
||||
|
||||
/*! Cancels all the transfers for this pipe. The cancellation is
|
||||
* asynchronous, so it won't have an immediate effect. If you want
|
||||
* to actually make sure that all the transfers get cancelled, you
|
||||
* will need to call libusbp_async_in_pipe_handle_events() and
|
||||
* libusbp_async_in_pipe_handle_finished_transfer() repeatedly until
|
||||
* libusbp_async_in_pipe_has_pending_transfers() indicates there are
|
||||
* no pending transfers left. */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_async_in_pipe_cancel_transfers(libusbp_async_in_pipe *);
|
||||
|
||||
|
||||
/** libusbp_device *************************************************************/
|
||||
|
||||
/*! Represents a single USB device. A composite device with multiple functions
|
||||
* is represented by a single libusbp_device object.
|
||||
*
|
||||
* A NULL libusbp_device pointer is valid and can be passed to any function in
|
||||
* this library that takes such pointers. */
|
||||
typedef struct libusbp_device
|
||||
libusbp_device;
|
||||
|
||||
/*! Finds all the USB devices connected to the computer and returns a list of them.
|
||||
*
|
||||
* The optional @a device_count parameter is used to return the number of
|
||||
* devices in the list. The list is actually one element larger because it ends
|
||||
* with a NULL pointer.
|
||||
*
|
||||
* If this function is successful (the returned error pointer is NULL), then you
|
||||
* must later free each device by calling libusbp_device_free() and free the
|
||||
* list by calling libusbp_list_free(). The order in which the retrieved
|
||||
* objects are freed does not matter. */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_list_connected_devices(
|
||||
libusbp_device *** device_list,
|
||||
size_t * device_count);
|
||||
|
||||
/*! Frees a device list returned by libusbp_list_connected_device(). */
|
||||
LIBUSBP_API
|
||||
void libusbp_list_free(libusbp_device ** list);
|
||||
|
||||
/*! Finds a device with the specified vendor ID and product ID and returns a
|
||||
* pointer to it. If no device can be found, returns a NULL pointer. If the
|
||||
* retrieved device pointer is not NULL, you must free it later by calling
|
||||
* libusbp_device_free(). The retrieved device pointer will always be NULL if
|
||||
* an error is returned. */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_find_device_with_vid_pid(
|
||||
uint16_t vendor_id,
|
||||
uint16_t product_id,
|
||||
libusbp_device ** device);
|
||||
|
||||
/*! Makes a copy of a device object. If this function is successful, you will
|
||||
* need to free the copy by calling libusbp_device_free() at some point. */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_device_copy(
|
||||
const libusbp_device * source,
|
||||
libusbp_device ** dest);
|
||||
|
||||
/*! Frees a device object. Passing a NULL pointer to this function is OK. Do
|
||||
* not free the same non-NULL device twice. */
|
||||
LIBUSBP_API void libusbp_device_free(libusbp_device *);
|
||||
|
||||
/*! Gets the USB vendor ID of the device (idVendor). */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_device_get_vendor_id(
|
||||
const libusbp_device *,
|
||||
uint16_t * vendor_id);
|
||||
|
||||
/*! Gets the USB product ID of the device (idProduct). */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_device_get_product_id(
|
||||
const libusbp_device *,
|
||||
uint16_t * product_id);
|
||||
|
||||
/*! Gets the USB revision code of the device (bcdDevice). */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_device_get_revision(
|
||||
const libusbp_device *,
|
||||
uint16_t * revision);
|
||||
|
||||
/*! Gets the serial number of the device as an ASCII-encoded string.
|
||||
*
|
||||
* On Windows, this just returns the segment of the Device Instance ID after the
|
||||
* last slash. If the device does not have a serial number, it will return some
|
||||
* other type of identifier that contains andpersands (&). Windows ignores
|
||||
* serial numbers with invalid characters in them. For more information, see:
|
||||
*
|
||||
* https://msdn.microsoft.com/en-us/library/windows/hardware/dn423379#usbsn
|
||||
*
|
||||
* On other systems, if the device does not have a serial number, then this
|
||||
* function returns an error with the code ::LIBUSBP_ERROR_NO_SERIAL_NUMBER.
|
||||
*
|
||||
* (Most applications should only call this function on specific USB devices
|
||||
* that are already known to have serial numbers, in which case the lack of a
|
||||
* serial number really does indicate a failure.)
|
||||
*
|
||||
* You should free the returned string by calling libusbp_string_free(). */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_device_get_serial_number(
|
||||
const libusbp_device *,
|
||||
char ** serial_number);
|
||||
|
||||
/*! Gets an operating system-specific string that identifies the device.
|
||||
*
|
||||
* Note that the level of specificity provided by the ID depends on the system
|
||||
* you are on, and whether your device has a USB serial number. As long as the
|
||||
* device remains connected to the bus, this ID is not expected to change and
|
||||
* there should be no other devices that have the same ID. However, if the
|
||||
* device gets disconnected from the bus, it may be possible for the ID to be
|
||||
* reused by another device.
|
||||
*
|
||||
* @b Windows: This will be a device instance ID, and it will look something
|
||||
* like this:
|
||||
*
|
||||
* <pre>
|
||||
* USB\\VID_1FFB&PID_DA01\6&11A23516&18&0000
|
||||
* </pre>
|
||||
*
|
||||
* If your device has a serial number, the part after the slash will be the
|
||||
* serial number. Otherwise, it will be a string with andpersands in it.
|
||||
*
|
||||
* @b Linux: This will be a sysfs path, and it will look like something like
|
||||
* this:
|
||||
*
|
||||
* <pre>
|
||||
* /sys/devices/pci0000:00/0000:00:06.0/usb1/1-2
|
||||
* </pre>
|
||||
*
|
||||
* <b>macOS:</b> This will be an integer from
|
||||
* IORegistryEntryGetRegistryEntryID, formatted as a lower-case hex number with
|
||||
* no leading zeros. It will look something like this:
|
||||
*
|
||||
* <pre>
|
||||
* 10000021a
|
||||
* </pre>
|
||||
*/
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_device_get_os_id(
|
||||
const libusbp_device *,
|
||||
char ** id);
|
||||
|
||||
|
||||
/** libusbp_generic_interface **************************************************/
|
||||
|
||||
/*! Represents a generic or vendor-defined interface of a USB device. A null
|
||||
* libusbp_generic_interface pointer is valid and can be passed to any function
|
||||
* in this library that takes such pointers. */
|
||||
typedef struct libusbp_generic_interface
|
||||
libusbp_generic_interface;
|
||||
|
||||
/*! Creates a generic interface object for a specified interface of the
|
||||
* specified USB device. This function does as many checks as possible to make
|
||||
* sure that a handle to the interface could be opened, without actually opening
|
||||
* it yet.
|
||||
*
|
||||
* On all platforms, if a record of the interface cannot be found, then an error
|
||||
* is returned with the code LIBUSBP_ERROR_NOT_READY, because this could just be
|
||||
* a temporary condition that happens right after the device is plugged in.
|
||||
*
|
||||
* On Windows, the generic interface must use the WinUSB driver, or this
|
||||
* function will fail. If it is using no driver, that could be a temporary
|
||||
* condition, and the error returned will use the LIBUSBP_ERROR_NOT_READY error
|
||||
* code.
|
||||
*
|
||||
* On Linux, if the corresponding devnode file does not exist, an error with
|
||||
* code LIBUSBP_ERROR_NOT_READY is returned. If the interface is assigned to a
|
||||
* driver that is not "usbfs", an error is returned.
|
||||
*
|
||||
* On macOS, we do not have any additional checks beyond just making sure
|
||||
* that an entry for the interface is found. For non-composite devices, that
|
||||
* check is deferred until a handle is opened.
|
||||
*
|
||||
* @param interface_number The lowest @a bInterfaceNumber for the interfaces in
|
||||
* the USB function you want to use.
|
||||
*
|
||||
* @param composite Should be true if the device is composite, and false
|
||||
* otherwise.
|
||||
*
|
||||
* The returned object must be freed with libusbp_generic_interface_free(). */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_generic_interface_create(
|
||||
const libusbp_device *,
|
||||
uint8_t interface_number,
|
||||
bool composite,
|
||||
libusbp_generic_interface **);
|
||||
|
||||
/*! Frees the specified generic interface object. Passing the NULL pointer to
|
||||
* this function is OK. Do not free the same non-NULL pointer twice. */
|
||||
LIBUSBP_API void libusbp_generic_interface_free(libusbp_generic_interface *);
|
||||
|
||||
/*! Makes a copy of the generic interface object. The copy must be freed with
|
||||
* libusbp_generic_interface_free(). */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_generic_interface_copy(
|
||||
const libusbp_generic_interface * source,
|
||||
libusbp_generic_interface ** dest);
|
||||
|
||||
/*! Returns an operating system-specific string that can be used to uniquely
|
||||
* identify this generic interface.
|
||||
*
|
||||
* <b>Windows:</b> This will be a device instance ID specific to this interface, and
|
||||
* it will look something like this:
|
||||
*
|
||||
* <pre>
|
||||
* USB\\VID_1FFB&PID_DA01&MI_00\6&11A23516&18&0000
|
||||
* </pre>
|
||||
*
|
||||
* <b>Linux:</b> This will be a sysfs path specific to this interface, and it will
|
||||
* look like something like this:
|
||||
*
|
||||
* <pre>
|
||||
* /sys/devices/pci0000:00/0000:00:06.0/usb1/1-2/1-2:1.0
|
||||
* </pre>
|
||||
*
|
||||
* <b>macOS:</b> This will be an integer from
|
||||
* IORegistryEntryGetRegistryEntryID, formatted as a lower-case hex number with
|
||||
* no leading zeros. It will look something like this:
|
||||
*
|
||||
* <pre>
|
||||
* 10000021a
|
||||
* </pre>
|
||||
*/
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_generic_interface_get_os_id(
|
||||
const libusbp_generic_interface *,
|
||||
char ** id);
|
||||
|
||||
/*! Returns an operating system-specific filename corresponding to this
|
||||
* interface.
|
||||
*
|
||||
* <b>Windows:</b> This will be the name of a file you can use with CreateFile to
|
||||
* access the device, and it will look something like this:
|
||||
*
|
||||
* <pre>
|
||||
* \\\\?\\usb#vid_1ffb&pid_da01&mi_00#6&11a23516&18&0000#{99c4bbb0-e925-4397-afee-981cd0702163}
|
||||
* </pre>
|
||||
*
|
||||
* <b>Linux:</b> this will return a device node file name that represents the
|
||||
* overall USB device. It will look something like:
|
||||
*
|
||||
* <pre>
|
||||
* /dev/bus/usb/001/007
|
||||
* </pre>
|
||||
*
|
||||
* <b>macOS:</b> This will be an integer from
|
||||
* IORegistryEntryGetRegistryEntryID, formatted as a lower-case hex number with
|
||||
* no leading zeros. It will look something like this:
|
||||
*
|
||||
* <pre>
|
||||
* 10000021a
|
||||
* </pre>
|
||||
*/
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_generic_interface_get_os_filename(
|
||||
const libusbp_generic_interface *,
|
||||
char ** filename);
|
||||
|
||||
|
||||
/** libusbp_generic_handle *****************************************************/
|
||||
|
||||
/*! Represents a generic handle to a USB device. This handle can be used to
|
||||
* perform operations such as control transfers and reading and writing data
|
||||
* from non-zero endpoints.
|
||||
*
|
||||
* NULL is a valid value for a libusbp_generic_handle pointer, and can be passed
|
||||
* in any functions of this library that take a libusbp_generic_handle
|
||||
* pointer. */
|
||||
typedef struct libusbp_generic_handle
|
||||
libusbp_generic_handle;
|
||||
|
||||
/*! Opens a generic handle to the specified interface of a USB device which can
|
||||
* be used to perform USB I/O operations.
|
||||
*
|
||||
* The handle must later be closed with libusbp_generic_handle_close().
|
||||
*
|
||||
* On Windows, for devices using WinUSB, if another application has a handle
|
||||
* open already when this function is called, then this function will fail and
|
||||
* the returned error will have code ::LIBUSBP_ERROR_ACCESS_DENIED.
|
||||
*
|
||||
* On macOS, this function will set the device's configuration to 1 as a
|
||||
* side effect in case it is not already configured. */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_generic_handle_open(
|
||||
const libusbp_generic_interface *,
|
||||
libusbp_generic_handle **);
|
||||
|
||||
/*! Closes and frees the specified generic handle. It is OK to pass NULL to
|
||||
* this function. Do not close the same non-NULL handle twice. All
|
||||
* ::libusbp_async_in_pipe objects created by the handle must be closed before
|
||||
* closing the handle. */
|
||||
LIBUSBP_API
|
||||
void libusbp_generic_handle_close(
|
||||
libusbp_generic_handle *);
|
||||
|
||||
/*! Creates a new asynchronous pipe object for reading data in from the device
|
||||
* on one of its bulk or interrupt IN endpoints.
|
||||
*
|
||||
* The behavior of this library is unspecified if you use both an asynchronous
|
||||
* IN pipe and synchronous reads with libusbp_read_pipe() on the same pipe of
|
||||
* the same generic handle. One reason for that is because for WinUSB devices,
|
||||
* this function enables RAW_IO for the pipe, and it does not turn off RAW_IO
|
||||
* again after the pipe is closed. So the behavior of libusbp_read_pipe() could
|
||||
* change depending on whether an asynchronous IN pipe has been used. */
|
||||
LIBUSBP_API
|
||||
libusbp_error * libusbp_generic_handle_open_async_in_pipe(
|
||||
libusbp_generic_handle *,
|
||||
uint8_t pipe_id,
|
||||
libusbp_async_in_pipe ** async_in_pipe);
|
||||
|
||||
/*! Sets a timeout for a particular pipe on the USB device.
|
||||
*
|
||||
* The @a pipe_id should either be 0 to specify control transfers on endpoint 0, or
|
||||
* should be a bEndpointAddress value from one of the device's endpoint
|
||||
* descriptors. Specifying an invalid pipe might result in an error.
|
||||
*
|
||||
* The timeout value is specified in milliseconds, and a value of 0 means no
|
||||
* timeout (wait forever). If this function is not called, the default behavior
|
||||
* of the handle is to have no timeout.
|
||||
*
|
||||
* The behavior of this function is unspecified if there is any data being
|
||||
* transferred on the pipe while this function is running.
|
||||
*
|
||||
* It is unspecified whether this timeout has an effect on asynchronous
|
||||
* transfers. */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_generic_handle_set_timeout(
|
||||
libusbp_generic_handle *,
|
||||
uint8_t pipe_id,
|
||||
uint32_t timeout);
|
||||
|
||||
/*! Performs a synchronous (blocking) control transfer on endpoint 0.
|
||||
*
|
||||
* Under Linux, this blocking transfer unfortunately cannot be interrupted with
|
||||
* Ctrl+C.
|
||||
*
|
||||
* The @a buffer parameter should point to a buffer that is at least @a wLength
|
||||
* bytes long.
|
||||
*
|
||||
* The @a transferred pointer is optional, and is used to return the number of
|
||||
* bytes that were actually transferred.
|
||||
*
|
||||
* The direction of the transfer is determined by the @a bmRequestType parameter.
|
||||
*/
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_control_transfer(
|
||||
libusbp_generic_handle *,
|
||||
uint8_t bmRequestType,
|
||||
uint8_t bRequest,
|
||||
uint16_t wValue,
|
||||
uint16_t wIndex,
|
||||
void * buffer,
|
||||
uint16_t wLength,
|
||||
size_t * transferred);
|
||||
|
||||
/*! Performs a synchronous (blocking) write of data to a bulk or interrupt
|
||||
* endpoint.
|
||||
*
|
||||
* Under Linux, this blocking transfer unfortunately cannot be interrupted with
|
||||
* Ctrl+C.
|
||||
*
|
||||
* The @a pipe_id parameter specifies which endpoint to use. This argument
|
||||
* should be bEndpointAddress value from one of the device's IN endpoint
|
||||
* descriptors. (Its most significant bit must be 0.)
|
||||
*
|
||||
* The @a transferred parameter is an optional pointer to a variable that will
|
||||
* receive the number of bytes transferred. */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_write_pipe(
|
||||
libusbp_generic_handle *,
|
||||
uint8_t pipe_id,
|
||||
const void * buffer,
|
||||
size_t size,
|
||||
size_t * transferred);
|
||||
|
||||
/*! Performs a synchronous (blocking) read of data from a bulk or interrupt
|
||||
* endpoint.
|
||||
*
|
||||
* It is best to set the buffer size to a multiple of the maximum
|
||||
* packet size of the endpoint. Otherwise, this function might return
|
||||
* an error when the device sends more data than can fit in the
|
||||
* buffer. This type of error is called an overflow.
|
||||
*
|
||||
* Under Linux, this blocking transfer unfortunately cannot be interrupted with
|
||||
* Ctrl+C.
|
||||
*
|
||||
* The @a pipe_id parameter specifies which endpoint to use. This argument
|
||||
* should be bEndpointAddress value from one of the device's IN endpoint
|
||||
* descriptors. (Its most significant bit must be 1.)
|
||||
*
|
||||
* The @a transferred parameter is an optional pointer to a variable that will
|
||||
* receive the number of bytes transferred. */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_read_pipe(
|
||||
libusbp_generic_handle *,
|
||||
uint8_t pipe_id,
|
||||
void * buffer,
|
||||
size_t size,
|
||||
size_t * transferred);
|
||||
|
||||
#ifdef __linux__
|
||||
/*! Gets the underlying file descriptor of the generic handle. This function is
|
||||
* only available on Linux, and is intended for advanced users. The returned
|
||||
* file descriptor will remain open and valid as long as the handle is open and
|
||||
* has not been closed. */
|
||||
LIBUSBP_API
|
||||
int libusbp_generic_handle_get_fd(libusbp_generic_handle *);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
/*! Gets the underlying WinUSB handle for the generic handle. This function is
|
||||
* only available on Windows, and is intended for advanced users. The returned
|
||||
* WinUSB handle will remain open and valid as long as the generic handle is
|
||||
* open and has not been closed. */
|
||||
LIBUSBP_API
|
||||
HANDLE libusbp_generic_handle_get_winusb_handle(libusbp_generic_handle *);
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
/*! Gets the underlying IOCFPlugInInterface object representing the interface.
|
||||
* You can cast the returned pointer to a `IOCFPlugInInterface **` and then use
|
||||
* `QueryInterface` to get the corresponding `IOUSBInterfaceInterface **`.
|
||||
* There is an example of this in generic_handle_test.cpp. */
|
||||
LIBUSBP_API
|
||||
void ** libusbp_generic_handle_get_cf_plug_in(libusbp_generic_handle *);
|
||||
#endif
|
||||
|
||||
|
||||
/** libusbp_serial_port ********************************************************/
|
||||
|
||||
/*! Represents a serial port. A null libusbp_serial_port pointer is valid and
|
||||
* can be passed to any function in this library that takes such pointers. */
|
||||
typedef struct libusbp_serial_port
|
||||
libusbp_serial_port;
|
||||
|
||||
/*! Creates a serial port object for a specified interface of the
|
||||
* specified USB device.
|
||||
*
|
||||
* On all platforms, if a record of the interface cannot be found, then an error
|
||||
* is returned with the code LIBUSBP_ERROR_NOT_READY, because this could just be
|
||||
* a temporary condition that happens right after the device is plugged in.
|
||||
*
|
||||
* On macOS, it is assumed that the interface with a @a bInterfaceNumber one
|
||||
* greater than @a interface_number is the interface that the IOSerialBSDClient
|
||||
* will attach to. This should be true if the device implements the USB CDC ACM
|
||||
* class and has ordered its interfaces so that the control interface is right
|
||||
* before the data interface.
|
||||
*
|
||||
* @param interface_number The lowest @a bInterfaceNumber for the USB interfaces
|
||||
* that comprise the serial port.
|
||||
*
|
||||
* @param composite Should be true if the device is composite, and false
|
||||
* otherwise.
|
||||
*
|
||||
* The returned object must be freed with libusbp_generic_interface_free(). */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_serial_port_create(
|
||||
const libusbp_device *,
|
||||
uint8_t interface_number,
|
||||
bool composite,
|
||||
libusbp_serial_port **);
|
||||
|
||||
/*! Frees the specified serial port object. Passing the NULL pointer to
|
||||
* this function is OK. Do not free the same non-NULL pointer twice. */
|
||||
LIBUSBP_API void libusbp_serial_port_free(libusbp_serial_port *);
|
||||
|
||||
/*! Makes a copy of the generic interface object. The copy must be freed with
|
||||
* libusbp_generic_interface_free(). */
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_serial_port_copy(
|
||||
const libusbp_serial_port * source,
|
||||
libusbp_serial_port ** dest);
|
||||
|
||||
/*! Gets the user-friendly name of the COM port
|
||||
* that could be used to open a handle.
|
||||
*
|
||||
* On Windows, this will be something like "COM12".
|
||||
*
|
||||
* On Linux, it will be something like "/dev/ttyACM0".
|
||||
*
|
||||
* On macOS, it will be something like "/dev/cu.usbmodem012345".
|
||||
* Specifically, it will be a call-out device, not a dial-in device.
|
||||
*
|
||||
* You should free the returned string by calling libusbp_string_free().
|
||||
*/
|
||||
LIBUSBP_API LIBUSBP_WARN_UNUSED
|
||||
libusbp_error * libusbp_serial_port_get_name(
|
||||
const libusbp_serial_port *,
|
||||
char ** name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
569
dep/libusbp/include/libusbp.hpp
Normal file
569
dep/libusbp/include/libusbp.hpp
Normal file
|
|
@ -0,0 +1,569 @@
|
|||
// Copyright (C) Pololu Corporation. See www.pololu.com for details.
|
||||
|
||||
/*! \file libusbp.hpp
|
||||
*
|
||||
* This header files provides the C++ API for libusbp. The classes and
|
||||
* functions here are just thin wrappers around the C API, so you should see
|
||||
* libusbp.h for full documentation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "libusbp.h"
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/** Display a nice error if C++11 is not enabled (e.g. --std=c++11 or --std=gnu++11).
|
||||
* The __GXX_EXPERIMENTAL_CXX0X__ check is needed for GCC 4.6, which defines __cplusplus as 1.
|
||||
* The _MSC_VER check is needed for Visual Studio 2015. */
|
||||
#if (!defined(__cplusplus) || (__cplusplus < 201103L)) && !defined(__GXX_EXPERIMENTAL_CXX0X__) && !defined(_MSC_VER)
|
||||
#error This header requires features from C++11.
|
||||
#endif
|
||||
|
||||
namespace libusbp
|
||||
{
|
||||
/*! \cond */
|
||||
inline void throw_if_needed(libusbp_error * err);
|
||||
/*! \endcond */
|
||||
|
||||
/*! Wrapper for libusbp_error_free(). */
|
||||
inline void pointer_free(libusbp_error * p) noexcept
|
||||
{
|
||||
libusbp_error_free(p);
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_error_copy(). */
|
||||
inline libusbp_error * pointer_copy(libusbp_error * p) noexcept
|
||||
{
|
||||
return libusbp_error_copy(p);
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_async_in_pipe_close(). */
|
||||
inline void pointer_free(libusbp_async_in_pipe * p) noexcept
|
||||
{
|
||||
libusbp_async_in_pipe_close(p);
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_device_free(). */
|
||||
inline void pointer_free(libusbp_device * p) noexcept
|
||||
{
|
||||
libusbp_device_free(p);
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_device_copy(). */
|
||||
inline libusbp_device * pointer_copy(libusbp_device * pointer)
|
||||
{
|
||||
libusbp_device * copy;
|
||||
throw_if_needed(libusbp_device_copy(pointer, ©));
|
||||
return copy;
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_generic_interface_free(). */
|
||||
inline void pointer_free(libusbp_generic_interface * p) noexcept
|
||||
{
|
||||
libusbp_generic_interface_free(p);
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_generic_interface_copy(). */
|
||||
inline libusbp_generic_interface * pointer_copy(libusbp_generic_interface * pointer)
|
||||
{
|
||||
libusbp_generic_interface * copy;
|
||||
throw_if_needed(libusbp_generic_interface_copy(pointer, ©));
|
||||
return copy;
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_generic_handle_free(). */
|
||||
inline void pointer_free(libusbp_generic_handle * p) noexcept
|
||||
{
|
||||
libusbp_generic_handle_close(p);
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_serial_port_copy(). */
|
||||
inline libusbp_serial_port * pointer_copy(libusbp_serial_port * pointer)
|
||||
{
|
||||
libusbp_serial_port * copy;
|
||||
throw_if_needed(libusbp_serial_port_copy(pointer, ©));
|
||||
return copy;
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_serial_port_free(). */
|
||||
inline void pointer_free(libusbp_serial_port * p) noexcept
|
||||
{
|
||||
libusbp_serial_port_free(p);
|
||||
}
|
||||
|
||||
/*! This class is not part of the public API of the library and you should
|
||||
* not use it directly, but you can use the public methods it provides to
|
||||
* the classes that inherit from it.
|
||||
*
|
||||
* For any type T, if you define pointer_free(T *), then
|
||||
* unique_pointer_wrapper<T> will be a well-behaved C++ class that provides
|
||||
* a constructor, implicit conversion to a bool, C++ move operations,
|
||||
* pointer operations, and forbids C++ copy operations. */
|
||||
template<class T>
|
||||
class unique_pointer_wrapper
|
||||
{
|
||||
public:
|
||||
/*! Constructor that takes a pointer. */
|
||||
explicit unique_pointer_wrapper(T * p = nullptr) noexcept
|
||||
: pointer(p)
|
||||
{
|
||||
}
|
||||
|
||||
/*! Move constructor. */
|
||||
unique_pointer_wrapper(unique_pointer_wrapper && other) noexcept
|
||||
{
|
||||
pointer = other.pointer_release();
|
||||
}
|
||||
|
||||
/*! Move assignment operator. */
|
||||
unique_pointer_wrapper & operator=(unique_pointer_wrapper && other) noexcept
|
||||
{
|
||||
pointer_reset(other.pointer_release());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*! Destructor. */
|
||||
~unique_pointer_wrapper() noexcept
|
||||
{
|
||||
pointer_reset();
|
||||
}
|
||||
|
||||
/*! Implicit conversion to bool. Returns true if the underlying pointer
|
||||
* is not NULL. */
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return pointer != nullptr;
|
||||
}
|
||||
|
||||
/*! Returns the underlying pointer. */
|
||||
T * pointer_get() const noexcept
|
||||
{
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/*! Sets the underlying pointer to the specified value, freeing the
|
||||
* previous pointer and taking ownership of the specified one. */
|
||||
void pointer_reset(T * p = nullptr) noexcept
|
||||
{
|
||||
pointer_free(pointer);
|
||||
pointer = p;
|
||||
}
|
||||
|
||||
/*! Releases the pointer, transferring ownership of it to the caller and
|
||||
* resetting the underlying pointer of this object to nullptr. The caller
|
||||
* is responsible for freeing the returned pointer if it is not NULL. */
|
||||
T * pointer_release() noexcept
|
||||
{
|
||||
T * p = pointer;
|
||||
pointer = nullptr;
|
||||
return p;
|
||||
}
|
||||
|
||||
/*! Returns a pointer to the underlying pointer. */
|
||||
T ** pointer_to_pointer_get() noexcept
|
||||
{
|
||||
return &pointer;
|
||||
}
|
||||
|
||||
/*! Copy constructor: forbid. */
|
||||
unique_pointer_wrapper(const unique_pointer_wrapper & other) = delete;
|
||||
|
||||
/*! Copy assignment operator: forbid. */
|
||||
unique_pointer_wrapper & operator=(const unique_pointer_wrapper & other) = delete;
|
||||
|
||||
protected:
|
||||
/*! The underlying pointer that is being wrapped. This pointer will be
|
||||
* freed when the object is destroyed. */
|
||||
T * pointer;
|
||||
};
|
||||
|
||||
/*! This class is not part of the public API of the library and you should
|
||||
* not use it directly, but you can use the public methods it provides to
|
||||
* the classes that inherit from it.
|
||||
*
|
||||
* For any type T, if you define pointer_free(T *) and pointer_copy(T *), then
|
||||
* unique_pointer_wrapper_with_copy<T> will be a well-behaved C++ class that provides
|
||||
* a constructor, implicit conversion to a bool, C++ move operations, C++ copy operations,
|
||||
* and pointer operations. */
|
||||
template <class T>
|
||||
class unique_pointer_wrapper_with_copy : public unique_pointer_wrapper<T>
|
||||
{
|
||||
public:
|
||||
/*! Constructor that takes a pointer. */
|
||||
explicit unique_pointer_wrapper_with_copy(T * p = nullptr) noexcept
|
||||
: unique_pointer_wrapper<T>(p)
|
||||
{
|
||||
}
|
||||
|
||||
/*! Move constructor. */
|
||||
unique_pointer_wrapper_with_copy(
|
||||
unique_pointer_wrapper_with_copy && other) noexcept = default;
|
||||
|
||||
/*! Copy constructor */
|
||||
unique_pointer_wrapper_with_copy(
|
||||
const unique_pointer_wrapper_with_copy & other)
|
||||
: unique_pointer_wrapper<T>()
|
||||
{
|
||||
this->pointer = pointer_copy(other.pointer);
|
||||
}
|
||||
|
||||
/*! Copy assignment operator. */
|
||||
unique_pointer_wrapper_with_copy & operator=(
|
||||
const unique_pointer_wrapper_with_copy & other)
|
||||
{
|
||||
this->pointer_reset(pointer_copy(other.pointer));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*! Move assignment operator. */
|
||||
unique_pointer_wrapper_with_copy & operator=(
|
||||
unique_pointer_wrapper_with_copy && other) = default;
|
||||
};
|
||||
|
||||
/*! Wrapper for a ::libusbp_error pointer. */
|
||||
class error : public unique_pointer_wrapper_with_copy<libusbp_error>, public std::exception
|
||||
{
|
||||
public:
|
||||
/*! Constructor that takes a pointer. */
|
||||
explicit error(libusbp_error * p = nullptr) noexcept
|
||||
: unique_pointer_wrapper_with_copy(p)
|
||||
{
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_error_get_message(). */
|
||||
const char * what() const noexcept override {
|
||||
return libusbp_error_get_message(pointer);
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_error_get_message() that returns a
|
||||
* std::string. */
|
||||
std::string message() const
|
||||
{
|
||||
return what();
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_error_has_code(). */
|
||||
bool has_code(uint32_t error_code) const noexcept
|
||||
{
|
||||
return libusbp_error_has_code(pointer, error_code);
|
||||
}
|
||||
};
|
||||
|
||||
/*! \cond */
|
||||
inline void throw_if_needed(libusbp_error * err)
|
||||
{
|
||||
if (err != nullptr)
|
||||
{
|
||||
throw error(err);
|
||||
}
|
||||
}
|
||||
/*! \endcond */
|
||||
|
||||
/*! Wrapper for a ::libusbp_async_in_pipe pointer. */
|
||||
class async_in_pipe : public unique_pointer_wrapper<libusbp_async_in_pipe>
|
||||
{
|
||||
public:
|
||||
/*! Constructor that takes a pointer. */
|
||||
explicit async_in_pipe(libusbp_async_in_pipe * pointer = nullptr)
|
||||
: unique_pointer_wrapper(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_async_in_pipe_allocate_transfers(). */
|
||||
void allocate_transfers(size_t transfer_count, size_t transfer_size)
|
||||
{
|
||||
throw_if_needed(libusbp_async_in_pipe_allocate_transfers(
|
||||
pointer, transfer_count, transfer_size));
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_async_in_pipe_start_endless_transfers(). */
|
||||
void start_endless_transfers()
|
||||
{
|
||||
throw_if_needed(libusbp_async_in_pipe_start_endless_transfers(pointer));
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_async_in_pipe_handle_events(). */
|
||||
void handle_events()
|
||||
{
|
||||
throw_if_needed(libusbp_async_in_pipe_handle_events(pointer));
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_async_in_pipe_has_pending_transfers(). */
|
||||
bool has_pending_transfers()
|
||||
{
|
||||
bool result;
|
||||
throw_if_needed(libusbp_async_in_pipe_has_pending_transfers(pointer, &result));
|
||||
return result;
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_async_in_pipe_handle_finished_transfer(). */
|
||||
bool handle_finished_transfer(void * buffer, size_t * transferred,
|
||||
error * transfer_error)
|
||||
{
|
||||
libusbp_error ** error_out = nullptr;
|
||||
if (transfer_error != nullptr)
|
||||
{
|
||||
transfer_error->pointer_reset();
|
||||
error_out = transfer_error->pointer_to_pointer_get();
|
||||
}
|
||||
|
||||
bool finished;
|
||||
throw_if_needed(libusbp_async_in_pipe_handle_finished_transfer(
|
||||
pointer, &finished, buffer, transferred, error_out));
|
||||
return finished;
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_async_in_pipe_cancel_transfers(). */
|
||||
void cancel_transfers()
|
||||
{
|
||||
throw_if_needed(libusbp_async_in_pipe_cancel_transfers(pointer));
|
||||
}
|
||||
};
|
||||
|
||||
/*! Wrapper for a ::libusbp_device pointer. */
|
||||
class device : public unique_pointer_wrapper_with_copy<libusbp_device>
|
||||
{
|
||||
public:
|
||||
/*! Constructor that takes a pointer. */
|
||||
explicit device(libusbp_device * pointer = nullptr) :
|
||||
unique_pointer_wrapper_with_copy(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_device_get_vendor_id(). */
|
||||
uint16_t get_vendor_id() const
|
||||
{
|
||||
uint16_t id;
|
||||
throw_if_needed(libusbp_device_get_vendor_id(pointer, &id));
|
||||
return id;
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_device_get_product_id(). */
|
||||
uint16_t get_product_id() const
|
||||
{
|
||||
uint16_t id;
|
||||
throw_if_needed(libusbp_device_get_product_id(pointer, &id));
|
||||
return id;
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_device_get_revision(). */
|
||||
uint16_t get_revision() const
|
||||
{
|
||||
uint16_t r;
|
||||
throw_if_needed(libusbp_device_get_revision(pointer, &r));
|
||||
return r;
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_device_get_serial_number(). */
|
||||
std::string get_serial_number() const
|
||||
{
|
||||
char * str;
|
||||
throw_if_needed(libusbp_device_get_serial_number(pointer, &str));
|
||||
std::string serial_number = str;
|
||||
libusbp_string_free(str);
|
||||
return serial_number;
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_device_get_os_id(). */
|
||||
std::string get_os_id() const
|
||||
{
|
||||
char * str;
|
||||
throw_if_needed(libusbp_device_get_os_id(pointer, &str));
|
||||
std::string serial_number = str;
|
||||
libusbp_string_free(str);
|
||||
return serial_number;
|
||||
}
|
||||
};
|
||||
|
||||
/*! Wrapper for libusbp_list_connected_devices(). */
|
||||
inline std::vector<libusbp::device> list_connected_devices()
|
||||
{
|
||||
libusbp_device ** device_list;
|
||||
size_t size;
|
||||
throw_if_needed(libusbp_list_connected_devices(&device_list, &size));
|
||||
std::vector<device> vector;
|
||||
for(size_t i = 0; i < size; i++)
|
||||
{
|
||||
vector.emplace_back(device_list[i]);
|
||||
}
|
||||
libusbp_list_free(device_list);
|
||||
return vector;
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_find_device_with_vid_pid(). */
|
||||
inline libusbp::device find_device_with_vid_pid(uint16_t vendor_id, uint16_t product_id)
|
||||
{
|
||||
libusbp_device * device_pointer;
|
||||
throw_if_needed(libusbp_find_device_with_vid_pid(
|
||||
vendor_id, product_id, &device_pointer));
|
||||
return device(device_pointer);
|
||||
}
|
||||
|
||||
/*! Wrapper for a ::libusbp_generic_interface pointer. */
|
||||
class generic_interface : public unique_pointer_wrapper_with_copy<libusbp_generic_interface>
|
||||
{
|
||||
public:
|
||||
/*! Constructor that takes a pointer. This object will free the pointer
|
||||
* when it is destroyed. */
|
||||
explicit generic_interface(libusbp_generic_interface * pointer = nullptr)
|
||||
: unique_pointer_wrapper_with_copy(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_generic_interface_create. */
|
||||
explicit generic_interface(const device & device,
|
||||
uint8_t interface_number = 0, bool composite = false)
|
||||
{
|
||||
throw_if_needed(libusbp_generic_interface_create(
|
||||
device.pointer_get(), interface_number, composite, &pointer));
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_generic_interface_get_os_id(). */
|
||||
std::string get_os_id() const
|
||||
{
|
||||
char * str;
|
||||
throw_if_needed(libusbp_generic_interface_get_os_id(pointer, &str));
|
||||
std::string id = str;
|
||||
libusbp_string_free(str);
|
||||
return id;
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_generic_interface_get_os_filename(). */
|
||||
std::string get_os_filename() const
|
||||
{
|
||||
char * str;
|
||||
throw_if_needed(libusbp_generic_interface_get_os_filename(pointer, &str));
|
||||
std::string filename = str;
|
||||
libusbp_string_free(str);
|
||||
return filename;
|
||||
}
|
||||
};
|
||||
|
||||
/*! Wrapper for a ::libusbp_generic_handle pointer. */
|
||||
class generic_handle : public unique_pointer_wrapper<libusbp_generic_handle>
|
||||
{
|
||||
public:
|
||||
/*! Constructor that takes a pointer. This object will free the pointer
|
||||
* when it is destroyed. */
|
||||
explicit generic_handle(libusbp_generic_handle * pointer = nullptr) noexcept
|
||||
: unique_pointer_wrapper(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_generic_handle_open(). */
|
||||
explicit generic_handle(const generic_interface & gi)
|
||||
{
|
||||
throw_if_needed(libusbp_generic_handle_open(gi.pointer_get(), &pointer));
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_generic_handle_close(). */
|
||||
void close() noexcept
|
||||
{
|
||||
pointer_reset();
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_generic_handle_open_async_in_pipe(). */
|
||||
async_in_pipe open_async_in_pipe(uint8_t pipe_id)
|
||||
{
|
||||
libusbp_async_in_pipe * pipe;
|
||||
throw_if_needed(libusbp_generic_handle_open_async_in_pipe(
|
||||
pointer, pipe_id, &pipe));
|
||||
return async_in_pipe(pipe);
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_generic_handle_set_timeout(). */
|
||||
void set_timeout(uint8_t pipe_id, uint32_t timeout)
|
||||
{
|
||||
throw_if_needed(libusbp_generic_handle_set_timeout(pointer, pipe_id, timeout));
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_control_transfer(). */
|
||||
void control_transfer(
|
||||
uint8_t bmRequestType,
|
||||
uint8_t bRequest,
|
||||
uint16_t wValue,
|
||||
uint16_t wIndex,
|
||||
void * buffer = nullptr,
|
||||
uint16_t wLength = 0,
|
||||
size_t * transferred = nullptr)
|
||||
{
|
||||
throw_if_needed(libusbp_control_transfer(pointer,
|
||||
bmRequestType, bRequest, wValue, wIndex,
|
||||
buffer, wLength, transferred));
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_write_pipe(). */
|
||||
void write_pipe(uint8_t pipe_id, const void * buffer,
|
||||
size_t size, size_t * transferred)
|
||||
{
|
||||
throw_if_needed(libusbp_write_pipe(pointer,
|
||||
pipe_id, buffer, size, transferred));
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_read_pipe(). */
|
||||
void read_pipe(uint8_t pipe_id, void * buffer,
|
||||
size_t size, size_t * transferred)
|
||||
{
|
||||
throw_if_needed(libusbp_read_pipe(pointer,
|
||||
pipe_id, buffer, size, transferred));
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
/*! Wrapper for libusbp_generic_handle_get_winusb_handle(). */
|
||||
HANDLE get_winusb_handle()
|
||||
{
|
||||
return libusbp_generic_handle_get_winusb_handle(pointer);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
/*! Wrapper for libusbp_generic_handle_get_fd(). */
|
||||
int get_fd()
|
||||
{
|
||||
return libusbp_generic_handle_get_fd(pointer);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
/*! Wrapper for libusbp_generic_handle_get_cf_plug_in(). */
|
||||
void ** get_cf_plug_in()
|
||||
{
|
||||
return libusbp_generic_handle_get_cf_plug_in(pointer);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
/*! Wrapper for a ::libusbp_serial_port pointer. */
|
||||
class serial_port : public unique_pointer_wrapper_with_copy<libusbp_serial_port>
|
||||
{
|
||||
public:
|
||||
/*! Constructor that takes a pointer. This object will free the pointer
|
||||
* when it is destroyed. */
|
||||
explicit serial_port(libusbp_serial_port * pointer = nullptr)
|
||||
: unique_pointer_wrapper_with_copy(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_serial_port_create(). */
|
||||
explicit serial_port(const device & device,
|
||||
uint8_t interface_number = 0, bool composite = false)
|
||||
{
|
||||
throw_if_needed(libusbp_serial_port_create(
|
||||
device.pointer_get(), interface_number, composite, &pointer));
|
||||
}
|
||||
|
||||
/*! Wrapper for libusbp_serial_port_get_name(). */
|
||||
std::string get_name() const
|
||||
{
|
||||
char * str;
|
||||
throw_if_needed(libusbp_serial_port_get_name(pointer, &str));
|
||||
std::string id = str;
|
||||
libusbp_string_free(str);
|
||||
return id;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
8
dep/libusbp/include/libusbp_config.h
Normal file
8
dep/libusbp/include/libusbp_config.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#define BUILD_SYSTEM_LIBUSBP_VERSION_MAJOR 1
|
||||
#define LIBUSBP_STATIC
|
||||
#undef LIBUSBP_LOG
|
||||
#undef VBOX_LINUX_ON_WINDOWS
|
||||
#undef USE_TEST_DEVICE_A
|
||||
#undef USE_TEST_DEVICE_B
|
||||
15
dep/libusbp/install_helper/CMakeLists.txt
Normal file
15
dep/libusbp/install_helper/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
use_c99()
|
||||
|
||||
add_library (install_helper SHARED install_helper_windows.c dll.def)
|
||||
|
||||
target_link_libraries (install_helper setupapi msi)
|
||||
|
||||
set_target_properties (install_helper PROPERTIES
|
||||
OUTPUT_NAME usbp-install-helper-${LIBUSBP_VERSION_MAJOR}
|
||||
LINK_FLAGS "-Wl,--enable-stdcall-fixup -static"
|
||||
)
|
||||
|
||||
install (TARGETS install_helper
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib)
|
||||
3
dep/libusbp/install_helper/README.md
Normal file
3
dep/libusbp/install_helper/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
The files in this directory compile a separate, statically linked DLL named
|
||||
`libusbp-install-helper-<x>.dll` that can be useful in installers of USB
|
||||
software.
|
||||
5
dep/libusbp/install_helper/dll.def
Normal file
5
dep/libusbp/install_helper/dll.def
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
EXPORTS
|
||||
libusbp_install_inf
|
||||
libusbp_install_infW
|
||||
libusbp_broadcast_setting_change
|
||||
libusbp_broadcast_setting_changeW
|
||||
167
dep/libusbp/install_helper/install_helper_windows.c
Normal file
167
dep/libusbp/install_helper/install_helper_windows.c
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/* This file contains special functions that can help install an application
|
||||
* that uses USB. The functions can be used from an MSI custom action or from
|
||||
* rundll32. */
|
||||
|
||||
#include <windows.h>
|
||||
#include <devpropdef.h>
|
||||
#include <msiquery.h>
|
||||
#include <setupapi.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#define LIBUSBP_UNUSED(param_name) (void)param_name;
|
||||
|
||||
typedef struct install_context
|
||||
{
|
||||
HWND owner;
|
||||
MSIHANDLE install;
|
||||
} install_context;
|
||||
|
||||
static void log_message(install_context * context, LPCWSTR message)
|
||||
{
|
||||
if (context->install == 0)
|
||||
{
|
||||
// The MSI handle is not available so just ignore log messages.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Report the log message through MSI, which will put it in the log
|
||||
// file.
|
||||
MSIHANDLE record = MsiCreateRecord(1);
|
||||
MsiRecordSetStringW(record, 0, message);
|
||||
MsiProcessMessage(context->install, INSTALLMESSAGE_INFO, record);
|
||||
MsiCloseHandle(record);
|
||||
}
|
||||
}
|
||||
|
||||
// Adds an error message to the Windows Installer log file and displays it
|
||||
// to the user in a dialog box with an OK button.
|
||||
static void error_message(install_context * context, LPCWSTR message)
|
||||
{
|
||||
if (context->install == 0)
|
||||
{
|
||||
// The MSIHANDLE is not available, so just display a dialog box.
|
||||
MessageBoxW(context->owner, message, L"Installation Error", MB_ICONERROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Report the error through MSI, which will in turn display the dialog
|
||||
// box.
|
||||
MSIHANDLE record = MsiCreateRecord(1);
|
||||
MsiRecordSetStringW(record, 0, message);
|
||||
MsiProcessMessage(context->install, INSTALLMESSAGE_ERROR, record);
|
||||
MsiCloseHandle(record);
|
||||
}
|
||||
}
|
||||
|
||||
/* You might need to call this function after modifying the PATH in order to
|
||||
* notify other programs about the change. This allows a newly launched Command
|
||||
* Prompt to see that the PATH has changed and start using it. */
|
||||
static void broadcast_setting_change_core(install_context * context)
|
||||
{
|
||||
DWORD_PTR result2 = 0;
|
||||
LRESULT result = SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
|
||||
(LPARAM)L"Environment", SMTO_ABORTIFHUNG, 5000, &result2);
|
||||
if (result == 0)
|
||||
{
|
||||
WCHAR message[1024];
|
||||
StringCbPrintfW(message, sizeof(message),
|
||||
L"SendMessageTimeout failed: Error code 0x%lx. Result %d",
|
||||
GetLastError(), result2);
|
||||
error_message(context, message);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage: rundll32 libusbp*.dll libusbp_broadcast_setting_change
|
||||
void __stdcall libusbp_broadcast_setting_changeW(
|
||||
HWND owner, HINSTANCE hinst, LPWSTR args, int n)
|
||||
{
|
||||
LIBUSBP_UNUSED(hinst);
|
||||
LIBUSBP_UNUSED(args);
|
||||
LIBUSBP_UNUSED(n);
|
||||
install_context context = {0};
|
||||
context.owner = owner;
|
||||
broadcast_setting_change_core(&context);
|
||||
}
|
||||
|
||||
// Usage: make a Custom Action in an MSI with this function as the entry point.
|
||||
UINT __stdcall libusbp_broadcast_setting_change(MSIHANDLE install)
|
||||
{
|
||||
install_context context = {0};
|
||||
context.install = install;
|
||||
log_message(&context, L"libusbp_broadcast_setting_change: Begin.");
|
||||
broadcast_setting_change_core(&context);
|
||||
log_message(&context, L"libusbp_broadcast_setting_change: End.");
|
||||
return 0; // Always return success.
|
||||
}
|
||||
|
||||
/* Calls SetupCopyOEMInf to install the specified INF file. The user may be
|
||||
* prompted to accept the driver software, and if everything works then the file
|
||||
* will be copied to the C:\Windows\inf directory. */
|
||||
static void install_inf_core(install_context * context, LPWSTR filename)
|
||||
{
|
||||
BOOL success = SetupCopyOEMInfW(filename, NULL, SPOST_PATH, 0, NULL, 0, NULL, NULL);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
WCHAR message[1024];
|
||||
|
||||
// NOTE: newlines do not show up in the MSI log, but they do show up in
|
||||
// the MSI error dialog box.
|
||||
|
||||
StringCbPrintfW(message, sizeof(message),
|
||||
L"There was an error installing the driver file %s. \n"
|
||||
L"You might have to manually install this file by right-clicking it "
|
||||
L"and selecting \"Install\". \n"
|
||||
L"Error code 0x%lx.", filename, GetLastError());
|
||||
error_message(context, message);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage: rundll32 libusbp*.dll libusbp_install_inf path
|
||||
void __stdcall libusbp_install_infW(HWND owner, HINSTANCE hinst, LPWSTR args, int n)
|
||||
{
|
||||
LIBUSBP_UNUSED(hinst);
|
||||
LIBUSBP_UNUSED(args);
|
||||
LIBUSBP_UNUSED(n);
|
||||
install_context context = {0};
|
||||
context.owner = owner;
|
||||
install_inf_core(&context, args);
|
||||
}
|
||||
|
||||
// Usage: make a Custom Action in your installer with a "CustomActionData"
|
||||
// property set equal to the full path of the inf file.
|
||||
UINT __stdcall libusbp_install_inf(MSIHANDLE install)
|
||||
{
|
||||
install_context context = {0};
|
||||
context.install = install;
|
||||
broadcast_setting_change_core(&context);
|
||||
|
||||
log_message(&context, L"libusbp_install_inf: Begin.");
|
||||
|
||||
WCHAR message[1024];
|
||||
|
||||
// Get the name of inf file.
|
||||
WCHAR filename[1024];
|
||||
DWORD length = 1024;
|
||||
UINT result = MsiGetPropertyW(install, L"CustomActionData", filename, &length);
|
||||
if (result != ERROR_SUCCESS)
|
||||
{
|
||||
StringCbPrintfW(message, sizeof(message),
|
||||
L"libusbp_install_inf: Unable to get filename parameter. Error code %d.", result);
|
||||
error_message(&context, message);
|
||||
return 0; // Return success anyway.
|
||||
}
|
||||
|
||||
StringCbPrintfW(message, sizeof(message), L"libusbp_install_inf: filename=%s", filename);
|
||||
log_message(&context, message);
|
||||
|
||||
install_inf_core(&context, filename);
|
||||
|
||||
StringCbPrintfW(message, sizeof(message), L"libusbp_install_inf: End. result2=%d", result);
|
||||
log_message(&context, message);
|
||||
|
||||
// Always return 0 even if there was an error, because we don't want to roll
|
||||
// back the rest of the installation just because this part fails. The user
|
||||
// can either manually install the INF files or try again.
|
||||
return 0;
|
||||
}
|
||||
4
dep/libusbp/manual_tests/CMakeLists.txt
Normal file
4
dep/libusbp/manual_tests/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
add_subdirectory(test_async_in)
|
||||
add_subdirectory(test_long_read)
|
||||
add_subdirectory(test_long_write)
|
||||
add_subdirectory(test_transitions)
|
||||
9
dep/libusbp/manual_tests/test_async_in/CMakeLists.txt
Normal file
9
dep/libusbp/manual_tests/test_async_in/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use_cxx11()
|
||||
|
||||
add_executable(test_async_in test_async_in.cpp)
|
||||
|
||||
include_directories (
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
|
||||
target_link_libraries(test_async_in usbp)
|
||||
107
dep/libusbp/manual_tests/test_async_in/test_async_in.cpp
Normal file
107
dep/libusbp/manual_tests/test_async_in/test_async_in.cpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/* Tests that libusbp is capable of reliably reading data from an IN endpoint on
|
||||
* every frame using asynchronous transfers. It prints to the standard output
|
||||
* as it successfully receives data, and prints to the standard error if there
|
||||
* was a gap in the data (a USB frame where the device did not generate a new
|
||||
* packet for the host).
|
||||
*
|
||||
* You can also check the CPU usage while running this function to make
|
||||
* sure libusbp is not doing anything too inefficient.
|
||||
*/
|
||||
|
||||
#include <libusbp.hpp>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#ifdef _MSC_VER
|
||||
#define usleep(x) Sleep(((x) + 999) / 1000)
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
const uint16_t vendor_id = 0x1FFB;
|
||||
const uint16_t product_id = 0xDA01;
|
||||
const uint8_t interface_number = 0;
|
||||
const bool composite = true;
|
||||
const uint8_t endpoint_address = 0x82;
|
||||
const size_t packet_size = 5;
|
||||
const size_t transfer_size = packet_size;
|
||||
const size_t transfer_count = 250;
|
||||
|
||||
int main_with_exceptions()
|
||||
{
|
||||
libusbp::device device = libusbp::find_device_with_vid_pid(vendor_id, product_id);
|
||||
if (!device)
|
||||
{
|
||||
std::cerr << "Device not found." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
libusbp::generic_interface gi(device, interface_number, composite);
|
||||
libusbp::generic_handle handle(gi);
|
||||
libusbp::async_in_pipe pipe = handle.open_async_in_pipe(endpoint_address);
|
||||
pipe.allocate_transfers(transfer_count, transfer_size);
|
||||
|
||||
pipe.start_endless_transfers();
|
||||
|
||||
uint8_t last_f = 0;
|
||||
|
||||
uint32_t finish_count = 0;
|
||||
while(true)
|
||||
{
|
||||
uint8_t buffer[transfer_size];
|
||||
size_t transferred;
|
||||
libusbp::error transfer_error;
|
||||
while(pipe.handle_finished_transfer(buffer, &transferred, &transfer_error))
|
||||
{
|
||||
if (transfer_error)
|
||||
{
|
||||
fprintf(stderr, "Transfer error.\n");
|
||||
throw transfer_error;
|
||||
}
|
||||
|
||||
if (transferred != transfer_size)
|
||||
{
|
||||
fprintf(stderr, "Got %d bytes instead of %d.\n",
|
||||
(int)transferred, (int)transfer_size);
|
||||
}
|
||||
|
||||
uint8_t f = buffer[0];
|
||||
if (f != (uint8_t)(last_f + transfer_size/packet_size))
|
||||
{
|
||||
// If this happens, it indicates there was a USB frame where the
|
||||
// device did not generate a new packet for the host, which is
|
||||
// bad. However, you should expect to see a few of these at the
|
||||
// very beginning of the test because there will be some old
|
||||
// packets queued up in the device from earlier, and because
|
||||
// last_f always starts at 0.
|
||||
fprintf(stderr, "Frame number gap: %d to %d\n", last_f, f);
|
||||
}
|
||||
last_f = f;
|
||||
|
||||
if ((++finish_count % 4096) == 0)
|
||||
{
|
||||
printf("Another 4096 transfers done.\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
pipe.handle_events();
|
||||
usleep(20000);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
// Suppress unused parameter warnings.
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
try
|
||||
{
|
||||
return main_with_exceptions();
|
||||
}
|
||||
catch(const std::exception & error)
|
||||
{
|
||||
std::cerr << "Error: " << error.what() << std::endl;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
9
dep/libusbp/manual_tests/test_long_read/CMakeLists.txt
Normal file
9
dep/libusbp/manual_tests/test_long_read/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use_cxx11()
|
||||
|
||||
add_executable(test_long_read test_long_read.cpp)
|
||||
|
||||
include_directories (
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
|
||||
target_link_libraries(test_long_read usbp)
|
||||
90
dep/libusbp/manual_tests/test_long_read/test_long_read.cpp
Normal file
90
dep/libusbp/manual_tests/test_long_read/test_long_read.cpp
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/* Tests what happens if we do a synchronous read from an IN endpoint that
|
||||
* takes a long time to complete. This can be used to check that pressing
|
||||
* Ctrl+C is able to interrupt the read. */
|
||||
|
||||
#include <libusbp.hpp>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
|
||||
const uint16_t vendor_id = 0x1FFB;
|
||||
const uint16_t product_id = 0xDA01;
|
||||
const uint8_t interface_number = 0;
|
||||
const bool composite = true;
|
||||
const uint8_t endpoint_address = 0x82;
|
||||
const size_t packet_size = 5;
|
||||
const size_t transfer_size = packet_size * 10000;
|
||||
|
||||
void long_read(libusbp::generic_handle & handle)
|
||||
{
|
||||
uint8_t buffer[transfer_size];
|
||||
size_t transferred;
|
||||
|
||||
printf("Reading %d bytes...\n", (unsigned int)transfer_size);
|
||||
fflush(stdout);
|
||||
handle.read_pipe(endpoint_address, buffer, sizeof(buffer), &transferred);
|
||||
if (transferred == transfer_size)
|
||||
{
|
||||
printf("Transfer successful.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Transferred only %d bytes out of %d.\n",
|
||||
(unsigned int)transferred, (unsigned int)transfer_size);
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void long_control_read(libusbp::generic_handle & handle)
|
||||
{
|
||||
uint8_t buffer[5];
|
||||
size_t transferred;
|
||||
|
||||
printf("Performing a slow control read...\n");
|
||||
fflush(stdout);
|
||||
handle.control_transfer(0xC0, 0x91, 10000, 5, buffer, sizeof(buffer), &transferred);
|
||||
if (transferred == 5)
|
||||
{
|
||||
printf("Control read successful.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Transferred only %d bytes out of %d.\n",
|
||||
(unsigned int)transferred, (unsigned int)sizeof(buffer));
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
int main_with_exceptions()
|
||||
{
|
||||
libusbp::device device = libusbp::find_device_with_vid_pid(vendor_id, product_id);
|
||||
if (!device)
|
||||
{
|
||||
std::cerr << "Device not found." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
libusbp::generic_interface gi(device, interface_number, composite);
|
||||
libusbp::generic_handle handle(gi);
|
||||
|
||||
long_read(handle);
|
||||
long_control_read(handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
// Suppress unused parameter warnings.
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
try
|
||||
{
|
||||
return main_with_exceptions();
|
||||
}
|
||||
catch(const std::exception & error)
|
||||
{
|
||||
std::cerr << "Error: " << error.what() << std::endl;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
9
dep/libusbp/manual_tests/test_long_write/CMakeLists.txt
Normal file
9
dep/libusbp/manual_tests/test_long_write/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use_cxx11()
|
||||
|
||||
add_executable(test_long_write test_long_write.cpp)
|
||||
|
||||
include_directories (
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
|
||||
target_link_libraries(test_long_write usbp)
|
||||
70
dep/libusbp/manual_tests/test_long_write/test_long_write.cpp
Normal file
70
dep/libusbp/manual_tests/test_long_write/test_long_write.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* Tests what happens if we do a synchronous write that takes a long time to
|
||||
* complete. This can be used to check that pressing Ctrl+C is able to
|
||||
* interrupt the write. */
|
||||
|
||||
#include <libusbp.hpp>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
|
||||
const uint16_t vendor_id = 0x1FFB;
|
||||
const uint16_t product_id = 0xDA01;
|
||||
const uint8_t interface_number = 0;
|
||||
const bool composite = true;
|
||||
const uint8_t endpoint_address = 0x03;
|
||||
const size_t packet_size = 32;
|
||||
const size_t transfer_size = packet_size * 4;
|
||||
|
||||
void long_write(libusbp::generic_handle & handle)
|
||||
{
|
||||
// First packet causes a delay of 4 seconds (0x0FA0 ms)
|
||||
uint8_t buffer[transfer_size] = { 0xDE, 0xA0, 0x0F };
|
||||
size_t transferred;
|
||||
|
||||
printf("Writing...\n");
|
||||
fflush(stdout);
|
||||
handle.write_pipe(endpoint_address, buffer, sizeof(buffer), &transferred);
|
||||
if (transferred == transfer_size)
|
||||
{
|
||||
printf("Transfer successful.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Transferred only %d bytes out of %d.\n",
|
||||
(unsigned int)transferred, (unsigned int)transfer_size);
|
||||
}
|
||||
}
|
||||
|
||||
int main_with_exceptions()
|
||||
{
|
||||
libusbp::device device = libusbp::find_device_with_vid_pid(
|
||||
vendor_id, product_id);
|
||||
if (!device)
|
||||
{
|
||||
std::cerr << "Device not found." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
libusbp::generic_interface gi(device, interface_number, composite);
|
||||
libusbp::generic_handle handle(gi);
|
||||
|
||||
long_write(handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
// Suppress unused parameter warnings.
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
try
|
||||
{
|
||||
return main_with_exceptions();
|
||||
}
|
||||
catch(const std::exception & error)
|
||||
{
|
||||
std::cerr << "Error: " << error.what() << std::endl;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
9
dep/libusbp/manual_tests/test_transitions/CMakeLists.txt
Normal file
9
dep/libusbp/manual_tests/test_transitions/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use_cxx11()
|
||||
|
||||
add_executable(test_transitions test_transitions.cpp)
|
||||
|
||||
include_directories (
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
|
||||
target_link_libraries(test_transitions usbp)
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/* This program helps us test the transitions that a USB device goes through as
|
||||
* it gets connected or disconnected from a computer. It helps us identify
|
||||
* errors that might occur so we can assign code to them such as
|
||||
* LIBUSBP_ERROR_NOT_READY. */
|
||||
|
||||
#include <libusbp.hpp>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
#ifdef _MSC_VER
|
||||
#define usleep(x) Sleep(((x) + 999) / 1000)
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 6
|
||||
typedef std::chrono::monotonic_clock clock_type;
|
||||
#else
|
||||
typedef std::chrono::steady_clock clock_type;
|
||||
#endif
|
||||
|
||||
std::ostream & log()
|
||||
{
|
||||
return std::cout
|
||||
<< clock_type::now().time_since_epoch().count()
|
||||
<< ": ";
|
||||
}
|
||||
|
||||
void check_test_device_a(libusbp::device device)
|
||||
{
|
||||
static std::string current_status;
|
||||
std::string status;
|
||||
|
||||
if (device)
|
||||
{
|
||||
status = "Found " + device.get_serial_number() + ".";
|
||||
|
||||
// Try to connect to the generic interface.
|
||||
try
|
||||
{
|
||||
libusbp::generic_interface gi(device, 0, true);
|
||||
libusbp::generic_handle handle(gi);
|
||||
status += " Interface 0 works.";
|
||||
}
|
||||
catch(const libusbp::error & error)
|
||||
{
|
||||
status += " Interface 0 error: " + error.message();
|
||||
if (!error.has_code(LIBUSBP_ERROR_NOT_READY))
|
||||
{
|
||||
status += " Lacks code LIBUSBP_ERROR_NOT_READY!";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
status = "Not found.";
|
||||
}
|
||||
|
||||
if (current_status != status)
|
||||
{
|
||||
log() << "Test device A: " << status << std::endl;
|
||||
current_status = status;
|
||||
}
|
||||
}
|
||||
|
||||
int main_with_exceptions()
|
||||
{
|
||||
std::cout
|
||||
<< "Clock tick period: "
|
||||
<< clock_type::period::num
|
||||
<< "/"
|
||||
<< clock_type::period::den
|
||||
<< " seconds" << std::endl;
|
||||
|
||||
while(1)
|
||||
{
|
||||
check_test_device_a(libusbp::find_device_with_vid_pid(0x1FFB, 0xDA01));
|
||||
usleep(10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
// Suppress unused parameter warnings.
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
try
|
||||
{
|
||||
return main_with_exceptions();
|
||||
}
|
||||
catch(const std::exception & error)
|
||||
{
|
||||
std::cerr << "Error: " << error.what() << std::endl;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
116
dep/libusbp/src/CMakeLists.txt
Normal file
116
dep/libusbp/src/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
use_c99()
|
||||
|
||||
# Settings for GCC
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
||||
# By default, symbols are not visible outside of the library.
|
||||
set (CMAKE_C_FLAGS "-fvisibility=hidden ${CMAKE_C_FLAGS}")
|
||||
|
||||
# Avoid tons of errors from strsafe.h.
|
||||
set (CMAKE_C_FLAGS "-fgnu89-inline ${CMAKE_C_FLAGS}")
|
||||
endif ()
|
||||
|
||||
# Define cross-platform source files.
|
||||
set (sources
|
||||
async_in_pipe.c
|
||||
error.c
|
||||
error_hresult.c
|
||||
find_device.c
|
||||
list.c
|
||||
pipe_id.c
|
||||
string.c)
|
||||
|
||||
# Define operating system-specific source files.
|
||||
if (WIN32)
|
||||
set (sources ${sources}
|
||||
windows/error_windows.c
|
||||
windows/device_windows.c
|
||||
windows/interface_windows.c
|
||||
windows/device_instance_id_windows.c
|
||||
windows/generic_interface_windows.c
|
||||
windows/list_windows.c
|
||||
windows/generic_handle_windows.c
|
||||
windows/async_in_transfer_windows.c
|
||||
windows/serial_port_windows.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/info.rc)
|
||||
elseif (LINUX)
|
||||
set (sources ${sources}
|
||||
linux/list_linux.c
|
||||
linux/device_linux.c
|
||||
linux/generic_interface_linux.c
|
||||
linux/generic_handle_linux.c
|
||||
linux/error_linux.c
|
||||
linux/udev_linux.c
|
||||
linux/usbfd_linux.c
|
||||
linux/async_in_transfer_linux.c
|
||||
linux/serial_port_linux.c)
|
||||
elseif (APPLE)
|
||||
set (sources ${sources}
|
||||
mac/list_mac.c
|
||||
mac/device_mac.c
|
||||
mac/error_mac.c
|
||||
mac/generic_interface_mac.c
|
||||
mac/generic_handle_mac.c
|
||||
mac/async_in_transfer_mac.c
|
||||
mac/serial_port_mac.c
|
||||
mac/iokit_mac.c)
|
||||
endif ()
|
||||
|
||||
add_library (usbp ${sources})
|
||||
|
||||
include_directories (
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries (usbp setupapi winusb uuid ole32)
|
||||
if (NOT BUILD_SHARED_LIBS)
|
||||
set (PC_MORE_LIBS "-lsetupapi -lwinusb -luuid -lole32")
|
||||
endif ()
|
||||
elseif (LINUX)
|
||||
pkg_check_modules(LIBUDEV REQUIRED libudev)
|
||||
string (REPLACE ";" " " LIBUDEV_CFLAGS "${LIBUDEV_CFLAGS}")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBUDEV_CFLAGS}")
|
||||
target_link_libraries (usbp udev)
|
||||
if (NOT BUILD_SHARED_LIBS)
|
||||
set (PC_REQUIRES "libudev")
|
||||
endif ()
|
||||
elseif (APPLE)
|
||||
set (link_flags "-framework IOKit -framework CoreFoundation ${link_flags}")
|
||||
if (NOT BUILD_SHARED_LIBS)
|
||||
set (PC_MORE_LIBS "${link_flags}")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
set_target_properties(usbp PROPERTIES
|
||||
OUTPUT_NAME usbp-${LIBUSBP_VERSION_MAJOR}
|
||||
SOVERSION ${LIBUSBP_VERSION}
|
||||
VERSION ${LIBUSBP_VERSION}
|
||||
DEFINE_SYMBOL LIBUSBP_EXPORTS
|
||||
LINK_FLAGS "${link_flags}"
|
||||
)
|
||||
|
||||
configure_file (
|
||||
"libusbp_config.h.in"
|
||||
"libusbp_config.h"
|
||||
)
|
||||
|
||||
configure_file (
|
||||
"info.rc.in"
|
||||
"info.rc"
|
||||
)
|
||||
|
||||
configure_file (
|
||||
"libusbp.pc.in"
|
||||
"libusbp-${LIBUSBP_VERSION_MAJOR}.pc"
|
||||
@ONLY
|
||||
)
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libusbp-${LIBUSBP_VERSION_MAJOR}.pc"
|
||||
DESTINATION lib/pkgconfig)
|
||||
|
||||
install(TARGETS usbp
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib)
|
||||
343
dep/libusbp/src/async_in_pipe.c
Normal file
343
dep/libusbp/src/async_in_pipe.c
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
#include <libusbp_internal.h>
|
||||
|
||||
struct libusbp_async_in_pipe
|
||||
{
|
||||
libusbp_generic_handle * handle;
|
||||
uint8_t pipe_id;
|
||||
async_in_transfer ** transfer_array;
|
||||
size_t transfer_size;
|
||||
size_t transfer_count;
|
||||
|
||||
bool endless_transfers_enabled;
|
||||
|
||||
// The number of transfers that are pending, meaning that they were
|
||||
// submitted (and possibly completed by the kernel) but not finished (handed
|
||||
// to the user of the pipe) yet. This variable allows us to distinguish
|
||||
// between the state where no transfers are pending and the state where all
|
||||
// of them are pending. Note that this definition of pending is different
|
||||
// than the definition of pending in the async_in_transfer struct.
|
||||
size_t pending_count;
|
||||
|
||||
// The index of the transfer that should finish next. That transfer will be
|
||||
// pending (and thus able to be checked) if pending_count > 0.
|
||||
size_t next_finish;
|
||||
|
||||
// The index of the transfer that will be submitted next when we need to
|
||||
// submit more transfers. That transfer can be submitted if pending_count <
|
||||
// transfer_count.
|
||||
size_t next_submit;
|
||||
};
|
||||
|
||||
static inline size_t increment_and_wrap_size(size_t n, size_t bound)
|
||||
{
|
||||
n++;
|
||||
return n >= bound ? 0 : n;
|
||||
}
|
||||
|
||||
static void async_in_transfer_array_free(async_in_transfer ** array, size_t transfer_count)
|
||||
{
|
||||
if (array == NULL) { return; }
|
||||
|
||||
for (size_t i = 0; i < transfer_count; i++)
|
||||
{
|
||||
async_in_transfer_free(array[i]);
|
||||
}
|
||||
free(array);
|
||||
}
|
||||
|
||||
void libusbp_async_in_pipe_close(libusbp_async_in_pipe * pipe)
|
||||
{
|
||||
if (pipe != NULL)
|
||||
{
|
||||
async_in_transfer_array_free(pipe->transfer_array, pipe->transfer_count);
|
||||
free(pipe);
|
||||
}
|
||||
}
|
||||
|
||||
libusbp_error * async_in_pipe_create(libusbp_generic_handle * handle,
|
||||
uint8_t pipe_id, libusbp_async_in_pipe ** pipe)
|
||||
{
|
||||
// Check the pipe output pointer.
|
||||
if (pipe == NULL)
|
||||
{
|
||||
return error_create("Pipe output pointer is null.");
|
||||
}
|
||||
|
||||
*pipe = NULL;
|
||||
|
||||
if (handle == NULL)
|
||||
{
|
||||
return error_create("Generic handle is null.");
|
||||
}
|
||||
|
||||
libusbp_error * error = NULL;
|
||||
|
||||
// Check the pipe_id parameter.
|
||||
if (error == NULL)
|
||||
{
|
||||
error = check_pipe_id(pipe_id);
|
||||
}
|
||||
if (error == NULL && !(pipe_id & 0x80))
|
||||
{
|
||||
error = error_create("Asynchronous pipes for OUT endpoints are not supported.");
|
||||
}
|
||||
|
||||
// Perform OS-specific setup.
|
||||
if (error == NULL)
|
||||
{
|
||||
error = async_in_pipe_setup(handle, pipe_id);
|
||||
}
|
||||
|
||||
libusbp_async_in_pipe * new_pipe = NULL;
|
||||
if (error == NULL)
|
||||
{
|
||||
new_pipe = calloc(1, sizeof(libusbp_async_in_pipe));
|
||||
if (new_pipe == NULL)
|
||||
{
|
||||
error = &error_no_memory;
|
||||
}
|
||||
}
|
||||
|
||||
if (error == NULL)
|
||||
{
|
||||
new_pipe->handle = handle;
|
||||
new_pipe->pipe_id = pipe_id;
|
||||
*pipe = new_pipe;
|
||||
new_pipe = NULL;
|
||||
}
|
||||
|
||||
free(new_pipe);
|
||||
return error;
|
||||
}
|
||||
|
||||
libusbp_error * libusbp_async_in_pipe_allocate_transfers(
|
||||
libusbp_async_in_pipe * pipe,
|
||||
size_t transfer_count,
|
||||
size_t transfer_size)
|
||||
{
|
||||
if (pipe == NULL)
|
||||
{
|
||||
return error_create("Pipe argument is null.");
|
||||
}
|
||||
|
||||
if (pipe->transfer_array != NULL)
|
||||
{
|
||||
return error_create("Transfers were already allocated for this pipe.");
|
||||
}
|
||||
|
||||
if (transfer_count == 0)
|
||||
{
|
||||
return error_create("Transfer count cannot be zero.");
|
||||
}
|
||||
|
||||
if (transfer_size == 0)
|
||||
{
|
||||
return error_create("Transfer size cannot be zero.");
|
||||
}
|
||||
|
||||
libusbp_error * error = NULL;
|
||||
|
||||
async_in_transfer ** new_transfer_array = NULL;
|
||||
if (error == NULL)
|
||||
{
|
||||
new_transfer_array = calloc(transfer_count, sizeof(async_in_transfer *));
|
||||
if (new_transfer_array == NULL)
|
||||
{
|
||||
error = &error_no_memory;
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i = 0; error == NULL && i < transfer_count; i++)
|
||||
{
|
||||
error = async_in_transfer_create(pipe->handle, pipe->pipe_id,
|
||||
transfer_size, &new_transfer_array[i]);
|
||||
}
|
||||
|
||||
// Put the new array and the information about it into the pipe.
|
||||
if (error == NULL)
|
||||
{
|
||||
pipe->transfer_array = new_transfer_array;
|
||||
pipe->transfer_count = transfer_count;
|
||||
pipe->transfer_size = transfer_size;
|
||||
new_transfer_array = NULL;
|
||||
}
|
||||
|
||||
async_in_transfer_array_free(new_transfer_array, transfer_count);
|
||||
|
||||
if (error != NULL)
|
||||
{
|
||||
error = error_add(error, "Failed to allocate transfers for asynchronous IN pipe.");
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static void async_in_pipe_submit_next_transfer(libusbp_async_in_pipe * pipe)
|
||||
{
|
||||
assert(pipe != NULL);
|
||||
assert(pipe->pending_count < pipe->transfer_count);
|
||||
|
||||
// Submit the next transfer.
|
||||
async_in_transfer_submit(pipe->transfer_array[pipe->next_submit]);
|
||||
|
||||
// Update the counts and indices.
|
||||
pipe->pending_count++;
|
||||
pipe->next_submit = increment_and_wrap_size(pipe->next_submit, pipe->transfer_count);
|
||||
}
|
||||
|
||||
libusbp_error * libusbp_async_in_pipe_start_endless_transfers(
|
||||
libusbp_async_in_pipe * pipe)
|
||||
{
|
||||
if (pipe == NULL)
|
||||
{
|
||||
return error_create("Pipe argument is null.");
|
||||
}
|
||||
|
||||
if (pipe->transfer_array == NULL)
|
||||
{
|
||||
return error_create("Pipe transfers have not been allocated yet.");
|
||||
}
|
||||
|
||||
pipe->endless_transfers_enabled = true;
|
||||
|
||||
while(pipe->pending_count < pipe->transfer_count)
|
||||
{
|
||||
async_in_pipe_submit_next_transfer(pipe);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
libusbp_error * libusbp_async_in_pipe_handle_events(libusbp_async_in_pipe * pipe)
|
||||
{
|
||||
if (pipe == NULL)
|
||||
{
|
||||
return error_create("Pipe argument is null.");
|
||||
}
|
||||
|
||||
return generic_handle_events(pipe->handle);
|
||||
}
|
||||
|
||||
libusbp_error * libusbp_async_in_pipe_has_pending_transfers(
|
||||
libusbp_async_in_pipe * pipe,
|
||||
bool * result)
|
||||
{
|
||||
libusbp_error * error = NULL;
|
||||
|
||||
if (error == NULL && result == NULL)
|
||||
{
|
||||
error = error_create("Boolean output pointer is null.");
|
||||
}
|
||||
|
||||
if (error == NULL)
|
||||
{
|
||||
*result = false;
|
||||
}
|
||||
|
||||
if (error == NULL && pipe == NULL)
|
||||
{
|
||||
error = error_create("Pipe argument is null.");
|
||||
}
|
||||
|
||||
if (error == NULL)
|
||||
{
|
||||
*result = pipe->pending_count ? 1 : 0;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
libusbp_error * libusbp_async_in_pipe_handle_finished_transfer(
|
||||
libusbp_async_in_pipe * pipe,
|
||||
bool * finished,
|
||||
void * buffer,
|
||||
size_t * transferred,
|
||||
libusbp_error ** transfer_error)
|
||||
{
|
||||
if (finished != NULL)
|
||||
{
|
||||
*finished = false;
|
||||
}
|
||||
|
||||
if (transferred != NULL)
|
||||
{
|
||||
*transferred = 0;
|
||||
}
|
||||
|
||||
if (transfer_error != NULL)
|
||||
{
|
||||
*transfer_error = NULL;
|
||||
}
|
||||
|
||||
if (pipe == NULL)
|
||||
{
|
||||
return error_create("Pipe argument is null.");
|
||||
}
|
||||
|
||||
if (pipe->pending_count == 0)
|
||||
{
|
||||
// There are no pending transfers that we could check for completion.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
async_in_transfer * transfer = pipe->transfer_array[pipe->next_finish];
|
||||
|
||||
if (async_in_transfer_pending(transfer))
|
||||
{
|
||||
// The next transfer we expect to finish is still pending;
|
||||
// the kernel has not told us that it is done.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
libusbp_error * error = async_in_transfer_get_results(transfer, buffer,
|
||||
transferred, transfer_error);
|
||||
|
||||
if (error == NULL)
|
||||
{
|
||||
if (finished != NULL)
|
||||
{
|
||||
*finished = true;
|
||||
}
|
||||
|
||||
pipe->pending_count--;
|
||||
pipe->next_finish = increment_and_wrap_size(pipe->next_finish, pipe->transfer_count);
|
||||
}
|
||||
|
||||
if (error == NULL && pipe->endless_transfers_enabled)
|
||||
{
|
||||
async_in_pipe_submit_next_transfer(pipe);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
libusbp_error * libusbp_async_in_pipe_cancel_transfers(libusbp_async_in_pipe * pipe)
|
||||
{
|
||||
if (pipe == NULL)
|
||||
{
|
||||
return error_create("Pipe argument is null.");
|
||||
}
|
||||
|
||||
pipe->endless_transfers_enabled = false;
|
||||
|
||||
libusbp_error * error = NULL;
|
||||
|
||||
#ifdef __linux__
|
||||
// In Linux, transfers need to be cancelled individually.
|
||||
for (size_t i = 0; error == NULL && i < pipe->transfer_count; i++)
|
||||
{
|
||||
error = async_in_transfer_cancel(pipe->transfer_array[i]);
|
||||
|
||||
// This doesn't help the performance issue in this function:
|
||||
//if (error == NULL) { error = generic_handle_events(pipe->handle); }
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// On other platforms, any of the transfers has all the information needed
|
||||
// to cancel the others.
|
||||
error = async_in_transfer_cancel(pipe->transfer_array[0]);
|
||||
|
||||
#endif
|
||||
|
||||
return error;
|
||||
}
|
||||
223
dep/libusbp/src/error.c
Normal file
223
dep/libusbp/src/error.c
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
#include <libusbp_internal.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#if (defined(__GNUC__) && !defined(__USE_MINGW_ANSI_STDIO)) || (defined(_MSC_VER) && _MSC_VER < 1900)
|
||||
#error This code depends on vsnprintf returning a number of characters.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct libusbp_error
|
||||
{
|
||||
bool do_not_free;
|
||||
char * message;
|
||||
size_t code_count;
|
||||
uint32_t * code_array;
|
||||
};
|
||||
|
||||
static uint32_t mem_error_code_array[1] = { LIBUSBP_ERROR_MEMORY };
|
||||
|
||||
static char error_no_memory_msg[] = "Failed to allocate memory.";
|
||||
libusbp_error error_no_memory =
|
||||
{
|
||||
.do_not_free = true,
|
||||
.message = error_no_memory_msg,
|
||||
.code_count = 1,
|
||||
.code_array = mem_error_code_array,
|
||||
};
|
||||
|
||||
static char error_masked_by_no_memory_msg[] = "Failed to allocate memory for reporting an error.";
|
||||
static libusbp_error error_masked_by_no_memory =
|
||||
{
|
||||
.do_not_free = true,
|
||||
.message = error_masked_by_no_memory_msg,
|
||||
.code_count = 1,
|
||||
.code_array = mem_error_code_array,
|
||||
};
|
||||
|
||||
static libusbp_error error_blank =
|
||||
{
|
||||
.do_not_free = true,
|
||||
.message = NULL,
|
||||
.code_count = 0,
|
||||
.code_array = NULL,
|
||||
};
|
||||
|
||||
void libusbp_error_free(libusbp_error * error)
|
||||
{
|
||||
if (error != NULL && !error->do_not_free)
|
||||
{
|
||||
free(error->message);
|
||||
free(error->code_array);
|
||||
free(error);
|
||||
}
|
||||
}
|
||||
|
||||
// Copies the error. If the input is not NULL, the output will always
|
||||
// be not NULL, but it might be a immutable error (do_not_free=1).
|
||||
libusbp_error * libusbp_error_copy(const libusbp_error * src_error)
|
||||
{
|
||||
if (src_error == NULL) { return NULL; }
|
||||
|
||||
const char * src_message = src_error->message;
|
||||
if (src_message == NULL) { src_message = ""; }
|
||||
size_t message_length = strlen(src_message);
|
||||
|
||||
size_t code_count = src_error->code_count;
|
||||
if (src_error->code_array == NULL) { code_count = 0; }
|
||||
|
||||
// Allocate memory.
|
||||
libusbp_error * new_error = malloc(sizeof(libusbp_error));
|
||||
char * new_message = malloc(message_length + 1);
|
||||
uint32_t * new_code_array = malloc(code_count * sizeof(uint32_t));
|
||||
if (new_error == NULL || new_message == NULL ||
|
||||
(code_count != 0 && new_code_array == NULL))
|
||||
{
|
||||
free(new_error);
|
||||
free(new_message);
|
||||
free(new_code_array);
|
||||
return &error_masked_by_no_memory;
|
||||
}
|
||||
|
||||
if (code_count != 0)
|
||||
{
|
||||
memcpy(new_code_array, src_error->code_array, code_count * sizeof(uint32_t));
|
||||
}
|
||||
strncpy(new_message, src_message, message_length + 1);
|
||||
new_error->do_not_free = false;
|
||||
new_error->message = new_message;
|
||||
new_error->code_count = code_count;
|
||||
new_error->code_array = new_code_array;
|
||||
return new_error;
|
||||
}
|
||||
|
||||
// Tries to make the error mutable. Always returns a non-NULL error,
|
||||
// but it might return an immutable error (do_not_free == 1) if memory
|
||||
// cannot be allocated.
|
||||
static libusbp_error * error_make_mutable(libusbp_error * error)
|
||||
{
|
||||
if (error == NULL) { error = &error_blank; }
|
||||
if (error->do_not_free)
|
||||
{
|
||||
error = libusbp_error_copy(error);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Tries to add a message to the error. After calling this, you
|
||||
// should not use the error you passed as an input, because it might
|
||||
// have been freed.
|
||||
libusbp_error * error_add_v(libusbp_error * error, const char * format, va_list ap)
|
||||
{
|
||||
if (format == NULL) { return error; }
|
||||
|
||||
error = error_make_mutable(error);
|
||||
if (error == NULL || error->do_not_free) { return error; }
|
||||
|
||||
if (error->message == NULL) { error->message = ""; }
|
||||
|
||||
// Determine all the string lengths.
|
||||
size_t outer_message_length = 0;
|
||||
{
|
||||
char x[1];
|
||||
va_list ap2;
|
||||
va_copy(ap2, ap);
|
||||
int result = vsnprintf(x, 0, format, ap2);
|
||||
if (result > 0)
|
||||
{
|
||||
outer_message_length = (size_t) result;
|
||||
}
|
||||
va_end(ap2);
|
||||
}
|
||||
size_t inner_message_length = strlen(error->message);
|
||||
size_t separator_length = (inner_message_length && outer_message_length) ? 2 : 0;
|
||||
size_t message_length = outer_message_length + separator_length + inner_message_length;
|
||||
|
||||
char * message = malloc(message_length + 1);
|
||||
if (message == NULL)
|
||||
{
|
||||
libusbp_error_free(error);
|
||||
return &error_masked_by_no_memory;
|
||||
}
|
||||
|
||||
// Assemble the message.
|
||||
vsnprintf(message, outer_message_length + 1, format, ap);
|
||||
if (separator_length)
|
||||
{
|
||||
strncpy(message + outer_message_length, " ", separator_length + 1);
|
||||
}
|
||||
strncpy(message + outer_message_length + separator_length,
|
||||
error->message, inner_message_length + 1);
|
||||
message[message_length] = 0;
|
||||
|
||||
free(error->message);
|
||||
error->message = message;
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
// Tries to add the specified code to the error.
|
||||
// This is just like error_add_v in terms of pointer ownership.
|
||||
libusbp_error * error_add_code(libusbp_error * error, uint32_t code)
|
||||
{
|
||||
error = error_make_mutable(error);
|
||||
if (error == NULL || error->do_not_free) { return error; }
|
||||
if (error->code_count >= SIZE_MAX / sizeof(uint32_t)) { return error; }
|
||||
|
||||
size_t size = (error->code_count + 1) * sizeof(uint32_t);
|
||||
uint32_t * new_array = realloc(error->code_array, size);
|
||||
if (new_array == NULL)
|
||||
{
|
||||
libusbp_error_free(error);
|
||||
return &error_masked_by_no_memory;
|
||||
}
|
||||
error->code_array = new_array;
|
||||
error->code_array[error->code_count++] = code;
|
||||
return error;
|
||||
}
|
||||
|
||||
// Variadic version of error_add_v.
|
||||
libusbp_error * error_add(libusbp_error * error, const char * format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
error = error_add_v(error, format, ap);
|
||||
va_end(ap);
|
||||
return error;
|
||||
}
|
||||
|
||||
// Creates a new error.
|
||||
libusbp_error * error_create(const char * format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
libusbp_error * error = error_add_v(NULL, format, ap);
|
||||
va_end(ap);
|
||||
return error;
|
||||
}
|
||||
|
||||
bool libusbp_error_has_code(const libusbp_error * error, uint32_t code)
|
||||
{
|
||||
if (error == NULL) { return false; }
|
||||
|
||||
for (size_t i = 0; i < error->code_count; i++)
|
||||
{
|
||||
if (error->code_array[i] == code)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char * libusbp_error_get_message(const libusbp_error * error)
|
||||
{
|
||||
if (error == NULL)
|
||||
{
|
||||
return "No error.";
|
||||
}
|
||||
if (error->message == NULL)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return error->message;
|
||||
}
|
||||
21
dep/libusbp/src/error_hresult.c
Normal file
21
dep/libusbp/src/error_hresult.c
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#include <libusbp_internal.h>
|
||||
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
|
||||
libusbp_error * error_create_hr(HRESULT hr, const char * format, ...)
|
||||
{
|
||||
// HRESULT should be an int32_t on the systems we care about (Mac OS X,
|
||||
// Win32, Win64), but let's assert it here in case that ever changes.
|
||||
assert(sizeof(HRESULT) == 4);
|
||||
assert((HRESULT)-1 < (HRESULT)0);
|
||||
|
||||
libusbp_error * error = error_create("HRESULT error code 0x%x.", (int32_t)hr);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
error = error_add_v(error, format, ap);
|
||||
va_end(ap);
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif
|
||||
70
dep/libusbp/src/find_device.c
Normal file
70
dep/libusbp/src/find_device.c
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#include <libusbp_internal.h>
|
||||
|
||||
static libusbp_error * check_device_vid_pid(const libusbp_device * device,
|
||||
uint16_t vendor_id, uint16_t product_id, bool * matches)
|
||||
{
|
||||
assert(matches != NULL);
|
||||
|
||||
*matches = false;
|
||||
|
||||
libusbp_error * error;
|
||||
|
||||
uint16_t device_vid;
|
||||
error = libusbp_device_get_vendor_id(device, &device_vid);
|
||||
if (error != NULL) { return error; }
|
||||
if (device_vid != vendor_id) { return NULL; }
|
||||
|
||||
uint16_t device_pid;
|
||||
error = libusbp_device_get_product_id(device, &device_pid);
|
||||
if (error != NULL) { return error; }
|
||||
if (device_pid != product_id) { return NULL; }
|
||||
|
||||
*matches = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
libusbp_error * libusbp_find_device_with_vid_pid(
|
||||
uint16_t vendor_id, uint16_t product_id, libusbp_device ** device)
|
||||
{
|
||||
if (device == NULL)
|
||||
{
|
||||
return error_create("Device output pointer is null.");
|
||||
}
|
||||
|
||||
*device = NULL;
|
||||
|
||||
libusbp_error * error = NULL;
|
||||
|
||||
libusbp_device ** new_list = NULL;
|
||||
size_t size = 0;
|
||||
error = libusbp_list_connected_devices(&new_list, &size);
|
||||
|
||||
assert(error != NULL || new_list != NULL);
|
||||
|
||||
for(size_t i = 0; error == NULL && i < size; i++)
|
||||
{
|
||||
// Each iteration of this loop checks one candidate device
|
||||
// and either passes it to the caller or frees it.
|
||||
|
||||
libusbp_device * candidate = new_list[i];
|
||||
|
||||
if (*device == NULL)
|
||||
{
|
||||
bool matches;
|
||||
error = check_device_vid_pid(candidate, vendor_id, product_id, &matches);
|
||||
if (error != NULL) { break; }
|
||||
|
||||
if (matches)
|
||||
{
|
||||
// Return the device to the caller.
|
||||
*device = candidate;
|
||||
candidate = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
libusbp_device_free(candidate);
|
||||
}
|
||||
|
||||
libusbp_list_free(new_list);
|
||||
return error;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue