mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Compare commits
2470 Commits
FluxEngine
...
imgui2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
866f35d4ed | ||
|
|
8fdcae5d1c | ||
|
|
121cdefd09 | ||
|
|
aa49e094f1 | ||
|
|
2b52131250 | ||
|
|
8474e1241c | ||
|
|
dcae381973 | ||
|
|
2142bc7cce | ||
|
|
ae3f82264a | ||
|
|
710e83c098 | ||
|
|
4f46fff3be | ||
|
|
58ea21a9a2 | ||
|
|
0fd1aa82a6 | ||
|
|
5b7f9d84f9 | ||
|
|
4b7e8e74a7 | ||
|
|
5375c72d02 | ||
|
|
5c257be164 | ||
|
|
7fa17322dc | ||
|
|
ed3640d945 | ||
|
|
87ce3ad61d | ||
|
|
6d75feb0ce | ||
|
|
168b8b6f6c | ||
|
|
3d063e932a | ||
|
|
157ec569b2 | ||
|
|
f63c8dadf1 | ||
|
|
d17f6116f0 | ||
|
|
2d6cb22e3a | ||
|
|
2de8b52e56 | ||
|
|
171576e538 | ||
|
|
2db9f65e8b | ||
|
|
2572b64bd1 | ||
|
|
533aaf85f2 | ||
|
|
f67ddc1f77 | ||
|
|
b1d64f3683 | ||
|
|
7e8840e03f | ||
|
|
b003297b22 | ||
|
|
7341cec2c4 | ||
|
|
a98b7f72fd | ||
|
|
2e97579394 | ||
|
|
f960c7efd0 | ||
|
|
c2e7f32cba | ||
|
|
137528fc53 | ||
|
|
cbf4cc35fb | ||
|
|
cd7b3de1b3 | ||
|
|
fddc2270e5 | ||
|
|
2a96d9bd78 | ||
|
|
fd554f0808 | ||
|
|
6776c51b23 | ||
|
|
ef58295304 | ||
|
|
2e2c3e3e34 | ||
|
|
e87bb44a2d | ||
|
|
0ba0a9cce5 | ||
|
|
97bb563ba0 | ||
|
|
8f047f842e | ||
|
|
9d596ef530 | ||
|
|
580ffa8cf7 | ||
|
|
341e0a320d | ||
|
|
cff0a9703c | ||
|
|
38618532c4 | ||
|
|
6026dcd86d | ||
|
|
3949971546 | ||
|
|
6146f442fb | ||
|
|
7090c1bfdf | ||
|
|
563babc969 | ||
|
|
b649c2b9af | ||
|
|
f7f887789c | ||
|
|
a8fcdcc528 | ||
|
|
a988578cc7 | ||
|
|
ee585b24f0 | ||
|
|
3d6e980990 | ||
|
|
f5d19416a9 | ||
|
|
4187fa5a09 | ||
|
|
eb7613c03f | ||
|
|
7910429037 | ||
|
|
cd1cc736a7 | ||
|
|
e6d6805f25 | ||
|
|
9733879360 | ||
|
|
725712f796 | ||
|
|
2122cea5c4 | ||
|
|
5466e716a9 | ||
|
|
0dc0e3d9a1 | ||
|
|
4bb12b2caa | ||
|
|
0d9c5f5150 | ||
|
|
4030031a2c | ||
|
|
3143c87f1c | ||
|
|
f16f02c4c7 | ||
|
|
3e13b2461d | ||
|
|
5fd0d1589e | ||
|
|
23e6d234d0 | ||
|
|
cf2a97f8aa | ||
|
|
5a815e0cd6 | ||
|
|
06a3af2a1d | ||
|
|
0558d95fa3 | ||
|
|
81f9246ab8 | ||
|
|
6979567429 | ||
|
|
348de4165d | ||
|
|
0755d420dd | ||
|
|
dead21bce5 | ||
|
|
4cf451ce60 | ||
|
|
72298ac805 | ||
|
|
3d1ad81652 | ||
|
|
88c79169b6 | ||
|
|
d9747b9021 | ||
|
|
256976a5a1 | ||
|
|
0ba4b82e10 | ||
|
|
ffd9e28b42 | ||
|
|
9c919c786d | ||
|
|
47a9a56959 | ||
|
|
6e03bc604a | ||
|
|
feea6a027a | ||
|
|
08fa06b7fe | ||
|
|
8a976edef9 | ||
|
|
c71d8d6c74 | ||
|
|
e809af7426 | ||
|
|
ab05db9040 | ||
|
|
04f916741e | ||
|
|
f6224f3718 | ||
|
|
10185bb7a1 | ||
|
|
d565960c70 | ||
|
|
c21073294f | ||
|
|
3cd95de434 | ||
|
|
6552dba9aa | ||
|
|
c8ebe55aa9 | ||
|
|
1eefa2d604 | ||
|
|
a359394eea | ||
|
|
9f13026bec | ||
|
|
8fcc99b2a1 | ||
|
|
125a0536ff | ||
|
|
4115947d80 | ||
|
|
2f1dcd7c9a | ||
|
|
5e00ffca13 | ||
|
|
ac27095493 | ||
|
|
e27ca5cd4c | ||
|
|
cc72ac6327 | ||
|
|
5443aa6501 | ||
|
|
902bf32169 | ||
|
|
d200633747 | ||
|
|
a48b749c2e | ||
|
|
46fab84b95 | ||
|
|
b0290f858c | ||
|
|
fe09c12cd6 | ||
|
|
b5ae5a1cea | ||
|
|
113cb85512 | ||
|
|
da276bcb3b | ||
|
|
9a78d0f38c | ||
|
|
ec2e1666e7 | ||
|
|
478df40d4b | ||
|
|
a8b9d79cb1 | ||
|
|
23865d1a10 | ||
|
|
458b3f24fe | ||
|
|
86fa23e6fa | ||
|
|
dd9d5aaed5 | ||
|
|
b22df17bb5 | ||
|
|
b81e609e66 | ||
|
|
d41e57cba6 | ||
|
|
da7e83e257 | ||
|
|
83be12fcf1 | ||
|
|
a999e2d6c9 | ||
|
|
6d6251e757 | ||
|
|
be8b26ef94 | ||
|
|
c6b8bce5d6 | ||
|
|
d8b3452c07 | ||
|
|
eddbd43cd9 | ||
|
|
168189180d | ||
|
|
9e092bab6a | ||
|
|
2c35126b3a | ||
|
|
7dc0e4ca31 | ||
|
|
96257f89d5 | ||
|
|
09919343b4 | ||
|
|
b070c1068c | ||
|
|
5628a576db | ||
|
|
073c78e25f | ||
|
|
6a826d6eb5 | ||
|
|
11a6143d4c | ||
|
|
6127c9a46d | ||
|
|
98f7febef7 | ||
|
|
85afadacf0 | ||
|
|
01cd812162 | ||
|
|
39329acc77 | ||
|
|
bdc96038ef | ||
|
|
93760d989a | ||
|
|
b306c7063b | ||
|
|
e3d7fa69d8 | ||
|
|
f6c0e5405a | ||
|
|
fc12a2662c | ||
|
|
ab5b16488c | ||
|
|
4d5900268b | ||
|
|
b5c5a4335d | ||
|
|
e76235541a | ||
|
|
e75e1a6e27 | ||
|
|
aa220ecbcb | ||
|
|
edc8d74418 | ||
|
|
2831aa09ae | ||
|
|
e1b4b0d3a3 | ||
|
|
e5df6ca33b | ||
|
|
68c3cbb020 | ||
|
|
ca3c37d20a | ||
|
|
6fcd9233ea | ||
|
|
3761c4b1e2 | ||
|
|
c89c53b1c7 | ||
|
|
be0f63a133 | ||
|
|
a8216995ad | ||
|
|
995359ef45 | ||
|
|
bc84e3c8a0 | ||
|
|
af12a25a9d | ||
|
|
f6b2821221 | ||
|
|
458601a139 | ||
|
|
a89130edbd | ||
|
|
c95cd8a4da | ||
|
|
4d313a8495 | ||
|
|
263eef3442 | ||
|
|
2e97996211 | ||
|
|
7035b9c3c2 | ||
|
|
5628d2ca06 | ||
|
|
61cf7fbccf | ||
|
|
ce347c6326 | ||
|
|
94119b19fe | ||
|
|
9c7be1268f | ||
|
|
a9d59f67ba | ||
|
|
8d2a72228f | ||
|
|
60b95dd3f3 | ||
|
|
b1094f40dc | ||
|
|
e40ea80e34 | ||
|
|
9e1222d38a | ||
|
|
4446785729 | ||
|
|
790f015d72 | ||
|
|
ccb0dcea3c | ||
|
|
15a0632af0 | ||
|
|
3c0da28947 | ||
|
|
95227f32ca | ||
|
|
edf75b5cda | ||
|
|
af87c48451 | ||
|
|
7cde8e3aa6 | ||
|
|
34fe6f0a5f | ||
|
|
76c9674f3f | ||
|
|
addbabd123 | ||
|
|
46b90d9c36 | ||
|
|
7ee67082aa | ||
|
|
e8042ed5f3 | ||
|
|
8828874c25 | ||
|
|
1bdb093319 | ||
|
|
a1e2191ad5 | ||
|
|
e61fcf1d9b | ||
|
|
610ef0dc4b | ||
|
|
273d38f237 | ||
|
|
8194a08382 | ||
|
|
6170b704b1 | ||
|
|
b05f5e7caa | ||
|
|
4b38fc6044 | ||
|
|
cee16a75ca | ||
|
|
9fd85a8289 | ||
|
|
2f1eff1474 | ||
|
|
8c582b8d72 | ||
|
|
e49673329d | ||
|
|
07ebed83bf | ||
|
|
1def87fdc3 | ||
|
|
d91fed7dd4 | ||
|
|
5f2f7e70ef | ||
|
|
83432beff6 | ||
|
|
979b550178 | ||
|
|
9062a531f3 | ||
|
|
e2a6fbcf3c | ||
|
|
ec16931f3a | ||
|
|
0ec0ca7495 | ||
|
|
51fa7c9371 | ||
|
|
6c69f10fe7 | ||
|
|
206e85a356 | ||
|
|
8d7dd4867b | ||
|
|
d1524f78fb | ||
|
|
b26735d520 | ||
|
|
603baee777 | ||
|
|
e105b7f498 | ||
|
|
bb3fbccb50 | ||
|
|
c8edcd963d | ||
|
|
3b60cdc707 | ||
|
|
ea061d65c9 | ||
|
|
da64c0237f | ||
|
|
d2b1602881 | ||
|
|
1afd45068c | ||
|
|
f01b30e112 | ||
|
|
b5f7fbe14e | ||
|
|
8b6073ccbb | ||
|
|
f902c759df | ||
|
|
996fdbc0f5 | ||
|
|
9ff3e3b42a | ||
|
|
0a5604521e | ||
|
|
786636ef5d | ||
|
|
18bdb27225 | ||
|
|
faca35dec0 | ||
|
|
f8813daae3 | ||
|
|
da5a20390f | ||
|
|
3ab3db92f5 | ||
|
|
a3cd3dd9dc | ||
|
|
918868e9e8 | ||
|
|
cf05a25445 | ||
|
|
5d5399a267 | ||
|
|
2de7af0ba5 | ||
|
|
0382c304ad | ||
|
|
182d9946fe | ||
|
|
f24e4029b4 | ||
|
|
4ebda29171 | ||
|
|
53026f3d02 | ||
|
|
99c0e95a2f | ||
|
|
dfa56c6b08 | ||
|
|
0419df4b2d | ||
|
|
70bdcd0978 | ||
|
|
022df995aa | ||
|
|
dcbe7ec41d | ||
|
|
df4d27eefe | ||
|
|
8f233f55e9 | ||
|
|
7db49aec21 | ||
|
|
b5eaec0778 | ||
|
|
06b126a2e7 | ||
|
|
ed96ebac79 | ||
|
|
c6e34d2d88 | ||
|
|
53ac8bad79 | ||
|
|
d2e163bc3b | ||
|
|
1404123281 | ||
|
|
01a7afd28a | ||
|
|
3a42911e6f | ||
|
|
8e5d52f2c7 | ||
|
|
dfff5d7230 | ||
|
|
19b63786c8 | ||
|
|
5293e1c18b | ||
|
|
f200bb8b00 | ||
|
|
ed11a5c412 | ||
|
|
cdcc63f519 | ||
|
|
7096e9fd9c | ||
|
|
c8fe56ea95 | ||
|
|
8a2a58b1a5 | ||
|
|
42aec98368 | ||
|
|
6d73371a79 | ||
|
|
4d60ff8e67 | ||
|
|
311ff4a89f | ||
|
|
5d57957a6e | ||
|
|
f89adce02d | ||
|
|
3e505f47bc | ||
|
|
06e29142e6 | ||
|
|
15a69f6dcb | ||
|
|
0f763fe06b | ||
|
|
f5adb89338 | ||
|
|
36b120bdbe | ||
|
|
cc169d414f | ||
|
|
0fcb2075e0 | ||
|
|
2bda78fb40 | ||
|
|
e878c6eef6 | ||
|
|
9ce405cec5 | ||
|
|
f064d413b3 | ||
|
|
e5a3331f24 | ||
|
|
6f99f88b29 | ||
|
|
8ff0153708 | ||
|
|
c7273c06da | ||
|
|
cd36caccc7 | ||
|
|
a022aab28a | ||
|
|
949e9c216d | ||
|
|
3fcf7d4e69 | ||
|
|
e335621558 | ||
|
|
9a0357c67b | ||
|
|
0953039369 | ||
|
|
d4a8eb5847 | ||
|
|
d48ab7c84e | ||
|
|
c43b88ac0b | ||
|
|
76ffbb96ba | ||
|
|
b6b28c8a02 | ||
|
|
a736e1da05 | ||
|
|
a8cc280574 | ||
|
|
da9d9385b9 | ||
|
|
149e5c6fba | ||
|
|
e14da81b48 | ||
|
|
49a0a6fdb3 | ||
|
|
da678dc52d | ||
|
|
6ff68f3f06 | ||
|
|
33feda7208 | ||
|
|
38af98ec9b | ||
|
|
d6a11d7164 | ||
|
|
33d241c33e | ||
|
|
944ac97ba4 | ||
|
|
bb6e8dc6b2 | ||
|
|
ed604f6284 | ||
|
|
3c67a67ae7 | ||
|
|
f3e7a4e439 | ||
|
|
fb5bb874dd | ||
|
|
d3ccb4b0d7 | ||
|
|
d096d7742f | ||
|
|
92b5accb90 | ||
|
|
1bcc8f45b5 | ||
|
|
5eef01377f | ||
|
|
4c140b1b90 | ||
|
|
4bcbf2b089 | ||
|
|
6d3969ab79 | ||
|
|
ea35551b9d | ||
|
|
0d4c747a05 | ||
|
|
6cc7d7b8fe | ||
|
|
3d0f82e4c6 | ||
|
|
26abcbaf81 | ||
|
|
f7efaa37f5 | ||
|
|
167bb0287e | ||
|
|
3020705012 | ||
|
|
ecd80775d8 | ||
|
|
c42e73f17a | ||
|
|
ec271a67ad | ||
|
|
ce4a9cfbd7 | ||
|
|
45f769893c | ||
|
|
8a23046187 | ||
|
|
ca8f2a886e | ||
|
|
9dc6bdb03b | ||
|
|
c9b3e5e7e5 | ||
|
|
f0ce9c1431 | ||
|
|
0b22593572 | ||
|
|
2fd1115ec9 | ||
|
|
38d4bbdba1 | ||
|
|
cb9325843e | ||
|
|
900ba11623 | ||
|
|
7da4e15b73 | ||
|
|
7983a4b883 | ||
|
|
ea4a147751 | ||
|
|
33495e812e | ||
|
|
c6e2958665 | ||
|
|
86ede224ac | ||
|
|
e07a906731 | ||
|
|
76d139c29e | ||
|
|
4b8487f164 | ||
|
|
a006e0bf6f | ||
|
|
c5373480ba | ||
|
|
e1f2494966 | ||
|
|
9ddfa5a56b | ||
|
|
6339cd6b31 | ||
|
|
7e80e255a4 | ||
|
|
c6cbae4bc4 | ||
|
|
68f239765c | ||
|
|
77c57d7344 | ||
|
|
259c5d67e6 | ||
|
|
4c2fa27717 | ||
|
|
f5b14cadf8 | ||
|
|
e154e41bc0 | ||
|
|
1f2ebe8c33 | ||
|
|
28444b7907 | ||
|
|
75f41fc630 | ||
|
|
d091479f29 | ||
|
|
665ceb9ea6 | ||
|
|
27ab1e3e5a | ||
|
|
a777470cda | ||
|
|
f48d1c5196 | ||
|
|
cee893b845 | ||
|
|
07ccb6319a | ||
|
|
a6981ff7ef | ||
|
|
fc8d0283b1 | ||
|
|
5e892e8fc3 | ||
|
|
8b124e5ced | ||
|
|
65dc707c29 | ||
|
|
65fb3e9e76 | ||
|
|
3a0c02459d | ||
|
|
33f3c97b03 | ||
|
|
c55032531c | ||
|
|
12f999e9e4 | ||
|
|
817cf03592 | ||
|
|
ca940d1599 | ||
|
|
d00fba02a0 | ||
|
|
045e5e7303 | ||
|
|
6fd98e3c67 | ||
|
|
98279e44ef | ||
|
|
22b78cee56 | ||
|
|
8f7203e38b | ||
|
|
38b8cd2574 | ||
|
|
6b28f36b97 | ||
|
|
d394b21920 | ||
|
|
14255a5076 | ||
|
|
8fa1a887ce | ||
|
|
fb6fa969a8 | ||
|
|
2840831017 | ||
|
|
a4a83c6cfc | ||
|
|
2c508cf51d | ||
|
|
e02aa00d07 | ||
|
|
dc384c3635 | ||
|
|
69db44d1cf | ||
|
|
6fdc9a252a | ||
|
|
11166a3c5c | ||
|
|
3b2a3c6e3a | ||
|
|
d890383ad2 | ||
|
|
4c4b6ee045 | ||
|
|
a55196e7e5 | ||
|
|
866d5a2933 | ||
|
|
8cba89722b | ||
|
|
6d1c623716 | ||
|
|
a8c7ffc77d | ||
|
|
fb05b6ac6d | ||
|
|
0e83b2e7df | ||
|
|
1baaa4402d | ||
|
|
2af61e4aca | ||
|
|
db235dae5e | ||
|
|
241878bd0e | ||
|
|
1386e343ec | ||
|
|
9ff51ec8ef | ||
|
|
45036b708f | ||
|
|
ec3b5b10df | ||
|
|
4817298dbb | ||
|
|
af0ce4cf35 | ||
|
|
3c3d8d080c | ||
|
|
dc6af483a5 | ||
|
|
9a0b487f4b | ||
|
|
cac4d1ce86 | ||
|
|
7a3a31a929 | ||
|
|
eee6f95b15 | ||
|
|
7a3d10451d | ||
|
|
e4f1a5a06f | ||
|
|
500fcde21b | ||
|
|
eb363a4b2a | ||
|
|
8a78e609b0 | ||
|
|
15c67b8cc1 | ||
|
|
00e9c5a07f | ||
|
|
7643457374 | ||
|
|
78d5584e21 | ||
|
|
1d1143a893 | ||
|
|
91093e1304 | ||
|
|
1175a06f3d | ||
|
|
6e5abd1189 | ||
|
|
34f97384e7 | ||
|
|
653a6a0189 | ||
|
|
f0b1b61eac | ||
|
|
c0fd121bdf | ||
|
|
b805b86ddb | ||
|
|
654e7e750c | ||
|
|
7501fcfe8b | ||
|
|
fdb7837e03 | ||
|
|
1c57cea483 | ||
|
|
0c8e8d4d69 | ||
|
|
8876aae2cc | ||
|
|
3e053b32e2 | ||
|
|
0611728537 | ||
|
|
a84cf83ce5 | ||
|
|
c064aa7862 | ||
|
|
195f7126cc | ||
|
|
50d466c9c1 | ||
|
|
5763574634 | ||
|
|
2da568b3e8 | ||
|
|
2732d9aec8 | ||
|
|
15d34aff15 | ||
|
|
af3e257c78 | ||
|
|
c2248c7e4a | ||
|
|
a7967b6dc3 | ||
|
|
c1f47921e6 | ||
|
|
cda93d516b | ||
|
|
8f9c12b26c | ||
|
|
cdb4e9ed21 | ||
|
|
1a612c3db5 | ||
|
|
72ab957134 | ||
|
|
621523db62 | ||
|
|
cdf9cc387c | ||
|
|
b84ecd289d | ||
|
|
d4cb8f414a | ||
|
|
8180c1f79e | ||
|
|
b23b21a7bf | ||
|
|
b30eff294d | ||
|
|
cacb397198 | ||
|
|
0f8c7d6969 | ||
|
|
7b35232cad | ||
|
|
43624796db | ||
|
|
bc17c624d3 | ||
|
|
61476ea0cc | ||
|
|
f0663030e1 | ||
|
|
989a11931b | ||
|
|
94372a7f09 | ||
|
|
5c2702c6ab | ||
|
|
586c09f5c3 | ||
|
|
7a94123f0d | ||
|
|
4cad34a8a4 | ||
|
|
c0ef3215df | ||
|
|
08982aae7a | ||
|
|
6366ac6639 | ||
|
|
3807d22a78 | ||
|
|
3adbfd7ef5 | ||
|
|
7e40dfa5da | ||
|
|
62ddca2bb4 | ||
|
|
1a59e5065d | ||
|
|
52c0c409e9 | ||
|
|
e74b16d28b | ||
|
|
c2369bcc31 | ||
|
|
2636352a49 | ||
|
|
222a88f097 | ||
|
|
dbc691726d | ||
|
|
9ff3aedf18 | ||
|
|
8303a1fbca | ||
|
|
0f584ee343 | ||
|
|
aafad0a628 | ||
|
|
9d03951da9 | ||
|
|
b15d6cb8e5 | ||
|
|
f9c1816e6f | ||
|
|
d960b020ea | ||
|
|
72e9d57b15 | ||
|
|
968b90dbab | ||
|
|
2bccdcc93b | ||
|
|
ce2a5eb6d9 | ||
|
|
353e4f6210 | ||
|
|
c115de9d40 | ||
|
|
df83b558bf | ||
|
|
7c2b5f116d | ||
|
|
30fe75f9bf | ||
|
|
401e7a9edb | ||
|
|
fd4ddc56f2 | ||
|
|
83d907bf71 | ||
|
|
327bc76c6e | ||
|
|
fdd39fb2d8 | ||
|
|
bfcfa8eb19 | ||
|
|
7095c03e28 | ||
|
|
45e796f15f | ||
|
|
3d1dcd6874 | ||
|
|
0033d0759f | ||
|
|
53f7dfe6c9 | ||
|
|
75446de29b | ||
|
|
1d119d6921 | ||
|
|
7462bd995f | ||
|
|
0dd99efad3 | ||
|
|
1234e81463 | ||
|
|
ea13d66e6b | ||
|
|
a7cb7eb995 | ||
|
|
29f5feb34d | ||
|
|
5dc60db7b6 | ||
|
|
fb9f7fe445 | ||
|
|
a548471652 | ||
|
|
3e47d66644 | ||
|
|
3bfa45a80c | ||
|
|
2d717af4db | ||
|
|
533b217c8f | ||
|
|
ff1fb761f2 | ||
|
|
95d49add2c | ||
|
|
8b75609b70 | ||
|
|
b8929dd589 | ||
|
|
2fd29f8786 | ||
|
|
38408820ca | ||
|
|
43e6840e78 | ||
|
|
15908c52bd | ||
|
|
c90b0e7dc2 | ||
|
|
d2ff9806bd | ||
|
|
1e6993c12d | ||
|
|
1122344016 | ||
|
|
0dbce00fe4 | ||
|
|
5af0b68e06 | ||
|
|
6038a11671 | ||
|
|
dcb92db519 | ||
|
|
dcaeabacc6 | ||
|
|
a2a5c7eff0 | ||
|
|
e1cf927bf3 | ||
|
|
8fd98d674a | ||
|
|
fd884027c0 | ||
|
|
26bd467f79 | ||
|
|
c7f22c0dab | ||
|
|
92d44f6ae3 | ||
|
|
9143f477b2 | ||
|
|
1a519bf837 | ||
|
|
ca6b90f8c1 | ||
|
|
44fc532d63 | ||
|
|
6a6cd025c0 | ||
|
|
d769f90704 | ||
|
|
9d8e3b21ba | ||
|
|
dabdfec3e7 | ||
|
|
6a00653d1e | ||
|
|
8fb786094f | ||
|
|
87e978c817 | ||
|
|
4a31046c9c | ||
|
|
db420b3495 | ||
|
|
c81dc166bc | ||
|
|
07aa416975 | ||
|
|
627820cddc | ||
|
|
a24fe420c4 | ||
|
|
986be921f4 | ||
|
|
f5f223f622 | ||
|
|
bbdfa0d651 | ||
|
|
e6bb0cb463 | ||
|
|
9e61670116 | ||
|
|
3876c07164 | ||
|
|
ed315eade9 | ||
|
|
7456fd0c90 | ||
|
|
44160e66ac | ||
|
|
9bd969a57b | ||
|
|
0b585078d8 | ||
|
|
0d495ed934 | ||
|
|
95b703b1ea | ||
|
|
688061397b | ||
|
|
1f00176455 | ||
|
|
90da6b1e72 | ||
|
|
4deb45dc3f | ||
|
|
eeec5d106a | ||
|
|
4e42d1d197 | ||
|
|
495d08c447 | ||
|
|
1b859015ae | ||
|
|
3db2109e01 | ||
|
|
294ac87503 | ||
|
|
c297adb0c7 | ||
|
|
446b965794 | ||
|
|
96d4df296d | ||
|
|
a149aac0e9 | ||
|
|
aacc7be9f3 | ||
|
|
7409955701 | ||
|
|
c623d95a80 | ||
|
|
1927cc7fe1 | ||
|
|
4eca254daf | ||
|
|
c7d4fee3f6 | ||
|
|
a6f798ae5b | ||
|
|
c9ae836e52 | ||
|
|
e3ffa63f7f | ||
|
|
4ffc2cc1dc | ||
|
|
7f9ba14687 | ||
|
|
a24933e272 | ||
|
|
20bdacbecf | ||
|
|
ab9d6cf5ed | ||
|
|
1f5903a9a0 | ||
|
|
bb073b6bb3 | ||
|
|
516241f8f5 | ||
|
|
977b6831a0 | ||
|
|
c61effb54f | ||
|
|
346d989944 | ||
|
|
60a73c8d1e | ||
|
|
e52db4a837 | ||
|
|
4e317643bc | ||
|
|
5f520bf375 | ||
|
|
2efe521b3a | ||
|
|
5c21103646 | ||
|
|
9444696f37 | ||
|
|
082fe4e787 | ||
|
|
5e13cf23f9 | ||
|
|
8f98a1f557 | ||
|
|
5b21e8798b | ||
|
|
b9ef5b7db8 | ||
|
|
9867f8c302 | ||
|
|
315889faf6 | ||
|
|
798e8fee89 | ||
|
|
e1c49db329 | ||
|
|
dae9537472 | ||
|
|
1330d56cdd | ||
|
|
6ce3ce20d0 | ||
|
|
362c5ee9b0 | ||
|
|
0f34ce0278 | ||
|
|
0c27c7c4c8 | ||
|
|
37595bf73c | ||
|
|
952aea46ba | ||
|
|
6a6536cf27 | ||
|
|
696368c92a | ||
|
|
e3edc9327e | ||
|
|
8d2e6a664d | ||
|
|
9db6efe7a2 | ||
|
|
8b8a22d7fb | ||
|
|
0a70344bc1 | ||
|
|
e77d01911c | ||
|
|
d4c0853e1f | ||
|
|
363a4e959c | ||
|
|
9336a04681 | ||
|
|
214ff38749 | ||
|
|
a8f3c01d8b | ||
|
|
4da6585ef9 | ||
|
|
df40100feb | ||
|
|
f2d92e93fb | ||
|
|
b4d8d569d2 | ||
|
|
854b3e9c59 | ||
|
|
28ca2b72f1 | ||
|
|
7781c8179f | ||
|
|
69ece3ffa0 | ||
|
|
53adcd92ed | ||
|
|
2bef6ca646 | ||
|
|
bab350d771 | ||
|
|
048dac223f | ||
|
|
b7634da310 | ||
|
|
882c92c64a | ||
|
|
4592dbe28b | ||
|
|
edc0f21ae7 | ||
|
|
8715b7f6c1 | ||
|
|
99511910dd | ||
|
|
a03478b011 | ||
|
|
5c428e1f07 | ||
|
|
ee57615735 | ||
|
|
67300e5769 | ||
|
|
873e05051c | ||
|
|
4daaec46a7 | ||
|
|
dd8cc7bfd4 | ||
|
|
5568ac382f | ||
|
|
dcdb3e4455 | ||
|
|
17b29b1626 | ||
|
|
dcfcc6271c | ||
|
|
1d77ba6429 | ||
|
|
ff5f019ac1 | ||
|
|
e61eeb8c6f | ||
|
|
68d22e7f54 | ||
|
|
790f0a42e3 | ||
|
|
08e9e508cc | ||
|
|
ad1a8d608f | ||
|
|
d74ed71023 | ||
|
|
0c7f9e0888 | ||
|
|
ba5f6528a8 | ||
|
|
65cf552ec2 | ||
|
|
715c0a0c42 | ||
|
|
9e383575d1 | ||
|
|
d84c366480 | ||
|
|
42e6c11081 | ||
|
|
9ba3f90f1e | ||
|
|
24ff51274b | ||
|
|
4c4c752827 | ||
|
|
5022b67e4a | ||
|
|
6b990a9f51 | ||
|
|
e69ce3b8df | ||
|
|
cf537b6222 | ||
|
|
9d1160faff | ||
|
|
ed4067f194 | ||
|
|
d4b55cd8f5 | ||
|
|
baaeb0bca7 | ||
|
|
466c3c34e5 | ||
|
|
099d7969ca | ||
|
|
5adfa95a85 | ||
|
|
bfa0846ad0 | ||
|
|
7099264334 | ||
|
|
69b44e7968 | ||
|
|
fe39977ff7 | ||
|
|
b9fc8de5b5 | ||
|
|
f7b8022d3a | ||
|
|
a62346c515 | ||
|
|
e372d757ad | ||
|
|
ab1b10f935 | ||
|
|
8e918706dc | ||
|
|
76450d00bf | ||
|
|
ee53542e18 | ||
|
|
db004bc787 | ||
|
|
71a7f3554e | ||
|
|
5c3f422a53 | ||
|
|
2fe0cec04a | ||
|
|
de59e781b5 | ||
|
|
8c77af651b | ||
|
|
638f6928cf | ||
|
|
ccc8e597a7 | ||
|
|
585f19d884 | ||
|
|
bb2b7d7df6 | ||
|
|
e75d218438 | ||
|
|
7f81b554fd | ||
|
|
2490f19a1a | ||
|
|
30f382bf22 | ||
|
|
ad03c187cf | ||
|
|
06560b5a5a | ||
|
|
7c40093698 | ||
|
|
d37c75d703 | ||
|
|
82bfb9a303 | ||
|
|
01682101a6 | ||
|
|
3c46f787b1 | ||
|
|
591d200283 | ||
|
|
195534c21e | ||
|
|
0f9d851a29 | ||
|
|
18a03baf99 | ||
|
|
5e06db4a52 | ||
|
|
bf78508ef7 | ||
|
|
137c0340fb | ||
|
|
e6d9de2d80 | ||
|
|
d9b319eaed | ||
|
|
ba1f8b8ed8 | ||
|
|
10605b3908 | ||
|
|
e31e547322 | ||
|
|
f2e713bde3 | ||
|
|
94e2e494c9 | ||
|
|
5af408e1d1 | ||
|
|
77bdc727ab | ||
|
|
eb26426424 | ||
|
|
f624bb6e5b | ||
|
|
4a8fb9288c | ||
|
|
f8f5873973 | ||
|
|
5f4903f2d1 | ||
|
|
b02a894663 | ||
|
|
510b530551 | ||
|
|
c36662205b | ||
|
|
a2ffe06792 | ||
|
|
0f56108bf5 | ||
|
|
199cefdb71 | ||
|
|
1bdeaa326c | ||
|
|
cce8cfe88d | ||
|
|
bcfc0217dc | ||
|
|
7cfa080220 | ||
|
|
45ebc0f40f | ||
|
|
38d575eda7 | ||
|
|
9cb284583b | ||
|
|
137b921e8d | ||
|
|
8c876f555d | ||
|
|
0988dd524b | ||
|
|
2dc649ef09 | ||
|
|
baf02cb849 | ||
|
|
51fa3c5293 | ||
|
|
134dd6c37d | ||
|
|
d766e1f9a9 | ||
|
|
d298f5b16e | ||
|
|
9484a1b870 | ||
|
|
ed634fbbf6 | ||
|
|
4c776d584b | ||
|
|
c2c04862a2 | ||
|
|
ccd9539015 | ||
|
|
624c597735 | ||
|
|
9300aa79c3 | ||
|
|
9e522c7da2 | ||
|
|
ef60bfff6b | ||
|
|
635c6c7bfe | ||
|
|
df6e47fa50 | ||
|
|
654cdcd3d1 | ||
|
|
a633b73e12 | ||
|
|
ba93dae240 | ||
|
|
8e0ca85d1e | ||
|
|
56a4926bd3 | ||
|
|
6a2aae4ef2 | ||
|
|
0a5a814a88 | ||
|
|
08ce455d1d | ||
|
|
ec68ce3bfa | ||
|
|
a777a5be30 | ||
|
|
b553a8b1fb | ||
|
|
b119e1f72d | ||
|
|
7345d3e6c1 | ||
|
|
e9b7a7bb52 | ||
|
|
2022732dd9 | ||
|
|
63544647b6 | ||
|
|
6b62585ad5 | ||
|
|
14027210f7 | ||
|
|
3df17b23b8 | ||
|
|
cbf3f56562 | ||
|
|
1f74d9189f | ||
|
|
137658d1d6 | ||
|
|
5b627bd2b1 | ||
|
|
38ff08885a | ||
|
|
a89993aabb | ||
|
|
d6353403e2 | ||
|
|
bc62ee04c0 | ||
|
|
d3ff836b63 | ||
|
|
a7aac5578e | ||
|
|
add5a141d3 | ||
|
|
330410ec61 | ||
|
|
d0f49dcfa6 | ||
|
|
124f6ab7cb | ||
|
|
471f63592e | ||
|
|
50e210c72f | ||
|
|
d3396aa535 | ||
|
|
5ed8b838bc | ||
|
|
d1757eacc2 | ||
|
|
0692e5f5d5 | ||
|
|
e4204196cd | ||
|
|
241d4342e4 | ||
|
|
c04cbc631c | ||
|
|
29b273ad7b | ||
|
|
9720dab2f6 | ||
|
|
bddc64a324 | ||
|
|
eb324f14de | ||
|
|
b78a057c81 | ||
|
|
5751725213 | ||
|
|
f194392f99 | ||
|
|
fea62178af | ||
|
|
33ef4ce8de | ||
|
|
3728120f95 | ||
|
|
2944b9b3f6 | ||
|
|
3430574364 | ||
|
|
fc5a5212c0 | ||
|
|
20f724ed13 | ||
|
|
94c1d21938 | ||
|
|
a1a9666b6f | ||
|
|
0551ddc276 | ||
|
|
049ffd3b04 | ||
|
|
c28f757c5c | ||
|
|
91dbb86e64 | ||
|
|
27a04ee22b | ||
|
|
5cefce9922 | ||
|
|
8fb4c90bed | ||
|
|
81753669cc | ||
|
|
0a0a72bcf3 | ||
|
|
c4a6e3e063 | ||
|
|
1138e6b77f | ||
|
|
030f9218d6 | ||
|
|
2fff32e8f2 | ||
|
|
5b2aa9926f | ||
|
|
921e178e83 | ||
|
|
25ffd900c8 | ||
|
|
7ea4e116cc | ||
|
|
a9daec36f5 | ||
|
|
cebc7c6cd2 | ||
|
|
3f85c9f006 | ||
|
|
ed5efd7b87 | ||
|
|
4984a53bfd | ||
|
|
b0c77653a2 | ||
|
|
909f0d628b | ||
|
|
e27e3ada92 | ||
|
|
339ea3b5a4 | ||
|
|
9bd8b8915e | ||
|
|
35008656a9 | ||
|
|
825089458f | ||
|
|
4a086d94b7 | ||
|
|
0aeddf7e98 | ||
|
|
4922d1deb4 | ||
|
|
86d0893261 | ||
|
|
e4c67f18bd | ||
|
|
d07c5a94e1 | ||
|
|
a91dee27e7 | ||
|
|
e3219087c9 | ||
|
|
cc9ec84aec | ||
|
|
a33cc5710c | ||
|
|
c2b148288a | ||
|
|
a483567564 | ||
|
|
bd99bc6d94 | ||
|
|
8f79071aad | ||
|
|
ef9071049b | ||
|
|
60e1ab8cca | ||
|
|
d3dbfd3154 | ||
|
|
ee2dffb498 | ||
|
|
6d9510cc65 | ||
|
|
49f0f5d000 | ||
|
|
d8528d889a | ||
|
|
ec439a5f2a | ||
|
|
d88110488e | ||
|
|
2f4b15293a | ||
|
|
3d71f32587 | ||
|
|
5136dda598 | ||
|
|
d26940304b | ||
|
|
f9f11b4966 | ||
|
|
1297b568ba | ||
|
|
fd10840cc0 | ||
|
|
7e86be979d | ||
|
|
731d1efcc4 | ||
|
|
dfda8be30c | ||
|
|
7dd1b6d8e9 | ||
|
|
ec1bcdb9e5 | ||
|
|
c0f46d2bd4 | ||
|
|
615dca3130 | ||
|
|
9cf9597c54 | ||
|
|
e24ee648e7 | ||
|
|
e3518dc389 | ||
|
|
693ba20606 | ||
|
|
b947c6c186 | ||
|
|
7f8ecb8514 | ||
|
|
4df6afa9c1 | ||
|
|
d0620f8efe | ||
|
|
2b1a6dbb03 | ||
|
|
c6ef667c3f | ||
|
|
8c1d1bec93 | ||
|
|
8be174e65a | ||
|
|
6d37fafb02 | ||
|
|
46ce882daa | ||
|
|
6d14cbdb9b | ||
|
|
4bf6b433ae | ||
|
|
87a1b2e6f8 | ||
|
|
c6ee48ec85 | ||
|
|
b58a6b1649 | ||
|
|
bd9736df93 | ||
|
|
3b9c966e3d | ||
|
|
96c9a89171 | ||
|
|
c374ffd15e | ||
|
|
c53109e1a1 | ||
|
|
4598b3a7a6 | ||
|
|
cf975b74bf | ||
|
|
5d65dcf3c8 | ||
|
|
f299ec1f8d | ||
|
|
6677034774 | ||
|
|
3c7c4639a9 | ||
|
|
7e9a1268a5 | ||
|
|
a60b8e68ca | ||
|
|
b2161aa67e | ||
|
|
d1fffb1d08 | ||
|
|
52d66d9555 | ||
|
|
66f2d359e2 | ||
|
|
8327f33ee6 | ||
|
|
d4a94551d9 | ||
|
|
d2a545d83e | ||
|
|
aee4eac271 | ||
|
|
cbeddb11bc | ||
|
|
4c51cf8316 | ||
|
|
345cd6bd92 | ||
|
|
48540245b5 | ||
|
|
088bd9434d | ||
|
|
8ba8c58377 | ||
|
|
4ae43d0503 | ||
|
|
00fdb90adf | ||
|
|
f8257e697a | ||
|
|
0ddfd3bead | ||
|
|
4797d1ea10 | ||
|
|
d20ce3dde7 | ||
|
|
ef5f7ec9c3 | ||
|
|
69c60c4026 | ||
|
|
2f4f2083ba | ||
|
|
77ada0471b | ||
|
|
00cab46a0d | ||
|
|
7f7460f3f3 | ||
|
|
c225eef9ad | ||
|
|
460f920502 | ||
|
|
c7677e5514 | ||
|
|
12fb39baa9 | ||
|
|
201fd22861 | ||
|
|
d0fb85e712 | ||
|
|
81cbd00cc8 | ||
|
|
4a565b5ea0 | ||
|
|
82f61eee12 | ||
|
|
88fc7ff9c3 | ||
|
|
8eb17bf104 | ||
|
|
9a8fc80220 | ||
|
|
e5e652fad2 | ||
|
|
924d077315 | ||
|
|
44a7505295 | ||
|
|
b1a6fa4084 | ||
|
|
0665fc0a6f | ||
|
|
4bf5fd49d6 | ||
|
|
0a6bc99ecd | ||
|
|
45cc617fc5 | ||
|
|
6dc5eabdf0 | ||
|
|
48d3bc55bf | ||
|
|
6b7e81d7fb | ||
|
|
dce6248193 | ||
|
|
0e349ede4c | ||
|
|
60117471a7 | ||
|
|
3c23e7b047 | ||
|
|
b48e1ba9e0 | ||
|
|
1267191e8e | ||
|
|
65a43b64ae | ||
|
|
09e446a26e | ||
|
|
2ecb3059e5 | ||
|
|
237cb42695 | ||
|
|
49b6bbff37 | ||
|
|
5e05083008 | ||
|
|
339e9cca10 | ||
|
|
d441ad8875 | ||
|
|
72fc6bf913 | ||
|
|
fcf278c61c | ||
|
|
890404ded5 | ||
|
|
6fd9b47c45 | ||
|
|
003651ec68 | ||
|
|
c63a761ca4 | ||
|
|
32dcd1551b | ||
|
|
90fbdd5fab | ||
|
|
1fea200582 | ||
|
|
55b8a62f64 | ||
|
|
a8906bf58f | ||
|
|
0d6b9263d4 | ||
|
|
f091a54ca6 | ||
|
|
5262929c16 | ||
|
|
c1ca8a3ae6 | ||
|
|
a7e36472d5 | ||
|
|
538a22e2f7 | ||
|
|
0c40a3e79c | ||
|
|
3cb098f9ba | ||
|
|
ea1ab029f3 | ||
|
|
92a76a6d39 | ||
|
|
c451950dbf | ||
|
|
644adc43ed | ||
|
|
63a7340c21 | ||
|
|
6824f00867 | ||
|
|
3cb48b40aa | ||
|
|
dda713a6be | ||
|
|
415aa82a6f | ||
|
|
4ae664fd93 | ||
|
|
5d1e304642 | ||
|
|
6b228d7a0a | ||
|
|
085ad5f2a4 | ||
|
|
e40e6bd07f | ||
|
|
a6db36e7b3 | ||
|
|
1a4caccd07 | ||
|
|
7f1017ebd9 | ||
|
|
f792cd2677 | ||
|
|
1361cab603 | ||
|
|
05b784d203 | ||
|
|
542c3e38f5 | ||
|
|
bcdead1eca | ||
|
|
a79a927a13 | ||
|
|
5bcc9071f6 | ||
|
|
88869ff6d4 | ||
|
|
1655505b95 | ||
|
|
9abfa3726e | ||
|
|
89a4abf9cf | ||
|
|
854afc6de3 | ||
|
|
03048797c5 | ||
|
|
2478ccd8ef | ||
|
|
827cfd818e | ||
|
|
b0e0cd6a1f | ||
|
|
003e919bd5 | ||
|
|
56dadfc228 | ||
|
|
b70603e92b | ||
|
|
6294bc4505 | ||
|
|
326bc931ad | ||
|
|
734913f638 | ||
|
|
157a525ec1 | ||
|
|
352abe07d8 | ||
|
|
0111ed37ba | ||
|
|
079c4c955c | ||
|
|
922715480e | ||
|
|
99ca175e9a | ||
|
|
c1fefb7e13 | ||
|
|
31495d484d | ||
|
|
1c6ae0bd88 | ||
|
|
177aadbb45 | ||
|
|
a6868acfa0 | ||
|
|
907d46a28b | ||
|
|
87dda265f4 | ||
|
|
7106882212 | ||
|
|
59e5f3d27e | ||
|
|
b2eba66bff | ||
|
|
b8b5509d49 | ||
|
|
e667e302dc | ||
|
|
ba68a13712 | ||
|
|
c562c6f23a | ||
|
|
fad79e51ac | ||
|
|
9438d68dae | ||
|
|
39b761ea16 | ||
|
|
64689cf59d | ||
|
|
17f80e4e53 | ||
|
|
4117e233b3 | ||
|
|
6c0bb3781a | ||
|
|
eb55fe1fe3 | ||
|
|
dc396d5cf7 | ||
|
|
80ec4407bc | ||
|
|
1e45374d41 | ||
|
|
92ae233f39 | ||
|
|
d3f8eb09d0 | ||
|
|
058ef8495b | ||
|
|
e2ac8eb43c | ||
|
|
56121e39e2 | ||
|
|
57bc2c459b | ||
|
|
311beacfff | ||
|
|
67e9c65313 | ||
|
|
9a0257caaa | ||
|
|
a121fb8c6e | ||
|
|
a044f9a4a4 | ||
|
|
0b7a2d7b34 | ||
|
|
07ba160119 | ||
|
|
d887c86cd4 | ||
|
|
372af03d9e | ||
|
|
aed6a6f142 | ||
|
|
f5fa89bafe | ||
|
|
6a1d181a34 | ||
|
|
dbcd8c27c5 | ||
|
|
2764be5d32 | ||
|
|
d69c534614 | ||
|
|
f436d6e48c | ||
|
|
e49fcd12fa | ||
|
|
f2699c4f1e | ||
|
|
2319243444 | ||
|
|
8e44e6625a | ||
|
|
4cd87ab08a | ||
|
|
98a125eb06 | ||
|
|
7c690695b9 | ||
|
|
066a12edaa | ||
|
|
4c0971800d | ||
|
|
c375b73db9 | ||
|
|
a392b84d9b | ||
|
|
34f034c6c4 | ||
|
|
f5640b970b | ||
|
|
b7028f20e6 | ||
|
|
0cb059e59f | ||
|
|
8d90a974c6 | ||
|
|
d44b83e60d | ||
|
|
ac7493fdc0 | ||
|
|
c6c2553b42 | ||
|
|
7d5460f8d1 | ||
|
|
f9fa8a3616 | ||
|
|
6c117df0a3 | ||
|
|
b8bf08eace | ||
|
|
027af76d5d | ||
|
|
b7eee599f4 | ||
|
|
42a350156a | ||
|
|
f382b70cdf | ||
|
|
f753929e87 | ||
|
|
b3c5ab0b4e | ||
|
|
9d8adcc511 | ||
|
|
1afa9ce697 | ||
|
|
b91edac0ba | ||
|
|
5d6f031973 | ||
|
|
72c56ee337 | ||
|
|
72498a200a | ||
|
|
1e9e95754c | ||
|
|
eb99cd895c | ||
|
|
5625cf5254 | ||
|
|
9c6dc0a2f8 | ||
|
|
3458874a68 | ||
|
|
4e93b43d8d | ||
|
|
b895545ec3 | ||
|
|
92316c4d83 | ||
|
|
af71409cab | ||
|
|
df5a60d946 | ||
|
|
0181ab1c03 | ||
|
|
1a5ccca67a | ||
|
|
99e1a90729 | ||
|
|
54e11e96e9 | ||
|
|
b339a776fc | ||
|
|
db1a84b490 | ||
|
|
3256b4f627 | ||
|
|
c16ab349b1 | ||
|
|
a981cb72d0 | ||
|
|
5251a5f195 | ||
|
|
917d5d2dd2 | ||
|
|
983f6caf46 | ||
|
|
c9a58e9d57 | ||
|
|
c5fd24496f | ||
|
|
0d502933ae | ||
|
|
cc4bb3a5ec | ||
|
|
b42f82ecb1 | ||
|
|
b6beaae7da | ||
|
|
e698900497 | ||
|
|
bbad2fc1ba | ||
|
|
e9f5087eb6 | ||
|
|
c1caa22524 | ||
|
|
5303903f89 | ||
|
|
27d520e42a | ||
|
|
8607f4af57 | ||
|
|
f0ec8bd5b9 | ||
|
|
9007e264cf | ||
|
|
529bd6fa33 | ||
|
|
eb8827bdf2 | ||
|
|
be78d91a07 | ||
|
|
20b7008994 | ||
|
|
84d7b1d4ba | ||
|
|
0154ed46e8 | ||
|
|
5a11fa4704 | ||
|
|
8b9e153ac4 | ||
|
|
badc7366c2 | ||
|
|
24ef479913 | ||
|
|
1dd94a7d82 | ||
|
|
40d5d92dbc | ||
|
|
0cc3433d6d | ||
|
|
0e157af8d9 | ||
|
|
0e719c2b86 | ||
|
|
82fd336792 | ||
|
|
e733dc90d8 | ||
|
|
9985d80432 | ||
|
|
7177a781dc | ||
|
|
498558c2b1 | ||
|
|
c8e6795a90 | ||
|
|
1edc51c067 | ||
|
|
6f7054c4b2 | ||
|
|
03d2a3a685 | ||
|
|
1d4dccf454 | ||
|
|
cb93890d13 | ||
|
|
f39b2801b8 | ||
|
|
073a8f5fbd | ||
|
|
669c19882a | ||
|
|
43ca6437ab | ||
|
|
e61a1ea582 | ||
|
|
a477655270 | ||
|
|
88e1acfdff | ||
|
|
39e1dcb900 | ||
|
|
2489b93de8 | ||
|
|
8eb25a911d | ||
|
|
2d5e91b853 | ||
|
|
4f8c68c40d | ||
|
|
9a5c66a311 | ||
|
|
69bb6a74b8 | ||
|
|
7aa5fc91cf | ||
|
|
de0b53de62 | ||
|
|
a1df18e3ca | ||
|
|
6a4c85245d | ||
|
|
52a121c9ed | ||
|
|
2acda695ba | ||
|
|
89953d9e84 | ||
|
|
7054a0eded | ||
|
|
6105e16b28 | ||
|
|
eb9d0dbbd4 | ||
|
|
567b074792 | ||
|
|
416c70f5e5 | ||
|
|
30681dfa2c | ||
|
|
c5b78ffa99 | ||
|
|
cb5e859c34 | ||
|
|
4470fb4312 | ||
|
|
da11fad696 | ||
|
|
72ae4b2b53 | ||
|
|
b8e72039b9 | ||
|
|
878ccd5128 | ||
|
|
45e0946b6b | ||
|
|
b57af55b8a | ||
|
|
677ee5da91 | ||
|
|
a6526ccc13 | ||
|
|
8323593720 | ||
|
|
dd00a69018 | ||
|
|
4d7ddf3d9e | ||
|
|
fbc39a41f8 | ||
|
|
3e934a8527 | ||
|
|
bc474724c6 | ||
|
|
553eccf029 | ||
|
|
63e344d6cf | ||
|
|
0dc992561e | ||
|
|
77006d5736 | ||
|
|
359f5583a6 | ||
|
|
58dbb1c7ef | ||
|
|
47ac8238f7 | ||
|
|
fa760b702c | ||
|
|
2a6ebe2c04 | ||
|
|
3e97faa704 | ||
|
|
a9e82676c7 | ||
|
|
d755fd9c08 | ||
|
|
c4f1d7bccb | ||
|
|
b55ebe95d9 | ||
|
|
cb5c0d5ebe | ||
|
|
404f583dc2 | ||
|
|
19cd5638ba | ||
|
|
b917552ca5 | ||
|
|
71deb14c85 | ||
|
|
e9c1041d9b | ||
|
|
ffece005c5 | ||
|
|
ff5d22adbe | ||
|
|
aaffe1b208 | ||
|
|
b86d2466d2 | ||
|
|
c2230d0462 | ||
|
|
dc4e4aa9c7 | ||
|
|
56e5b4e529 | ||
|
|
3577812dd1 | ||
|
|
256c42833a | ||
|
|
372a9075d7 | ||
|
|
9a5d0db3d4 | ||
|
|
82fe952d7f | ||
|
|
da8382236f | ||
|
|
4d2d03b8fc | ||
|
|
b025e6bb88 | ||
|
|
521bbd4ea5 | ||
|
|
95d5d42608 | ||
|
|
90884d8877 | ||
|
|
62258be400 | ||
|
|
8cbdce1d5b | ||
|
|
9099d59c08 | ||
|
|
f6bcc37168 | ||
|
|
0db1ddc788 | ||
|
|
93821c8991 | ||
|
|
195c4ca3e5 | ||
|
|
f6023ebbd0 | ||
|
|
41dde0e516 | ||
|
|
07b1719b17 | ||
|
|
d31399702b | ||
|
|
601e220b18 | ||
|
|
246e580bb5 | ||
|
|
e141267cfb | ||
|
|
eb21f33f99 | ||
|
|
f91ddaeec7 | ||
|
|
afc432078c | ||
|
|
4ba30fcfec | ||
|
|
d7fd99cad6 | ||
|
|
eaab2a3ec4 | ||
|
|
1f938457db | ||
|
|
855a9d5224 | ||
|
|
d1a9531175 | ||
|
|
58aacd3f28 | ||
|
|
4aee2732bc | ||
|
|
24a3468928 | ||
|
|
dff7502a92 | ||
|
|
c34f4bff08 | ||
|
|
5753cd4877 | ||
|
|
0949cf254f | ||
|
|
935a68d871 | ||
|
|
7592556e4d | ||
|
|
e3f06cbdd4 | ||
|
|
5397482ca3 | ||
|
|
eafb5d9f7f | ||
|
|
5efbb38270 | ||
|
|
c4603e1230 | ||
|
|
c6fd31564d | ||
|
|
c4d72d3c11 | ||
|
|
d7ce10001b | ||
|
|
4220a3fedd | ||
|
|
65100a18d2 | ||
|
|
d913b0910b | ||
|
|
968184fa7a | ||
|
|
7e2d300017 | ||
|
|
6cf86a4797 | ||
|
|
feb5eac02e | ||
|
|
a107d4f17f | ||
|
|
5e4c7719ff | ||
|
|
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 | ||
|
|
194b9b1193 | ||
|
|
27fceb3f41 | ||
|
|
930e0bcd67 | ||
|
|
d81e0a3ed4 | ||
|
|
ebb5c17be4 | ||
|
|
1e99fe5b04 | ||
|
|
5f5f22c82b | ||
|
|
709e300960 | ||
|
|
d2f677d84e | ||
|
|
18d90c44dd | ||
|
|
298f77f52e | ||
|
|
70b2f7efb4 | ||
|
|
78a070bd99 | ||
|
|
c88ff973b1 | ||
|
|
66f82f764d | ||
|
|
d36a18c17a | ||
|
|
8efd95346a | ||
|
|
04b91377c9 | ||
|
|
f5d04d989c | ||
|
|
06a497f346 | ||
|
|
b1cf706a8f | ||
|
|
91f04e36e9 | ||
|
|
5d76eec0f6 | ||
|
|
7822325866 | ||
|
|
521123f139 | ||
|
|
b3f511b025 | ||
|
|
ff63840beb | ||
|
|
1fbcf4aa5f | ||
|
|
8d6a811204 | ||
|
|
08fe2a9e27 | ||
|
|
d8465f4374 | ||
|
|
8a3cd14723 | ||
|
|
08c1671f21 | ||
|
|
91e794add1 | ||
|
|
9c98caf21d | ||
|
|
f97c42017f | ||
|
|
3033a2cb95 | ||
|
|
bcf6f48d46 | ||
|
|
4c97f15a65 | ||
|
|
5dc6349818 | ||
|
|
6ab5c4012a | ||
|
|
1d50e45777 | ||
|
|
3cbf420acd | ||
|
|
ea407b2182 | ||
|
|
5228885760 | ||
|
|
676845aaf3 | ||
|
|
61af9b3810 | ||
|
|
406a433c3f | ||
|
|
2abd413c08 | ||
|
|
ad96c9bb9f | ||
|
|
b412d54998 | ||
|
|
6247d3c5e6 | ||
|
|
b7ee513dfd | ||
|
|
3795abc19e | ||
|
|
1d32a4d984 | ||
|
|
a20a78cd64 | ||
|
|
a2f51b36ec | ||
|
|
910ccb273a | ||
|
|
2cbe39e553 | ||
|
|
994c8fc02d | ||
|
|
57dcd6d520 | ||
|
|
20ade1de7b | ||
|
|
28aff78469 | ||
|
|
3b95e56418 | ||
|
|
056055bf0b | ||
|
|
368d329459 | ||
|
|
2fe1ffeaf1 | ||
|
|
e252e8eb1d | ||
|
|
e160bcc7d5 | ||
|
|
41bd035690 | ||
|
|
b0003fc7e5 | ||
|
|
8e1be97589 | ||
|
|
181f2f38d8 | ||
|
|
661940114e | ||
|
|
9874d4bec5 | ||
|
|
7d5fedf35f | ||
|
|
69ad36a9ae | ||
|
|
ce5fcaf172 | ||
|
|
a00137d742 | ||
|
|
e002842640 | ||
|
|
41e9c46cba | ||
|
|
bc2d58bee3 | ||
|
|
c54de27503 | ||
|
|
d9bfd77fba | ||
|
|
336cc0077c | ||
|
|
0be673b210 | ||
|
|
028e2498fb | ||
|
|
249f1dea9d | ||
|
|
07c7b22669 | ||
|
|
6623058a18 | ||
|
|
68e4d569d0 | ||
|
|
7186b25a69 | ||
|
|
42cacb18bd | ||
|
|
d09c03f4d2 | ||
|
|
ad240b150e | ||
|
|
1177ef6f8d | ||
|
|
2ff50c0c56 | ||
|
|
4f4ed1307f | ||
|
|
6d5b7cb64e | ||
|
|
00fc4e3890 | ||
|
|
7344ee4402 | ||
|
|
740eacc7ac | ||
|
|
8b1bcf21ee | ||
|
|
b0a5174c0a | ||
|
|
b7cca1b95b | ||
|
|
44c51f1246 | ||
|
|
516d43d7a8 | ||
|
|
86d49d563e | ||
|
|
a2911a9585 | ||
|
|
9a12b651f9 | ||
|
|
9fa631acca | ||
|
|
8df7998a83 | ||
|
|
c81c1926c0 | ||
|
|
7ab1288424 | ||
|
|
3ee88adfa9 | ||
|
|
29e8c99b4f | ||
|
|
8b115f8156 | ||
|
|
11c546f113 | ||
|
|
c087539eb7 | ||
|
|
27f7cbb892 | ||
|
|
11ffb09b66 | ||
|
|
114f9f522d | ||
|
|
91dc51d59a | ||
|
|
11b3922b02 | ||
|
|
05552cc3e5 | ||
|
|
3db7964509 | ||
|
|
2a06adcabb | ||
|
|
8cec3354ea | ||
|
|
f9df728f45 | ||
|
|
46eead34c4 | ||
|
|
786b35fee2 | ||
|
|
5c6c609991 | ||
|
|
0be8fedf26 | ||
|
|
1f9aaf1ac0 | ||
|
|
3926de3fa1 | ||
|
|
9086f18413 | ||
|
|
ad2576bc76 | ||
|
|
4523407d9d | ||
|
|
53162ac934 | ||
|
|
1dac51d4ba | ||
|
|
13deef3416 | ||
|
|
b887bebb26 | ||
|
|
d51160babb | ||
|
|
44b9e7a398 | ||
|
|
065cd113c1 | ||
|
|
a5f618b7e4 | ||
|
|
a06e4e862d | ||
|
|
084c858446 | ||
|
|
5d889d4d95 | ||
|
|
f589e094b2 | ||
|
|
2eff798d74 | ||
|
|
239cecff9e | ||
|
|
fa36af454e | ||
|
|
458d0f7a1b | ||
|
|
f1f27ffd33 | ||
|
|
4a2e09e8eb | ||
|
|
4b3fada646 | ||
|
|
690befc98b | ||
|
|
c6b3c0f9eb | ||
|
|
b423a71b38 | ||
|
|
e226773066 | ||
|
|
98918d160a | ||
|
|
969298fb58 | ||
|
|
bc60f3b45a | ||
|
|
f7a4785d22 | ||
|
|
1bf41cbfd7 | ||
|
|
e9d80423ae | ||
|
|
928ffbd7af | ||
|
|
25ebad2448 | ||
|
|
dd951dc0ed | ||
|
|
aed9e44b6b | ||
|
|
1fa2547aff | ||
|
|
164ceb845e | ||
|
|
ce463686dc | ||
|
|
92aa28cac2 | ||
|
|
f8674230ed | ||
|
|
326969e488 | ||
|
|
155e8c2a4b | ||
|
|
7f95f6b43f | ||
|
|
b2d7ba1a65 | ||
|
|
b550bbbd08 | ||
|
|
cc79977ac0 | ||
|
|
4cbb75df60 | ||
|
|
7d8926659e | ||
|
|
37ca3f74c7 | ||
|
|
56cbf39d59 | ||
|
|
a783e7f500 | ||
|
|
2f4ac42684 | ||
|
|
ec689e100e | ||
|
|
6f0909d992 | ||
|
|
0092dec49e | ||
|
|
3447689c19 | ||
|
|
d4563fd842 | ||
|
|
d2ecec6009 | ||
|
|
26f26aec50 | ||
|
|
34dfc2767f | ||
|
|
bc3d3cabce | ||
|
|
23d80b93ae | ||
|
|
0d59d3d195 | ||
|
|
192427cf80 | ||
|
|
98e4094d70 | ||
|
|
e88b939866 | ||
|
|
aed5a02ee1 | ||
|
|
9f1e1bb2b6 | ||
|
|
15e9383d0b | ||
|
|
4ec056900c | ||
|
|
983feb59b0 | ||
|
|
8b2ce33f83 | ||
|
|
44b452b30b | ||
|
|
f60c9981b8 | ||
|
|
7c3df93580 | ||
|
|
601d7dfcf8 | ||
|
|
41cb53a550 | ||
|
|
87c13ae20c | ||
|
|
c30482af66 | ||
|
|
5f4f2f10d7 | ||
|
|
ae630a0e18 | ||
|
|
c66931cb12 | ||
|
|
fb2dbe25cd | ||
|
|
bf4831be9f | ||
|
|
0f83082cf0 | ||
|
|
fbfde604e7 | ||
|
|
5e2dfbed73 | ||
|
|
571602d3de | ||
|
|
85693f2577 | ||
|
|
60bc8ad888 | ||
|
|
9bf309eb5c | ||
|
|
eeddfa87b6 | ||
|
|
861fe2466c | ||
|
|
3a2cce78ca | ||
|
|
4e01f1de0d | ||
|
|
4f0a5e854c | ||
|
|
c669a9c808 | ||
|
|
e4d827256f | ||
|
|
1c1ad2725e | ||
|
|
d18e54c5a3 | ||
|
|
7a885e23d3 | ||
|
|
33dde3a465 | ||
|
|
8d4ac59f03 | ||
|
|
4fbfc492ce | ||
|
|
ac1fb71bc5 | ||
|
|
01d6b37b83 | ||
|
|
2284b4dce1 | ||
|
|
b92f80e6be | ||
|
|
bd5596fa30 | ||
|
|
4614b63c30 | ||
|
|
cf41b6cbb2 | ||
|
|
87f3e0e291 | ||
|
|
1997abcde6 | ||
|
|
8b761298ee | ||
|
|
df0356b841 | ||
|
|
dc158ebff4 | ||
|
|
f8192b90f4 | ||
|
|
5237049e2c | ||
|
|
9d70fb261e | ||
|
|
8370b8a743 | ||
|
|
25656d81a1 | ||
|
|
be97791428 | ||
|
|
18b683d22e | ||
|
|
34e9742f71 | ||
|
|
087eb7777e | ||
|
|
c469c5e3a1 | ||
|
|
485c29bb49 | ||
|
|
aa0cf150bd | ||
|
|
91c52e91cd | ||
|
|
a1f4014738 | ||
|
|
8a136ac4f6 | ||
|
|
ea43b3dc6a | ||
|
|
57fc787819 | ||
|
|
a0164b8de3 | ||
|
|
9df35c1814 | ||
|
|
9d2a5fee86 | ||
|
|
a6b2e932fa | ||
|
|
05aaa2634b | ||
|
|
2c98f5c542 | ||
|
|
d246fca9df | ||
|
|
c79feb405c | ||
|
|
b1145f8da3 | ||
|
|
3588d681a2 | ||
|
|
55d894ae1f | ||
|
|
0509caf432 | ||
|
|
5d1d807e78 | ||
|
|
22ba38b2e0 | ||
|
|
42f858267c | ||
|
|
5b1a3173f8 | ||
|
|
97822bd9a8 | ||
|
|
b926f5a692 | ||
|
|
191dfdf13f | ||
|
|
a100f1fe1e | ||
|
|
471ccf281c | ||
|
|
6c959be188 | ||
|
|
010887f18f | ||
|
|
e4b5e4c502 | ||
|
|
2de3b4f92e | ||
|
|
225a93330d | ||
|
|
41b36649a9 | ||
|
|
55c26ab1c4 | ||
|
|
8b1e60a1fd | ||
|
|
ed0f38748b | ||
|
|
7d75a720ca | ||
|
|
5a38db166f | ||
|
|
3c9fb79263 | ||
|
|
98d5a2dad9 | ||
|
|
4ab8b4984d | ||
|
|
f741ad058e | ||
|
|
2512a4fc32 | ||
|
|
5554d5e608 | ||
|
|
48d5ed2ff9 | ||
|
|
2632668d0e | ||
|
|
f46e444aa2 | ||
|
|
1149ad86a2 | ||
|
|
e1398d98b0 | ||
|
|
8133e2b7aa | ||
|
|
ebe678b18b | ||
|
|
509217606c | ||
|
|
6fb694669c | ||
|
|
5a63172a86 | ||
|
|
93dcc7e242 | ||
|
|
243eea33e9 | ||
|
|
38a8367f62 | ||
|
|
a02953cccc | ||
|
|
f13f96967e | ||
|
|
f7c31281e0 | ||
|
|
ac34a43d9b | ||
|
|
c8d0950979 | ||
|
|
a4ff59eccb | ||
|
|
c3a50f9442 | ||
|
|
57e81ee72e | ||
|
|
05df0a37b1 | ||
|
|
25f2c3a8c1 | ||
|
|
c3aa12db78 | ||
|
|
a3bd7cc644 | ||
|
|
5a186b6960 | ||
|
|
639588fa68 | ||
|
|
f9510c54b2 | ||
|
|
3a8ddf8025 | ||
| a2801ea88c | |||
|
|
c2aae7ee18 | ||
|
|
9d0804eff4 | ||
|
|
6ff84b3693 | ||
|
|
b641e0282b | ||
|
|
37a467cabc | ||
|
|
e01a7110ac | ||
|
|
5dad5de548 | ||
|
|
295325a20b | ||
|
|
df0a9bac96 | ||
|
|
cf9cef6f87 | ||
|
|
f92814b24b | ||
|
|
331b59cd1e | ||
|
|
ed6d211aff | ||
|
|
a8f1469d36 | ||
|
|
74cb332706 | ||
|
|
89165369b1 | ||
|
|
a54e3d33a6 | ||
|
|
0f17984f41 | ||
|
|
6527ceb913 | ||
|
|
64a57ba837 | ||
|
|
74da9330f8 | ||
|
|
4fa1dd6860 | ||
|
|
e55effc9ca | ||
|
|
924b862f7c | ||
|
|
5f077762d5 | ||
|
|
1b0ec50711 | ||
|
|
22f320f1c4 | ||
|
|
9949476584 | ||
|
|
1b5b170557 | ||
|
|
925a3a4bdb | ||
|
|
720fe9f95f | ||
|
|
7c4f8e1443 | ||
|
|
79d24dff46 | ||
|
|
928435a31d | ||
|
|
26ac92eaa3 | ||
|
|
2974c08b08 | ||
|
|
5a7b0b3050 | ||
|
|
cc2d9bbdd1 | ||
|
|
e912152784 | ||
|
|
d00681f623 | ||
|
|
52942c3d2a | ||
|
|
dedabdd8d8 | ||
|
|
3061499860 | ||
|
|
20f18755ed | ||
|
|
333f2aba54 | ||
|
|
1cb673ed80 | ||
|
|
57e0bc784a | ||
|
|
425afa93da | ||
|
|
259b2cebc7 | ||
|
|
06589826c8 | ||
|
|
2245cd982a | ||
|
|
065b50769f | ||
|
|
d1e99852bc | ||
|
|
bf8f6ae687 | ||
|
|
4ad6805ea1 | ||
|
|
4f4e3f0b89 | ||
|
|
b51f2c1ec8 | ||
|
|
1bec06fd75 | ||
|
|
451d2e2d1d | ||
|
|
9cee12f9f4 | ||
|
|
f5d6011a77 | ||
|
|
64b2ff19ea | ||
|
|
9c17a64773 | ||
|
|
69c877ff66 | ||
|
|
ac557ffedc | ||
|
|
b1e41bc583 | ||
|
|
a144395ec9 | ||
|
|
0cf9d05489 | ||
|
|
0e6c0a41d0 | ||
|
|
8a83652d08 | ||
|
|
91ee72a8d6 | ||
|
|
91b1c8c13c | ||
|
|
26effeabe6 | ||
|
|
611c9740ed | ||
|
|
2a048c3228 | ||
|
|
f4fd83d999 | ||
|
|
cf68585808 | ||
|
|
9f9e926cff | ||
|
|
9dc0dd75fd | ||
|
|
9f285710f8 | ||
|
|
ee1c448327 | ||
|
|
e85bf1713e | ||
|
|
7341c71277 | ||
|
|
d579863311 | ||
|
|
c79cfc19aa | ||
|
|
997a6be267 | ||
|
|
762bb3006d | ||
|
|
07f2bd8cab | ||
|
|
daf83db9b3 | ||
|
|
55c6e19af4 | ||
|
|
9cadc94c5a | ||
|
|
cacdf9ef56 | ||
|
|
a3042fc6c0 | ||
|
|
3efd492525 | ||
|
|
55a5cbc356 | ||
|
|
2887b024ab | ||
|
|
917303edb9 | ||
|
|
c712c15a30 | ||
|
|
0c541db8e0 | ||
|
|
603b1520d7 | ||
|
|
c7eb8ad5c8 | ||
|
|
0b285aa7f4 | ||
|
|
e8665bd00c | ||
|
|
fb4eaa4332 | ||
|
|
874a9eae76 | ||
|
|
8230520956 | ||
|
|
66da9675f1 | ||
|
|
61ff48c005 | ||
|
|
5fc8a1e52a | ||
|
|
df1ac74069 | ||
|
|
91f718bf38 | ||
|
|
46e987e393 | ||
|
|
a59b4f7be7 | ||
|
|
ca66e3c35c | ||
|
|
31e2ad8cba | ||
|
|
320f32895a | ||
|
|
d4db131d3c | ||
|
|
27c2c9045e | ||
|
|
f97b7c7d62 | ||
|
|
9eb33d31ac | ||
|
|
6dd84d6fc2 | ||
|
|
daddd60581 | ||
|
|
e832723ee4 | ||
|
|
35f4a63c0e | ||
|
|
28478ea4ac | ||
|
|
0f93a68694 | ||
|
|
1a2d5d13b3 | ||
|
|
5f2894fc5b | ||
|
|
66cb39dce2 | ||
|
|
d44c871c54 | ||
|
|
dff0378ba8 | ||
|
|
4f7b1b7140 | ||
|
|
662514304b | ||
|
|
0913e9e0c0 | ||
|
|
d403733627 | ||
|
|
fae5b439d0 | ||
|
|
bc66de6d85 | ||
|
|
57e598156c | ||
|
|
b570e44ee4 | ||
|
|
b115d0b55b | ||
|
|
03fc1419de | ||
|
|
8b71c0d737 | ||
|
|
a833aa0a00 | ||
|
|
52332b04ac | ||
|
|
529488cab0 | ||
|
|
b2429a7ca3 | ||
|
|
0bce12d3b4 | ||
|
|
75f557cb18 | ||
|
|
035dd1fad1 | ||
|
|
d2df79a665 | ||
|
|
103e0a13bb | ||
|
|
d1b5eec84a | ||
|
|
6b1e6b31ed | ||
|
|
f6f6db913e | ||
|
|
ec0a6416fd | ||
|
|
1787402be9 | ||
|
|
5f6d99f138 | ||
|
|
d1e2b0d1f8 | ||
|
|
c2c51bbe33 | ||
|
|
bb806e3853 | ||
|
|
a11d0e75c8 | ||
|
|
5406ff0ea3 | ||
|
|
c88317b44a | ||
|
|
6898062d66 | ||
|
|
6e1f264e6a | ||
|
|
082be14232 | ||
|
|
231aa44d03 | ||
|
|
cdb12f35d4 | ||
|
|
e831ee8b44 | ||
|
|
40e9a6082f | ||
|
|
53cec292d0 | ||
|
|
3f85309ee5 | ||
|
|
70944f8521 | ||
|
|
2ab00c42ff | ||
|
|
a572742caa | ||
|
|
400e5f8580 | ||
|
|
74f0fd89b6 | ||
|
|
09f9bea7a2 | ||
|
|
8bffb38117 | ||
|
|
eb5d545c35 | ||
|
|
a79a545730 | ||
|
|
3863dab944 | ||
|
|
e53b7ecd8b | ||
|
|
7d88673ed5 | ||
|
|
41f2da71e4 | ||
|
|
cb4ee0fd74 | ||
|
|
088381a5a6 | ||
|
|
629af2a697 | ||
|
|
884edfd497 | ||
|
|
83dd9e462e | ||
|
|
70a6dfd98a | ||
|
|
7f5d96382b | ||
|
|
fd4d1c4bb7 | ||
|
|
7eaf3de572 | ||
|
|
4b608de3fb | ||
|
|
b47e6e852b | ||
|
|
a8a8ce4a36 | ||
|
|
c61376d5a1 | ||
|
|
d3a5bb08d3 | ||
|
|
f1506d0dbd | ||
|
|
15e6d4959e | ||
|
|
41216fd1cd | ||
|
|
b8786866db | ||
|
|
82bd1bead4 | ||
|
|
6e2bdcad79 | ||
|
|
ef3c9f3d03 | ||
|
|
5427f24df2 | ||
|
|
b374340303 | ||
|
|
c78ed2c6ad | ||
|
|
3b02bc8cf1 | ||
|
|
c7e48a7e76 | ||
|
|
77d125c03d | ||
|
|
8aa52aeefd | ||
|
|
0bab038454 | ||
|
|
6c3b49f4d0 | ||
|
|
03dd689f17 | ||
|
|
c375c948c0 | ||
|
|
cbcf457ce3 | ||
|
|
4855f825e2 | ||
|
|
85bc1637f2 | ||
|
|
73398b83a9 | ||
|
|
2727e66d40 | ||
|
|
8b6be5a501 | ||
|
|
4fee29307c | ||
|
|
35f8249c67 | ||
|
|
d1467a14b8 | ||
|
|
3e6b9eb74d | ||
|
|
ce2e8fb4b5 | ||
|
|
7eaa75c05d | ||
|
|
e86de4483a | ||
|
|
203a74713f | ||
|
|
59ed2a6793 | ||
|
|
a03283ce64 | ||
|
|
984cdaeb03 | ||
|
|
a1ed4a9171 | ||
|
|
93caf8e549 | ||
|
|
3841942153 | ||
|
|
5706877b67 | ||
|
|
d60900262b | ||
|
|
54ea34400b | ||
|
|
db2ab8841a | ||
|
|
adba93ae0a | ||
|
|
98587d04a7 | ||
|
|
0051b64648 | ||
|
|
603009ba15 | ||
|
|
adb9809692 | ||
|
|
06eb10d2a0 | ||
|
|
2244299bd9 | ||
|
|
6ca06ecafb | ||
|
|
9a5958f80b | ||
|
|
2b53ac057c | ||
|
|
5deba8af41 | ||
|
|
3c54a663b8 | ||
|
|
1fd65452c4 | ||
|
|
30646ccb07 | ||
|
|
5be7249a30 | ||
|
|
067af18103 | ||
|
|
8dbd2a72a7 | ||
|
|
c29e131a3b | ||
|
|
a9e30c1e49 | ||
|
|
972c8c6b61 | ||
|
|
2007ff7546 | ||
|
|
64694580cd | ||
|
|
deaab94494 | ||
|
|
1509e1f89d | ||
|
|
29e1ddc2ff | ||
|
|
1fe6434563 | ||
|
|
0367b7e77d | ||
|
|
e6da85bf64 | ||
|
|
cd19fcdadd | ||
|
|
1954f02cfb | ||
|
|
39b23200b0 | ||
|
|
0644d6d965 | ||
|
|
a075694d8e | ||
|
|
b1ea5a9a35 | ||
|
|
00087cbb6b | ||
|
|
1b48ea20c4 | ||
|
|
3d0f019fc4 | ||
|
|
a08bfc183f | ||
|
|
c5aef9b051 | ||
|
|
fc2655ecd6 | ||
|
|
a737c723d3 | ||
|
|
37aa8b62b0 | ||
|
|
a401173f6d | ||
|
|
ce76dc4279 | ||
|
|
1025bd857b | ||
|
|
025802b2d0 | ||
|
|
adbcb2cd31 | ||
|
|
c47a563790 | ||
|
|
04c09d1a5b | ||
|
|
323da8272a | ||
|
|
38700c79fc | ||
|
|
d504d1890a | ||
|
|
d53e757cfb | ||
|
|
4983239458 | ||
|
|
376985828a | ||
|
|
dce0a26820 | ||
|
|
14e0a67e7d | ||
|
|
1656947764 | ||
|
|
647862cdbd | ||
|
|
4a8d83838c | ||
|
|
8acf8e181d | ||
|
|
2df9920209 | ||
|
|
1a6c6b5420 | ||
|
|
edc56d44d6 | ||
|
|
ef4eff0195 | ||
|
|
df8d45bf66 | ||
|
|
89a27619ff | ||
|
|
387a86969a | ||
|
|
acb5059d17 | ||
|
|
a4002d2617 | ||
|
|
a63a90bbd0 | ||
|
|
d25f96dd24 | ||
|
|
e8febe6508 | ||
|
|
ad3a930c6a | ||
|
|
be41c1de76 | ||
|
|
d528978667 | ||
|
|
827fcf69d2 | ||
|
|
711ff545e0 | ||
|
|
5befa31050 | ||
|
|
8e5c2d0ebb | ||
|
|
f95fceeb3d | ||
|
|
003b20dbf0 | ||
|
|
cd9bbaa4b6 | ||
|
|
71e622bf72 | ||
|
|
2a065a08df | ||
|
|
6087228378 | ||
|
|
efd74e0d7b | ||
|
|
b68a9dcc4f | ||
|
|
008855daa9 | ||
|
|
7a9d36de2a | ||
|
|
c56e982c9a | ||
|
|
002cc171a2 | ||
|
|
32e721b47a | ||
|
|
1e82f697a9 | ||
|
|
fa09631e32 | ||
|
|
e06436ce1e | ||
|
|
b2f443e1ad | ||
|
|
2e07be0cf7 | ||
|
|
bf0b14d094 | ||
|
|
c9f5803194 | ||
|
|
5293560c02 | ||
|
|
c49823aa9d |
@@ -1,41 +0,0 @@
|
||||
version: '{branch}.{build}'
|
||||
clone_depth: 1
|
||||
skip_tags: true
|
||||
|
||||
environment:
|
||||
MSYSTEM: MINGW32
|
||||
|
||||
init:
|
||||
- git config --global core.autocrlf input
|
||||
|
||||
install:
|
||||
- set PATH=c:\msys64\mingw32\bin;c:\msys64\usr\bin;c:\msys64\bin;%PATH%
|
||||
- echo %PATH%
|
||||
- pacman -S --noconfirm --needed make ninja mingw-w64-i686-libusb mingw-w64-i686-sqlite3 mingw-w64-i686-zlib mingw-w64-i686-gcc zip
|
||||
|
||||
build_script:
|
||||
- make
|
||||
- zip -9 fluxengine.zip fluxengine.exe brother120tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
|
||||
|
||||
artifacts:
|
||||
- path: fluxengine.zip
|
||||
name: fluxengine.zip
|
||||
|
||||
deploy:
|
||||
release: FluxEngine Windows client version $(APPVEYOR_BUILD_NUMBER)
|
||||
description: >
|
||||
This is an automatically built version of the FluxEngine Windows client
|
||||
which is generated whenever a significant checkin has happened. It's had
|
||||
no testing whatsoever.
|
||||
|
||||
To use, download it, put it somewhere, and then run it from a cmd window
|
||||
or other command line shell.
|
||||
provider: GitHub
|
||||
auth_token:
|
||||
secure: dfJjj7fWCoDUz+Ni11OcNPB/U3TNJFwNA2AsL++ChFjniUsZLlC6SDWHiL/t4FZo
|
||||
artifact: fluxengine.zip
|
||||
draft: false
|
||||
prerelease: false
|
||||
on:
|
||||
branch: master
|
||||
|
||||
44
.clang-format
Normal file
44
.clang-format
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
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'
|
||||
BreakBeforeBraces: Allman
|
||||
BreakConstructorInitializers: 'AfterColon'
|
||||
BreakInheritanceList: AfterColon
|
||||
BreakStringLiterals: 'true'
|
||||
ColumnLimit: '80'
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
|
||||
FixNamespaceComments: 'false'
|
||||
IncludeBlocks: Preserve
|
||||
IndentCaseLabels: 'true'
|
||||
IndentWidth: '4'
|
||||
IndentWrappedFunctionNames: 'false'
|
||||
KeepEmptyLinesAtTheStartOfBlocks: 'true'
|
||||
NamespaceIndentation: All
|
||||
PointerAlignment: Left
|
||||
ReflowComments: 'true'
|
||||
SortIncludes: 'false'
|
||||
SortUsingDeclarations: 'true'
|
||||
SpaceAfterTemplateKeyword: 'true'
|
||||
SpaceBeforeAssignmentOperators: 'true'
|
||||
SpaceBeforeCtorInitializerColon: 'false'
|
||||
SpaceBeforeInheritanceColon: 'true'
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: 'false'
|
||||
...
|
||||
139
.github/workflows/ccpp.yml
vendored
139
.github/workflows/ccpp.yml
vendored
@@ -2,33 +2,134 @@ name: C/C++ CI
|
||||
|
||||
on: [push]
|
||||
|
||||
concurrency:
|
||||
group: environment-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'davidgiven/fluxengine'
|
||||
path: 'fluxengine'
|
||||
submodules: 'true'
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'davidgiven/fluxengine-testdata'
|
||||
path: 'fluxengine-testdata'
|
||||
- name: apt
|
||||
run: sudo apt install libusb-1.0-0-dev libsqlite3-dev ninja-build
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install libudev-dev libsqlite3-dev protobuf-compiler libwxgtk3.2-dev libfmt-dev libprotobuf-dev libmagic-dev libmbedtls-dev libcurl4-openssl-dev libmagic-dev nlohmann-json3-dev libdbus-1-dev libglfw3-dev libmd4c-dev libfreetype-dev libcli11-dev libboost-regex-dev
|
||||
- name: make
|
||||
run: make
|
||||
run: CXXFLAGS="-Wp,-D_GLIBCXX_ASSERTIONS" make -j`nproc` -C fluxengine
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
#build-linux-debian-11:
|
||||
# runs-on: ubuntu-22.04
|
||||
# container: debian:11
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
# with:
|
||||
# repository: 'davidgiven/fluxengine'
|
||||
# path: 'fluxengine'
|
||||
# - uses: actions/checkout@v4
|
||||
# with:
|
||||
# repository: 'davidgiven/fluxengine-testdata'
|
||||
# path: 'fluxengine-testdata'
|
||||
# - name: apt update
|
||||
# run: apt update
|
||||
# - name: apt
|
||||
# run: >
|
||||
# apt install -y python3 make xz-utils python3 python3-hamcrest
|
||||
# protobuf-compiler libprotobuf-dev libsqlite3-dev
|
||||
# libfmt-dev libprotobuf-dev wx-common pkg-config
|
||||
# libudev-dev g++ libwxgtk3.0-gtk3-dev
|
||||
# - name: make
|
||||
# run: make -C fluxengine
|
||||
|
||||
build-macos-current:
|
||||
strategy:
|
||||
matrix:
|
||||
runs-on: [macos-15, macos-15-intel]
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'davidgiven/fluxengine'
|
||||
path: 'fluxengine'
|
||||
submodules: 'true'
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'davidgiven/fluxengine-testdata'
|
||||
path: 'fluxengine-testdata'
|
||||
- name: brew
|
||||
run: brew install sqlite pkg-config libusb ninja
|
||||
run: |
|
||||
brew install sqlite pkg-config libusb protobuf wxwidgets fmt make coreutils dylibbundler libjpeg libmagic nlohmann-json cli11 boost glfw3 md4c ninja python freetype2 mbedtls
|
||||
brew upgrade
|
||||
- name: make
|
||||
run: make
|
||||
run: gmake -C fluxengine
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ github.event.repository.name }}.${{ github.sha }}.fluxengine.${{ runner.arch }}.pkg
|
||||
path: |
|
||||
fluxengine/FluxEngine.pkg
|
||||
fluxengine/FluxEngine.app.zip
|
||||
|
||||
# build-windows:
|
||||
# runs-on: windows-latest
|
||||
# steps:
|
||||
# - uses: numworks/setup-msys2@v1
|
||||
# with:
|
||||
# msystem: MSYS
|
||||
# - uses: actions/checkout@v1
|
||||
# - name: pacman
|
||||
# run: msys2do pacman -S --noconfirm --needed make ninja mingw-w64-i686-libusb mingw-w64-i686-sqlite3 mingw-w64-i686-zlib mingw-w64-i686-gcc zip
|
||||
# - name: make
|
||||
# run: msys2do make
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
|
||||
steps:
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: mingw64
|
||||
update: true
|
||||
install: |
|
||||
python diffutils ninja make zip
|
||||
pacboy: |
|
||||
protobuf:p pkgconf:p curl-winssl:p file:p glfw:p mbedtls:p
|
||||
sqlite:p freetype:p boost:p gcc:p binutils:p nsis:p abseil-cpp:p
|
||||
|
||||
- name: debug
|
||||
run: |
|
||||
pacboy -Q --info protobuf:p
|
||||
cat /mingw64/lib/pkgconfig/protobuf.pc
|
||||
/mingw64/bin/pkg-config.exe protobuf --cflags
|
||||
/mingw64/bin/pkg-config.exe protobuf --cflags --static
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'davidgiven/fluxengine'
|
||||
path: 'fluxengine'
|
||||
submodules: 'true'
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'davidgiven/fluxengine-testdata'
|
||||
path: 'fluxengine-testdata'
|
||||
|
||||
- name: run
|
||||
run: |
|
||||
make -C fluxengine BUILDTYPE=windows
|
||||
|
||||
- name: nsis
|
||||
run: |
|
||||
cd fluxengine
|
||||
strip fluxengine.exe -o fluxengine-stripped.exe
|
||||
strip fluxengine-gui.exe -o fluxengine-gui-stripped.exe
|
||||
makensis -v2 -nocd -dOUTFILE=fluxengine-installer.exe extras/windows-installer.nsi
|
||||
|
||||
- name: zip
|
||||
run: |
|
||||
cd fluxengine && zip -9 fluxengine-windows.zip fluxengine.exe fluxengine-gui.exe upgrade-flux-file.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex fluxengine-installer.exe
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ github.event.repository.name }}.${{ github.sha }}.windows.zip
|
||||
path: fluxengine/fluxengine-windows.zip
|
||||
|
||||
134
.github/workflows/release.yml
vendored
Normal file
134
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
name: Autorelease
|
||||
|
||||
concurrency:
|
||||
group: environment-release-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
|
||||
jobs:
|
||||
dev-release:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: setup WSL
|
||||
run: |
|
||||
curl -L https://github.com/WhitewaterFoundry/Fedora-Remix-for-WSL/releases/download/41.0.0/Fedora-Remix-for-WSL-SL_41.0.0.0_x64_arm64.msixbundle -o fedora.msixbundle
|
||||
unzip fedora.msixbundle Fedora-Remix-for-WSL-SL_41.0.0.0_x64.msix
|
||||
unzip Fedora-Remix-for-WSL-SL_41.0.0.0_x64.msix install.tar.gz
|
||||
wsl --update
|
||||
wsl --set-default-version 1
|
||||
wsl --import fedora fedora install.tar.gz
|
||||
wsl --set-default fedora
|
||||
wsl sh -c 'dnf -y install https://github.com/rpmsphere/noarch/raw/master/r/rpmsphere-release-40-1.noarch.rpm'
|
||||
wsl sh -c 'dnf -y install gcc gcc-c++ protobuf-c-compiler protobuf-devel fmt-devel systemd-devel sqlite-devel wxGTK-devel mingw32-gcc mingw32-gcc-c++ mingw32-zlib-static mingw32-protobuf-static mingw32-sqlite-static mingw32-wxWidgets3-static mingw32-libpng-static mingw32-libjpeg-static mingw32-libtiff-static mingw32-nsis png2ico ninja-build'
|
||||
|
||||
- name: fix line endings
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.eol lf
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'davidgiven/fluxengine'
|
||||
path: 'fluxengine'
|
||||
|
||||
- name: run
|
||||
run: |
|
||||
wsl sh -c 'cd fluxengine && make BUILDTYPE=windows -j$(nproc)'
|
||||
|
||||
- name: nsis
|
||||
run: |
|
||||
wsl sh -c 'cd fluxengine && strip fluxengine.exe -o fluxengine-stripped.exe'
|
||||
wsl sh -c 'cd fluxengine && strip fluxengine-gui.exe -o fluxengine-gui-stripped.exe'
|
||||
wsl sh -c 'cd fluxengine && makensis -v2 -nocd -dOUTFILE=fluxengine-installer.exe extras/windows-installer.nsi'
|
||||
|
||||
- name: zip
|
||||
run: |
|
||||
wsl sh -c 'cd fluxengine && zip -9 fluxengine-windows.zip fluxengine.exe fluxengine-gui.exe upgrade-flux-file.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex fluxengine-installer.exe'
|
||||
|
||||
- name: date
|
||||
run: |
|
||||
echo "RELEASE_DATE=$(date --rfc-3339=date)" >> ${GITHUB_ENV}
|
||||
|
||||
- name: tag
|
||||
uses: EndBug/latest-tag@latest
|
||||
with:
|
||||
tag-name: dev
|
||||
force-branch: false
|
||||
git-directory: 'fluxengine'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: delete-old-assets
|
||||
uses: mknejp/delete-release-assets@v1
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
tag: dev
|
||||
assets: |
|
||||
fluxengine.zip
|
||||
fluxengine-installer.exe
|
||||
fail-if-no-assets: false
|
||||
|
||||
- name: release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
name: Development build ${{ env.RELEASE_DATE }}
|
||||
files: |
|
||||
fluxengine/fluxengine.zip
|
||||
fluxengine/fluxengine-installer.exe
|
||||
tag_name: dev
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
build-macos:
|
||||
strategy:
|
||||
matrix:
|
||||
runs-on: [macos-13, macos-latest]
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: brew
|
||||
run: brew install sqlite pkg-config libusb protobuf wxwidgets fmt make coreutils dylibbundler libjpeg
|
||||
|
||||
- name: make
|
||||
run: |
|
||||
gmake
|
||||
mv FluxEngine.pkg FluxEngine-${{ runner.arch }}.pkg
|
||||
mv FluxEngine.app.zip FluxEngine-${{ runner.arch }}.app.zip
|
||||
|
||||
- name: tag
|
||||
uses: EndBug/latest-tag@latest
|
||||
with:
|
||||
tag-name: dev
|
||||
force-branch: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: delete-old-assets
|
||||
uses: mknejp/delete-release-assets@v1
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
tag: dev
|
||||
assets: |
|
||||
FluxEngine-${{ runner.arch }}.pkg
|
||||
FluxEngine-${{ runner.arch }}.app.zip
|
||||
fail-if-no-assets: false
|
||||
|
||||
- name: release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
name: Development build ${{ env.RELEASE_DATE }}
|
||||
files: |
|
||||
FluxEngine-${{ runner.arch }}.pkg
|
||||
FluxEngine-${{ runner.arch }}.app.zip
|
||||
tag_name: dev
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,2 +1,10 @@
|
||||
.obj
|
||||
.project
|
||||
/.ninja*
|
||||
/brother120tool
|
||||
/brother120tool-*
|
||||
/brother240tool
|
||||
/brother240tool-*
|
||||
/fluxengine
|
||||
/fluxengine-*
|
||||
/upgrade-flux-file
|
||||
|
||||
36
.gitmodules
vendored
Normal file
36
.gitmodules
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
[submodule "dep/imhex"]
|
||||
path = dep/imhex
|
||||
url = git@github.com:davidgiven/ImHex.git
|
||||
[submodule "dep/libwolv"]
|
||||
path = dep/libwolv
|
||||
url = https://github.com/WerWolv/libwolv.git
|
||||
[submodule "dep/imgui"]
|
||||
path = dep/imgui
|
||||
url = https://github.com/ocornut/imgui.git
|
||||
[submodule "dep/pattern-language"]
|
||||
path = dep/pattern-language
|
||||
url = https://github.com/WerWolv/PatternLanguage.git
|
||||
[submodule "dep/native-file-dialog"]
|
||||
path = dep/native-file-dialog
|
||||
url = https://github.com/btzy/nativefiledialog-extended.git
|
||||
[submodule "dep/xdgpp"]
|
||||
path = dep/xdgpp
|
||||
url = https://github.com/WerWolv/xdgpp.git
|
||||
[submodule "dep/libromfs"]
|
||||
path = dep/libromfs
|
||||
url = https://github.com/WerWolv/libromfs.git
|
||||
[submodule "dep/throwing_ptr"]
|
||||
path = dep/throwing_ptr
|
||||
url = https://github.com/rockdreamer/throwing_ptr.git
|
||||
[submodule "dep/lunasvg"]
|
||||
path = dep/lunasvg
|
||||
url = https://github.com/sammycage/lunasvg.git
|
||||
[submodule "dep/md4c"]
|
||||
path = dep/md4c
|
||||
url = https://github.com/mity/md4c
|
||||
[submodule "dep/nlohmann_json"]
|
||||
path = dep/nlohmann_json
|
||||
url = https://github.com/nlohmann/json
|
||||
[submodule "dep/cli11"]
|
||||
path = dep/cli11
|
||||
url = https://github.com/CLIUtils/CLI11
|
||||
@@ -1,250 +1,250 @@
|
||||
:4000000000800020110000007910000079100000064A08B5136843F020031360044B1A6803F53F5302331A6001F044F8E8460040FA46004010B5054C237833B9044B13B1D7
|
||||
:400040000448AFF300800123237010BD6081FF1F0000000028360000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF00000000EB
|
||||
:400080006481FF1F28360000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F00F030133136011685368994202BF024B01221A72704700BF8081FF1F9E
|
||||
:4000C0000F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F00F030133536051681368994202BF024B01221A72704700BF8081FF1F0F000080024B012200205A722B
|
||||
:4001000002F086B88081FF1F10B5C4B2204601F077F90128FAD110BD08B572B60F4B0F49DA680132DA601A690132C82A08BF00221A615A6918690132A72A08BF00224A617F
|
||||
:400140005B69002B0CBF02230023002814BF184643F0010002F054FD62B608BD8081FF1F10B504460D4B8278997E91421CBF00225A7702689A610279094B1A718378002BB3
|
||||
:4001800014BF0220012002F0FFFCE07802F0F6FC2079BDE8104002F02DBD00BF8081FF1F9881FF1F70B5C4B220460E4601F030F9314605460246204601F090F9204601F0D1
|
||||
:4001C0001FF90128FAD0284670BD000038B50B4CA57F5DB904F11800FFF7C2FF012001F0ADFF4FF47A7002F04FF96577E36823620123A377BDE8384002F084B98081FF1F9D
|
||||
:4002000038B50446C5B2284601F0A4FF062002F051F944F00200C0B201F09CFF062002F049F9284601F096FFBDE83840062002F02BB910B5642401F087FF20B10120BDE8B6
|
||||
:400240001040FFF7DDBF0120FFF7DAFF013CF2D1F4E7000038B5044D0024285D013402F0D9F8102CF9D138BDA481FF1F08B502F0F3FA002002F0FCFA02F00EFB02F018FB10
|
||||
:4002800080B208BD10B50446012001F07BFF642002F0FAF8FFF7EAFF2080002001F072FF642002F0F1F8FFF7E1FF608010BD08B502F0FEFB002002F007FC02F019FC02F0AB
|
||||
:4002C00023FC80B208BD10B50446FFF7B2FF322002F0DAF8FFF7EBFF20800020FFF790FF322002F0D1F8FFF7E2FF608010BD0FB400B593B014AB53F8042B402102A80193D3
|
||||
:4003000002F07AFD02A801F08AFF01F094FF13B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF7EFFE62782146BDE81040042001F080B83A36000007B50023B1
|
||||
:40034000ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB0000F8B51D4C0646FFF733FF637F03B156B91A48FFF7BEFFFFF75EFF012000236077636302F048
|
||||
:4003800099F83246616B1548FFF7B1FF114D0027636B9E4216D001F0D7FE00B16F63636B9E4205DD0020FFF72BFF6B6B013305E005DA0120FFF724FF6B6B013B6B6302F059
|
||||
:4003C000A1F8E5E7322002F05FF8BDE8F8400448FFF78DBF8081FF1F473600004E3600006B3600002DE9F04F9BB062B602F0F4F8B749042002F018F9B64801F0B1FEB64878
|
||||
:4004000002F0E4FBB54801F0E5FE02F0C5FA02F097F9002002F0B8FB01F000FF0221002000F0A4FFAE4D0321084602F04BF82E462C46DFF8D4B202F065F8AB7F73B12A6A46
|
||||
:40044000EB689B1A41F28832934207D9002001F075FE002002F098FB0023AB7700F0BEFF18B9A048FFF743FF04E000F0BDFF0028F7D109E000F0B2FF0028FBD09A48FFF7F1
|
||||
:4004800036FF032001F016F8032000F0B9FF0128D1D19648FFF764FE95490320FFF782FE94F838109348FFF722FF94F83830023B122B00F2F583DFE813F01300F3031C0015
|
||||
:4004C000F3032200F3034400F3036800F303A001F3033503F3035403F3035A03F303650303238DF828308DF829300B238DF82A3044E394F83A00FFF731FF7F4B3BE3FFF7AA
|
||||
:4005000065FE00236372E068627A02F0FF0132B9EB681B1AB3F57A7FF6DD0B4608E03BB100227272F168627A12B9EB685B1AFAE707228DF8282004228DF82920ADF82A30A6
|
||||
:400540001CE30220FFF7E0FD4FF000090DF1280A4FF480780027C8EB0903DA1907F80A200137402FF9D10220FFF7CEFD3A465146022000F061FFB8F10108EBD109F1010983
|
||||
:40058000B9F1400FE4D15D4BC7E294F83A0001F0F9FD606BFFF7E2FE02F09AFB584BDFF86C811A78002742F004021A701A7842F001021A701A7802F0FE021A701A7802F0A2
|
||||
:4005C000FE021A7002F088FB0220FFF79DFD41F6FF734FF480420121022002F0DBFA84F8780001F0FDFE08F807000137102FF8D1DFF81CA100270AF15D081FFA88F901377D
|
||||
:40060000102F14BF3A4600221AF8010F2244062392F82420402101F017FF4A4646F242419AF8000001F022FF09F14009102F1FFA89F9E4D196F83B3033B100237372637A9C
|
||||
:40064000002BFCD000237372142200210AA802F09FFB40234FF0FF320D9300232360626023722368274F234493F8241094F878000C9701F06DFE94F8780001F02BFE01210C
|
||||
:4006800094F8780001F0FEFD04972368002BFCD000277760D6F80CA0237A7BB901F032FFA98F04E0EB68CAEB03038B4206D2626823689A422ED12B7A002BF3D094F808801C
|
||||
:4006C000042194F878005FFA88F801F04DFE54E019010000F900000091000000C50000008081FF1F793600008C3600009881FF1FB881FF1F963600002C3600002E3600006F
|
||||
:40070000926400400086FF1F9C640040A481FF1FA381FF1F4FF0000962680AA808EB82124A440A92C9F140020B9200F0DBFA0B9A0137C2F1400209EB02030D9A5FFA83F900
|
||||
:400740005AB90220FFF7E0FC4022BD49022000F073FE049B0C9340230D93B9F13F0FDBD96268B84B01321340002BBEBF03F1FF3363F00F03013363608EE794F8780001F0B3
|
||||
:4007800003FE0028F9D10AA800F0E0FA0220FFF7BBFC0D9B402B07D002204022A84900F04BFE0220FFF7B0FC0D9B022033F040021DBFC3F1400292B2A149114600F03CFEEF
|
||||
:4007C0000220FFF7A1FCFFF745FDB8F1000F06D09D48FFF78CFD0220FFF7B0FD06E09B4B09A81B88ADF82430FFF796FD627A3946237A9748FFF77BFD55E29648FFF777FDEF
|
||||
:40080000E76B17F03F0701D003204AE2012001F0BFFC95F83A0001F0B5FC02F059FA9BF80030DFF8348203F0FB038BF800309BF8003043F001038BF800309BF8003003F027
|
||||
:40084000FE038BF800309BF8003003F0FE038BF8003002F041FA686BFFF780FD01214FF4804341F6FF72084601F098FC85F8780001F0B6FD08F807000137102FF8D1DFF88D
|
||||
:40088000DC91002709F15D031FFA83F807930137102F14BF3A46002219F8010F2244052392F82420402101F0CFFD414646F24C4299F8000001F0DAFD08F14008102F1FFA56
|
||||
:4008C00088F8E4D10027B946BA46F36B4FF0FF389B09142239460AA837600493C6F80480377202F055FA402301200D9300F0E2FDCDF81880059701F005FE2268514B01322B
|
||||
:400900001340002BBCBF03F1FF3363F00F036168B8BF01338B4200F0A380BAF1000F07D0237A002B40F0B0806B7A002B40F0AC800B9B002B34D1B9F1000F0BD07F22404926
|
||||
:400940005A540133402BFAD10A910B9328E0BAF1000F06D1012000F053FD01288046F6D107E0237A002B40F08F806B7A002BF1D08AE03349FFF716FC8146314B0B90404665
|
||||
:400980000A9300F097FDB9F13F0F07F1010706DD049BDB1BD3F1000949EB030900E0C1460B9BDBB12368079A0AA802EB83120D9BC3F1400313440C9300F0D7F90D9B6BB9FF
|
||||
:4009C0002A68204B01321340002BBEBF03F1FF3363F00F030133236040230D93636801333ED12B680F2B3BD14FF00008C5F8048001F002FC85F808806B6895F878002B44DD
|
||||
:400A000093F8241001F0A4FC95F8780001F062FC012195F8780001F035FC85F80980637A002BFCD04FF00008012086F8098001F0EFFB404601F0ACFBCDF8188013E000BF78
|
||||
:400A40000086FF1F0F000080A536000030360000BF360000D2360000A481FF1FA381FF1FBAF1000F05D0237A73B96B7A63B94FF0010A6368069A93423FF43DAF059B0133AA
|
||||
:400A800005936B68069336E701F0BCFB012001F07FFB002001F0BCFB042194F8780001F063FC94F8780001F06FFC0028F9D196F8780001F0FDFB737A327A02930123039278
|
||||
:400AC0000193CDF80090059B3A4604997A48FFF70EFCB9F1000F16D1049BBB420ADD012000F08EFC01288046F6D17449FFF75AFB3F2803DC012000F017FD04E0404600F04D
|
||||
:400B0000D9FC0137E8E7FFF7A5FB6D48FFF7EFFB237A0BB10220C4E06A4B1B8809A8ADF824302CE094F83A0001F02CFB606BFFF715FC6548FFF7DBFB00236372637A002B58
|
||||
:400B4000FCD0012001F064FB00237372637A002BFCD0002001F05CFB5C48FFF7C8FB5C4B09E000206077FFF7F9FB5A4B03E05A48FFF7F6FA594B1B88ADF828300AA8FFF72A
|
||||
:400B8000CBFB90E0A37F3BB1002001F0D7FA002001F0FAFF0023AB7701F07CFF002001F01FFF2A2701F04AFE002001F0EDFD3A4600210AA802F0ECF815238DF828308DF8DD
|
||||
:400BC000297001F09FFC002001F0B8FA002001F0DBFFC82001F058FC0DEB0700FFF752FB0DF13E00FFF76FFB01F08CFC012001F0CBFF322001F048FC0DF12E00FFF742FBDC
|
||||
:400C00000DF14200FFF75FFB012001F097FA4FF4967001F039FC01F075FC0DF13600FFF731FB0DF14A00FFF74EFB002001F086FA4FF4967001F028FC01F064FC022001F080
|
||||
:400C4000A3FF322001F020FC0DF13200FFF71AFB0DF14600FFF737FB012001F06FFA4FF4967001F011FC01F04DFC0DF13A00FFF709FB0DF14E00FFF726FB002001F05EFAD2
|
||||
:400C80004FF4967001F000FC01F03CFC002001F07BFF0023637701F07FFE01F051FD6DE70120FFF74BFB032000F004FC0C48FFF71EFBFFF7C0BB00BFDC3600000086FF1F93
|
||||
:400CC0000C370000323600001B370000293700003436000036360000B881FF1F383600003637000010B54268002A2ED0C368002B2BD00368048A591C01601B78013A13F007
|
||||
:400D0000800F817C42601DBF03F0010242EA84030231083114BF43F0020343EA0423817414BF03820382837C072BDCD9028A083B42FA03F38268511C81601370C368013B5A
|
||||
:400D4000C360837C083B8374CDE710BD07B5827C42B102AA002102F8011D026001224260FFF7C0FF03B05DF804FB30B543686BB3C2685AB3827C072A0CD8046890F91050D4
|
||||
:400D8000611C01602178013B41EA05210832018243608274827C018AA2F108042141CBB2090608D5C3F3801363F07F03023A03F08103827402E08474002BD7D08268511C1C
|
||||
:400DC00081601370C368013BC360CFE730BD00002DE9F04172B6854B61221A70A3F5F06301221A801924824A9C7092E803008033062283F8002283E80300522203F5807397
|
||||
:400E00001A707C4B7C4A1B787C4EDBB2137040F618027B4B00251A8041F2512223F8022C33784FF4F07003F0010343EA450501F047FF013C05F003052ED0032DF0D1714B92
|
||||
:400E40004FF480721A8007221A706F4A002548211570917002221D705D7103F8032C0422DA716A4A6A4C13786A4E43F00103137012F8013C062743F0030302F8013C237823
|
||||
:400E8000012243F080032370584B1A70624A137843F02003137000E0FEE707FB056300219A881868013501F073FF072DF5D15B485B4E002550F8041F05F1105303F1520287
|
||||
:400EC00021F0FF075333C9B20B4452005B0002329A4206D012F802EC12F801CC0EF807C0F5E7B0420D44E5D14E4B00221A604E4B4E4F1A684E4BDFF87C811A604D4A137891
|
||||
:400F000043F002031370137C43F0020313742378A2F5863243F040032370413A137843F010031370444A454B07CA03C31A80444A2833106843F8250C127903F8212C414A6C
|
||||
:400F400007CA03C31A80404AE83B07CA03C31A803E4A083307CA03C31A803D4A3D4BA2F5616203CBC2F8100EC2F8141E1378042043F008031370384B02F5AA521B783D7845
|
||||
:400F8000DBB298F80060EDB203F007010C321B091170F6B2537045F003033B7046F0030388F800302D4B48221A702D4A402313702C49937013729372082382F81F322022F7
|
||||
:400FC0000A7048710A72284A0A20137001F072FA264B88F8006044223D70254D1A7094E80F0007C52B80BDE8F08100BF00480040840F00480F010049A14600402542004005
|
||||
:40100000224200400440004006400040A2430040A04300403B370000E8460040FCFFFF47A0000048007600408C0F0048F84600400876004003500140440F0048C05100405C
|
||||
:40104000500F0048580F0048640F0048700F0048325100407C0F0048CF0100491D51004001590040235B0040585B004076580040B0430040F946004008B501F05DFE0368BC
|
||||
:401080000C2B00D1FEE7FEE7084908B50B68084A1844821A802A01DC086005E001F04CFE0C2303604FF0FF33184608BDCC80FF1F4087FF1F80B51148114B0025C0B1A3F1FB
|
||||
:4010C000100192C922460439161BB74204D051F8046F42F8046BF7E7114653F8046C8C1AA64202D041F8045BF9E701381033E5E701F028FEFFF776F9FEE700BF010000004B
|
||||
:401100000C390000124A134B10B51A60124A134C1368134843F4007313600023032B98BF54F823204FEA830188BF0E4A0133302B4250F3D10C4B1A780C4B1A700C4B084AD8
|
||||
:401140001A60FFF745FEBDE8104001F085B800BF0004FA050CED00E014ED00E0000000000080FF1F79100000BC760040C080FF1F08ED00E0F8B501F0ABFD444A01271378CA
|
||||
:40118000022643F001031370137C414C43F001031374404B02F5E3521F700B3203F8946C1378054603F07F031370002001F082F92378394A03F0F90323701378384603F0FF
|
||||
:4011C000DF03137023783B43237001F073F9282001F070F9314B30461A7802F07F021A701A7802F0BF021A7023783343237001F061F923782A4A43F004032370002313701E
|
||||
:401200002846537001F068FD0721172001F09AF92449172001F088F90721182001F092F92149182001F080F90721152001F08AF91E49152001F078F90721052001F082F9BE
|
||||
:401240001B49052001F070F90721062001F07AF91849062001F068F90721084601F072F91549072001F060F90721082001F06AF91249082001F058F907210C2001F062F956
|
||||
:40128000BDE8F8400E490C2001F04EB9A5430040944300409D60004012600040F851004084600040CF19000009180000CD190000011900002D1900005D1900009519000088
|
||||
:4012C000D319000008B51D4B1D4A1870002313701C4A13701C4A13701C4A13701C4A13701C4A13701C4A13701C4B4FF400021A604FF080721A604FF400121A6020221A6076
|
||||
:4013000040221A6080221A604FF480721A60144B19B91A7802F0FE0202E01A7842F001021A70104B03221A70802203F8202C012001F0C6FC0C4B04221A7008BD7C86FF1F6B
|
||||
:401340008286FF1F8086FF1F8186FF1F7D86FF1F6C86FF1F7F86FF1FF486FF1F00E100E009600040286000401260004070B5074C054623780E461BB9FFF7FCFE0123237001
|
||||
:4013800031462846BDE87040FFF79CBF4086FF1F0A4A002313700A4A13700A4A13700A4A13700A4A13700A4A13700A4A13700A4B03221A70802203F8202C70478286FF1FF0
|
||||
:4013C0008086FF1F8186FF1F7D86FF1F6C86FF1F7F86FF1FF486FF1F28600040014B1878704700BF8186FF1F044B1A7802F0FF001AB118780022C0B21A7070478086FF1F53
|
||||
:40140000024A0C2303FB0020407870478886FF1F431E072B0CD8074A064B00010344805C5B7800F00F0043EA0020023880B2704700207047FC5F0040431E072BF0B531D846
|
||||
:40144000194B0C2505FB0035EC88184FA4B2C4F50074A2424FF00C0404FB003488BFEA88E67884BFC2F5007292B2104C46EA12260501EE552C44D6B2667041B90C2202FB17
|
||||
:401480000030002343704379DBB2A370F0BD0E46074F2F44751AADB2AA42EFD916F8015B3D72F7E7F0BD00BF8886FF1FFC5F004070600040431E072B0AD8064A0C2303FB32
|
||||
:4014C000002300225A705A79034BD2B200011A54704700BF8886FF1FFE5F004038B505461446D1B1431E072B19D8FFF791FFA04203D22846FFF78CFF04460A4609482B01AF
|
||||
:401500001844531A9BB29C4203D9037A02F8013BF7E72846FFF7CEFF02E00C4600E00024204638BD70600040431E072B9FBF024B000108221A547047FE5F004030B51A4A02
|
||||
:401540001A491B4D0878138803449BB21380194A00231488D8B2A4B27CB1082B0CD050680078C0B2E85450680133013050601088013880B21080ECE718460B780E4C082BA1
|
||||
:401580000E4A00D040B10E4D2B7883F080032B700F232370022301E0022323701370094B1870087030BD00BFF886FF1FF486FF1F006000407086FF1F6D86FF1F8286FF1F5E
|
||||
:4015C0007E86FF1FF586FF1F074B02221A70074B80221A70064B0F221A70064A00231370054A0120137070478286FF1F7E86FF1F6D86FF1FF486FF1FF586FF1F30B5164B36
|
||||
:4016000016491B780A8803F00F03023BDBB21A4492B20A80124C134A0020118889B279B173B15568215C013BC9B229705168DBB20131516011880130013989B21180ECE7D8
|
||||
:40164000094A1370094A137883F080031370084B0B221A7030BD00BF29600040F886FF1F006000407086FF1FF586FF1F7E86FF1F6D86FF1F064A06231370064A01201370B2
|
||||
:40168000054B80221A70054B00221A70704700BF8286FF1F6D86FF1F7E86FF1FF586FF1F054B9A683AB19A68044910709A680988518000229A6070477086FF1FF886FF1F57
|
||||
:4016C00008B5124B1A78D2B21A701B78DBB21A0602D50F4A137008BD0220FFF7E1FF0D4B1B7803F06003202B05D0402B06D043B900F0D4FB04E001F0F3FA01E000F008FD8B
|
||||
:4017000010B9034B03221A7008BD00BF286000406D86FF1F0060004008B5084A084B0120197813880B449BB21380064B00221A70FFF7B6FF044B03221A7008BDF886FF1F01
|
||||
:40174000F486FF1F8286FF1F6D86FF1F08B50C4B1B78DBB2042B07D0062B09D0022B0DD1BDE80840FFF7D8BFBDE80840FFF746BF0320FFF795FF034B03221A7008BD00BFBB
|
||||
:401780008286FF1F6D86FF1F08B5054B002201201A70FFF785FF034B03221A7008BD00BF8286FF1F6D86FF1F08B50A4B1A7832B11A78094942F080020A7000221A70074B4F
|
||||
:4017C000002201201A70FFF76BFF054B03221A7008BD00BF6C86FF1F086000408286FF1F6D86FF1F074B1B78DBB2042B05D0062B05D0022B05D1FFF7A1BEFFF7C5BFFFF706
|
||||
:40180000D3BF70478286FF1F38B51D4C2378DBB2DD0634D518060AD503F00F03012B2ED1FFF74EFF174B1B78190609D538BD5A0602D5FFF7D7FF03E09D0620D5FFF786FF7F
|
||||
:4018400023781B061BD4104B1A78104B1B7813430F4A13701278934211D10A4A0849154613782078DBB2000605D41378DBB20B700B7803F00F0328788342F1D138BD38BDB4
|
||||
:40188000286000406D86FF1F7E86FF1FF586FF1F29600040054A00231380054A916819B191680B7092685380704700BFF886FF1F7086FF1F0E4808B503889BB213B9FFF7A0
|
||||
:4018C00083FE13E00B4B02221A700B4B00221A70FFF7E0FF094AD1799379028843EA012392B2934238BF0380FFF728FE012008BD7086FF1F8286FF1F7E86FF1F0060004084
|
||||
:40190000084B01221A700F3B9B7C074B1A7B02F00302012A1EBFDA7B82F08002DA7301225A7370470B6000408886FF1F094B02221A700F3B93F82230074B1A7E02F0030242
|
||||
:40194000012A1EBFDA7E82F08002DA7601225A76704700BF0B6000408886FF1F0B4B04221A700F3B93F83230094B93F8242002F00302012A1EBF93F8272082F0800283F8E3
|
||||
:401980002720012283F82520704700BF0B6000408886FF1F0B4B08221A700F3B93F84230094B93F8302002F00302012A1EBF93F8332082F0800283F83320012283F83120A2
|
||||
:4019C000704700BF0B6000408886FF1F7047FFF7DFBC00F039BC000070B50446184B88B003AA03F11006154618685968083303C5B3422A46F7D11B782B70FCB1222323702D
|
||||
:401A000001AD03232846637000F016FE002220461146AB5C08AC04EB131414F8144C03F00F03847008AC234413F8143C0132082AC1700371417100F10400EAD108B070BDB1
|
||||
:401A4000653700002DE9F0411A4D01222E460C2080274FF0080E184B00FB025C14012344187016499CF805802144B8F1000F07D09CF8044024064CBF887081F802E000E0F1
|
||||
:401A80008F7000FB0261CC880132E4B29C71CC88092AC4F30724DC71CC88E4B21C71C988C1F307215971D6D1054BFF221A70BDE8F08100BF8886FF1F70600040FC5F00402D
|
||||
:401AC0000A600040064B074A1B7802EBC30253681A7C824286BF03EBC0035869002070477C86FF1FC83700002DE9F84F424B1A78002A7ED01878414D0138C0B2FFF7E2FFFF
|
||||
:401B0000A8463F4AC3681478007ADFF800C1E4B203EBC0000C2600274FF0010E834268D01A78A24263D11CF80420597891425ED19A7893F8039002F07F0206FB02FA05EB38
|
||||
:401B40000A01CF7093F802B009F0030981F804B093F803B005F80AB0B3F804A0A1F808A093F902A0BAF1000F0BDAB9F1010F0CBF4FF007094FF00D0981F8059081F801E0C9
|
||||
:401B800009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEA02191A4906FB0282494481F802E0B2F808A0CAF3072A81F800A0B2F808A05FFA8AFA81F801A0B2F8DC
|
||||
:401BC00006A011495FFA8AFA494481F806A0B2F80690C9F3072981F80790B2F806905FFA89F981F80490D288C2F307224A71083394E7BDE8F88F00BF8186FF1F8886FF1F13
|
||||
:401C00007D86FF1FFC5F0040706000406E86FF1F08B5064B18780138C0B2FFF753FF20B143681B7900EBC300406908BD8186FF1F00212DE9F84F0B464E4E0C2707FB01F44F
|
||||
:401C400001313219092933554FF000059370494CD3701381937253705371EFD118B1464B1D70464B1D70464B1A78002A7FD0187801250138C0B2FFF725FFA8464368DFF8A2
|
||||
:401C8000F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010202F0FE021A70027AD20007FB0541C36803EB02094B4531D093F802A00AF07F06AE4229D10E89B3F866
|
||||
:401CC00004B0B6B25E4538BFA1F808B01E7893F801B01EF80660B3451AD181F804A0DE780E7093F902A0DE78BAF1000F06F0030607DA012E0CBF07260D264E7181F801808A
|
||||
:401D000006E0012E0CBF052609264E7181F801C00833CBE70135092DC3D1C1680A328B1C0A440C200833934209D013F8081C13F80A5C01F07F0100FB01418D72F2E7FFF7F8
|
||||
:401D400067FF114B0121186000230C2000FB0142D3801289013113449BB203F00102134409299BB2F2D1BDE8F84FFFF76BBEBDE8F88F00BF8886FF1F6E86FF1FF686FF1F59
|
||||
:401D80008186FF1F7F86FF1F8486FF1F114B1B7903F07F035A1E072A19D80F490C2202FB031291781B0141F0010191700021D170517841F002015170127912F0800F074A36
|
||||
:401DC0001A4414BF8D2389239370FFF753BC0020704700BF006000408886FF1FFC5F004030B4194B1A7902F07F02531E072B27D8164B0C2404FB02339978154D01F0FE0139
|
||||
:401E000099700021D97029461201505D114400F07F0050555A7802F0FD025A701A795B78120605D5012B01D18C7006E00D2303E0012B0CBF082309238B7030BCFFF71ABCBE
|
||||
:401E4000002030BC704700BF006000408886FF1FFC5F004010B50D4B0D4C21791878C9B20138C0B2FFF72EFE43681B798B4201D2012909D8074A0848535CDBB24354A37818
|
||||
:401E80000120DBB2535410BD002010BD8186FF1F006000406E86FF1FF686FF1F38B58A4A8A4C13780021DBB221801806517840F18D800A2900F20581DFE811F05D00030138
|
||||
:401EC00003010301030103010B0003017E0003018200D3787C49012B09D17D4B1A787D4B03EBC2035B685B686360122310E0CB78022B12D18878FFF7E5FD002800F0E180C2
|
||||
:401F0000436863606368DA7863689B7843EA02232380BDE83840FFF7CDBCCB78032B26D16D4B00228878D5B2854209D3664A91786A4AEE2908BF1346634A917881B106E0C9
|
||||
:401F4000187801320028F1D018780344EAE764499278097C914203D16248FFF73DFD614B1A78002A00F0AD801A78228018E0BDE8384000F0B5BE13F0030313D0022B40F0D6
|
||||
:401F8000A0802380504B0C211B7903F07F02564B01FB02339A78554BD2B21A7000225A706360B6E702222280514A11784F4AC9B2117053706260ACE7012323804D4BEFE7E4
|
||||
:401FC0000123238013794C4A1344E9E701390A2977D8DFE801F037764F76067676760A7620009378454ADBB25AE0937803F0FF0153B9404B1A7891425FD01970404B012024
|
||||
:402000001870FFF715FE58E0481EC0B2FFF75AFD0028EED155E0FFF71DFF002851D02A4A384913791279DBB2D2B20A70364A3249D25CCB5C9A4240D0314B01221A70FFF749
|
||||
:4020400053FD3AE003F00303012B2BD009D3022B37D11D4B9B78002B33D1BDE83840FFF7BFBE194B9B78012B2BD1214A137803F0FD0315E003F00303012B13D008D3022BFE
|
||||
:402080001FD1114B9B78E3B9BDE83840FFF77EBE0D4B9B78012B14D1154A137843F0020313700AE0084B1A795AB998781B791749DBB2CA5C22EA0002CA54BDE83840FFF7DC
|
||||
:4020C000D9BA002038BD00BF006000407086FF1F7C86FF1FC83700002C380000B43700009F3800001487FF1F8886FF1F4186FF1F7F86FF1F8186FF1F6E86FF1F6C86FF1F4C
|
||||
:402100008086FF1F7D86FF1FF686FF1F8386FF1F074B1A78120609D55B78012B06D1054B054A5A6012781A80FFF7C4BB00207047006000407086FF1F8C370000014B1870D5
|
||||
:40214000704700BF76650040014B1878704700BF6B650040014B1870704700BF78650040064A0123136002F688321268E0211064034A1170A2F540721360704780E100E0FB
|
||||
:4021800000E400E0014B1870704700BF7E640040014B1870704700BF7864004073B515461E460B4C01230022019200920A4601461846237000F0F4F832462946207800F00D
|
||||
:4021C000AFF80221207800F099F8207802B070BDD080FF1F064A0423136002F688321268E0219064034A1170A2F202321360704780E100E002E400E0014B04221A607047A2
|
||||
:4022000000E100E0014B04221A60704780E100E0014B1870704700BF77640040704738B505460078012428B100F0F2FC285D0134E4B2F8E738BD08B50D2000F0E9FCBDE8C9
|
||||
:4022400008400A2000F0E4BC024B1878C0F38010704700BF8F450040074A7F23802113705170064A013BDBB202F80839002BF9D1034A1370704700BFE080FF1FF87B00401A
|
||||
:402280000078004017280FD8084B0001C25C11B142F0200201E002F0DF02C254C25C42F00102C25400207047012070471070004017280BD8064B0001C25C02F0FE02C2547C
|
||||
:4022C000C25C02F0DF02C25400207047012070471070004017280DD8074900010B4603441A7942F004021A71435C43F00103435400207047012070471070004017280BD807
|
||||
:40230000064A0001835C490003F0F10301F00E011943815400207047012070471070004041F6FF73994208BF4FF400519A4208BF4FF4005217289FBFC00000F1804000F583
|
||||
:40234000EC4081809ABFC280002001207047000017289FBF034B00011954002088BF0120704700BF1970004017289FBF054B00011A5C01F007019DBF1143195400200120C2
|
||||
:40238000704700BF1470004017289FBF034B0001185C00F0070088BFFF20704714700040172810B51AD8C00001F07F0100F1804441EAC21204F5EC44D2B222709DF80820D4
|
||||
:4023C00003F00F0343EA0213DBB263709DF80C30002003F00F03A370E07010BD012010BD10B500F075FC0A4A5378182B0AD91478013B5370E30003F1804303F5F0431B78D4
|
||||
:40240000137000E0FF2400F067FC204610BD00BFE080FF1F030610B5044611D400F058FC084AE300117803F1804303F5F04319705378147001335370BDE8104000F04CBCB6
|
||||
:4024400010BD00BFE080FF1F30B504060CD411F4704509D1C40004F1804404F5F0442180A270E370284630BD012030BD03065FBFC00000F1804000F5F04081805ABFC280BD
|
||||
:40248000002001207047000038B50446084DB4F5004F05D9286800F013FCA4F50044F6E7034B58686043BDE8384000F009BC00BFE880FF1F024B1B7A584300F001BC00BFC8
|
||||
:4024C000E880FF1F0E4B00F003001A78490102F0FC02104318701A7801F0600142F080021A701A7802F07F021A701A7802F09F020A431A701A7842F010021A70704700BFE0
|
||||
:4025000083430040014B01221A70704784430040044B00F00F021B6853F8220043F82210704700BF08ED00E0054A00F01F00126800F1100352F8230042F82310704700BFBA
|
||||
:4025400008ED00E000F01F0000F16040490100F56440C9B2017070470F4B10B50F4900240F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040313600A4B4FF47A72A2
|
||||
:40258000DB68B3FBF2F3084A1360084B4FF400421C60C3F8E82010BD4486FF1F0526000010E000E0E880FF1F14E000E018E000E0024A136843F002031360704710E000E08E
|
||||
:4025C00008B5FFF7F5FF034A136843F00103136008BD00BF10E000E010B5054CA3691BB9FFF7BAFF0123A361BDE81040FFF7E8BF4486FF1F024B1868C0F30040704700BFF1
|
||||
:4026000010E000E038B5FFF7F5FF012808D1054D002455F8243003B198470134052CF8D138BD00BF4886FF1F024B03EB80035868596070474486FF1F134B144A1B78DBB2C6
|
||||
:402640000360127843EA0223114A0360127843EA0243104A0360127843EA026303600E4B0E4A1B78DBB24360127843EA02230C4A4360127843EA02430A4A4360127843EA32
|
||||
:4026800002634360704700BF0301004904010049EC460040020100490101004900010049050100490601004910B500F011FB204A044613780A2043F002031370137C43F068
|
||||
:4026C0000203137412F80A3C43F0010302F80A3C937943F00103937102F5AB52137843F003031370134B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8EC
|
||||
:40270000012CCE2203F8062CA3F597530222183B1A70094A137843F008031370FFF7CAFE064B10222046BDE810401A6000F0D4BAAB4300400E5900402F5B004080E200E0B8
|
||||
:4027400008B500F0C5FA0F4A137803F0FE031370A2F5AA521D3A137803F0FD031370137C03F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE039371BDE8084000F039
|
||||
:40278000ABBA00BF08590040044A137803F03F0343EA8010C0B21070704700BF08590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF00A
|
||||
:4027C000000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470A59004076370000FE86FF1F0087FF1F0487FF1F9A
|
||||
:4028000008B5102000F0A6F907210420FFF79AFE07490420FFF788FE064A0C20137843F006031370FFF7BCFF034B00221A8008BDF928000009590040FC86FF1F10B5054C93
|
||||
:4028400023781BB9FFF7DCFF01232370BDE81040FFF72ABF6086FF1F044B1A7802F0FB021A701A7842F001021A7070470859004010B5084B1C7814F0010403D10028F9D00C
|
||||
:40288000002404E02046FFF715FE024B1B78204610BD00BF09590040034A044B1B881088181A00B2704700BF0487FF1FA25B00400E4A13881BB223B111880A2309B2594365
|
||||
:4028C00001E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B27047FE86FF1F0087FF1FFC86FF1F7047000010B500F07F
|
||||
:40290000E7F9214A044613780A2043F001031370137C43F00103137412F80A3C43F0020302F80A3C937943F00203937102F5AA521832137843F003031370144B18221A708F
|
||||
:4029400013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222123B1A70094A137843F008031370FFF79FFD074B08222046BDE8104091
|
||||
:402980001A6000F0A9B900BFAB43004006590040275B004080E200E008B500F099F90F4A137803F0FE031370A2F5AA52153A137803F0FE031370137C03F0FE03137412F829
|
||||
:4029C0000A3C03F0FD0302F80A3C937903F0FD039371BDE8084000F07FB900BF00590040044A137803F03F0343EA8010C0B21070704700BF00590040082804D00A280CBF21
|
||||
:402A00008223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B73
|
||||
:402A40001A80704702590040803700000A87FF1F1087FF1F0887FF1F08B5102000F084F807210320FFF76EFD07490320FFF75CFD064A0C20137843F006031370FFF7BCFF64
|
||||
:402A8000034B00221A8008BD512B0000015900400C87FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF728BF6186FF1F044B1A7802F0FB021A701A7842F001027B
|
||||
:402AC0001A7070470059004010B5084B1C7814F0010403D10028F9D0002404E02046FFF7E9FC024B1B78204610BD00BF01590040034A044B1B881088181A00B2704700BF9C
|
||||
:402B00000887FF1FA05B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F05F
|
||||
:402B400000B270470A87FF1F1087FF1F0C87FF1F70470000034A00F0F800137803431370704700BF02410040034A00F0F800137803431370704700BF06410040014B187044
|
||||
:402B8000704700BF73640040014B1870704700BF7664004073B515461E460B4C0023019300930A46014618462370FFF7F9FB324629462078FFF7B4FB02212078FFF79EFBBC
|
||||
:402BC000207802B070BD00BFF880FF1F074A0223136002F688321268E0215064044A11706FF440710A441360704700BF80E100E001E400E0014B1870704700BF7B650040B5
|
||||
:402C0000014B1870704700BF7465004000000000FEB5494652465B460EB40746244909688A46244A12682448022100F071F8030020480068C018204900F06AF814388346AE
|
||||
:402C40000121C9430C460125002600F041F8814651460B7823400B705846013000F030F83800F04028400B78234003430B70584600F026F80136072EF2D9002001300138D5
|
||||
:402C8000013001200B78234003430B705846043000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF30080D480FF1FF480FF1F00C2010000000000C4
|
||||
:402CC0000230800803D000BF01380046FCD17047EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D113790C2103F07F02044B01FB02339B7A0D
|
||||
:402D000000E0137900207047006000408886FF1F002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB11217047704700BF014B1868704700BF0E
|
||||
:402D40005C81FF1F0E4B70B51E460E4C0025E41AA410A54204D056F8253098470135F8E700F0BAFD084B094C1E46E41AA4100025A54204D056F8253098470135F8E770BDEA
|
||||
:402D8000E4380000E4380000E4380000EC38000003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B604FF0FF301DE04FF40273ADF80C300CBFC5
|
||||
:402DC000234604F1FF33029305934FF6FF7300910491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B00221A701BB030BD000007B5009313460A460E
|
||||
:402E0000014603480068FFF7CBFF03B05DF804FB5C81FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD02568096902236F1A656905EB450595FBF3F57F
|
||||
:402E40007B1C43449D4238BF1D4653050FD5294600F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A4600F098FB064670B92169504600F0E8FA63
|
||||
:402E80000C23CAF80030A3894FF0FF3043F04003A381BDE8F08726613E44266046466561ED1BA560464528BF464649463246206800F0B3FAA36800209B1BA36023681E44B8
|
||||
:402EC0002660BDE8F08700002DE9F04F9DB003938B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F80030CDE040236B610023099320238DF832
|
||||
:402F00002930DFF89CB130238DF82A3037463C4614F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF771FF013000F0A780099B4B4409933B78C5
|
||||
:402F4000002B00F0A08000234FF0FF3204930793059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46801A06FA00F018430490EFE7D90644BF72
|
||||
:402F800020228DF853201A0744BF2B228DF8532022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F00200079204900BE027463B780134303B092B14
|
||||
:402FC00003D800FB02320121F5E701B107923B782E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E0002319460593781C0A2407463A78013093
|
||||
:40300000303A092A03D804FB01210123F5E703B1059103223978224800F0E6F940B14023CBEB000003FA00F0049B013718430490397806221B487E1C8DF8281000F0D4F991
|
||||
:4030400088B1194B33B9039B073323F007030833039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A9404600F093F8B0F1FF3F824603D0099BEA
|
||||
:403080005344099342E7AB895B0601D4099801E04FF0FF301DB0BDE8F08F00BFB3380000B9380000BD38000000000000152E00002DE9F04791461F460A698B680646934286
|
||||
:4030C000B8BF1346C9F8003091F843200C46DDF8208012B10133C9F800302368990642BFD9F800300233C9F80030256815F0060510D104F1190A07E00123524639463046F4
|
||||
:40310000C04701301AD00135E368D9F800209B1A9D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F845102244023382F8431003E04FF0FF3053
|
||||
:40314000BDE8F08704F1430239463046C0470130F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500259342C4BF9B1AED184FF000091A344D4582
|
||||
:4031800009D00123224639463046C0470130D5D009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9B01F1430E00F0AE8011D8632A22D009D8F6
|
||||
:4031C000002A00F0BB80582A40F0CA8081F84520834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B8800A6842F020020A603EE0752A24D0782A4A
|
||||
:403200003AD0ADE01A6801F14205111D1960136884F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F10401196002D0B2F9003000E01368002B3CDA2D224F
|
||||
:403240005B4284F8432037E021681A6811F0800F02D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A14BF0A2208221BE078225A4984F8452018
|
||||
:403280002268186812F0800F00F104051D6003D1550601D5038800E00368D00744BF42F0200222601BB9226822F0200222601022002084F8430001E049490A226568002DB3
|
||||
:4032C000A56008DB206820F0040020602BB9002D7DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D1082A0BD12368DA0708D5236962689A42A3
|
||||
:40330000DEBF302305F8013C05F1FF35C5EB0E0323612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F104001860136801D0198000E019600023236135
|
||||
:40334000754616E01A68111D1960156800216268284600F049F808B1401B6060636804E004F1420584F8422001232361002384F84330CDF800803B4603AA21463046FFF7CF
|
||||
:4033800097FE013002D14FF0FF3026E023692A4639463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046C0470130E7D00135E368039A9B1A9D4293
|
||||
:4033C000F2DBE068039B9842B8BF184605E00B7804F1420584F842308AE705B0BDE8F08365370000C438000010B5C9B202449042034605D01C7801308C42F8D1184610BD06
|
||||
:40340000002010BD10B5431E0A44914204D011F8014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21AD34204D013F8014D01F8014DF8E710BD33
|
||||
:40344000994204D011F8014B02F8014FF8E710BD38B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B96360146030E0A3420DD92268A0188342B0
|
||||
:4034800001BF18685B681218226063600C6023E0A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D110685268014419605A600DE002D90C23ED
|
||||
:4034C0002B6009E021686018824201BF106852680918216062605C602846BDE8384000F098B838BD6886FF1F70B5CD1C25F0030508350C2D38BF0C25002D064601DBA942AC
|
||||
:4035000002D90C23336046E000F082F8234B1C681A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B681360636018BF0C4615E00C464968E9E7CF
|
||||
:40354000174C23681BB9304600F052F820602946304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B00231D20F00700C31A0ED05A42E25070BDFA
|
||||
:40358000211A304600F034F80130EBD10C233360304600F03EF8002070BD00BF6886FF1F6486FF1FF8B5074615460E4621B91146BDE8F840FFF798BF1AB9FFF749FF284650
|
||||
:4035C000F8BD00F027F885420ED929463846FFF78BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD38B5064C0023054608462360FDF744FDB9
|
||||
:40360000431C02D1236803B12B6038BD3887FF1F7047704751F8040C0028BEBF091851F8043CC0180438704700000000050209020B020D020F02110213027265706C792028
|
||||
:4036400030782530327800686F6D696E6700626567696E6E696E67207365656B2066726F6D20256420746F2025640066696E6973686564207365656B0057616974696E679A
|
||||
:4036800020666F72205553422E2E2E0055534220726561647900636F6D6D616E642030782530327800756E64657272756E206166746572202564207061636B65747300638E
|
||||
:4036C0006F756E743D256420693D256420643D256400636D645F777269746500703D25642063723D25642063773D256420663D256420773D256420696E6465783D2564206F
|
||||
:40370000756E64657272756E3D25640077726974652066696E69736865640073746172742065726173696E670073746F702065726173696E670069646C6500005100401076
|
||||
:403740000040510040300000000140001000140140000800400140000A004C0140000200500140200030313233343536373839414243444546000001000000040000001018
|
||||
:4037800000010000000400000010000028000000000104000100000000000000000157494E5553420000303030303100000000000000000012034D0053004600540031007C
|
||||
:4037C000300030000100000001000000D0370000010000009F380000000000000000000001000000E83700000100000071380000040000000A380000000000000000000078
|
||||
:403800000000000008380000FF00000001024000FF00000082024000FF00000003034000FF00000084034000FF00020304030904160346006C007500780045006E0067004E
|
||||
:4038400069006E0065002A0343006F0077006C00610072006B00200054006500630068006E006F006C006F0067006900650073000009022E0001010080320904000004FF0B
|
||||
:4038800000000107050102400000070582024000000705030340000A0705840340000A12010002FF0001080912006E0100020180014300232D302B2000686C4C0065666724
|
||||
:4038C000454647003031323334353637383961626364656600000000F8B500BFF8BC08BC9E4670475900000005110000F8B500BFF8BC08BC9E4670473500000010390000A9
|
||||
:40390000C880FF1F98000000E005000000000000000000004087FF1FFF000000675000400C000000070000007F800000030000000000007D00FA0000400000000090D0039A
|
||||
:40394000FF0000000000000000000000000000000000000000000000000000000000000000000000B13800000000000000000000000000000000000000000000000000005F
|
||||
:40398000000000000000000000000000000000000000000000000000000000000000000000000000FC80FF1F0000000000000000000000000000000000000000000000006D
|
||||
:4039C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C7
|
||||
:403A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086
|
||||
:403A40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046
|
||||
:403A80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006
|
||||
:403AC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C6
|
||||
:403B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000085
|
||||
:403B40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045
|
||||
:403B80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005
|
||||
:403BC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C5
|
||||
:403C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084
|
||||
:403C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044
|
||||
:403C80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004
|
||||
:403CC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C4
|
||||
:403D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083
|
||||
:403D40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043
|
||||
:403D80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003
|
||||
:400000000080002011000000A1100000A1100000064A08B5136843F020031360044B1A6803F53F5302331A6001F058F8E8460040FA46004010B5054C237833B9044B13B173
|
||||
:400040000448AFF300800123237010BD6081FF1F00000000F0380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF0000000021
|
||||
:400080006481FF1FF0380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8081FF1FA4
|
||||
:4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8081FF1F3F000080114BDA68196919B905
|
||||
:4001000001221A75597514E09969521A19698A4294BF587D00201875187D094908B1002204E0086982428CBF002201224A75DA689A611B7D13B1002002F082B9704700BF34
|
||||
:400140008081FF1F10B5C4B2204601F089F90128FAD110BD08B572B60F4B0F49DA680132DA60DA690132C82A08BF0022DA611A6AD8690132A72A08BF00220A621B6A002BEB
|
||||
:400180000CBF02230023002814BF184643F0010002F0DAFB62B608BD8081FF1F38B50446C5B2284602F0C4F8062002F099FD44F00200C0B202F0BCF8062002F091FD284602
|
||||
:4001C00002F0B6F8BDE83840062002F073BD10B5642402F0A7F828B9FFF7E0FF013CF8D1204610BD012010BD70B5C4B2054620460E4601F035F9012805D0204601F04EFAE3
|
||||
:400200002846FFF79FFF204601F032F9314605460246204601F0EEF9204601F021F90028FAD1284670BD000038B5044D0024285D013402F005FD402CF9D138BDA481FF1F75
|
||||
:4002400008B502F0FDF9002002F006FA02F018FA02F022FA80B208BD10B50446012002F07FF8642002F026FDFFF7EAFF2080002002F076F8642002F01DFDFFF7E1FF608047
|
||||
:4002800010BD08B502F008FB002002F011FB02F023FB02F02DFB80B208BD10B50446FFF796FF322002F006FDFFF7EBFF20800120FFF774FF322002F0FDFCFFF7E2FF6080A1
|
||||
:4002C00010BD0FB400B593B014AB53F8042B402102A8019302F0F4FE02A802F0BEF802F0C8F813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF723FF62787E
|
||||
:400300002146BDE81040042001F002B90C3A000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8643043B1002001F0ECFF79
|
||||
:40034000002002F083F8002384F8643010BD00BF8081FF1F38B5124D837895F8672004469A4204D0FFF7E4FF002385F86A302368C5F8653022790B4B1A71A378002B14BF38
|
||||
:400380000220012002F062F8E078B0FA80F0400902F0D4FA2079BDE8384002F0DBBA00BF8081FF1FE581FF1F38B50D4C94F8645065B904F16500FFF7CDFF012001F0ACFF17
|
||||
:4003C0004FF47A7002F076FC84F86A50E368E366012384F86430BDE8384002F0A9BC00BF8081FF1FF8B5214C0546FFF7DDFF94F86A3003B15DB91E48FFF763FFFFF7E7FE5C
|
||||
:400400000120002384F86A00236702F069FC2A46216F1848FFF755FF144E0027236F9D4216D001F07FFF00B13767236F9D4205DD0120FFF7B3FE336F013305E005DA002094
|
||||
:40044000FFF7ACFE336F013B336702F071FCE5E7322002F02FFC2A2DCCBF0020012002F07FFABDE8F8400448FFF72BBF8081FF1F193A0000203A00003D3A00002DE9F04F9B
|
||||
:4004800099B062B602F0BEFC9F49042002F0E2FC9E4801F053FF9E4801F084FF9D4801F0B1FF02F06DF902F03FF8002001F0CEFF01F0D2FF0221002000F088FF012001F05D
|
||||
:4004C00001F9954D0321084602F012FC2E462C4602F02EFC95F8643043B1EA6EEB689B1A41F28832934201D9FFF722FF00F0A6FF18B98A48FFF7E5FE04E000F0A5FF0028D3
|
||||
:40050000F7D134E000F09AFF10B902F011FCF9E78348FFF7D6FE032001F0C0F88148FFF7D0FE002386F86730FFF73EFFFFF74FFE86F87400FFF7FCFE012386F86730FFF701
|
||||
:4005400033FFFFF744FE86F87500FFF7F1FE96F874007549754B96F87520002A14BF0A461A46002808BF19467148FFF7AAFE032000F076FF0128ABD16E48FFF7EBFE6E49CB
|
||||
:400580000320FFF731FE94F876106C48FFF799FE94F87630023B142B00F2D783DFE813F01500D5031E00D5032400D5035000D5037600D503DA00D503C201D5030903D50356
|
||||
:4005C0002D03D5033403D5034E0303238DF820308DF8213011238DF822302BE394F87800FFF700FF564B22E340F2DC57FFF7DCFE00232375E068227D02F0FF012AB9EB680B
|
||||
:400600001B1ABB42F7DD0B4611E083B10022174696F87810F068277594F814E0BEF1000F02D1EB681B1AF7E701329142F3DA07228DF8202004228DF82120ADF82230F9E2BF
|
||||
:400640000220FFF77FFD4FF000080DF1200A02F06FFB4FF480790027C9EB0803DA1907F80A200137402FF9D10220FFF76BFD3A465146022000F04CFFB9F10109EBD108F10F
|
||||
:400680000108B8F1400FE2D12E4B38E04FF0010A4FF000080DF1200B02F04AFB4FF0000959460120FFF7A0FD08EB090300270493049B1BF807203B44DBB29A4209D08DE89D
|
||||
:4006C0000C0041463B464A461F48FFF7FAFD4FF0000A0137402FEBD109F10109B9F5807FDED108F10108B8F1400FD5D151461748FFF7E7FDBAF1000F00F01C81144B1B883D
|
||||
:4007000007A8ADF81C3096E255010000F900000091000000C50000008081FF1F523A0000653A00006F3A00004B3A00004F3A0000823A0000E581FF1FF681FF1F9A3A000052
|
||||
:40074000F4380000F6380000A93A0000C53A0000F8380000206FFFF745FE94F8780001F0FFFD94F8780001F0E3FD02F015FCB94BDFF8FC821A78002702F0FB021A701A785F
|
||||
:4007800042F001021A701A7802F0FE021A701A7802F0FE021A7002F003FC0220FFF7D2FC012141F6FF734FF48042084601F0DEFD84F8B60002F02AFA08F807000137402FA7
|
||||
:4007C000F8D1DFF8B0A200270AF195091FFA89F80137402F14BF3A4600221AF8010F2244062392F82420402102F044FA424646F24E419AF8000002F04FFA08F14008402FB5
|
||||
:400800001FFA88F8E4D196F8793053B196F87C30336100233375237D002BFCD000233375336100234FF0FF32236062602372236894F8B600234493F8241002F09FF994F82A
|
||||
:40084000B60002F05DF9012194F8B60002F030F92368002BFCD0002398467360D6F80CA0012702F065FAE368B4F87A20CAEB030393420DD367B1042195F8B60002F08AF9BC
|
||||
:4008800094F8B60002F096F90028F9D107463072237AFBB96A682B689A4202D1002FE0D118E00220FFF74EFC6968402209EB8111022000F02DFE6A68674B01321340002BCA
|
||||
:4008C000BEBF03F1FF3363F03F03013308F101086360C6E70220277AFFF734FC00221146022000F015FE0220FFF72CFCFFB2FFF79BFC002001F01CFD37B15848FFF7E1FC90
|
||||
:400900000220FFF705FD06E0554B08A81B88ADF82030FFF7EBFC227D4146237A5148FFF7D0FC15E25048FFF7CCFCD4F87A7017F03F0701D0032009E2286FFFF753FD95F83B
|
||||
:40094000780001F00DFD95F8780001F0F1FC012001F098FD02F020FB444BDFF814811A7842F004021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F0BF
|
||||
:400980000FFB01214FF4804341F6FF72084601F01DFD85F8B60002F039F908F807000137402FF8D1DFF8CC90002709F195031FFA83F804930137402F14BF3A46002219F8F2
|
||||
:4009C000010F2244052392F82420402102F052F9414646F2484299F8000002F05DF908F14008402F1FFA88F8E4D100274FF0FF33376098467360BB463B46D6F87A903772EA
|
||||
:400A00005FEA99190CBF4FF0010A4FF0000A2168114A01310A40002ABCBF02F1FF3262F03F026068B8BF013282426FD02BB1227A002A7AD12A7D002A77D12068049A059368
|
||||
:400A400002EB8010BAF1000F16D040223F2102F003FB1CE09E6400403F000080CF3A0000FA380000E93A0000FC3A000098640040A481FF1FA381FF1F014601370120FFF7BF
|
||||
:400A8000B3FBC7EB0903D3F1000A4AEB030A2168B34A01310A40002ABEBF02F1FF3262F03F02013222606268059B01322ED12A683F2A2BD14FF00008C5F8048001F0B4FCE9
|
||||
:400AC00085F808806B6895F8B6002B4493F8241002F054F895F8B60002F012F8012195F8B60001F0E5FF95F87E302B6185F81480237D002BFCD04FF00008012086F814800D
|
||||
:400B000001F09EFC404601F0BDFC00E023B1237A5BB92B7D4BB90123626842453FF477AF0BF1010BD5F8048071E701F083FC012001F0A8FC002001F083FC042194F8B6004D
|
||||
:400B400002F028F894F8B60002F034F880460028F8D196F8B60001F0C1FF337D327A0293012303920193CDF800A05B463A4649467C48FFF7A6FBC6F81080BAF1000F0BD0C6
|
||||
:400B8000FFF752FB002001F0D3FB237A63B17648FFF797FB0220D9E0B945F1D073490120FFF722FB0137F7E77148FFF78AFB714B3DE094F8780001F0D3FB206FFFF712FC19
|
||||
:400BC0006D48FFF77EFB94F87930236100232375237D002BFCD0012001F032FC00233375237D002BFCD0002001F02AFC002363483361FFF766FB624B19E0002084F86A0088
|
||||
:400C0000FFF7F0FB5F4B12E094F8743023B195F875200AB985F8782094F875201AB113B9012385F878305848FFF794FB574B1B88ADF8203008A8FFF759FB89E0FFF778FB0D
|
||||
:400C400001F01CFE002001F0BFFD2A2701F0EAFC002001F08DFC3A46002108A802F0FCF917238DF820308DF8217002F061F8002001F052FB002001F0E9FBC82002F01AF8F3
|
||||
:400C80000DF12200FFF7E8FA0DF13600FFF705FB02F04EF8012001F0D9FB322002F00AF80DF12600FFF7D8FA0DF13A00FFF7F5FA012001F031FB4FF4967001F0FBFF02F041
|
||||
:400CC00037F80DF12E00FFF7C7FA0DF14200FFF7E4FA002001F020FB4FF4967001F0EAFF02F026F8022001F0B1FB322001F0E2FF0DEB0700FFF7B0FA0DF13E00FFF7CDFAF4
|
||||
:400D0000012001F009FB4FF4967001F0D3FF02F00FF80DF13200FFF79FFA0DF14600FFF7BCFA002001F0F8FA4FF4967001F0C2FF01F0FEFF002001F089FB002384F86A302F
|
||||
:400D400001F01EFD01F0F0FB74E70120FFF7E0FA032000F0A3FC0E48FFF7B3FAFFF7B8BB3F000080063B0000363B00003892FF1F403B0000FC380000483B0000563B00000F
|
||||
:400D8000FE38000000390000F681FF1F02390000633B00000F4B1A78120616D55878C0B2012814D11B79DBB2042B02D0052B05D07047094A094B5A60282203E0084A074B07
|
||||
:400DC0005A60E0221A8000F043BE002070470120704700BF0060004004390000BC92FF1F2C3900002DE9F04172B6884B61221A70A3F5F06301221A801924854A9C7092E807
|
||||
:400E000003008033062283F8002283E80300522203F580731A707F4B7F4A1B787F4EDBB2137040F618027E4B00251A8041F2512223F8022C33784FF4F07003F0010343EAA4
|
||||
:400E4000450502F0A1F8013C05F003052ED0032DF0D1744B4FF480721A8007221A70724A002548211570917002221D705D7103F8032C0422DA716D4A6D4C13786D4E43F060
|
||||
:400E80000103137012F8013C062743F0030302F8013C2378012243F0800323705B4B1A70654A137843F02003137000E0FEE707FB056300219A881868013502F0CDF8072D6A
|
||||
:400EC000F5D15E485E4E002550F8041F05F1105303F14A0221F0FF074B33C9B20B4452005B0002329A4206D012F802EC12F801CC0EF807C0F5E7B0420D44E5D1514A002390
|
||||
:400F000013609360136193614F4B504F1A68504BDFF888811A604F4B1A684F4B1A604F4A137843F002031370137C43F0020313742378A2F5863243F040032370413A1378DD
|
||||
:400F400043F010031370464A464B07CA03C31A80454A2833106843F8250C127903F8212C424A07CA03C31A80414AE83B07CA03C31A80404A083307CA03C31A803E4A3F4B11
|
||||
:400F8000A2F5616203CBC2F8100EC2F8141E1378042043F008031370394B02F5AA521B783D78DBB298F80060EDB203F007010C321B091170F6B2537045F003033B7046F095
|
||||
:400FC000030388F800302F4B48221A702E4A402313702E49937013729372082382F81F3220220A7048710A72294A0A20137001F077FE284B88F8006044223D70264D1A709B
|
||||
:4010000094E80F0007C52B80BDE8F08100480040680A00480F010049A146004025420040224200400440004006400040A2430040A0430040683B0000E8460040FCFFFF47E7
|
||||
:401040009000004800760040700A0048F846004020760040740A00482876004003500140280A0048C0510040340A00483C0A0048480A0048540A004832510040600A004800
|
||||
:40108000CF0100491D51004001590040235B0040585B004076580040B0430040F946004008B501F0ADFF03680C2B00D1FEE7FEE7084908B50B68084A1844821A802A01DC75
|
||||
:4010C000086005E001F09CFF0C2303604FF0FF33184608BDCC80FF1F8893FF1F80B51148114B0025C0B1A3F1100192C922460439161BB74204D051F8046F42F8046BF7E7E8
|
||||
:40110000114653F8046C8C1AA64202D041F8045BF9E701381033E5E701F078FFFFF7AEF9FEE700BF01000000FC3C0000124A134B10B51A60124A134C1368134843F4007367
|
||||
:4011400013600023032B98BF54F823204FEA830188BF0E4A0133302B4250F3D10C4B1A780C4B1A700C4B084A1A60FFF73BFEBDE8104001F087BC00BF0004FA050CED00E0A4
|
||||
:4011800014ED00E0000000000080FF1FA1100000BC760040C080FF1F08ED00E0F8B501F0FBFE4B4A01271378022643F001031370137C484C43F001031374474B02F5E352FA
|
||||
:4011C0001F700B3203F8946C1378054603F07F031370002001F084FD2378404A03F0F90323701378384603F0DF03137023783B43237001F075FD282001F072FD384B304610
|
||||
:401200001A7802F07F021A701A7802F0BF021A7023783343237001F063FD2378314A43F0040323700023137053702F4AFF2199540133092BFBD1284601F0B2FE07211720E7
|
||||
:4012400001F096FD2949172001F084FD0721182001F08EFD2649182001F07CFD0721152001F086FD2349152001F074FD0721052001F07EFD2049052001F06CFD072106201F
|
||||
:4012800001F076FD1D49062001F064FD0721084601F06EFD1A49072001F05CFD0721082001F066FD1749082001F054FD0021162001F05EFD1449162001F04CFD07210C2014
|
||||
:4012C00001F056FDBDE8F84010490C2001F042BDA5430040944300409D60004012600040F851004084600040AD92FF1F6B1B0000A5190000691B00009D1A0000C91A0000BE
|
||||
:40130000F91A0000311B0000711B0000E51B0000214B224A10B5187000231370204A40201370204A0F2413701F4A13701F4A13701F4A13701F4A13701F4B4FF400021A60B1
|
||||
:401340004FF080721A604FF400121A6020221A601860802018604FF480701860174804704FF480001860164B1A70933B19B91A7802F0FE0202E01A7842F001021A70114B50
|
||||
:4013800003221A70802203F8202C012001F0FCFD0D4B04221A7010BDC892FF1FCE92FF1FCC92FF1FCD92FF1FC992FF1FB892FF1FCB92FF1F4093FF1F00E100E09E6000407A
|
||||
:4013C0009C600040286000401260004070B5074C054623780E461BB9FFF7E0FE0123237031462846BDE87040FFF792BF7892FF1F0A4A002313700A4A13700A4A13700A4A81
|
||||
:4014000013700A4A13700A4A13700A4A13700A4B03221A70802203F8202C7047CE92FF1FCC92FF1FCD92FF1FC992FF1FB892FF1FCB92FF1F4093FF1F28600040014B187898
|
||||
:40144000704700BFCD92FF1F044B1A7802F0FF001AB118780022C0B21A707047CC92FF1F024A0C2303FB002040787047D492FF1F431E072B0CD8074A064B00010344805C32
|
||||
:401480005B7800F00F0043EA0020023880B2704700207047FC5F00401A4A38B50C2303FB00231B79090C13F0800F00F1FF35044619BF8AB24FF480438BB24FF48042032DA1
|
||||
:4014C00018D8DFE805F002070C110021084601F0A1FA0DE00021084601F080FA08E00021084601F05FFA03E00021084601F03EFA054B1855EDB2072D03D801F087FB034B10
|
||||
:40150000185538BDD492FF1FA492FF1FAD92FF1F431E072B2DE9F0470446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB2C3F500739D424FF00C0303FB007388BF8A
|
||||
:40154000D588DB7884BFC5F50075ADB2254A43EA15230601B354B244EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FB047200215170B9F1000F28D03DB31B4FEB
|
||||
:40158000385D01F0ABFA11232946FE2218F8040001F070FB06F5C04278321FFA89F118F8040001F079FB124D18F80410385D01F0E5FA0121385D01F07BFA735D43F002030D
|
||||
:4015C0007355735D03F0FD037355BDE8F08703FB04746379DBB28AF80230BDE8F08700BFD492FF1FFC5F0040AD92FF1FA492FF1F706000402DE9F047044615468846002945
|
||||
:4016000040D0431E072B3FD8FFF732FFA84203D22046FFF72DFF05461D4E335DFF2B03D141462046FFF738FFDFF868A027011AF8040001F053FA1223FE222946305D01F087
|
||||
:4016400019FB07F5C0411FFA88F27831305D01F023FBDFF84490315D1AF8040001F08EFA01211AF8040001F023FA17F8093043F0020307F8093017F8093003F0FD0307F881
|
||||
:40168000093002E00D4600E000252846BDE8F087AD92FF1FA492FF1F70600040431E072B0AD8064A0C2303FB002300225A705A79034BD2B200011A54704700BFD492FF1F5D
|
||||
:4016C000FE5F0040431E072B9FBF024B000108221A547047FE5F004030B51A4A1A491B4D0878138803449BB21380194A00231488D8B2A4B27CB1082B0CD050680078C0B2EC
|
||||
:40170000E85450680133013050601088013880B21080ECE718460B780E4C082B0E4A00D040B10E4D2B7883F080032B700F232370022301E0022323701370094B18700870CA
|
||||
:4017400030BD00BF4493FF1F4093FF1F00600040BC92FF1FB992FF1FCE92FF1FCA92FF1F4193FF1F074B02221A70074B80221A70064B0F221A70064A00231370054A012088
|
||||
:4017800013707047CE92FF1FCA92FF1FB992FF1F4093FF1F4193FF1F30B5164B16491B780A8803F00F03023BDBB21A4492B20A80124C134A0020118889B279B173B155682C
|
||||
:4017C000215C013BC9B229705168DBB20131516011880130013989B21180ECE7094A1370094A137883F080031370084B0B221A7030BD00BF296000404493FF1F006000400F
|
||||
:40180000BC92FF1F4193FF1FCA92FF1FB992FF1F064A06231370064A01201370054B80221A70054B00221A70704700BFCE92FF1FB992FF1FCA92FF1F4193FF1F054B9A68E4
|
||||
:401840003AB19A68044910709A680988518000229A607047BC92FF1F4493FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A137008BD0220FFF7E1FF0D4B1B7803F0CE
|
||||
:401880006003202B05D0402B06D043B900F012FC04E001F089FB01E0FFF77CFA10B9034B03221A7008BD00BF28600040B992FF1F0060004008B5084A084B012019781388FA
|
||||
:4018C0000B449BB21380064B00221A70FFF7B6FF044B03221A7008BD4493FF1F4093FF1FCE92FF1FB992FF1F08B50C4B1B78DBB2042B07D0062B09D0022B0DD1BDE8084045
|
||||
:40190000FFF7D8BFBDE80840FFF746BF0320FFF795FF034B03221A7008BD00BFCE92FF1FB992FF1F08B5054B002201201A70FFF785FF034B03221A7008BD00BFCE92FF1FCA
|
||||
:40194000B992FF1F08B50A4B1A7832B11A78094942F080020A7000221A70074B002201201A70FFF76BFF054B03221A7008BD00BFB892FF1F08600040CE92FF1FB992FF1FC0
|
||||
:40198000074B1B78DBB2042B05D0062B05D0022B05D1FFF7A1BEFFF7C5BFFFF7D3BF7047CE92FF1F38B51D4C2378DBB2DD0634D518060AD503F00F03012B2ED1FFF74EFF42
|
||||
:4019C000174B1B78190609D538BD5A0602D5FFF7D7FF03E09D0620D5FFF786FF23781B061BD4104B1A78104B1B7813430F4A13701278934211D10A4A0849154613782078EB
|
||||
:401A0000DBB2000605D41378DBB20B700B7803F00F0328788342F1D138BD38BD28600040B992FF1FCA92FF1F4193FF1F29600040054A00231380054A916819B191680B701D
|
||||
:401A400092685380704700BF4493FF1FBC92FF1F0E4808B503889BB213B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FF094AD1799379028843EA012392B2934229
|
||||
:401A800038BF0380FFF728FE012008BDBC92FF1FCE92FF1FCA92FF1F00600040084B01221A700F3B9B7C074B1A7B02F00302012A1EBFDA7B82F08002DA7301225A73704722
|
||||
:401AC0000B600040D492FF1F094B02221A700F3B93F82230074B1A7E02F00302012A1EBFDA7E82F08002DA7601225A76704700BF0B600040D492FF1F0B4B04221A700F3B21
|
||||
:401B000093F83230094B93F8242002F00302012A1EBF93F8272082F0800283F82720012283F82520704700BF0B600040D492FF1F0B4B08221A700F3B93F84230094B93F856
|
||||
:401B4000302002F00302012A1EBF93F8332082F0800283F83320012283F83120704700BF0B600040D492FF1F7047FFF741BC0000F0B5184B184E19780C27C9B201234FF028
|
||||
:401B8000000C31B3CA0720D5144A4FEA031E7244947850782040C5070DD507FB03652C79240608D5147804F0FE0414706D790C4CEDB204F80E50840706D507FB036425795F
|
||||
:401BC0002D0658BF84F801C090700133DBB24908D7E7F0BD9F600040D492FF1F70600040FE5F004000F032BF70B50446184B88B003AA03F11006154618685968083303C530
|
||||
:401C0000B3422A46F7D11B782B70FCB12223237001AD03232846637001F024F9002220461146AB5C08AC04EB131414F8144C03F00F03847008AC234413F8143C0132082AB1
|
||||
:401C4000C1700371417100F10400EAD108B070BD923B00002DE9F0431C4D01222E460C201F274FF0800E4FF0080C194B00FB02581401234418705F70164998F8059021445B
|
||||
:401C8000B9F1000F07D098F8044024064CBF887081F802C001E081F802E000FB0261CC880132E4B29C71CC88092AC4F30724DC71CC88E4B21C71C988C1F307215971D4D1CB
|
||||
:401CC000054BFF221A70BDE8F08300BFD492FF1F70600040FC5F00400A600040064B074A1B7802EBC30253681A7C824286BF03EBC003586900207047C892FF1FB83B000044
|
||||
:401D00002DE9F84F424B1A78002A7ED01878414D0138C0B2FFF7E2FFA8463F4AC3681478007ADFF800C1E4B203EBC0000C2600274FF0010E834268D01A78A24263D11CF829
|
||||
:401D40000420597891425ED19A7893F8039002F07F0206FB02FA05EB0A01CF7093F802B009F0030981F804B093F803B005F80AB0B3F804A0A1F808A093F902A0BAF1000FB6
|
||||
:401D80000BDAB9F1010F0CBF4FF007094FF00D0981F8059081F801E009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEA02191A4906FB0282494481F802E0B2F806
|
||||
:401DC00008A0CAF3072A81F800A0B2F808A05FFA8AFA81F801A0B2F806A011495FFA8AFA494481F806A0B2F80690C9F3072981F80790B2F806905FFA89F981F80490D288FA
|
||||
:401E0000C2F307224A71083394E7BDE8F88F00BFCD92FF1FD492FF1FC992FF1FFC5F004070600040BA92FF1F08B5064B18780138C0B2FFF753FF20B143681B7900EBC3008A
|
||||
:401E4000406908BDCD92FF1F00212DE9F84F0B464E4E0C2707FB01F401313219092933554FF000059370494CD3701381937253705371EFD118B1464B1D70464B1D70464B16
|
||||
:401E80001A78002A7FD0187801250138C0B2FFF725FFA8464368DFF8F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010202F0FE021A70027AD20007FB0541C368D0
|
||||
:401EC00003EB02094B4531D093F802A00AF07F06AE4229D10E89B3F804B0B6B25E4538BFA1F808B01E7893F801B01EF80660B3451AD181F804A0DE780E7093F902A0DE78D3
|
||||
:401F0000BAF1000F06F0030607DA012E0CBF07260D264E7181F8018006E0012E0CBF052609264E7181F801C00833CBE70135092DC3D1C1680A328B1C0A440C20083393423E
|
||||
:401F400009D013F8081C13F80A5C01F07F0100FB01418D72F2E7FFF767FF114B0121186000230C2000FB0142D3801289013113449BB203F00102134409299BB2F2D1BDE88B
|
||||
:401F8000F84FFFF767BEBDE8F88F00BFD492FF1FBA92FF1F4293FF1FCD92FF1FCB92FF1FD092FF1F114B1B7903F07F035A1E072A19D80F490C2202FB031291781B0141F08E
|
||||
:401FC000010191700021D170517841F002015170127912F0800F074A1A4414BF8D2389239370FFF715BC0020704700BF00600040D492FF1FFC5F004030B4194B1A7902F0D8
|
||||
:402000007F02531E072B27D8164B0C2404FB02339978154D01F0FE0199700021D97029461201505D114400F07F0050555A7802F0FD025A701A795B78120605D5012B01D167
|
||||
:402040008C7006E00D2303E0012B0CBF082309238B7030BCFFF7DCBB002030BC704700BF00600040D492FF1FFC5F004010B50D4B0D4C21791878C9B20138C0B2FFF72EFE80
|
||||
:4020800043681B798B4201D2012909D8074A0848535CDBB24354A3780120DBB2535410BD002010BDCD92FF1F00600040BA92FF1F4293FF1F38B5874A874C13780021DBB254
|
||||
:4020C00021801806517840F188800A2900F20081DFE811F05800FE00FE00FE00FE00FE000B00FE007900FE007D00D3787949012B09D17A4B1A787A4B03EBC2035B685B68FE
|
||||
:402100006360122310E0CB78022B12D18878FFF7E5FD002800F0DC80436863606368DA7863689B7843EA02232380BDE83840FFF78FBCCB78032B21D16A4B00228878D5B29B
|
||||
:40214000854203D3634A92783AB910E0187801320028F7D018780344F0E75E4A62499278097C914203D16148FFF73EFD5F4B1A78002A00F0AD801A78228018E0BDE8384099
|
||||
:4021800000F012BF13F0030313D0022B40F0A0802380504B0C211B7903F07F02544B01FB02339A78534BD2B21A7000225A706360BBE702222280504A11784E4AC9B2117026
|
||||
:4021C00053706260B1E7012323804C4BEFE70123238013794A4A1344E9E701390A2977D8DFE801F037764F76067676760A7620009378444ADBB25AE0937803F0FF0153B928
|
||||
:402200003E4B1A7891425FD019703F4B01201870FFF71AFE58E0481EC0B2FFF75FFD0028EED155E0FFF722FF002851D0294A374913791279DBB2D2B20A70354A3049D25C1E
|
||||
:40224000CB5C9A4240D0304B01221A70FFF758FD3AE003F00303012B2BD009D3022B37D11C4B9B78002B33D1BDE83840FFF7C4BE184B9B78012B2BD11F4A137803F0FD03CA
|
||||
:4022800015E003F00303012B13D008D3022B1FD1104B9B78E3B9BDE83840FFF783BE0D4B9B78012B14D1144A137843F0020313700AE0084B1A795AB998781B791549DBB239
|
||||
:4022C000CA5C22EA0002CA54BDE83840FFF7A0BA002038BD00600040BC92FF1FC892FF1FB83B00001C3C00008F3C00006093FF1FD492FF1F7992FF1FCB92FF1FCD92FF1FBA
|
||||
:40230000BA92FF1FB892FF1FCC92FF1FC992FF1F4293FF1FCF92FF1F014B1870704700BF72640040014B1878704700BF68650040014B1870704700BF7A650040064A0123C8
|
||||
:40234000136002F688321268E0211064034A1170A2F540721360704780E100E000E400E0014B1870704700BF7A64004073B515461E460B4C04230022019200920A46014658
|
||||
:402380001846237000F022FC32462946207800F0DDFB0221207800F0C7FB207802B070BDD080FF1F074A0223136002F688321268E0215064044A11706FF440710A44136017
|
||||
:4023C000704700BF80E100E001E400E073B515461E460B4C05230022019200920A4601461846237000F0F2FB32462946207800F0ADFB0221207800F097FB207802B070BD84
|
||||
:40240000D180FF1F064A0423136002F688321268E0219064034A1170A2F202321360704780E100E002E400E0014B04221A60704700E100E0014B04221A60704780E100E013
|
||||
:40244000014B1870704700BF7B650040014B1870704700BF73640040704738B505460078012428B100F038FD285D0134E4B2F8E738BD08B50D2000F02FFDBDE808400A20C6
|
||||
:4024800000F02ABD014B1870704700BF7865004010B500F081FD204A044613780A2043F002031370137C43F00203137412F80A3C43F0010302F80A3C937943F0010393712B
|
||||
:4024C00002F5AB52137843F003031370134B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222183B1A70094A137843F0AA
|
||||
:402500000803137000F0ECFB064B10222046BDE810401A6000F044BDAB4300400E5900402F5B004080E200E008B500F035FD0F4A137803F0FE031370A2F5AA521D3A13785D
|
||||
:4025400003F0FD031370137C03F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE039371BDE8084000F01BBD00BF08590040044A137803F03F0343EA8010C0B2107082
|
||||
:40258000704700BF08590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2E4
|
||||
:4025C000883103F6C41393FBF1F305490B60054B1A8070470A590040A43B00004A93FF1F4C93FF1F5093FF1F08B5102000F036FA0721042000F0BCFB0749042000F0AAFB02
|
||||
:40260000064A0C20137843F006031370FFF7BCFF034B00221A8008BDE1260000095900404893FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF72ABF7B92FF1FA6
|
||||
:40264000044B1A7802F0FB021A701A7842F001021A7070470859004010B5084B1C7814F0010403D10028F9D0002404E0204600F037FB024B1B78204610BD00BF09590040D9
|
||||
:40268000034A044B1B881088181A00B2704700BF5093FF1FA25B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F20223F1
|
||||
:4026C00091FBF3F30028D8BF5B42134493FBF1F000B270474A93FF1F4C93FF1F4893FF1F7047000010B500F057FC214A044613780A2043F001031370137C43F001031374BC
|
||||
:4027000012F80A3C43F0020302F80A3C937943F00203937102F5AA521832137843F003031370144B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CBE
|
||||
:40274000CE2203F8062CA3F597530222123B1A70094A137843F00803137000F0C1FA074B08222046BDE810401A6000F019BC00BFAB43004006590040275B004080E200E0CF
|
||||
:4027800008B500F009FC0F4A137803F0FE031370A2F5AA52153A137803F0FE031370137C03F0FE03137412F80A3C03F0FD0302F80A3C937903F0FD039371BDE8084000F0BB
|
||||
:4027C000EFBB00BF00590040044A137803F03F0343EA8010C0B21070704700BF00590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF095
|
||||
:40280000000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A80704702590040AE3B00005693FF1F5C93FF1F5493FF1FFC
|
||||
:4028400008B5102000F014F90721032000F090FA0749032000F07EFA064A0C20137843F006031370FFF7BCFF034B00221A8008BD39290000015900405893FF1F10B5054C6D
|
||||
:4028800023781BB9FFF7DCFF01232370BDE81040FFF728BF7C92FF1F044B1A7802F0FB021A701A7842F001021A7070470059004010B5084B1C7814F0010403D10028F9D0AE
|
||||
:4028C000002404E0204600F00BFA024B1B78204610BD00BF01590040034A044B1B881088181A00B2704700BF5493FF1FA05B00400E4A13881BB223B111880A2309B25943E7
|
||||
:4029000001E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475693FF1F5C93FF1F5893FF1F70470000014B1870E9
|
||||
:40294000704700BF7B640040014B1870704700BF7F640040014B1870704700BF7C640040014B1870704700BF7E640040F7B516461F460B4C00230325019300930A460146B2
|
||||
:402980002846257000F022F93A463146207800F0DDF80221207800F0C7F8207803B0F0BDE080FF1FF7B516461F460B4C00230225019300930A4601462846257000F006F917
|
||||
:4029C0003A463146207800F0C1F82946207800F0ABF8207803B0F0BDE180FF1FF7B516461F460B4C00230125019300930A4601462846257000F0EAF83A463146207800F06F
|
||||
:402A0000A5F80221207800F08FF8207803B0F0BDE280FF1F73B515461E460B4C0023019300930A4601461846237000F0CFF832462946207800F08AF80221207800F074F880
|
||||
:402A4000207802B070BD00BFE380FF1F024B1878C0F38010704700BF8F450040034A00F0F800137803431370704700BF02410040034A00F0F800137803431370704700BF74
|
||||
:402A800006410040074A7F23802113705170064A013BDBB202F80839002BF9D1034A1370704700BFE480FF1FF87B00400078004017280FD8084B0001C25C11B142F020028D
|
||||
:402AC00001E002F0DF02C254C25C42F00102C25400207047012070471070004017280BD8064B0001C25C02F0FE02C254C25C02F0DF02C25400207047012070471070004024
|
||||
:402B000017280DD8074900010B4603441A7942F004021A71435C43F00103435400207047012070471070004017280BD8064A0001835C490003F0F10301F00E0119438154A3
|
||||
:402B400000207047012070471070004041F6FF73994208BF4FF400519A4208BF4FF4005217289FBFC00000F1804000F5EC4081809ABFC280002001207047000017289FBF6F
|
||||
:402B8000034B00011954002088BF0120704700BF1970004017289FBF054B00011A5C01F007019DBF1143195400200120704700BF1470004017289FBF034B0001185C00F04D
|
||||
:402BC000070088BFFF20704714700040172810B51AD8C00001F07F0100F1804441EAC21204F5EC44D2B222709DF8082003F00F0343EA0213DBB263709DF80C30002003F08B
|
||||
:402C00000F03A370E07010BD012010BD10B500F0C3F90A4A5378182B0AD91478013B5370E30003F1804303F5F0431B78137000E0FF2400F0B5F9204610BD00BFE480FF1F33
|
||||
:402C4000030610B5044611D400F0A6F9084AE300117803F1804303F5F04319705378147001335370BDE8104000F09AB910BD00BFE480FF1F30B504060CD411F4704509D1B0
|
||||
:402C8000C40004F1804404F5F0442180A270E370284630BD012030BD03065FBFC00000F1804000F5F04081805ABFC280002001207047000038B50446084DB4F5004F05D988
|
||||
:402CC000286800F061F9A4F50044F6E7034B58686043BDE8384000F057B900BFEC80FF1F024B1B7A584300F04FB900BFEC80FF1F0E4B00F003001A78490102F0FC02104300
|
||||
:402D000018701A7801F0600142F080021A701A7802F07F021A701A7802F09F020A431A701A7842F010021A70704700BF83430040014B01221A70704784430040044B00F08C
|
||||
:402D40000F021B6853F8220043F82210704700BF08ED00E0054A00F01F00126800F1100352F8230042F82310704700BF08ED00E000F01F0000F16040490100F56440C9B29B
|
||||
:402D8000017070470F4B10B50F4900240F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040313600A4B4FF47A72DB68B3FBF2F3084A1360084B4FF400421C60C3F883
|
||||
:402DC000E82010BD8092FF1F312E000010E000E0EC80FF1F14E000E018E000E0024A136843F002031360704710E000E008B5FFF7F5FF034A136843F00103136008BD00BFD3
|
||||
:402E000010E000E010B5054CA3691BB9FFF7BAFF0123A361BDE81040FFF7E8BF8092FF1F024B1868C0F30040704700BF10E000E038B5FFF7F5FF012808D1054D002455F891
|
||||
:402E4000243003B198470134052CF8D138BD00BF8492FF1F024B03EB80035868596070478092FF1F134B144A1B78DBB20360127843EA0223114A0360127843EA0243104A07
|
||||
:402E80000360127843EA026303600E4B0E4A1B78DBB24360127843EA02230C4A4360127843EA02430A4A4360127843EA02634360704700BF0301004904010049EC460040B2
|
||||
:402EC000020100490101004900010049050100490601004900000000FEB5494652465B460EB40746244909688A46244A12682448022100F071F8030020480068C018204936
|
||||
:402F000000F06AF8143883460121C9430C460125002600F041F8814651460B7823400B705846013000F030F83800F04028400B78234003430B70584600F026F80136072E00
|
||||
:402F4000F2D9002001300138013001200B78234003430B705846043000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF30080D480FF1FF880FF1F6B
|
||||
:402F800000C20100000000000230800803D000BF01380046FCD17047EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D113790C2103F07F021C
|
||||
:402FC000044B01FB02339B7A00E013790020704700600040D492FF1F002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB11217047704700BFA1
|
||||
:40300000014B1868704700BF5C81FF1F0E4B70B51E460E4C0025E41AA410A54204D056F8253098470135F8E700F04EFE084B094C1E46E41AA4100025A54204D056F8253071
|
||||
:4030400098470135F8E770BDD43C0000D43C0000D43C0000DC3C000003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B604FF0FF301DE04FF432
|
||||
:403080000273ADF80C300CBF234604F1FF33029305934FF6FF7300910491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B00221A701BB030BD000022
|
||||
:4030C00007B5009313460A46014603480068FFF7CBFF03B05DF804FB5C81FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD02568096902236F1A656977
|
||||
:4031000005EB450595FBF3F57B1C43449D4238BF1D4653050FD5294600F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A4600F098FB064670B9E0
|
||||
:403140002169504600F0E8FA0C23CAF80030A3894FF0FF3043F04003A381BDE8F08726613E44266046466561ED1BA560464528BF464649463246206800F0B3FAA3680020A9
|
||||
:403180009B1BA36023681E442660BDE8F08700002DE9F04F9DB003938B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F80030CDE040236B6150
|
||||
:4031C0000023099320238DF82930DFF89CB130238DF82A3037463C4614F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF771FF013000F0A780FE
|
||||
:40320000099B4B4409933B78002B00F0A08000234FF0FF3204930793059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46801A06FA00F0184379
|
||||
:403240000490EFE7D90644BF20228DF853201A0744BF2B228DF8532022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F00200079204900BE027468C
|
||||
:403280003B780134303B092B03D800FB02320121F5E701B107923B782E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E0002319460593781CA7
|
||||
:4032C0000A2407463A780130303A092A03D804FB01210123F5E703B1059103223978224800F0E6F940B14023CBEB000003FA00F0049B013718430490397806221B487E1CEB
|
||||
:403300008DF8281000F0D4F988B1194B33B9039B073323F007030833039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A9404600F093F8B0F12A
|
||||
:40334000FF3F824603D0099B5344099342E7AB895B0601D4099801E04FF0FF301DB0BDE8F08F00BFA33C0000A93C0000AD3C000000000000DD3000002DE9F04791461F4627
|
||||
:403380000A698B6806469342B8BF1346C9F8003091F843200C46DDF8208012B10133C9F800302368990642BFD9F800300233C9F80030256815F0060510D104F1190A07E05B
|
||||
:4033C0000123524639463046C04701301AD00135E368D9F800209B1A9D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F845102244023382F884
|
||||
:40340000431003E04FF0FF30BDE8F08704F1430239463046C0470130F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500259342C4BF9B1AED1843
|
||||
:403440004FF000091A344D4509D00123224639463046C0470130D5D009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9B01F1430E00F0AE8054
|
||||
:4034800011D8632A22D009D8002A00F0BB80582A40F0CA8081F84520834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B8800A6842F020020A6091
|
||||
:4034C0003EE0752A24D0782A3AD0ADE01A6801F14205111D1960136884F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F10401196002D0B2F9003000E045
|
||||
:403500001368002B3CDA2D225B4284F8432037E021681A6811F0800F02D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A14BF0A2208221BE068
|
||||
:4035400078225A4984F845202268186812F0800F00F104051D6003D1550601D5038800E00368D00744BF42F0200222601BB9226822F0200222601022002084F8430001E08A
|
||||
:4035800049490A226568002DA56008DB206820F0040020602BB9002D7DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D1082A0BD12368DA0737
|
||||
:4035C00008D5236962689A42DEBF302305F8013C05F1FF35C5EB0E0323612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F104001860136801D0198064
|
||||
:4036000000E0196000232361754616E01A68111D1960156800216268284600F049F808B1401B6060636804E004F1420584F8422001232361002384F84330CDF800803B468C
|
||||
:4036400003AA21463046FFF797FE013002D14FF0FF3026E023692A4639463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046C0470130E7D00135CC
|
||||
:40368000E368039A9B1A9D42F2DBE068039B9842B8BF184605E00B7804F1420584F842308AE705B0BDE8F083923B0000B43C000010B5C9B202449042034605D01C78013064
|
||||
:4036C0008C42F8D1184610BD002010BD10B5431E0A44914204D011F8014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21AD34204D013F8014DA2
|
||||
:4037000001F8014DF8E710BD994204D011F8014B02F8014FF8E710BD38B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B96360146030E0A342E7
|
||||
:403740000DD92268A018834201BF18685B681218226063600C6023E0A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D11068526801441960EE
|
||||
:403780005A600DE002D90C232B6009E021686018824201BF106852680918216062605C602846BDE8384000F098B838BDA092FF1F70B5CD1C25F0030508350C2D38BF0C2534
|
||||
:4037C000002D064601DBA94202D90C23336046E000F082F8234B1C681A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B681360636018BF0C4695
|
||||
:4038000015E00C464968E9E7174C23681BB9304600F052F820602946304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B00231D20F00700C31A48
|
||||
:403840000ED05A42E25070BD211A304600F034F80130EBD10C233360304600F03EF8002070BD00BFA092FF1F9C92FF1FF8B5074615460E4621B91146BDE8F840FFF798BFAB
|
||||
:403880001AB9FFF749FF2846F8BD00F027F885420ED929463846FFF78BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD38B5064C002305467D
|
||||
:4038C00008462360FDF7F4FB431C02D1236803B12B6038BD8493FF1F7047704751F8040C0028BEBF091851F8043CC0180438704700000000050209020B020D020F021102BB
|
||||
:403900001302150228000000000104000100000000000000000157494E55534200003030303031000000000000000000E0000000000105000100D6000000070000002A0075
|
||||
:4039400044006500760069006300650049006E0074006500720066006100630065004700550049004400730000009E0000007B00330064003200370035006300660065004E
|
||||
:403980002D0035003400330035002D0034006400640035002D0061006300630061002D003900660062003900390035006500320066003600330038007D0000007B00330058
|
||||
:4039C00064003200370035006300660065002D0035003400330035002D0034006400640035002D0061006300630061002D00390066006200390039003500650032006600B4
|
||||
:403A00003600330038007D00000000007265706C792030782530327800686F6D696E6700626567696E6E696E67207365656B2066726F6D20256420746F2025640066696E14
|
||||
:403A40006973686564207365656B00796573006E6F0057616974696E6720666F72205553422E2E2E00555342207265616479005363616E6E696E67206472697665732E2E1C
|
||||
:403A80002E00647269766520303A20257320647269766520313A20257300636F6D6D616E6420307825303278006661696C2025642B25642B2564203D3D2025642C206E6F40
|
||||
:403AC00074202564007061737365643D256400756E64657272756E206166746572202564207061636B65747300636F756E743D256420693D256420643D256400636D645FEF
|
||||
:403B0000777269746500703D25642063723D25642063773D256420663D256420773D256420696E6465783D256420756E64657272756E3D256400756E64657272756E210015
|
||||
:403B4000737563636573730073746172742065726173696E670073746F702065726173696E670069646C650000510040100040510040300000000140001000140140000858
|
||||
:403B800000400140000A004C01400002005001402000303132333435363738394142434445460000000100000004000000100001000000040000001001000000C03B000072
|
||||
:403BC000010000008F3C0000000000000000000001000000D83B000001000000613C000004000000FA3B0000000000000000000000000000F83B0000FF0000000102400099
|
||||
:403C0000FF00000082024000FF00000003034000FF00000084034000FF00020304030904160346006C007500780045006E00670069006E0065002A0343006F0077006C00CE
|
||||
:403C4000610072006B00200054006500630068006E006F006C006F0067006900650073000009022E0001010080320904000004FF00000107050102400000070582024000E5
|
||||
:403C8000000705030340000A0705840340000A12010002FF0001080912006E0100020180014300232D302B2000686C4C00656667454647003031323334353637383961629E
|
||||
:403CC0006364656600000000F8B500BFF8BC08BC9E467047590000002D110000F8B500BFF8BC08BC9E46704735000000003D0000C880FF1F980000002812000000000000F3
|
||||
:403D0000000000008893FF1FFFFF0000675000400C00000007000000FFFFFFFF7F8000003F0000000000007D00FA0000400000000090D003000000000000000000000000EE
|
||||
:403D40000000000000000000000000000000000000000000A13C00000000000000000000000000000000000000000000000000000000000000000000000000000000000066
|
||||
:403D80000000000000000000000000000000000000000000FC80FF1F0000000000000000000000000000000000000000000000000000000000000000000000000000000069
|
||||
:403DC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C3
|
||||
:403E00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082
|
||||
:403E40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042
|
||||
@@ -4098,69 +4098,69 @@
|
||||
:40FF80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041
|
||||
:40FFC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
|
||||
:0200000480007A
|
||||
:400000000145004007520040015B00400264004001010140490201404103014059040140540501404C0601404707014047080140470901404C0A0140500B0140590C0140B7
|
||||
:40004000520D0140460E0140380F01404E1401405F1501405516014058170140491801404C1901404B1A0140581B01400D400140094101400C4201400A43014003440140AF
|
||||
:4000800004450140064601400E4701400B480140104901401D4C01400A4D014008500140045101407E020841090210801801600361157C4027212D0A3501E60103400410EF
|
||||
:4000C0000620070E09300A1C0B020C100D3E0E4011011461150816061710186219301A01200B210822602320266027302A802B012C7C2D042F3032FC337E360337013980D2
|
||||
:400100003E403F404201470E481049FF4AFF4BFF4F83580459045A045B045C995D095F01823F843F8A028C019208963F9A3F9C3F9D01A210A604AA20AC3FAD02B502B63F2A
|
||||
:40014000B701BE40BF50D804D904DB04DF010085020A04100504060108A90D040E040F2010201120134215401680171418021C101D801E20200222102680272028022D08FD
|
||||
:400180002E802F4832403410350437013D423E083F20458046084720550858405A205C405E2A6501660867066A8080A08404870288408A028C20A508B108C0EFC26FC4FF5D
|
||||
:4001C000CAF8CCE8CEF0D010D6FCD8F0E607EE20020703D3040805130610072009010B080DF20E280F04111212401380151217401804190A1A301B051C021E381F12203F7E
|
||||
:40020000251327E028382A012C342E08307F33F0350737083B203F405608580459045B045C995D905F018002821883018404860887018B018E188F0191019207950198180E
|
||||
:400240009B019C049E109F01A01FA301A418A501A601AB01AE20AF01B03FB101B301BF05C036C9FFCAFFCBFFD004D601D804D904DA04DB04DC09DD09DF01E080E240E44082
|
||||
:40028000E5640102032204020504060209410B080E840F02120B130215081742184019021E801F0121442298250127502B202C102D202E052F22310432923698370238028F
|
||||
:4002C00039483A103D843EA247084A024B804F1059806002680269406B026D046E216F0675287622861094449548960497C698819B809C1A9D0D9E819F15A0E4A110A501E4
|
||||
:40030000A605A708A801AB80AC81AE88B001B401B510C0BDC2DBC4BBCAF2CCFFCEFFD040D220D608D808E202EA0B02010301040107010A010B010D040E010F0312011302F6
|
||||
:400340001503160117041A011B011E011F012201230125012601270428012B012E012F01330734013E103F04580459045B045F0180038140821C844C860387078B408C8032
|
||||
:400380008D1F9104931095189620970199189A059E4F9F20A003A102A210A318A504A708A80FAA40AB18AD80AE0AB03FB280B3C0B440B53FBA02BE04D804D904DB04DC9907
|
||||
:4003C000DF010142032004020528060209400B280E65124A13021404158016A019131A601F10201023042504280229242B402D282E812F62320A33603404369A3810390293
|
||||
:400400003A403B043D843E2E3F8059405B8069406E806F0D7A027B018580918C92019486975C988199209A229BD29C089D859F09A0E6A110A298A302A540A607A729A8400D
|
||||
:40044000C0FDC2F7C4FBCAFFCCFFCEFFE208EE0C0156020103210403070108030B020C030D061003110713081602170119061A031C031F042106220423782540260827069F
|
||||
:4004800029112A032B262C032D7F320F3403357F3E103F10580459045B045C095F0180018103830C843F870F89088A028E048F01910F923F95049620983F99089D089E3F1D
|
||||
:4004C000A108A208A508A610AA3FAB02AC3FAF08B23FB70FBE04BF40D804D904DF01008801020244044005150AB10B040E450F111105120813801401153017861B081C2078
|
||||
:400500001D041E081F042302270429102A452D102E022F223218334234013510368838403A203B093C503D043E016D806E406F028A028B0490509145929095209645975408
|
||||
:40054000980299139AA59B409C809D049E02A044A288A411A54FA604A72AB404C0FFC2FFC4FFCAFFCCFFCEFFE208EE0C0105050F0601071009050A040C010D080F021105AF
|
||||
:40058000120115051602180119101C081D052001230524012505292C2A012B022C082D132F2030073408373F3A223F40580459045C095F018001820C8303853D860187C0FF
|
||||
:4005C000880689808C068D809003918092089407958099309A069C069D809E01A10CA201A340A5A9A602A801A980AA06AC06AD56B1FFB40FBE10BF01D608D804D904DB0439
|
||||
:40060000DD90DF010008030A044205100608072009800A200B840D080E451108120A130214201521160219111A881C042180260229122A042B022E092F4632013364350557
|
||||
:400640003680372239403A213B043C403D023F145F40600861106680868088028904905A914592329308940496C99714982099029A259B649C88A044A288A411A54EA60479
|
||||
:40068000A720AB20AD80AE20AF01B412B610B701C0F7C2FFC4FFCAFFCCFFCEFFD610D810E8C0EE1002490502060108400A300C480E20100115021602184919011E04204807
|
||||
:4006C00021052210244928012D022E013207330134383504364037023A083BC03F04400145C047E0482249FF4AFF4BFF50045609580459045A045B045C995D995F0162C025
|
||||
:40070000825D8501861D881D8C1D9207941D98019A309C1D9E40A01DA45DA801AA08AC0CAE22B040B203B301B43CBE15BF04C043C702C810C9FFCAFFCBFFCD20CEF0D11014
|
||||
:40074000D804D904DA04DD09DF01E108E240E340E480E640E7400062032004800511072008090A810B080E420F2410101188128013101450151016011A201B121C101D9077
|
||||
:400780001F8421042402269827202C442E202F81361438083C044104430245804C405004550159015A015CB05D1065406D456E106F547444750276A57709828083408504AB
|
||||
:4007C000860189408A808E04904094109505965997149C0F9D829E859F38A201A410A50FA680A720C0FFC2FFC4FFCA90CC60CE42D013D610D810E280E63001030208040194
|
||||
:40080000070808080B020D030E0F100F1110140815031A021B031C031D031E0C2008230424082703280429032C082F01300F310F35103E013F10580459045B045C905F0131
|
||||
:400840008001812E8341850F861087108A3F8B088D0E8E028F70903F930295219620974E983F9B019E3F9F04A10EA208A604A70EAA3FAB01AC3FAD7FB57FB63FBE40BF103B
|
||||
:40088000D804D904DF010040010202040320041005800680071008040AC90C100D420E190F0411051201132015011601172819011E022040210122102404281929012C40C4
|
||||
:4008C0002E502F203002310832403320340236843720380439823B103C603D083E0168028101821088408A04C0FFC2FFC4FFCAFFCCFFCEFFE40400AA0101024405080644A9
|
||||
:4009000009010CF10D0112221401170218CC1A221B011C881F01221123042B012D01300E3108330734F0360F3B083E013F015608580859045B045C995D905F0180028210E0
|
||||
:4009400083078540860A89188B018D028F18907A9104920493109401968098199A629B209C829E059F40A040A21BA318A41BA518A620A904AB08AD1FB13FB280B478B540A1
|
||||
:40098000B607B920BA80BE04BF10D804D904DC99DF01000A01800504074009880A400B040C020D080E090F20110114401620188019801B501D041E091F2420202101231042
|
||||
:4009C000250126012708280829012B022C022F64302431403642371038043A413B503D863E2058805A1060066180680469106A4079808080830884118501880489108B44A1
|
||||
:400A00008C0A8D408E40910592109380948095809748984099419A219B509C089D049E40A022A140A242A302A410A601A760AA01B508C03DC2FFC438CAFDCCBECEFFD60C07
|
||||
:400A4000D80CDE08E008E280E402E649EA06EE02001F0101034006200720090B0A180C180F0B1080124013021418160119421A071B051C021E1822C02440250126802702E3
|
||||
:400A800028042A082B082C042D022E102F1431403280343F350736403738388839803B203E443F01580459045B045C995F01803F8501861087868A3F8B288C01916892048B
|
||||
:400AC000963F983F99869B019D039E089F80A108A202A43FA984AA20AD50AE3FB03FB118B307B580B760B908BE01BF14D608D804D908DB04DC90DD90DF0100A802400440A7
|
||||
:400B000006880710088909200C400D040E8810A0112013801514174519011C021D081E082101220123192402254026A22740280229102A022C022D102E802F24304032818E
|
||||
:400B400033203622381039083D883E223F01410442205A8060026F0179808044812082108322850188018E408F40901091089380948695029758988199509A089B949C0AE3
|
||||
:400B80009D049F08A0E6A114A240A540A607A720B610C0FFC2FFC4FFCA7DCCADCEF6D608D808DE08E28DE820EE0B03220401050709800A020C0C0E020F111107140A160473
|
||||
:400BC00017071B081C102107223025442601280829552B222D662E042F11310F320F330F34103580362037703B0A3E103F10580459045B045C995F01810182068402850160
|
||||
:400C0000880289018C028F0190039101930294029501970299019E039F01A002A101A402A701A801A901AA04AC02AD01B003B204B503BE05BF10D804D904DF010088010262
|
||||
:400C40000215031804240580070108040A510C080E0211451208168A170819081B011C201E061F302220252F26042782280629082A012C082D802E082F80310833923624D6
|
||||
:400C8000370239613A043D103E023F846D9081048410870889208C049050914592509308952096059714980299139A849B409C809F80A044A288A302A401A50FA720B34087
|
||||
:400CC000C0EFC2ADC4FFCAFFCCEFCEFFE211E440E860EE0B0002010104020501080209010C020F01100215011602180219011C081D011E0120022301240326042704281059
|
||||
:400D000029012C1C2E032F02321F3507370739A03E04580459045C905F018006810282018360850186088738880489328C078D3290079102931095339602970498079932E7
|
||||
:400D40009D329E10A007A133A30CA601A732A805A932AA02AD1FAF40B30FB570B61FBF14D804D904DB04DC09DF01005002A0040205400608071008020A010B280E600F0644
|
||||
:400D800010021109120815101620170219081D401E011F80200A2110221023042718290A2B0A2FA63004321033403402351136A439443A113C503D013F0448035F406C099A
|
||||
:400DC0006D106F018511875088048A128B098C108EA88FC090509144924093289420952096019714980499139A919B429E0A9F01A104A28CA32BA409A7C0AB02AD40B480B4
|
||||
:400E0000C0FFC2FFC47FCAFFCCFECEFFD610E090E260E440E6B1EC10EE021280170430203304368438083B403C808004C430CCF0CE70E68032043380364037029740A420E0
|
||||
:400E4000A604B040CCF0530456109308960897409F02A420A644A780AF04D460EE1097409E109F02A640A780A820AA04AE04EA80EE3014808301C4045D0893029880D60148
|
||||
:400E80001B0493029880B108C608E80108080B080D800F4086019302970898809B40AB04AF04B740C20FE8082580890297409E10A580AB02AE40AF80B180C820EED053045D
|
||||
:400EC00056207240750282408B048E209902AE10AF40D460DC80DE20E220EA40EE40070408020E010F201F10538056205B40670283048A208F109302960198809B40A38043
|
||||
:400F0000A980AC08AF40B780C001C20DC601D407D601E208E401E8027210808092109880AF10B402B610DC01EA08EC08010109010B010D010F0111011B011D0100FF01AB85
|
||||
:400F400002021005BF0000A09F001F000000000000000000100000004000000000000000C0000000FF0000B84700470000010000800000008000800000000000000303005F
|
||||
:400F800003000000270018012700180100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000AD
|
||||
: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
|
||||
:400B40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075
|
||||
:400B80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035
|
||||
:400BC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F5
|
||||
:400C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B4
|
||||
:400C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074
|
||||
:400C80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034
|
||||
:400CC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F4
|
||||
:400D000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B3
|
||||
:400D40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000073
|
||||
:400D80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000033
|
||||
:400DC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F3
|
||||
:400E000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B2
|
||||
:400E40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000072
|
||||
:400E80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032
|
||||
:400EC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F2
|
||||
:400F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B1
|
||||
:400F40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000071
|
||||
:400F80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031
|
||||
:400FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F1
|
||||
:4010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B0
|
||||
:401040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070
|
||||
@@ -4615,12 +4615,12 @@
|
||||
:0200000490105A
|
||||
:04000000BC90ACAF55
|
||||
:0200000490303A
|
||||
:0200000001A954
|
||||
:0200000021B02D
|
||||
:0200000490402A
|
||||
:4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0
|
||||
:400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
|
||||
:400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
|
||||
:4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
:0200000490501A
|
||||
:0C00000000012E16106900002E301212B4
|
||||
:0C00000000012E16106900002E3032198D
|
||||
:00000001FF
|
||||
@@ -20,33 +20,34 @@ module FIFOout (
|
||||
wire [7:0] po;
|
||||
assign d = po;
|
||||
|
||||
localparam STATE_WAIT = 1'b0;
|
||||
localparam STATE_READ = 1'b1;
|
||||
localparam STATE_WAITFORREQ = 0;
|
||||
localparam STATE_READFROMFIFO = 1;
|
||||
localparam STATE_WAITFORNREQ = 2;
|
||||
|
||||
reg state;
|
||||
reg oldreq;
|
||||
reg [1:0] state;
|
||||
wire readfromfifo;
|
||||
|
||||
assign ack = (state != STATE_READ);
|
||||
assign ack = (state == STATE_WAITFORNREQ);
|
||||
assign readfromfifo = (state == STATE_READFROMFIFO);
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
case (state)
|
||||
STATE_WAIT:
|
||||
/* opcode is not valid; req is low; wait for req to go high. */
|
||||
STATE_WAITFORREQ:
|
||||
begin
|
||||
if (!empty)
|
||||
begin
|
||||
if (req && !oldreq)
|
||||
begin
|
||||
state <= STATE_READ;
|
||||
end
|
||||
oldreq <= req;
|
||||
end
|
||||
if (!empty && req)
|
||||
state <= STATE_READFROMFIFO;
|
||||
end
|
||||
|
||||
STATE_READ:
|
||||
begin
|
||||
state <= STATE_WAIT;
|
||||
end
|
||||
/* Fetch a single value from the FIFO. */
|
||||
STATE_READFROMFIFO:
|
||||
state <= STATE_WAITFORNREQ;
|
||||
|
||||
/* opcode is valid; ack is high. Wait for req to go low. */
|
||||
STATE_WAITFORNREQ:
|
||||
if (!req)
|
||||
state <= STATE_WAITFORREQ;
|
||||
endcase
|
||||
end
|
||||
|
||||
@@ -55,11 +56,11 @@ cy_psoc3_dp #(.cy_dpconfig(
|
||||
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
|
||||
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
|
||||
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
|
||||
`CS_CMP_SEL_CFGA, /*CFGRAM0: STATE_WAITFORREQ*/
|
||||
`CS_CMP_SEL_CFGA, /*CFGRAM0: idle */
|
||||
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
|
||||
`CS_SHFT_OP_PASS, `CS_A0_SRC___F0, `CS_A1_SRC_NONE,
|
||||
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
|
||||
`CS_CMP_SEL_CFGA, /*CFGRAM1: STATE_LOAD*/
|
||||
`CS_CMP_SEL_CFGA, /*CFGRAM1: read from fifo */
|
||||
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
|
||||
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
|
||||
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
|
||||
@@ -102,7 +103,7 @@ cy_psoc3_dp #(.cy_dpconfig(
|
||||
)) dp(
|
||||
/* input */ .reset(1'b0),
|
||||
/* input */ .clk(clk),
|
||||
/* input [02:00] */ .cs_addr({2'b0, state}),
|
||||
/* input [02:00] */ .cs_addr({2'b0, readfromfifo}),
|
||||
/* input */ .route_si(1'b0),
|
||||
/* input */ .route_ci(1'b0),
|
||||
/* input */ .f0_load(1'b0),
|
||||
|
||||
@@ -814,9 +814,9 @@
|
||||
</Group>
|
||||
<Group key="Component">
|
||||
<Group key="v1">
|
||||
<Data key="cy_boot" value="cy_boot_v5_80" />
|
||||
<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" />
|
||||
@@ -848,15 +848,18 @@
|
||||
<Data key="efd5f185-0c32-4824-ba72-3ceb5356f5a7" value="Clock_1" />
|
||||
</Group>
|
||||
<Group key="Pin">
|
||||
<Data key="3e1862bb-be82-47b0-9549-7ebfe76b6f7b" value="Pin_2" />
|
||||
<Data key="4a398466-709f-4228-9500-96178658e13e" value="RDATA" />
|
||||
<Data key="5a3407c1-b434-4438-a7b4-b9dfd2280495" value="MOTEA" />
|
||||
<Data key="8d318d8b-cf7b-4b6b-b02c-ab1c5c49d0ba" value="SW1" />
|
||||
<Data key="8fc20a4f-e4d1-44b3-a5d4-546e8628d61e" value="LED" />
|
||||
<Data key="12e00eac-69b5-4717-85c8-25ef6b224d4c" value="DEBUG_PINS" />
|
||||
<Data key="41e2d8ed-5494-4d8c-8ff7-f4f789cece51" value="REDWC" />
|
||||
<Data key="264be2d3-9481-494b-8d9c-c1905a45e9cc" value="FDD" />
|
||||
<Data key="472f8fdb-f772-44fb-8897-cc690694237b" value="WDATA" />
|
||||
<Data key="736cb12b-c863-43d4-a8f0-42f06023f8b5" value="SIDE1" />
|
||||
<Data key="4249c923-fcff-453b-8629-bec6fddd00c1" value="STEP" />
|
||||
<Data key="27315b0e-6a8c-4b7f-be77-73ab434fa803" value="Pin_1" />
|
||||
<Data key="1425177d-0d0e-4468-8bcc-e638e5509a9b" value="UartRx" />
|
||||
<Data key="a5d987c6-e45b-45b9-ad93-656fab06d720" value="TRK00" />
|
||||
<Data key="a93ef5b3-00f4-42c0-8fad-0e275a7e2537" value="MOTEB" />
|
||||
@@ -868,6 +871,7 @@
|
||||
<Data key="c5367cde-21d5-4866-9a32-d16abfea0c61" value="WPT" />
|
||||
<Data key="d19368c5-6855-41bb-a9ff-808938abef00" value="INDEX" />
|
||||
<Data key="e9f14b5a-b2bf-49b8-98f3-d7b5a43ace8d" value="DRVSB" />
|
||||
<Data key="e16b5ef8-00d3-40a4-bc1c-194983c8eb3d" value="LOW_CURRENT" />
|
||||
<Data key="e851a3b9-efb8-48be-bbb8-b303b216c393" value="INDEX300" />
|
||||
<Data key="e51063a9-4fad-40c7-a06b-7cc4b137dc18" value="DSKCHG" />
|
||||
<Data key="ea7ee228-8b3f-426c-8bb8-cd7a81937769" value="DIR" />
|
||||
@@ -3962,6 +3966,11 @@
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="Pin2">
|
||||
<Group key="3e1862bb-be82-47b0-9549-7ebfe76b6f7b">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="0,6" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="4a398466-709f-4228-9500-96178658e13e">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="1,5" />
|
||||
@@ -3977,6 +3986,11 @@
|
||||
<Data key="Port Format" value="2,2" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="8fc20a4f-e4d1-44b3-a5d4-546e8628d61e">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="2,1" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="12e00eac-69b5-4717-85c8-25ef6b224d4c">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="2,2" />
|
||||
@@ -4058,6 +4072,11 @@
|
||||
<Data key="Port Format" value="1,0" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="27315b0e-6a8c-4b7f-be77-73ab434fa803">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="0,7" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="1425177d-0d0e-4468-8bcc-e638e5509a9b">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="12,6" />
|
||||
@@ -4143,6 +4162,11 @@
|
||||
<Data key="Port Format" value="12,3" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="e16b5ef8-00d3-40a4-bc1c-194983c8eb3d">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="3,2" />
|
||||
</Group>
|
||||
</Group>
|
||||
<Group key="e851a3b9-efb8-48be-bbb8-b303b216c393">
|
||||
<Group key="0">
|
||||
<Data key="Port Format" value="3,0" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
||||
|
||||
//`#start header` -- edit after this line, do not edit this line
|
||||
`include "cypress.v"
|
||||
`include "../SuperCounter/SuperCounter.v"
|
||||
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
// Generated on 12/11/2019 at 21:18
|
||||
@@ -9,7 +8,7 @@
|
||||
module Sampler (
|
||||
output [2:0] debug_state,
|
||||
output reg [7:0] opcode,
|
||||
output req,
|
||||
output reg req,
|
||||
input clock,
|
||||
input index,
|
||||
input rdata,
|
||||
@@ -19,113 +18,76 @@ module Sampler (
|
||||
|
||||
//`#start body` -- edit after this line, do not edit this line
|
||||
|
||||
localparam STATE_RESET = 0;
|
||||
localparam STATE_WAITING = 1;
|
||||
localparam STATE_INTERVAL = 2;
|
||||
localparam STATE_DISPATCH = 3;
|
||||
localparam STATE_OPCODE = 4;
|
||||
localparam STATE_COUNTING = 5;
|
||||
// NOTE: Reset pulse is used in both clock domains, and must be long enough
|
||||
// to be detected in both.
|
||||
|
||||
reg [2:0] state;
|
||||
wire [6:0] counter;
|
||||
reg [5:0] counter;
|
||||
|
||||
wire countnow;
|
||||
assign countnow = (state == STATE_COUNTING);
|
||||
reg index_edge;
|
||||
reg rdata_edge;
|
||||
|
||||
wire counterreset;
|
||||
assign counterreset = (state == STATE_INTERVAL) || (state == STATE_OPCODE);
|
||||
reg req_toggle;
|
||||
|
||||
SuperCounter #(.Delta(1), .ResetValue(0)) Counter
|
||||
(
|
||||
/* input */ .clk(clock),
|
||||
/* input */ .reset(counterreset),
|
||||
/* input */ .count(countnow),
|
||||
/* output */ .d(counter)
|
||||
);
|
||||
reg rdata_toggle;
|
||||
reg old_rdata_toggle;
|
||||
|
||||
reg oldsampleclock;
|
||||
wire sampleclocked;
|
||||
assign sampleclocked = !oldsampleclock && sampleclock;
|
||||
reg index_toggle;
|
||||
reg old_index_toggle;
|
||||
|
||||
reg oldindex;
|
||||
wire indexed;
|
||||
assign indexed = !oldindex && index;
|
||||
always @(posedge rdata)
|
||||
begin
|
||||
rdata_toggle <= ~rdata_toggle;
|
||||
end
|
||||
|
||||
wire rdataed;
|
||||
reg oldrdata;
|
||||
assign rdataed = !oldrdata && rdata;
|
||||
always @(posedge index)
|
||||
begin
|
||||
index_toggle <= ~index_toggle;
|
||||
end
|
||||
|
||||
assign req = (state == STATE_INTERVAL) || (state == STATE_OPCODE);
|
||||
|
||||
always @(posedge clock)
|
||||
always @(posedge sampleclock)
|
||||
begin
|
||||
if (reset)
|
||||
begin
|
||||
state <= STATE_RESET;
|
||||
opcode <= 0;
|
||||
oldsampleclock <= 0;
|
||||
oldindex <= 0;
|
||||
oldrdata <= 0;
|
||||
old_rdata_toggle <= 0;
|
||||
old_index_toggle <= 0;
|
||||
|
||||
index_edge <= 0;
|
||||
rdata_edge <= 0;
|
||||
counter <= 0;
|
||||
req_toggle <= 0;
|
||||
end
|
||||
else
|
||||
case (state)
|
||||
STATE_RESET:
|
||||
state <= STATE_WAITING;
|
||||
|
||||
STATE_WAITING:
|
||||
begin
|
||||
if (rdataed || indexed)
|
||||
begin
|
||||
opcode <= {0, counter};
|
||||
state <= STATE_INTERVAL;
|
||||
end
|
||||
else if (sampleclocked)
|
||||
begin
|
||||
oldsampleclock <= 1;
|
||||
if (counter == 7'h7f)
|
||||
begin
|
||||
opcode <= {0, counter};
|
||||
state <= STATE_OPCODE;
|
||||
end
|
||||
else
|
||||
state <= STATE_COUNTING;
|
||||
end
|
||||
|
||||
if (oldrdata && !rdata)
|
||||
oldrdata <= 0;
|
||||
if (oldindex && !index)
|
||||
oldindex <= 0;
|
||||
if (oldsampleclock && !sampleclock)
|
||||
oldsampleclock <= 0;
|
||||
end
|
||||
|
||||
STATE_INTERVAL: /* interval byte sent here; counter reset */
|
||||
state <= STATE_DISPATCH;
|
||||
|
||||
STATE_DISPATCH: /* relax after interval byte, dispatch for opcode */
|
||||
begin
|
||||
if (rdataed)
|
||||
begin
|
||||
oldrdata <= 1;
|
||||
opcode <= 8'h80;
|
||||
state <= STATE_OPCODE;
|
||||
end
|
||||
else if (indexed)
|
||||
begin
|
||||
oldindex <= 1;
|
||||
opcode <= 8'h81;
|
||||
state <= STATE_OPCODE;
|
||||
end
|
||||
else
|
||||
state <= STATE_WAITING;
|
||||
end
|
||||
|
||||
STATE_OPCODE: /* opcode byte sent here */
|
||||
state <= STATE_WAITING;
|
||||
|
||||
STATE_COUNTING: /* counter changes here */
|
||||
state <= STATE_WAITING;
|
||||
endcase
|
||||
begin
|
||||
/* If data_toggle or index_toggle have changed state, this means that they've
|
||||
* gone high since the last sampleclock. */
|
||||
|
||||
index_edge <= index_toggle != old_index_toggle;
|
||||
old_index_toggle <= index_toggle;
|
||||
|
||||
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 };
|
||||
req_toggle <= ~req_toggle;
|
||||
counter <= 1; /* remember to count this tick */
|
||||
end else begin
|
||||
counter <= counter + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
reg req_toggle_q;
|
||||
|
||||
always @(posedge clock)
|
||||
begin
|
||||
if (reset) begin
|
||||
req_toggle_q <= 0;
|
||||
req <= 0;
|
||||
end else begin
|
||||
req_toggle_q <= req_toggle;
|
||||
req <= (req_toggle != req_toggle_q);
|
||||
end
|
||||
end
|
||||
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
|
||||
@@ -18,21 +18,16 @@ module Sequencer (
|
||||
|
||||
//`#start body` -- edit after this line, do not edit this line
|
||||
|
||||
localparam STATE_IDLE = 0;
|
||||
localparam STATE_LOAD = 1;
|
||||
localparam STATE_WAITING = 2;
|
||||
localparam STATE_PULSING = 3;
|
||||
localparam STATE_INDEXING = 4;
|
||||
localparam STATE_LOAD = 0;
|
||||
localparam STATE_WRITING = 1;
|
||||
|
||||
localparam OPCODE_PULSE = 8'h80;
|
||||
localparam OPCODE_INDEX = 8'h81;
|
||||
reg state;
|
||||
reg [5:0] countdown;
|
||||
reg pulsepending;
|
||||
|
||||
reg [2:0] state;
|
||||
reg [6:0] countdown;
|
||||
|
||||
assign req = (state == STATE_LOAD);
|
||||
assign wdata = (state == STATE_PULSING);
|
||||
assign debug_state = state;
|
||||
assign req = (!reset && (state == STATE_LOAD));
|
||||
assign wdata = (!reset && (state == STATE_WRITING) && (countdown == 0) && pulsepending);
|
||||
assign debug_state = 0;
|
||||
|
||||
reg olddataclock;
|
||||
wire dataclocked;
|
||||
@@ -40,9 +35,7 @@ always @(posedge clock) olddataclock <= dataclock;
|
||||
assign dataclocked = !olddataclock && dataclock;
|
||||
|
||||
reg oldsampleclock;
|
||||
wire sampleclocked;
|
||||
always @(posedge clock) oldsampleclock <= sampleclock;
|
||||
assign sampleclocked = !oldsampleclock && sampleclock;
|
||||
reg sampleclocked;
|
||||
|
||||
reg oldindex;
|
||||
wire indexed;
|
||||
@@ -53,48 +46,47 @@ always @(posedge clock)
|
||||
begin
|
||||
if (reset)
|
||||
begin
|
||||
state <= STATE_IDLE;
|
||||
state <= STATE_LOAD;
|
||||
countdown <= 0;
|
||||
pulsepending <= 0;
|
||||
oldsampleclock <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if (!oldsampleclock && sampleclock)
|
||||
sampleclocked <= 1;
|
||||
oldsampleclock <= sampleclock;
|
||||
|
||||
case (state)
|
||||
STATE_IDLE:
|
||||
state <= STATE_LOAD;
|
||||
|
||||
STATE_LOAD:
|
||||
begin
|
||||
/* A posedge on dataclocked indicates that another opcode has
|
||||
* arrived. */
|
||||
if (dataclocked)
|
||||
case (opcode)
|
||||
OPCODE_PULSE:
|
||||
state <= STATE_PULSING;
|
||||
|
||||
OPCODE_INDEX:
|
||||
state <= STATE_INDEXING;
|
||||
|
||||
default:
|
||||
begin
|
||||
countdown <= opcode[6:0];
|
||||
state <= STATE_WAITING;
|
||||
end
|
||||
endcase
|
||||
begin
|
||||
pulsepending <= opcode[7];
|
||||
if (opcode[5:0] == 0)
|
||||
countdown <= 0;
|
||||
else
|
||||
countdown <= opcode[5:0] - 1; /* compensate for extra tick in state machine */
|
||||
|
||||
state <= STATE_WRITING;
|
||||
end
|
||||
end
|
||||
|
||||
STATE_WAITING:
|
||||
STATE_WRITING:
|
||||
begin
|
||||
if (sampleclocked)
|
||||
begin
|
||||
if (countdown == 0)
|
||||
state <= STATE_LOAD;
|
||||
else
|
||||
countdown <= countdown - 1;
|
||||
sampleclocked <= 0;
|
||||
end
|
||||
|
||||
STATE_PULSING:
|
||||
state <= STATE_LOAD;
|
||||
|
||||
STATE_INDEXING:
|
||||
if (indexed)
|
||||
state <= STATE_LOAD;
|
||||
else
|
||||
state <= STATE_INDEXING;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
//`#end` -- edit above this line, do not edit this line
|
||||
|
||||
Binary file not shown.
@@ -5,7 +5,6 @@
|
||||
#include <setjmp.h>
|
||||
#include "project.h"
|
||||
#include "../protocol.h"
|
||||
#include "../lib/common/crunch.h"
|
||||
|
||||
#define MOTOR_ON_TIME 5000 /* milliseconds */
|
||||
#define STEP_INTERVAL_TIME 6 /* ms */
|
||||
@@ -14,11 +13,18 @@
|
||||
#define DISKSTATUS_WPT 1
|
||||
#define DISKSTATUS_DSKCHG 2
|
||||
|
||||
#define STEP_TOWARDS0 1
|
||||
#define STEP_AWAYFROM0 0
|
||||
#define STEP_TOWARDS0 0
|
||||
#define STEP_AWAYFROM0 1
|
||||
|
||||
static bool drive0_present;
|
||||
static bool drive1_present;
|
||||
|
||||
static volatile uint32_t clock = 0; /* ms */
|
||||
static volatile bool index_irq = false;
|
||||
/* Duration in ms. 0 causes every pulse to be an index pulse. Durations since
|
||||
* last pulse greater than this value imply sector pulse. Otherwise is an index
|
||||
* pulse. */
|
||||
static volatile uint32_t hardsec_index_threshold = 0;
|
||||
|
||||
static bool motor_on = false;
|
||||
static uint32_t motor_on_time = 0;
|
||||
@@ -26,7 +32,7 @@ static bool homed = false;
|
||||
static int current_track = 0;
|
||||
static struct set_drive_frame current_drive_flags;
|
||||
|
||||
#define BUFFER_COUNT 16
|
||||
#define BUFFER_COUNT 64 /* the maximum */
|
||||
#define BUFFER_SIZE 64
|
||||
static uint8_t td[BUFFER_COUNT];
|
||||
static uint8_t dma_buffer[BUFFER_COUNT][BUFFER_SIZE] __attribute__((aligned()));
|
||||
@@ -41,6 +47,8 @@ static volatile bool dma_underrun = false;
|
||||
#define DECLARE_REPLY_FRAME(STRUCT, TYPE) \
|
||||
STRUCT r = {.f = { .type = TYPE, .size = sizeof(STRUCT) }}
|
||||
|
||||
static void stop_motor(void);
|
||||
|
||||
static void system_timer_cb(void)
|
||||
{
|
||||
CyGlobalIntDisable;
|
||||
@@ -65,12 +73,41 @@ static void system_timer_cb(void)
|
||||
|
||||
CY_ISR(index_irq_cb)
|
||||
{
|
||||
index_irq = true;
|
||||
/* Hard sectored media has sector pulses at the beginning of every sector
|
||||
* and the index pulse is an extra pulse in the middle of the last sector.
|
||||
* When the extra pulse is seen, the next sector pulse is also the start of
|
||||
* the track. */
|
||||
static bool hardsec_index_irq_primed = false;
|
||||
static uint32_t hardsec_last_pulse_time = 0;
|
||||
uint32_t index_pulse_duration = clock - hardsec_last_pulse_time;
|
||||
|
||||
if (!hardsec_index_threshold)
|
||||
{
|
||||
index_irq = true;
|
||||
hardsec_index_irq_primed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's only an index pulse if the previous pulse is less than
|
||||
* the threshold.
|
||||
*/
|
||||
index_irq = (index_pulse_duration <= hardsec_index_threshold) ?
|
||||
hardsec_index_irq_primed : false;
|
||||
|
||||
if (index_irq)
|
||||
hardsec_index_irq_primed = false;
|
||||
else
|
||||
hardsec_index_irq_primed =
|
||||
index_pulse_duration <= hardsec_index_threshold;
|
||||
|
||||
hardsec_last_pulse_time = clock;
|
||||
}
|
||||
|
||||
/* Stop writing the instant the index pulse comes along; it may take a few
|
||||
* moments for the main code to notice the pulse, and we don't want to overwrite
|
||||
* the beginning of the track. */
|
||||
ERASE_REG_Write(0);
|
||||
if (index_irq)
|
||||
ERASE_REG_Write(0);
|
||||
}
|
||||
|
||||
CY_ISR(capture_dma_finished_irq_cb)
|
||||
@@ -103,11 +140,14 @@ static void print(const char* msg, ...)
|
||||
static void set_drive_flags(struct set_drive_frame* flags)
|
||||
{
|
||||
if (current_drive_flags.drive != flags->drive)
|
||||
{
|
||||
stop_motor();
|
||||
homed = false;
|
||||
}
|
||||
|
||||
current_drive_flags = *flags;
|
||||
DRIVESELECT_REG_Write(flags->drive ? 2 : 1); /* select drive 1 or 0 */
|
||||
DENSITY_REG_Write(flags->high_density); /* density bit */
|
||||
DENSITY_REG_Write(!flags->high_density); /* double density bit */
|
||||
INDEX_REG_Write(flags->index_mode);
|
||||
}
|
||||
|
||||
@@ -142,6 +182,12 @@ static void wait_until_writeable(int ep)
|
||||
;
|
||||
}
|
||||
|
||||
static void wait_until_readable(int ep)
|
||||
{
|
||||
while (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_FULL)
|
||||
;
|
||||
}
|
||||
|
||||
static void send_reply(struct any_frame* f)
|
||||
{
|
||||
print("reply 0x%02x", f->f.type);
|
||||
@@ -159,9 +205,15 @@ static void send_error(int code)
|
||||
/* buffer must be big enough for a frame */
|
||||
static int usb_read(int ep, uint8_t buffer[FRAME_SIZE])
|
||||
{
|
||||
if (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_FULL)
|
||||
{
|
||||
USBFS_EnableOutEP(ep);
|
||||
wait_until_readable(ep);
|
||||
}
|
||||
|
||||
int length = USBFS_GetEPCount(ep);
|
||||
USBFS_ReadOutEP(ep, buffer, length);
|
||||
while (USBFS_GetEPState(ep) == USBFS_OUT_BUFFER_FULL)
|
||||
while (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_EMPTY)
|
||||
;
|
||||
return length;
|
||||
}
|
||||
@@ -169,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);
|
||||
}
|
||||
|
||||
@@ -183,19 +235,19 @@ static void step(int dir)
|
||||
CyDelay(STEP_INTERVAL_TIME);
|
||||
}
|
||||
|
||||
static void home(void)
|
||||
/* returns true if it looks like a drive is attached */
|
||||
static bool home(void)
|
||||
{
|
||||
for (int i=0; i<100; i++)
|
||||
{
|
||||
/* Don't keep stepping forever, because if a drive's
|
||||
* not connected bad things happen. */
|
||||
if (TRACK0_REG_Read())
|
||||
break;
|
||||
return true;
|
||||
step(STEP_TOWARDS0);
|
||||
}
|
||||
|
||||
/* Step to -1, which should be a nop, to reset the disk on disk change. */
|
||||
step(STEP_TOWARDS0);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void seek_to(int track)
|
||||
@@ -230,6 +282,7 @@ static void seek_to(int track)
|
||||
CyWdtClear();
|
||||
}
|
||||
CyDelay(STEP_SETTLING_TIME);
|
||||
TK43_REG_Write(track < 43); /* high if 0..42, low if 43 or up */
|
||||
print("finished seek");
|
||||
}
|
||||
|
||||
@@ -248,7 +301,7 @@ static void cmd_recalibrate(void)
|
||||
send_reply(&r);
|
||||
}
|
||||
|
||||
static void cmd_measure_speed(struct any_frame* f)
|
||||
static void cmd_measure_speed(struct measurespeed_frame* f)
|
||||
{
|
||||
start_motor();
|
||||
|
||||
@@ -258,7 +311,7 @@ static void cmd_measure_speed(struct any_frame* f)
|
||||
while (!index_irq)
|
||||
{
|
||||
elapsed = clock - start_clock;
|
||||
if (elapsed > 1000)
|
||||
if (elapsed > 1500)
|
||||
{
|
||||
elapsed = 0;
|
||||
break;
|
||||
@@ -267,10 +320,14 @@ static void cmd_measure_speed(struct any_frame* f)
|
||||
|
||||
if (elapsed != 0)
|
||||
{
|
||||
index_irq = false;
|
||||
int target_pulse_count = f->hard_sector_count + 1;
|
||||
start_clock = clock;
|
||||
while (!index_irq)
|
||||
elapsed = clock - start_clock;
|
||||
for (int x=0; x<target_pulse_count; x++)
|
||||
{
|
||||
index_irq = false;
|
||||
while (!index_irq)
|
||||
elapsed = clock - start_clock;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_REPLY_FRAME(struct speed_frame, F_FRAME_MEASURE_SPEED_REPLY);
|
||||
@@ -278,12 +335,14 @@ static void cmd_measure_speed(struct any_frame* f)
|
||||
send_reply((struct any_frame*) &r);
|
||||
}
|
||||
|
||||
static void cmd_bulk_test(struct any_frame* f)
|
||||
static void cmd_bulk_write_test(struct any_frame* f)
|
||||
{
|
||||
uint8_t buffer[64];
|
||||
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
for (int x=0; x<64; x++)
|
||||
{
|
||||
CyWdtClear();
|
||||
for (int y=0; y<256; y++)
|
||||
{
|
||||
for (unsigned z=0; z<sizeof(buffer); z++)
|
||||
@@ -292,11 +351,44 @@ static void cmd_bulk_test(struct any_frame* f)
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, buffer, sizeof(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_TEST_REPLY);
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_WRITE_TEST_REPLY);
|
||||
send_reply(&r);
|
||||
}
|
||||
|
||||
static void cmd_bulk_read_test(struct any_frame* f)
|
||||
{
|
||||
uint8_t buffer[64];
|
||||
|
||||
bool passed = true;
|
||||
for (int x=0; x<64; x++)
|
||||
{
|
||||
CyWdtClear();
|
||||
for (int y=0; y<256; y++)
|
||||
{
|
||||
usb_read(FLUXENGINE_DATA_OUT_EP_NUM, buffer);
|
||||
for (unsigned z=0; z<sizeof(buffer); z++)
|
||||
{
|
||||
if (buffer[z] != (uint8)(x+y+z))
|
||||
{
|
||||
print("fail %d+%d+%d == %d, not %d", x, y, z, buffer[z], (uint8)(x+y+z));
|
||||
passed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("passed=%d", passed);
|
||||
if (passed)
|
||||
{
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_READ_TEST_REPLY);
|
||||
send_reply(&r);
|
||||
}
|
||||
else
|
||||
send_error(F_ERROR_INVALID_VALUE);
|
||||
}
|
||||
|
||||
static void deinit_dma(void)
|
||||
{
|
||||
for (int i=0; i<BUFFER_COUNT; i++)
|
||||
@@ -306,7 +398,7 @@ static void deinit_dma(void)
|
||||
static void init_capture_dma(void)
|
||||
{
|
||||
dma_channel = SAMPLER_DMA_DmaInitialize(
|
||||
2 /* bytes */,
|
||||
1 /* bytes */,
|
||||
true /* request per burst */,
|
||||
HI16(CYDEV_PERIPH_BASE),
|
||||
HI16(CYDEV_SRAM_BASE));
|
||||
@@ -327,14 +419,14 @@ static void init_capture_dma(void)
|
||||
|
||||
static void cmd_read(struct read_frame* f)
|
||||
{
|
||||
SIDE_REG_Write(f->side);
|
||||
seek_to(current_track);
|
||||
|
||||
SIDE_REG_Write(f->side);
|
||||
STEP_REG_Write(f->side); /* for drives which multiplex SIDE and DIR */
|
||||
/* Do slow setup *before* we go into the real-time bit. */
|
||||
|
||||
{
|
||||
uint8_t i = CyEnterCriticalSection();
|
||||
SAMPLER_FIFO_SET_LEVEL_MID;
|
||||
SAMPLER_FIFO_SET_LEVEL_NORMAL;
|
||||
SAMPLER_FIFO_CLEAR;
|
||||
SAMPLER_FIFO_SINGLE_BUFFER_UNSET;
|
||||
CyExitCriticalSection(i);
|
||||
@@ -347,16 +439,14 @@ static void cmd_read(struct read_frame* f)
|
||||
|
||||
if (f->synced)
|
||||
{
|
||||
hardsec_index_threshold = f->hardsec_threshold_ms;
|
||||
index_irq = false;
|
||||
while (!index_irq)
|
||||
;
|
||||
index_irq = false;
|
||||
hardsec_index_threshold = 0;
|
||||
}
|
||||
|
||||
crunch_state_t cs = {};
|
||||
cs.outputptr = usb_buffer;
|
||||
cs.outputlen = BUFFER_SIZE;
|
||||
|
||||
dma_writing_to_td = 0;
|
||||
dma_reading_from_td = -1;
|
||||
dma_underrun = false;
|
||||
@@ -371,75 +461,64 @@ static void cmd_read(struct read_frame* f)
|
||||
while (dma_writing_to_td == 0)
|
||||
;
|
||||
dma_reading_from_td = 0;
|
||||
bool dma_running = true;
|
||||
|
||||
/* Start transferring. */
|
||||
|
||||
uint32_t start_time = clock;
|
||||
while (!dma_underrun)
|
||||
for (;;)
|
||||
{
|
||||
CyWdtClear();
|
||||
|
||||
/* Wait for the next block to be read. */
|
||||
while (dma_reading_from_td == dma_writing_to_td)
|
||||
/* If the sample session is over, stop reading but continue processing until
|
||||
* the DMA chain is empty. */
|
||||
|
||||
if ((clock - start_time) >= f->milliseconds)
|
||||
{
|
||||
/* On an underrun, give up immediately. */
|
||||
if (dma_underrun)
|
||||
goto abort;
|
||||
|
||||
/* Also finish if the sample session is over. */
|
||||
if ((clock - start_time) >= f->milliseconds)
|
||||
goto abort;
|
||||
}
|
||||
|
||||
uint8_t dma_buffer_usage = 0;
|
||||
while (dma_buffer_usage < BUFFER_SIZE)
|
||||
{
|
||||
cs.inputptr = dma_buffer[dma_reading_from_td] + dma_buffer_usage;
|
||||
cs.inputlen = BUFFER_SIZE - dma_buffer_usage;
|
||||
crunch(&cs);
|
||||
dma_buffer_usage += BUFFER_SIZE - cs.inputlen;
|
||||
count++;
|
||||
|
||||
/* If there is no available space in the output buffer, flush the buffer via
|
||||
* USB and go again. */
|
||||
if (cs.outputlen == 0)
|
||||
if (dma_running)
|
||||
{
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE);
|
||||
|
||||
cs.outputptr = usb_buffer;
|
||||
cs.outputlen = BUFFER_SIZE;
|
||||
CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN);
|
||||
while (CyDmaChGetRequest(dma_channel))
|
||||
;
|
||||
dma_running = false;
|
||||
dma_underrun = false;
|
||||
}
|
||||
}
|
||||
dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td);
|
||||
|
||||
/* If there's an underrun event, stop immediately. */
|
||||
|
||||
if (dma_underrun)
|
||||
goto abort;
|
||||
|
||||
/* If there are no more blocks to be read, check to see if we've finished. */
|
||||
|
||||
if (dma_reading_from_td == dma_writing_to_td)
|
||||
{
|
||||
/* Also if we've run out of blocks to send. */
|
||||
|
||||
if (!dma_running)
|
||||
goto abort;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, there's a block waiting, so attempt to send it. */
|
||||
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, dma_buffer[dma_reading_from_td], BUFFER_SIZE);
|
||||
count++;
|
||||
dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td);
|
||||
}
|
||||
}
|
||||
abort:;
|
||||
bool saved_dma_underrun = dma_underrun;
|
||||
CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN);
|
||||
while (CyDmaChGetRequest(dma_channel))
|
||||
;
|
||||
|
||||
donecrunch(&cs);
|
||||
/* Terminate the transfer (all transfers are an exact number of fragments). */
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
/* If there's a complete packet waiting, send it. */
|
||||
if (cs.outputlen != BUFFER_SIZE)
|
||||
{
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE);
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
}
|
||||
if ((cs.outputlen != 0) && (cs.outputlen != BUFFER_SIZE))
|
||||
{
|
||||
/* If there's a partial packet waiting, send it; this will also terminate the transfer. */
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE-cs.outputlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise just terminate the transfer. */
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, NULL, 0);
|
||||
}
|
||||
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, NULL, 0);
|
||||
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
|
||||
deinit_dma();
|
||||
|
||||
STEP_REG_Write(0);
|
||||
if (saved_dma_underrun)
|
||||
{
|
||||
print("underrun after %d packets");
|
||||
@@ -485,36 +564,32 @@ static void cmd_write(struct write_frame* f)
|
||||
return;
|
||||
}
|
||||
|
||||
SEQUENCER_CONTROL_Write(1); /* put the sequencer into reset */
|
||||
|
||||
seek_to(current_track);
|
||||
SIDE_REG_Write(f->side);
|
||||
STEP_REG_Write(f->side); /* for drives which multiplex SIDE and DIR */
|
||||
SEQUENCER_CONTROL_Write(1); /* put the sequencer into reset */
|
||||
{
|
||||
uint8_t i = CyEnterCriticalSection();
|
||||
REPLAY_FIFO_SET_LEVEL_NORMAL;
|
||||
REPLAY_FIFO_SET_LEVEL_MID;
|
||||
REPLAY_FIFO_CLEAR;
|
||||
REPLAY_FIFO_SINGLE_BUFFER_UNSET;
|
||||
CyExitCriticalSection(i);
|
||||
}
|
||||
seek_to(current_track);
|
||||
|
||||
init_replay_dma();
|
||||
bool writing = false; /* to the disk */
|
||||
bool finished = false;
|
||||
int packets = f->bytes_to_write / FRAME_SIZE;
|
||||
bool finished = (packets == 0);
|
||||
int count_written = 0;
|
||||
int count_read = 0;
|
||||
dma_writing_to_td = 0;
|
||||
dma_reading_from_td = -1;
|
||||
dma_underrun = false;
|
||||
|
||||
crunch_state_t cs = {};
|
||||
cs.outputlen = BUFFER_SIZE;
|
||||
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
|
||||
int old_reading_from_td = -1;
|
||||
for (;;)
|
||||
{
|
||||
CyWdtClear();
|
||||
//CyWdtClear();
|
||||
|
||||
/* Read data from USB into the buffers. */
|
||||
|
||||
@@ -523,54 +598,25 @@ static void cmd_write(struct write_frame* f)
|
||||
if (writing && (dma_underrun || index_irq))
|
||||
goto abort;
|
||||
|
||||
/* Read crunched data, if necessary. */
|
||||
|
||||
if (cs.inputlen == 0)
|
||||
uint8_t* buffer = dma_buffer[dma_writing_to_td];
|
||||
if (finished)
|
||||
{
|
||||
if (finished)
|
||||
{
|
||||
/* There's no more data to read, so fake some. */
|
||||
|
||||
for (int i=0; i<BUFFER_SIZE; i++)
|
||||
usb_buffer[i+0] = 0x7f;
|
||||
cs.inputptr = usb_buffer;
|
||||
cs.inputlen = BUFFER_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) != USBFS_OUT_BUFFER_FULL)
|
||||
{
|
||||
if (writing && (dma_underrun || index_irq))
|
||||
goto abort;
|
||||
}
|
||||
|
||||
int length = usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer);
|
||||
cs.inputptr = usb_buffer;
|
||||
cs.inputlen = length;
|
||||
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
|
||||
count_read++;
|
||||
if ((length < FRAME_SIZE) || (count_read == packets))
|
||||
finished = true;
|
||||
}
|
||||
/* There's no more data to read, so fake some. */
|
||||
|
||||
memset(buffer, 0x3f, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/* If there *is* data waiting in the buffer, uncrunch it. */
|
||||
|
||||
if (cs.inputlen != 0)
|
||||
else
|
||||
{
|
||||
cs.outputptr = dma_buffer[dma_writing_to_td] + BUFFER_SIZE - cs.outputlen;
|
||||
uncrunch(&cs);
|
||||
if (cs.outputlen == 0)
|
||||
{
|
||||
/* Completed a DMA buffer; queue it for writing. */
|
||||
|
||||
dma_writing_to_td = NEXT_BUFFER(dma_writing_to_td);
|
||||
cs.outputlen = BUFFER_SIZE;
|
||||
}
|
||||
(void) usb_read(FLUXENGINE_DATA_OUT_EP_NUM, buffer);
|
||||
count_read++;
|
||||
|
||||
if (count_read == packets)
|
||||
finished = true;
|
||||
}
|
||||
dma_writing_to_td = NEXT_BUFFER(dma_writing_to_td);
|
||||
|
||||
/* Once all the buffers are full, start writing. */
|
||||
|
||||
/* If we have a full buffer, start writing. */
|
||||
if ((dma_reading_from_td == -1) && (dma_writing_to_td == BUFFER_COUNT-1))
|
||||
{
|
||||
dma_reading_from_td = old_reading_from_td = 0;
|
||||
@@ -585,7 +631,7 @@ static void cmd_write(struct write_frame* f)
|
||||
|
||||
/* Wait for the index marker. While this happens, the DMA engine
|
||||
* will prime the FIFO. */
|
||||
|
||||
hardsec_index_threshold = f->hardsec_threshold_ms;
|
||||
index_irq = false;
|
||||
while (!index_irq)
|
||||
;
|
||||
@@ -596,7 +642,7 @@ static void cmd_write(struct write_frame* f)
|
||||
SEQUENCER_CONTROL_Write(0); /* start writing! */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (writing && (dma_underrun || index_irq))
|
||||
goto abort;
|
||||
|
||||
@@ -620,31 +666,29 @@ abort:
|
||||
}
|
||||
|
||||
print("p=%d cr=%d cw=%d f=%d w=%d index=%d underrun=%d", packets, count_read, count_written, finished, writing, index_irq, dma_underrun);
|
||||
hardsec_index_threshold = 0;
|
||||
if (!finished)
|
||||
{
|
||||
while (count_read < packets)
|
||||
/* There's still some data to read, so just read and blackhole it ---
|
||||
* easier than trying to terminate the connection. */
|
||||
while (count_read != packets)
|
||||
{
|
||||
if (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL)
|
||||
{
|
||||
int length = usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer);
|
||||
if (length < FRAME_SIZE)
|
||||
break;
|
||||
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
count_read++;
|
||||
}
|
||||
(void) usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer);
|
||||
count_read++;
|
||||
}
|
||||
USBFS_DisableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
}
|
||||
|
||||
deinit_dma();
|
||||
print("write finished");
|
||||
|
||||
STEP_REG_Write(0);
|
||||
if (dma_underrun)
|
||||
{
|
||||
print("underrun!");
|
||||
send_error(F_ERROR_UNDERRUN);
|
||||
return;
|
||||
}
|
||||
|
||||
print("success");
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_WRITE_REPLY);
|
||||
send_reply((struct any_frame*) &r);
|
||||
}
|
||||
@@ -652,10 +696,11 @@ abort:
|
||||
static void cmd_erase(struct erase_frame* f)
|
||||
{
|
||||
SIDE_REG_Write(f->side);
|
||||
seek_to(current_track);
|
||||
seek_to(current_track);
|
||||
/* Disk is now spinning. */
|
||||
|
||||
print("start erasing");
|
||||
hardsec_index_threshold = f->hardsec_threshold_ms;
|
||||
index_irq = false;
|
||||
while (!index_irq)
|
||||
;
|
||||
@@ -664,6 +709,7 @@ static void cmd_erase(struct erase_frame* f)
|
||||
while (!index_irq)
|
||||
;
|
||||
ERASE_REG_Write(0);
|
||||
hardsec_index_threshold = 0;
|
||||
print("stop erasing");
|
||||
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_ERASE_REPLY);
|
||||
@@ -672,6 +718,10 @@ static void cmd_erase(struct erase_frame* f)
|
||||
|
||||
static void cmd_set_drive(struct set_drive_frame* f)
|
||||
{
|
||||
if (drive0_present && !drive1_present)
|
||||
f->drive = 0;
|
||||
if (drive1_present && !drive0_present)
|
||||
f->drive = 1;
|
||||
set_drive_flags(f);
|
||||
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_SET_DRIVE_REPLY);
|
||||
@@ -785,11 +835,15 @@ static void handle_command(void)
|
||||
break;
|
||||
|
||||
case F_FRAME_MEASURE_SPEED_CMD:
|
||||
cmd_measure_speed(f);
|
||||
cmd_measure_speed((struct measurespeed_frame*) f);
|
||||
break;
|
||||
|
||||
case F_FRAME_BULK_TEST_CMD:
|
||||
cmd_bulk_test(f);
|
||||
case F_FRAME_BULK_WRITE_TEST_CMD:
|
||||
cmd_bulk_write_test(f);
|
||||
break;
|
||||
|
||||
case F_FRAME_BULK_READ_TEST_CMD:
|
||||
cmd_bulk_read_test(f);
|
||||
break;
|
||||
|
||||
case F_FRAME_READ_CMD:
|
||||
@@ -821,6 +875,21 @@ static void handle_command(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void detect_drives(void)
|
||||
{
|
||||
current_drive_flags.drive = 0;
|
||||
start_motor();
|
||||
drive0_present = home();
|
||||
stop_motor();
|
||||
|
||||
current_drive_flags.drive = 1;
|
||||
start_motor();
|
||||
drive1_present = home();
|
||||
stop_motor();
|
||||
|
||||
print("drive 0: %s drive 1: %s", drive0_present ? "yes" : "no", drive1_present ? "yes" : "no");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
CyGlobalIntEnable;
|
||||
@@ -834,11 +903,10 @@ int main(void)
|
||||
DRIVESELECT_REG_Write(0);
|
||||
UART_Start();
|
||||
USBFS_Start(0, USBFS_DWR_VDDD_OPERATION);
|
||||
USBFS_DisableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
|
||||
CyWdtStart(CYWDT_1024_TICKS, CYWDT_LPMODE_DISABLED);
|
||||
|
||||
/* UART_PutString("GO\r"); */
|
||||
|
||||
for (;;)
|
||||
{
|
||||
CyWdtClear();
|
||||
@@ -854,9 +922,11 @@ int main(void)
|
||||
{
|
||||
print("Waiting for USB...");
|
||||
while (!USBFS_GetConfiguration())
|
||||
;
|
||||
CyWdtClear();
|
||||
print("USB ready");
|
||||
USBFS_EnableOutEP(FLUXENGINE_CMD_OUT_EP_NUM);
|
||||
print("Scanning drives...");
|
||||
detect_drives();
|
||||
}
|
||||
|
||||
if (USBFS_GetEPState(FLUXENGINE_CMD_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL)
|
||||
@@ -868,3 +938,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
|
||||
163
Makefile
163
Makefile
@@ -1,47 +1,140 @@
|
||||
PACKAGES = zlib sqlite3 libusb-1.0
|
||||
ifeq ($(BUILDTYPE),)
|
||||
buildtype_Darwin = osx
|
||||
buildtype_Haiku = haiku
|
||||
BUILDTYPE := $(buildtype_$(shell uname -s ))
|
||||
ifeq ($(BUILDTYPE),)
|
||||
BUILDTYPE := unix
|
||||
endif
|
||||
endif
|
||||
export BUILDTYPE
|
||||
|
||||
export CFLAGS = -Os -g --std=c++14 \
|
||||
-ffunction-sections -fdata-sections
|
||||
export LDFLAGS = -Os
|
||||
OPTFLAGS = -g -O3
|
||||
|
||||
ifeq ($(OS), Windows_NT)
|
||||
export CXX = /mingw32/bin/g++
|
||||
export AR = /mingw32/bin/ar rcs
|
||||
export STRIP = /mingw32/bin/strip
|
||||
export CFLAGS += -I/mingw32/include/libusb-1.0
|
||||
export LDFLAGS +=
|
||||
export LIBS = -static -lz -lsqlite3 -lusb-1.0
|
||||
export EXTENSION = .exe
|
||||
ifeq ($(BUILDTYPE),windows)
|
||||
MINGW = x86_64-w64-mingw32-
|
||||
CC = $(MINGW)gcc
|
||||
CXX = $(MINGW)g++
|
||||
CFLAGS += \
|
||||
$(OPTFLAGS) \
|
||||
-ffunction-sections \
|
||||
-fdata-sections \
|
||||
-Wno-attributes \
|
||||
-Wa,-mbig-obj \
|
||||
-static
|
||||
CXXFLAGS += \
|
||||
$(OPTFLAGS) \
|
||||
-std=c++23 \
|
||||
-Wno-deprecated-enum-float-conversion \
|
||||
-Wno-deprecated-enum-enum-conversion \
|
||||
-Wno-attributes \
|
||||
-Wa,-mbig-obj \
|
||||
-static
|
||||
LDFLAGS += -Wl,--gc-sections -static
|
||||
AR = $(MINGW)gcc-ar
|
||||
PKG_CONFIG = $(MINGW)pkg-config --static
|
||||
WINDRES = $(MINGW)windres
|
||||
WX_CONFIG = /usr/i686-w64-mingw32/sys-root/mingw/bin/wx-config-3.0 --static=yes
|
||||
NINJA = /bin/ninja
|
||||
PROTOC = /mingw64/bin/protoc
|
||||
PROTOC_SEPARATOR = ;
|
||||
EXT = .exe
|
||||
|
||||
AB_SANDBOX = no
|
||||
else
|
||||
|
||||
packages-exist = $(shell pkg-config --exists $(PACKAGES) && echo yes)
|
||||
ifneq ($(packages-exist),yes)
|
||||
$(warning These pkg-config packages are installed: $(shell pkg-config --list-all | sort | awk '{print $$1}'))
|
||||
$(error You must have these pkg-config packages installed: $(PACKAGES))
|
||||
CC = clang
|
||||
CXX = clang++
|
||||
CFLAGS = \
|
||||
$(OPTFLAGS) \
|
||||
-I/opt/homebrew/include -I/usr/local/include \
|
||||
-Wno-unknown-warning-option
|
||||
CXXFLAGS = \
|
||||
$(OPTFLAGS) \
|
||||
-std=c++23 \
|
||||
-fexperimental-library \
|
||||
-I/opt/homebrew/include -I/usr/local/include \
|
||||
-Wformat \
|
||||
-Wformat-security \
|
||||
-Wno-deprecated-enum-float-conversion \
|
||||
-Wno-deprecated-enum-enum-conversion
|
||||
LDFLAGS =
|
||||
AR = ar
|
||||
PKG_CONFIG = pkg-config
|
||||
ifeq ($(BUILDTYPE),osx)
|
||||
else
|
||||
LDFLAGS += -pthread -Wl,--no-as-needed
|
||||
endif
|
||||
endif
|
||||
|
||||
export CXX = g++
|
||||
export AR = ar rcs
|
||||
export STRIP = strip
|
||||
export CFLAGS += $(shell pkg-config --cflags $(PACKAGES))
|
||||
export LDFLAGS +=
|
||||
export LIBS = $(shell pkg-config --libs $(PACKAGES))
|
||||
export EXTENSION =
|
||||
HOSTCC = gcc
|
||||
HOSTCXX = g++ -std=c++20
|
||||
HOSTCFLAGS = -g -O3
|
||||
HOSTLDFLAGS =
|
||||
|
||||
REALOBJ = .obj
|
||||
OBJ = $(REALOBJ)/$(BUILDTYPE)
|
||||
DESTDIR ?=
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
|
||||
# Special Windows settings.
|
||||
|
||||
#ifeq ($(OS), Windows_NT)
|
||||
# EXT ?= .exe
|
||||
# MINGWBIN = /mingw32/bin
|
||||
# CCPREFIX = $(MINGWBIN)/
|
||||
# PKG_CONFIG = $(MINGWBIN)/pkg-config
|
||||
# WX_CONFIG = /usr/bin/sh $(MINGWBIN)/wx-config --static=yes
|
||||
# PROTOC = $(MINGWBIN)/protoc
|
||||
# WINDRES = windres
|
||||
# LDFLAGS += \
|
||||
# -static
|
||||
# CXXFLAGS += \
|
||||
# -fext-numeric-literals \
|
||||
# -Wno-deprecated-enum-float-conversion \
|
||||
# -Wno-deprecated-enum-enum-conversion
|
||||
#
|
||||
# # Required to get the gcc run - time libraries on the path.
|
||||
# export PATH := $(PATH):$(MINGWBIN)
|
||||
#endif
|
||||
|
||||
# Special OSX settings.
|
||||
|
||||
ifeq ($(shell uname),Darwin)
|
||||
LDFLAGS += \
|
||||
-framework IOKit \
|
||||
-framework AppKit \
|
||||
-framework UniformTypeIdentifiers \
|
||||
-framework UserNotifications
|
||||
endif
|
||||
|
||||
CFLAGS += -Ilib -Idep/fmt -Iarch
|
||||
.PHONY: all
|
||||
all: +all README.md
|
||||
|
||||
export OBJDIR = .obj
|
||||
.PHONY: binaries tests
|
||||
binaries: all
|
||||
tests: all
|
||||
|
||||
README.md: $(OBJ)/scripts/+mkdocindex/mkdocindex$(EXT)
|
||||
@echo $(PROGRESSINFO)MKDOC $@
|
||||
@csplit -s -f$(OBJ)/README. README.md '/<!-- FORMATSSTART -->/' '%<!-- FORMATSEND -->%'
|
||||
@(cat $(OBJ)/README.00 && $< && cat $(OBJ)/README.01) > README.md
|
||||
|
||||
all: .obj/build.ninja
|
||||
@ninja -f .obj/build.ninja -v
|
||||
.PHONY: tests
|
||||
|
||||
clean:
|
||||
@echo CLEAN
|
||||
@rm -rf $(OBJDIR)
|
||||
clean::
|
||||
$(hide) rm -rf $(REALOBJ)
|
||||
|
||||
.obj/build.ninja: mkninja.sh Makefile
|
||||
@echo MKNINJA $@
|
||||
@mkdir -p $(OBJDIR)
|
||||
@sh $< > $@
|
||||
include build/ab.mk
|
||||
|
||||
DOCKERFILES = \
|
||||
debian11 \
|
||||
debian12 \
|
||||
fedora40 \
|
||||
fedora41 \
|
||||
manjaro
|
||||
|
||||
docker-%: tests/docker/Dockerfile.%
|
||||
docker build -t $* -f $< .
|
||||
|
||||
.PHONY: dockertests
|
||||
dockertests: $(foreach f,$(DOCKERFILES), docker-$(strip $f) .WAIT)
|
||||
|
||||
200
README.md
200
README.md
@@ -2,7 +2,14 @@ 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 2024-10-01, the FluxEngine client software works
|
||||
(to a point) with [Applesauce](doc/applesauce.md) hardware.
|
||||
|
||||
<div style="text-align: center">
|
||||
<a href="doc/screenshot.jpg"><img src="doc/screenshot.jpg" style="width:60%" alt="screenshot of the GUI in action"></a>
|
||||
</div>
|
||||
|
||||
What?
|
||||
-----
|
||||
@@ -24,17 +31,15 @@ Don't believe me? Watch the demo reel!
|
||||
<iframe width="373" height="210" src="https://www.youtube.com/embed/m_s1iw8eW7o" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div>
|
||||
|
||||
**Important note.** On 2019-02-09 I did a hardware redesign and moved the pins on
|
||||
the board. Sorry for the inconvenience, but it means you don't have to modify
|
||||
the board any more to make it work. If you built the hardware prior to then,
|
||||
you'll need to adjust it.
|
||||
|
||||
**Another important note.** On 2019-07-03 I've revamped the build process and
|
||||
the (command line) user interface. It should be much nicer now, not least in
|
||||
that there's a single client binary with all the functionality in it. The
|
||||
interface is a little different, but not much. The build process is now
|
||||
better (simpler). See [the building](doc/building.md) and
|
||||
[using](doc/using.md) pages for more details.
|
||||
**New!** The FluxEngine client software now works with
|
||||
[Greaseweazle](https://github.com/keirf/Greaseweazle/wiki) and
|
||||
[Applesauce](https://applesaucefdc.com/) hardware. So, if you can't find a PSoC5
|
||||
development kit, or don't want to use the Cypress Windows tools for programming
|
||||
it, you can use one of these instead. Very nearly all FluxEngine features are
|
||||
available with the Greaseweazle and it works out-of-the box; the Applesauce is a
|
||||
bit less supported but still works. See the [dedicated Greaseweazle
|
||||
documentation page](doc/greaseweazle.md) or the [Applesauce
|
||||
page](doc/applesauce.md) for more information.
|
||||
|
||||
Where?
|
||||
------
|
||||
@@ -59,56 +64,87 @@ following friendly articles:
|
||||
- [Using a FluxEngine](doc/using.md) ∾ what to do with your new hardware ∾
|
||||
flux files and image files ∾ knowing what you're doing
|
||||
|
||||
- [Troubleshooting dubious disks](doc/problems.md) ∾ it's not an exact science ∾
|
||||
the sector map ∾ clock detection and the histogram
|
||||
- [Using Greaseweazle hardware with the FluxEngine client
|
||||
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
|
||||
|
||||
- [Direct filesystem access](doc/filesystem.md) ∾ imaging files is a pain
|
||||
∾ accessing files directly ∾ features and limitation ∾ it works on disk
|
||||
images too, you say?
|
||||
|
||||
- [Troubleshooting dubious disks](doc/problems.md) ∾ it's not an exact
|
||||
science ∾ the sector map ∾ clock detection and the histogram
|
||||
|
||||
- [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?
|
||||
------
|
||||
|
||||
The current support state is as follows.
|
||||
|
||||
Dinosaurs (🦖) have yet to be observed in real life --- I've written the
|
||||
decoder based on Kryoflux (or other) dumps I've found. I don't (yet) have
|
||||
real, physical disks in my hand to test the capture process.
|
||||
Dinosaurs (🦖) have yet to be observed in real life --- I've written the encoder
|
||||
and/or decoder based on Kryoflux (or other) dumps I've found. I don't (yet) have
|
||||
real, physical disks in my hand to test the capture process, or hardware to
|
||||
verify that written disks work.
|
||||
|
||||
Unicorns (🦄) are completely real --- this means that I've read actual,
|
||||
physical disks with these formats and so know they work (or had reports from
|
||||
people who've had it work).
|
||||
Unicorns (🦄) are completely real --- this means that I've read actual, physical
|
||||
disks with these formats and/or written real, physical disks and then used them
|
||||
on real hardware, and so know they work (or had reports from people who've had
|
||||
it work).
|
||||
|
||||
### Old disk formats
|
||||
If a filesystem is listed, this means that FluxEngine natively supports that
|
||||
particular filesystem and can read (and sometimes write, support varies) files
|
||||
directly from disks, flux files or disk images. Some formats have multiple
|
||||
choices because they can store multiple types of file system.
|
||||
|
||||
| Format | Read? | Write? | Notes |
|
||||
|:-----------------------------------------|:-----:|:------:|-------|
|
||||
| IBM PC compatible | 🦄 | | and compatibles (like the Atari ST) |
|
||||
| [Acorn ADFS](doc/disk-acornadfs.md) | 🦄 | | single- and double- sided |
|
||||
| [Acorn DFS](doc/disk-acorndfs.md) | 🦄 | | |
|
||||
| [Ampro Little Board](doc/disk-ampro.md) | 🦖 | | |
|
||||
| [Apple II DOS 3.3](doc/disk-apple2.md) | 🦄 | | doesn't do logical sector remapping |
|
||||
| [Amiga](doc/disk-amiga.md) | 🦄 | | |
|
||||
| [Commodore 64 1541](doc/disk-c64.md) | 🦖 | | and probably the other GCR formats |
|
||||
| [Brother 120kB](doc/disk-brother.md) | 🦄 | | |
|
||||
| [Brother 240kB](doc/disk-brother.md) | 🦄 | 🦄 | |
|
||||
| [Brother FB-100](doc/disk-fb100.md) | 🦖 | | Tandy Model 100, Husky Hunter, knitting machines |
|
||||
| [Macintosh 800kB](doc/disk-macintosh.md) | 🦖 | | and probably the 400kB too |
|
||||
| [TRS-80](doc/disk-trs80.md) | 🦖 | | a minor variation of the IBM scheme |
|
||||
<!-- FORMATSSTART -->
|
||||
<!-- This section is automatically generated. Do not edit. -->
|
||||
|
||||
| Profile | Format | Read? | Write? | Filesystem? |
|
||||
|:--------|:-------|:-----:|:------:|:------------|
|
||||
| [`acornadfs`](doc/disk-acornadfs.md) | Acorn ADFS: BBC Micro, Archimedes | 🦖 | | |
|
||||
| [`acorndfs`](doc/disk-acorndfs.md) | Acorn DFS: Acorn Atom, BBC Micro series | 🦄 | | ACORNDFS |
|
||||
| [`aeslanier`](doc/disk-aeslanier.md) | AES Lanier "No Problem": 616kB 5.25" 77-track SSDD hard sectored | 🦖 | | |
|
||||
| [`agat`](doc/disk-agat.md) | Agat: 840kB 5.25" 80-track DS | 🦖 | 🦖 | |
|
||||
| [`amiga`](doc/disk-amiga.md) | Amiga: 880kB 3.5" DSDD | 🦄 | 🦄 | AMIGAFFS |
|
||||
| [`ampro`](doc/disk-ampro.md) | Ampro Little Board: CP/M | 🦖 | | CPMFS |
|
||||
| [`apple2`](doc/disk-apple2.md) | Apple II: Prodos, Appledos, and CP/M | 🦄 | 🦄 | APPLEDOS CPMFS PRODOS |
|
||||
| [`atarist`](doc/disk-atarist.md) | Atari ST: Almost PC compatible | 🦄 | 🦄 | |
|
||||
| [`bk`](doc/disk-bk.md) | BK: 800kB 5.25"/3.5" 80-track 10-sector DSDD | 🦖 | 🦖 | |
|
||||
| [`brother`](doc/disk-brother.md) | Brother word processors: GCR family | 🦄 | 🦄 | BROTHER120 FATFS |
|
||||
| [`commodore`](doc/disk-commodore.md) | Commodore: 1541, 1581, 8050 and variations | 🦄 | 🦄 | CBMFS |
|
||||
| [`eco1`](doc/disk-eco1.md) | VDS Eco1: CP/M; 1210kB 77-track mixed format DSHD | 🦖 | | CPMFS |
|
||||
| [`epsonpf10`](doc/disk-epsonpf10.md) | Epson PF-10: CP/M; 3.5" 40-track DSDD | 🦖 | | CPMFS |
|
||||
| [`f85`](doc/disk-f85.md) | Durango F85: 461kB 5.25" 77-track SS | 🦖 | | |
|
||||
| [`fb100`](doc/disk-fb100.md) | Brother FB-100: 100kB 3.5" 40-track SSSD | 🦖 | | |
|
||||
| [`hplif`](doc/disk-hplif.md) | Hewlett-Packard LIF: a variety of disk formats used by HP | 🦄 | 🦄 | LIF |
|
||||
| [`ibm`](doc/disk-ibm.md) | IBM PC: Generic PC 3.5"/5.25" disks | 🦄 | 🦄 | FATFS |
|
||||
| [`icl30`](doc/disk-icl30.md) | ICL Model 30: CP/M; 263kB 35-track DSSD | 🦖 | | CPMFS |
|
||||
| [`juku`](doc/disk-juku.md) | Juku E5104: CP/M | | | CPMFS |
|
||||
| [`mac`](doc/disk-mac.md) | Macintosh: 400kB/800kB 3.5" GCR | 🦄 | 🦄 | MACHFS |
|
||||
| [`micropolis`](doc/disk-micropolis.md) | Micropolis: 100tpi MetaFloppy disks | 🦄 | 🦄 | |
|
||||
| [`ms2000`](doc/disk-ms2000.md) | : MS2000 Microdisk Development System | | | MICRODOS |
|
||||
| [`mx`](doc/disk-mx.md) | DVK MX: Soviet-era PDP-11 clone | 🦖 | | |
|
||||
| [`n88basic`](doc/disk-n88basic.md) | N88-BASIC: PC8800/PC98 5.25" 77-track 26-sector DSHD | 🦄 | 🦄 | |
|
||||
| [`northstar`](doc/disk-northstar.md) | Northstar: 5.25" hard sectored | 🦄 | 🦄 | |
|
||||
| [`psos`](doc/disk-psos.md) | pSOS: 800kB DSDD with PHILE | 🦄 | 🦄 | PHILE |
|
||||
| [`rolandd20`](doc/disk-rolandd20.md) | Roland D20: 3.5" electronic synthesiser disks | 🦄 | 🦖 | ROLAND |
|
||||
| [`rx50`](doc/disk-rx50.md) | Digital RX50: 400kB 5.25" 80-track 10-sector SSDD | 🦖 | 🦖 | |
|
||||
| [`smaky6`](doc/disk-smaky6.md) | Smaky 6: 308kB 5.25" 77-track 16-sector SSDD, hard sectored | 🦖 | | SMAKY6 |
|
||||
| [`tartu`](doc/disk-tartu.md) | Tartu: The Palivere and variations | 🦄 | 🦖 | CPMFS |
|
||||
| [`ti99`](doc/disk-ti99.md) | TI-99: 90kB 35-track SSSD | 🦖 | | |
|
||||
| [`tids990`](doc/disk-tids990.md) | Texas Instruments DS990: 1126kB 8" DSSD | 🦖 | 🦖 | |
|
||||
| [`tiki`](doc/disk-tiki.md) | Tiki 100: CP/M | | | CPMFS |
|
||||
| [`victor9k`](doc/disk-victor9k.md) | Victor 9000 / Sirius One: 1224kB 5.25" DSDD GCR | 🦖 | 🦖 | |
|
||||
| [`zilogmcz`](doc/disk-zilogmcz.md) | Zilog MCZ: 320kB 8" 77-track SSSD hard-sectored | 🦖 | | ZDOS |
|
||||
{: .datatable }
|
||||
|
||||
### Even older disk formats
|
||||
|
||||
These formats are for particularly old, weird architectures, even by the
|
||||
standards of floppy disks. They've largely been implemented from single flux
|
||||
files with no access to physical hardware. Typically the reads were pretty
|
||||
bad and I've had to make a number of guesses as to how things work. They do,
|
||||
at least, check the CRC so what data's there is probably good.
|
||||
|
||||
| Format | Read? | Write? | Notes |
|
||||
|:-----------------------------------------|:-----:|:------:|-------|
|
||||
| [AES Superplus / No Problem](doc/disk-aeslanier.md) | 🦖 | | hard sectors! |
|
||||
| [Durango F85](doc/disk-durangof85.md) | 🦖 | | 5.25" |
|
||||
| [DVK MX](doc/disk-mx.md) | 🦖 | | Soviet PDP-11 clone |
|
||||
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8-inch |
|
||||
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8-inch _and_ hard sectors |
|
||||
{: .datatable }
|
||||
<!-- FORMATSEND -->
|
||||
|
||||
### Notes
|
||||
|
||||
@@ -125,7 +161,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.
|
||||
|
||||
@@ -179,18 +215,60 @@ There may or may not be anything interesting there.
|
||||
License
|
||||
-------
|
||||
|
||||
Everything here _except the contents of the `dep` directory_ is © 2019 David
|
||||
Given and is licensed under the MIT open source license. Please see
|
||||
Everything here _except the contents of the `dep` directory_ is © 2022 The
|
||||
FluxEngine Authors (mostly me, David Given; see the VCS history for the other
|
||||
people) and is licensed under the MIT open source license. Please see
|
||||
[COPYING](COPYING) for the full text. The tl;dr is: you can do what you like
|
||||
with it provided you don't claim you wrote it.
|
||||
|
||||
As an exception, `dep/fmt` contains a copy of [fmt](http://fmtlib.net),
|
||||
maintained by Victor Zverovich (`vitaut <https://github.com/vitaut>`) and
|
||||
Jonathan Müller (`foonathan <https://github.com/foonathan>`) with
|
||||
contributions from many other people. It is licensed under the terms of the
|
||||
BSD license. Please see the contents of the directory for the full text.
|
||||
|
||||
As an exception, `dep/emu` contains parts of the OpenBSD C library
|
||||
code, Todd Miller and William A. Rowe (and probably others). It is licensed
|
||||
code, maintained by Todd Miller and William A. Rowe (and probably others). It is licensed
|
||||
under the terms of the 3-clause BSD license. Please see the contents of the
|
||||
directory for the full text. It's been lightly modified by me.
|
||||
|
||||
As an exception, `dep/agg` contains parts of the Anti-Grain Antialiasing
|
||||
library, written by Maxim Semanarev (and others). It is licensed under the
|
||||
terms of the 3-clause BSD license. Please see the contents of the directory for
|
||||
the full text. It's been lightly modified by me.
|
||||
|
||||
As an exception, `dep/stb` contains parts of the libstb utility library,
|
||||
written by Sean T Barett (and others). It is public domain/Unlicense/MIT
|
||||
licensed, at your choice. Please see the contents of the directory for the full
|
||||
text.
|
||||
|
||||
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. Note
|
||||
that this is only used during the build and no code ends up in the output
|
||||
binaries.
|
||||
|
||||
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.
|
||||
|
||||
As an exception, `dep/fatfs` contains the fatfs library, taken from
|
||||
http://elm-chan.org/fsw/ff/00index_e.html. It is single-clause BSD licensed.
|
||||
Please see the contents of the directory for the full text.
|
||||
|
||||
As an exception, `dep/adflib` contains the adflib library, written by Laurent
|
||||
Clevy et al, taken from https://github.com/lclevy/ADFlib. It is GPL 2.0
|
||||
licensed. Please see the contents of the directory for the full text.
|
||||
|
||||
As an exception, `dep/hfsutils` contains a partial copy of the hfsutils
|
||||
package, written by Robert Leslie et al, taken from
|
||||
https://www.mars.org/home/rob/proj/hfs. It is GPL 2.0 licensed. Please see the
|
||||
contents of the directory for the full text.
|
||||
|
||||
As an exception, `dep/lexy` contains a partial copy of the lexy package, written
|
||||
by foonathen@github, taken from https://github.com/foonathan/lexy. It is BSL 1.0
|
||||
licensed. Please see the contents of the directory for the full text.
|
||||
|
||||
As an exception, `dep/alphanum` contains a copy of the alphanum package,
|
||||
written by Dave Koelle, taken from
|
||||
https://web.archive.org/web/20210207124255/davekoelle.com/alphanum.html. It is
|
||||
MIT licensed. Please see the source for the full text.
|
||||
|
||||
__Important:__ Because of all these exceptions, if you distribute the
|
||||
FluxEngine package as a whole, you must comply with the terms of _all_ of the
|
||||
licensing terms. This means that __effectively the FluxEngine package is
|
||||
distributable under the terms of the GPL 2.0__.
|
||||
|
||||
@@ -2,19 +2,10 @@
|
||||
#define AESLANIER_H
|
||||
|
||||
#define AESLANIER_RECORD_SEPARATOR 0x55555122
|
||||
#define AESLANIER_SECTOR_LENGTH 256
|
||||
#define AESLANIER_RECORD_SIZE (AESLANIER_SECTOR_LENGTH + 5)
|
||||
#define AESLANIER_SECTOR_LENGTH 256
|
||||
#define AESLANIER_RECORD_SIZE (AESLANIER_SECTOR_LENGTH + 5)
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
|
||||
class AesLanierDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~AesLanierDecoder() {}
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
};
|
||||
extern std::unique_ptr<Decoder> createAesLanierDecoder(
|
||||
const DecoderProto& config);
|
||||
|
||||
#endif
|
||||
|
||||
4
arch/aeslanier/aeslanier.proto
Normal file
4
arch/aeslanier/aeslanier.proto
Normal file
@@ -0,0 +1,4 @@
|
||||
syntax = "proto2";
|
||||
|
||||
message AesLanierDecoderProto {}
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
#include "globals.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "aeslanier.h"
|
||||
#include "crc.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "sector.h"
|
||||
#include "bytes.h"
|
||||
#include "record.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "arch/aeslanier/aeslanier.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include <string.h>
|
||||
|
||||
static const FluxPattern SECTOR_PATTERN(32, AESLANIER_RECORD_SEPARATOR);
|
||||
|
||||
/* This is actually M2FM, rather than MFM, but it our MFM/FM decoder copes fine with it. */
|
||||
/* This is actually M2FM, rather than MFM, but it our MFM/FM decoder copes fine
|
||||
* with it. */
|
||||
|
||||
static Bytes reverse_bits(const Bytes& input)
|
||||
class AesLanierDecoder : public Decoder
|
||||
{
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
|
||||
for (uint8_t b : input)
|
||||
bw.write_8(reverse_bits(b));
|
||||
return output;
|
||||
}
|
||||
|
||||
AbstractDecoder::RecordType AesLanierDecoder::advanceToNextRecord()
|
||||
{
|
||||
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
|
||||
if (_fmr->eof() || !_sector->clock)
|
||||
return UNKNOWN_RECORD;
|
||||
return SECTOR_RECORD;
|
||||
}
|
||||
|
||||
void AesLanierDecoder::decodeSectorRecord()
|
||||
{
|
||||
/* Skip ID mark. */
|
||||
|
||||
readRawBits(16);
|
||||
|
||||
const auto& rawbits = readRawBits(AESLANIER_RECORD_SIZE*16);
|
||||
const auto& bytes = decodeFmMfm(rawbits).slice(0, AESLANIER_RECORD_SIZE);
|
||||
const auto& reversed = reverse_bits(bytes);
|
||||
|
||||
_sector->logicalTrack = reversed[1];
|
||||
_sector->logicalSide = 0;
|
||||
_sector->logicalSector = reversed[2];
|
||||
|
||||
/* Check header 'checksum' (which seems far too simple to mean much). */
|
||||
public:
|
||||
AesLanierDecoder(const DecoderProto& config): Decoder(config) {}
|
||||
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
uint8_t wanted = reversed[3];
|
||||
uint8_t got = reversed[1] + reversed[2];
|
||||
if (wanted != got)
|
||||
return;
|
||||
return seekToPattern(SECTOR_PATTERN);
|
||||
}
|
||||
|
||||
/* Check data checksum, which also includes the header and is
|
||||
* significantly better. */
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
/* Skip ID mark (we know it's a AESLANIER_RECORD_SEPARATOR). */
|
||||
|
||||
_sector->data = reversed.slice(1, AESLANIER_SECTOR_LENGTH);
|
||||
uint16_t wanted = reversed.reader().seek(0x101).read_le16();
|
||||
uint16_t got = crc16ref(MODBUS_POLY_REF, _sector->data);
|
||||
_sector->status = (wanted == got) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
readRawBits(16);
|
||||
|
||||
const auto& rawbits = readRawBits(AESLANIER_RECORD_SIZE * 16);
|
||||
const auto& bytes =
|
||||
decodeFmMfm(rawbits).slice(0, AESLANIER_RECORD_SIZE);
|
||||
const auto& reversed = bytes.reverseBits();
|
||||
|
||||
_sector->logicalCylinder = reversed[1];
|
||||
_sector->logicalHead = 0;
|
||||
_sector->logicalSector = reversed[2];
|
||||
|
||||
/* Check header 'checksum' (which seems far too simple to mean much). */
|
||||
|
||||
{
|
||||
uint8_t wanted = reversed[3];
|
||||
uint8_t got = reversed[1] + reversed[2];
|
||||
if (wanted != got)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check data checksum, which also includes the header and is
|
||||
* significantly better. */
|
||||
|
||||
_sector->data = reversed.slice(1, AESLANIER_SECTOR_LENGTH);
|
||||
uint16_t wanted = reversed.reader().seek(0x101).read_le16();
|
||||
uint16_t got = crc16ref(MODBUS_POLY_REF, _sector->data);
|
||||
_sector->status = (wanted == got) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createAesLanierDecoder(const DecoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Decoder>(new AesLanierDecoder(config));
|
||||
}
|
||||
|
||||
20
arch/agat/agat.cc
Normal file
20
arch/agat/agat.cc
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "arch/agat/agat.h"
|
||||
#include "lib/core/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;
|
||||
}
|
||||
19
arch/agat/agat.h
Normal file
19
arch/agat/agat.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef AGAT_H
|
||||
#define AGAT_H
|
||||
|
||||
#define AGAT_SECTOR_SIZE 256
|
||||
|
||||
static constexpr uint64_t SECTOR_ID = 0x8924555549111444;
|
||||
static constexpr uint64_t DATA_ID = 0x8924555514444911;
|
||||
|
||||
class Encoder;
|
||||
class EncoderProto;
|
||||
class Decoder;
|
||||
class DecoderProto;
|
||||
|
||||
extern std::unique_ptr<Decoder> createAgatDecoder(const DecoderProto& config);
|
||||
extern std::unique_ptr<Encoder> createAgatEncoder(const EncoderProto& config);
|
||||
|
||||
extern uint8_t agatChecksum(const Bytes& bytes);
|
||||
|
||||
#endif
|
||||
19
arch/agat/agat.proto
Normal file
19
arch/agat/agat.proto
Normal file
@@ -0,0 +1,19 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "lib/config/common.proto";
|
||||
|
||||
message AgatDecoderProto {}
|
||||
|
||||
message AgatEncoderProto {
|
||||
optional double target_clock_period_us = 1
|
||||
[default=2.00, (help)="Data clock period of target format."];
|
||||
optional double target_rotational_period_ms = 2
|
||||
[default=200.0, (help)="Rotational period of target format."];
|
||||
optional int32 post_index_gap_bytes = 3
|
||||
[default=40, (help)="Post-index gap before first sector header."];
|
||||
optional int32 pre_sector_gap_bytes = 4
|
||||
[default=11, (help)="Gap before each sector header."];
|
||||
optional int32 pre_data_gap_bytes = 5
|
||||
[default=2, (help)="Gap before each sector data record."];
|
||||
}
|
||||
|
||||
90
arch/agat/decoder.cc
Normal file
90
arch/agat/decoder.cc
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "arch/agat/agat.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include <string.h>
|
||||
|
||||
// clang-format off
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
// clang-format on
|
||||
|
||||
static const FluxPattern SECTOR_PATTERN(64, SECTOR_ID);
|
||||
static const FluxPattern DATA_PATTERN(64, DATA_ID);
|
||||
|
||||
static const FluxMatchers ALL_PATTERNS = {&SECTOR_PATTERN, &DATA_PATTERN};
|
||||
|
||||
class AgatDecoder : public Decoder
|
||||
{
|
||||
public:
|
||||
AgatDecoder(const DecoderProto& config): Decoder(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->logicalCylinder = bytes[1] >> 1;
|
||||
_sector->logicalSector = bytes[2];
|
||||
_sector->logicalHead = 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<Decoder> createAgatDecoder(const DecoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Decoder>(new AgatDecoder(config));
|
||||
}
|
||||
115
arch/agat/encoder.cc
Normal file
115
arch/agat/encoder.cc
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/core/utils.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "arch/agat/agat.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/image.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include "arch/agat/agat.pb.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
|
||||
class AgatEncoder : public Encoder
|
||||
{
|
||||
public:
|
||||
AgatEncoder(const EncoderProto& config):
|
||||
Encoder(config),
|
||||
_config(config.agat())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void writeRawBits(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;
|
||||
}
|
||||
}
|
||||
|
||||
void writeBytes(const Bytes& bytes)
|
||||
{
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
}
|
||||
|
||||
void writeByte(uint8_t byte)
|
||||
{
|
||||
Bytes b;
|
||||
b.writer().write_8(byte);
|
||||
writeBytes(b);
|
||||
}
|
||||
|
||||
void writeFillerRawBytes(int count, uint16_t byte)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
writeRawBits(byte, 16);
|
||||
};
|
||||
|
||||
void writeFillerBytes(int count, uint8_t byte)
|
||||
{
|
||||
Bytes b{byte};
|
||||
for (int i = 0; i < count; i++)
|
||||
writeBytes(b);
|
||||
};
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
double clockRateUs = _config.target_clock_period_us() / 2.0;
|
||||
int bitsPerRevolution =
|
||||
(_config.target_rotational_period_ms() * 1000.0) / clockRateUs;
|
||||
_bits.resize(bitsPerRevolution);
|
||||
_cursor = 0;
|
||||
|
||||
writeFillerRawBytes(_config.post_index_gap_bytes(), 0xaaaa);
|
||||
|
||||
for (const auto& sector : sectors)
|
||||
{
|
||||
/* Header */
|
||||
|
||||
writeFillerRawBytes(_config.pre_sector_gap_bytes(), 0xaaaa);
|
||||
writeRawBits(SECTOR_ID, 64);
|
||||
writeByte(0x5a);
|
||||
writeByte((sector->logicalCylinder << 1) | sector->logicalHead);
|
||||
writeByte(sector->logicalSector);
|
||||
writeByte(0x5a);
|
||||
|
||||
/* Data */
|
||||
|
||||
writeFillerRawBytes(_config.pre_data_gap_bytes(), 0xaaaa);
|
||||
auto data = sector->data.slice(0, AGAT_SECTOR_SIZE);
|
||||
writeRawBits(DATA_ID, 64);
|
||||
writeBytes(data);
|
||||
writeByte(agatChecksum(data));
|
||||
writeByte(0x5a);
|
||||
}
|
||||
|
||||
if (_cursor >= _bits.size())
|
||||
error("track data overrun");
|
||||
fillBitmapTo(_bits, _cursor, _bits.size(), {true, false});
|
||||
|
||||
auto fluxmap = std::make_unique<Fluxmap>();
|
||||
fluxmap->appendBits(_bits,
|
||||
calculatePhysicalClockPeriod(_config.target_clock_period_us() * 1e3,
|
||||
_config.target_rotational_period_ms() * 1e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const AgatEncoderProto& _config;
|
||||
uint32_t _cursor;
|
||||
bool _lastBit;
|
||||
std::vector<bool> _bits;
|
||||
};
|
||||
|
||||
std::unique_ptr<Encoder> createAgatEncoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Encoder>(new AgatEncoder(config));
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "globals.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "amiga.h"
|
||||
#include "bytes.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "arch/amiga/amiga.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
uint32_t amigaChecksum(const Bytes& bytes)
|
||||
@@ -19,61 +18,61 @@ uint32_t amigaChecksum(const Bytes& bytes)
|
||||
|
||||
static uint8_t everyother(uint16_t x)
|
||||
{
|
||||
/* aabb ccdd eeff gghh */
|
||||
x &= 0x6666; /* 0ab0 0cd0 0ef0 0gh0 */
|
||||
x >>= 1; /* 00ab 00cd 00ef 00gh */
|
||||
x |= x << 2; /* abab cdcd efef ghgh */
|
||||
x &= 0x3c3c; /* 00ab cd00 00ef gh00 */
|
||||
x >>= 2; /* 0000 abcd 0000 efgh */
|
||||
x |= x >> 4; /* 0000 abcd abcd efgh */
|
||||
return x;
|
||||
/* aabb ccdd eeff gghh */
|
||||
x &= 0x6666; /* 0ab0 0cd0 0ef0 0gh0 */
|
||||
x >>= 1; /* 00ab 00cd 00ef 00gh */
|
||||
x |= x << 2; /* abab cdcd efef ghgh */
|
||||
x &= 0x3c3c; /* 00ab cd00 00ef gh00 */
|
||||
x >>= 2; /* 0000 abcd 0000 efgh */
|
||||
x |= x >> 4; /* 0000 abcd abcd efgh */
|
||||
return x;
|
||||
}
|
||||
|
||||
Bytes amigaInterleave(const Bytes& input)
|
||||
{
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
|
||||
/* Write all odd bits. (Numbering starts at 0...) */
|
||||
/* Write all odd bits. (Numbering starts at 0...) */
|
||||
|
||||
{
|
||||
ByteReader br(input);
|
||||
while (!br.eof())
|
||||
{
|
||||
uint16_t x = br.read_be16();
|
||||
x &= 0xaaaa; /* a0b0 c0d0 e0f0 g0h0 */
|
||||
x |= x >> 1; /* aabb ccdd eeff gghh */
|
||||
x = everyother(x); /* 0000 0000 abcd efgh */
|
||||
bw.write_8(x);
|
||||
}
|
||||
}
|
||||
{
|
||||
ByteReader br(input);
|
||||
while (!br.eof())
|
||||
{
|
||||
uint16_t x = br.read_be16();
|
||||
x &= 0xaaaa; /* a0b0 c0d0 e0f0 g0h0 */
|
||||
x |= x >> 1; /* aabb ccdd eeff gghh */
|
||||
x = everyother(x); /* 0000 0000 abcd efgh */
|
||||
bw.write_8(x);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write all even bits. */
|
||||
/* Write all even bits. */
|
||||
|
||||
{
|
||||
ByteReader br(input);
|
||||
while (!br.eof())
|
||||
{
|
||||
uint16_t x = br.read_be16();
|
||||
x &= 0x5555; /* 0a0b 0c0d 0e0f 0g0h */
|
||||
x |= x << 1; /* aabb ccdd eeff gghh */
|
||||
x = everyother(x); /* 0000 0000 abcd efgh */
|
||||
bw.write_8(x);
|
||||
}
|
||||
}
|
||||
{
|
||||
ByteReader br(input);
|
||||
while (!br.eof())
|
||||
{
|
||||
uint16_t x = br.read_be16();
|
||||
x &= 0x5555; /* 0a0b 0c0d 0e0f 0g0h */
|
||||
x |= x << 1; /* aabb ccdd eeff gghh */
|
||||
x = everyother(x); /* 0000 0000 abcd efgh */
|
||||
bw.write_8(x);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
return output;
|
||||
}
|
||||
|
||||
Bytes amigaDeinterleave(const uint8_t*& input, size_t len)
|
||||
{
|
||||
assert(!(len & 1));
|
||||
const uint8_t* odds = &input[0];
|
||||
const uint8_t* evens = &input[len/2];
|
||||
const uint8_t* evens = &input[len / 2];
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
|
||||
for (size_t i=0; i<len/2; i++)
|
||||
for (size_t i = 0; i < len / 2; i++)
|
||||
{
|
||||
uint8_t o = *odds++;
|
||||
uint8_t e = *evens++;
|
||||
@@ -82,11 +81,15 @@ Bytes amigaDeinterleave(const uint8_t*& input, size_t len)
|
||||
* http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
|
||||
*/
|
||||
uint16_t result =
|
||||
(((e * 0x0101010101010101ULL & 0x8040201008040201ULL)
|
||||
* 0x0102040810204081ULL >> 49) & 0x5555) |
|
||||
(((o * 0x0101010101010101ULL & 0x8040201008040201ULL)
|
||||
* 0x0102040810204081ULL >> 48) & 0xAAAA);
|
||||
|
||||
(((e * 0x0101010101010101ULL & 0x8040201008040201ULL) *
|
||||
0x0102040810204081ULL >>
|
||||
49) &
|
||||
0x5555) |
|
||||
(((o * 0x0101010101010101ULL & 0x8040201008040201ULL) *
|
||||
0x0102040810204081ULL >>
|
||||
48) &
|
||||
0xAAAA);
|
||||
|
||||
bw.write_be16(result);
|
||||
}
|
||||
|
||||
@@ -96,6 +99,6 @@ Bytes amigaDeinterleave(const uint8_t*& input, size_t len)
|
||||
|
||||
Bytes amigaDeinterleave(const Bytes& input)
|
||||
{
|
||||
const uint8_t* ptr = input.cbegin();
|
||||
return amigaDeinterleave(ptr, input.size());
|
||||
const uint8_t* ptr = input.cbegin();
|
||||
return amigaDeinterleave(ptr, input.size());
|
||||
}
|
||||
|
||||
@@ -1,37 +1,16 @@
|
||||
#ifndef AMIGA_H
|
||||
#define AMIGA_H
|
||||
|
||||
#include "encoders/encoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
|
||||
#define AMIGA_SECTOR_RECORD 0xaaaa44894489LL
|
||||
|
||||
#define AMIGA_TRACKS_PER_DISK 80
|
||||
#define AMIGA_SECTORS_PER_TRACK 11
|
||||
#define AMIGA_RECORD_SIZE 0x21f
|
||||
#define AMIGA_RECORD_SIZE 0x21c
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
class SectorSet;
|
||||
|
||||
class AmigaDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~AmigaDecoder() {}
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
};
|
||||
|
||||
class AmigaEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
virtual ~AmigaEncoder() {}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
|
||||
};
|
||||
|
||||
extern FlagGroup amigaEncoderFlags;
|
||||
extern std::unique_ptr<Decoder> createAmigaDecoder(const DecoderProto& config);
|
||||
extern std::unique_ptr<Encoder> createAmigaEncoder(const EncoderProto& config);
|
||||
|
||||
extern uint32_t amigaChecksum(const Bytes& bytes);
|
||||
extern Bytes amigaInterleave(const Bytes& input);
|
||||
|
||||
13
arch/amiga/amiga.proto
Normal file
13
arch/amiga/amiga.proto
Normal file
@@ -0,0 +1,13 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "lib/config/common.proto";
|
||||
|
||||
message AmigaDecoderProto {}
|
||||
|
||||
message AmigaEncoderProto {
|
||||
optional double clock_rate_us = 1
|
||||
[default=2.00, (help)="Encoded data clock rate."];
|
||||
optional double post_index_gap_ms = 2
|
||||
[default=0.5, (help)="Post-index gap before first sector header."];
|
||||
}
|
||||
|
||||
@@ -1,58 +1,85 @@
|
||||
#include "globals.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "protocol.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "sector.h"
|
||||
#include "amiga.h"
|
||||
#include "bytes.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "arch/amiga/amiga.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include "lib/decoders/decoders.pb.h"
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
/*
|
||||
/*
|
||||
* Amiga disks use MFM but it's not quite the same as IBM MFM. They only use
|
||||
* a single type of record with a different marker byte.
|
||||
*
|
||||
*
|
||||
* See the big comment in the IBM MFM decoder for the gruesome details of how
|
||||
* MFM works.
|
||||
*/
|
||||
|
||||
|
||||
static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD);
|
||||
|
||||
AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord()
|
||||
class AmigaDecoder : public Decoder
|
||||
{
|
||||
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
|
||||
if (_fmr->eof() || !_sector->clock)
|
||||
return UNKNOWN_RECORD;
|
||||
return SECTOR_RECORD;
|
||||
}
|
||||
public:
|
||||
AmigaDecoder(const DecoderProto& config):
|
||||
Decoder(config),
|
||||
_config(config.amiga())
|
||||
{
|
||||
}
|
||||
|
||||
void AmigaDecoder::decodeSectorRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
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();
|
||||
|
||||
Bytes header = amigaDeinterleave(ptr, 4);
|
||||
Bytes recoveryinfo = amigaDeinterleave(ptr, 16);
|
||||
|
||||
_sector->logicalCylinder = header[1] >> 1;
|
||||
_sector->logicalHead = header[1] & 1;
|
||||
_sector->logicalSector = header[2];
|
||||
|
||||
uint32_t wantedheaderchecksum =
|
||||
amigaDeinterleave(ptr, 4).reader().read_be32();
|
||||
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(56, 1024));
|
||||
|
||||
Bytes data;
|
||||
data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo);
|
||||
_sector->data = data;
|
||||
_sector->status = (gotdatachecksum == wanteddatachecksum)
|
||||
? Sector::OK
|
||||
: Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
private:
|
||||
const AmigaDecoderProto& _config;
|
||||
nanoseconds_t _clock;
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createAmigaDecoder(const DecoderProto& config)
|
||||
{
|
||||
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
|
||||
const auto& rawbytes = toBytes(rawbits).slice(0, AMIGA_RECORD_SIZE*2);
|
||||
const auto& bytes = decodeFmMfm(rawbits).slice(0, AMIGA_RECORD_SIZE);
|
||||
|
||||
const uint8_t* ptr = bytes.begin() + 3;
|
||||
|
||||
Bytes header = amigaDeinterleave(ptr, 4);
|
||||
Bytes recoveryinfo = amigaDeinterleave(ptr, 16);
|
||||
|
||||
_sector->logicalTrack = header[1] >> 1;
|
||||
_sector->logicalSide = header[1] & 1;
|
||||
_sector->logicalSector = header[2];
|
||||
|
||||
uint32_t wantedheaderchecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(6, 40));
|
||||
if (gotheaderchecksum != wantedheaderchecksum)
|
||||
return;
|
||||
|
||||
uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(62, 1024));
|
||||
|
||||
_sector->data.clear();
|
||||
_sector->data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo);
|
||||
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
return std::unique_ptr<Decoder>(new AmigaDecoder(config));
|
||||
}
|
||||
|
||||
@@ -1,126 +1,149 @@
|
||||
#include "globals.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "amiga.h"
|
||||
#include "crc.h"
|
||||
#include "sectorset.h"
|
||||
#include "writer.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/core/utils.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "arch/amiga/amiga.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/image.h"
|
||||
#include "arch/amiga/amiga.pb.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
|
||||
FlagGroup amigaEncoderFlags;
|
||||
|
||||
static DoubleFlag clockRateUs(
|
||||
{ "--clock-rate" },
|
||||
"Encoded data clock rate (microseconds).",
|
||||
2.00);
|
||||
|
||||
static DoubleFlag postIndexGapMs(
|
||||
{ "--post-index-gap" },
|
||||
"Post-index gap before first sector header (milliseconds).",
|
||||
0.5);
|
||||
static bool lastBit;
|
||||
|
||||
static int charToInt(char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
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())
|
||||
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;
|
||||
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_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
{
|
||||
assert(!(bytes.size() & 3));
|
||||
Bytes interleaved = amigaInterleave(bytes);
|
||||
encodeMfm(bits, cursor, interleaved);
|
||||
ByteReader br(bytes);
|
||||
BitReader bitr(br);
|
||||
|
||||
while (!bitr.eof())
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bitr.get();
|
||||
}
|
||||
}
|
||||
|
||||
static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, uint32_t data)
|
||||
static void write_sector(std::vector<bool>& bits,
|
||||
unsigned& cursor,
|
||||
const std::shared_ptr<const Sector>& sector)
|
||||
{
|
||||
Bytes b(4);
|
||||
ByteWriter bw(b);
|
||||
bw.write_be32(data);
|
||||
write_interleaved_bytes(bits, cursor, b);
|
||||
if ((sector->data.size() != 512) && (sector->data.size() != 528))
|
||||
error("unsupported sector size --- you must pick 512 or 528");
|
||||
|
||||
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_word = [&](uint32_t word)
|
||||
{
|
||||
Bytes b(4);
|
||||
b.writer().write_be32(word);
|
||||
write_interleaved_bytes(b);
|
||||
};
|
||||
|
||||
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->logicalCylinder << 1) | sector->logicalHead),
|
||||
(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);
|
||||
}
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector)
|
||||
class AmigaEncoder : public Encoder
|
||||
{
|
||||
if ((sector->data.size() != 512) && (sector->data.size() != 528))
|
||||
Error() << "unsupported sector size --- you must pick 512 or 528";
|
||||
|
||||
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6*8);
|
||||
|
||||
std::vector<bool> headerBits(20*16);
|
||||
unsigned headerCursor = 0;
|
||||
|
||||
Bytes header =
|
||||
{
|
||||
0xff, /* Amiga 1.0 format byte */
|
||||
(uint8_t) ((sector->logicalTrack<<1) | sector->logicalSide),
|
||||
(uint8_t) sector->logicalSector,
|
||||
(uint8_t) (AMIGA_SECTORS_PER_TRACK - sector->logicalSector)
|
||||
};
|
||||
write_interleaved_bytes(headerBits, headerCursor, header);
|
||||
Bytes recoveryInfo(16);
|
||||
if (sector->data.size() == 528)
|
||||
recoveryInfo = sector->data.slice(512, 16);
|
||||
write_interleaved_bytes(headerBits, headerCursor, recoveryInfo);
|
||||
|
||||
std::vector<bool> dataBits(512*16);
|
||||
unsigned dataCursor = 0;
|
||||
write_interleaved_bytes(dataBits, dataCursor, sector->data);
|
||||
|
||||
write_bits(bits, cursor, headerBits);
|
||||
uint32_t headerChecksum = amigaChecksum(toBytes(headerBits));
|
||||
write_interleaved_bytes(bits, cursor, headerChecksum);
|
||||
uint32_t dataChecksum = amigaChecksum(toBytes(dataBits));
|
||||
write_interleaved_bytes(bits, cursor, dataChecksum);
|
||||
write_bits(bits, cursor, dataBits);
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> AmigaEncoder::encode(
|
||||
int physicalTrack, int physicalSide, const SectorSet& allSectors)
|
||||
{
|
||||
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
fillBitmapTo(bits, cursor, postIndexGapMs * 1000 / clockRateUs, { true, false });
|
||||
|
||||
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
|
||||
{
|
||||
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
|
||||
write_sector(bits, cursor, sectorData);
|
||||
public:
|
||||
AmigaEncoder(const EncoderProto& config):
|
||||
Encoder(config),
|
||||
_config(config.amiga())
|
||||
{
|
||||
}
|
||||
|
||||
if (cursor >= bits.size())
|
||||
Error() << "track data overrun";
|
||||
fillBitmapTo(bits, cursor, bits.size(), { true, false });
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
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;
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs*1e3);
|
||||
return fluxmap;
|
||||
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);
|
||||
|
||||
if (cursor >= bits.size())
|
||||
error("track data overrun");
|
||||
fillBitmapTo(bits, cursor, bits.size(), {true, false});
|
||||
|
||||
auto fluxmap = std::make_unique<Fluxmap>();
|
||||
fluxmap->appendBits(bits,
|
||||
calculatePhysicalClockPeriod(_config.clock_rate_us() * 1e3, 200e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const AmigaEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<Encoder> createAmigaEncoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Encoder>(new AmigaEncoder(config));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
#ifndef APPLE2_H
|
||||
#define APPLE2_H
|
||||
|
||||
#define APPLE2_SECTOR_RECORD 0xd5aa96
|
||||
#define APPLE2_DATA_RECORD 0xd5aaad
|
||||
#include <memory.h>
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
|
||||
#define APPLE2_SECTOR_LENGTH 256
|
||||
#define APPLE2_SECTOR_RECORD 0xd5aa96
|
||||
#define APPLE2_DATA_RECORD 0xd5aaad
|
||||
|
||||
#define APPLE2_SECTOR_LENGTH 256
|
||||
#define APPLE2_ENCODED_SECTOR_LENGTH 342
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
|
||||
class Apple2Decoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~Apple2Decoder() {}
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
void decodeDataRecord();
|
||||
};
|
||||
#define APPLE2_SECTORS 16
|
||||
|
||||
extern std::unique_ptr<Decoder> createApple2Decoder(const DecoderProto& config);
|
||||
extern std::unique_ptr<Encoder> createApple2Encoder(const EncoderProto& config);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
22
arch/apple2/apple2.proto
Normal file
22
arch/apple2/apple2.proto
Normal file
@@ -0,0 +1,22 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "lib/config/common.proto";
|
||||
|
||||
message Apple2DecoderProto {
|
||||
optional uint32 side_one_track_offset = 1
|
||||
[ default = 0, (help) = "offset to apply to track numbers on side 1" ];
|
||||
}
|
||||
|
||||
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" ];
|
||||
|
||||
optional uint32 side_one_track_offset = 3
|
||||
[ default = 0, (help) = "offset to apply to track numbers on side 1" ];
|
||||
}
|
||||
@@ -1,34 +1,40 @@
|
||||
#include "globals.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "protocol.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "sector.h"
|
||||
#include "apple2.h"
|
||||
#include "bytes.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include "arch/apple2/apple2.h"
|
||||
#include "arch/apple2/apple2.pb.h"
|
||||
#include "lib/decoders/decoders.pb.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
const FluxPattern SECTOR_RECORD_PATTERN(24, APPLE2_SECTOR_RECORD);
|
||||
const FluxPattern DATA_RECORD_PATTERN(24, APPLE2_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;
|
||||
};
|
||||
}
|
||||
|
||||
/* 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
|
||||
/* 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
|
||||
*/
|
||||
static Bytes decode_crazy_data(const uint8_t* inp, Sector::Status& status)
|
||||
{
|
||||
@@ -48,9 +54,11 @@ static Bytes decode_crazy_data(const uint8_t* inp, Sector::Status& status)
|
||||
{
|
||||
/* 3 * 2 bit */
|
||||
output[i + 0] = ((checksum >> 1) & 0x01) | ((checksum << 1) & 0x02);
|
||||
output[i + 86] = ((checksum >> 3) & 0x01) | ((checksum >> 1) & 0x02);
|
||||
output[i + 86] =
|
||||
((checksum >> 3) & 0x01) | ((checksum >> 1) & 0x02);
|
||||
if ((i + 172) < APPLE2_SECTOR_LENGTH)
|
||||
output[i + 172] = ((checksum >> 5) & 0x01) | ((checksum >> 3) & 0x02);
|
||||
output[i + 172] =
|
||||
((checksum >> 5) & 0x01) | ((checksum >> 3) & 0x02);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,53 +68,111 @@ static Bytes decode_crazy_data(const uint8_t* inp, Sector::Status& status)
|
||||
return output;
|
||||
}
|
||||
|
||||
uint8_t combine(uint16_t word)
|
||||
static uint8_t combine(uint16_t word)
|
||||
{
|
||||
return word & (word >> 7);
|
||||
}
|
||||
|
||||
AbstractDecoder::RecordType Apple2Decoder::advanceToNextRecord()
|
||||
class Apple2Decoder : public Decoder
|
||||
{
|
||||
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;
|
||||
}
|
||||
public:
|
||||
Apple2Decoder(const DecoderProto& config): Decoder(config) {}
|
||||
|
||||
void Apple2Decoder::decodeSectorRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
if (readRaw24() != APPLE2_SECTOR_RECORD)
|
||||
return;
|
||||
|
||||
/* Read header. */
|
||||
|
||||
auto header = toBytes(readRawBits(8 * 8)).slice(0, 8);
|
||||
ByteReader br(header);
|
||||
|
||||
uint8_t volume = combine(br.read_be16());
|
||||
_sector->logicalCylinder = combine(br.read_be16());
|
||||
_sector->logicalHead = _ltl->logicalHead;
|
||||
_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->logicalCylinder ^ _sector->logicalSector))
|
||||
_sector->status =
|
||||
Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
|
||||
if (_sector->logicalHead == 1)
|
||||
_sector->logicalCylinder -=
|
||||
_config.apple2().side_one_track_offset();
|
||||
|
||||
/* Sanity check. */
|
||||
|
||||
if (_sector->logicalCylinder > 100)
|
||||
{
|
||||
_sector->status = Sector::MISSING;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
/* Check ID. */
|
||||
|
||||
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. */
|
||||
|
||||
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::unique_ptr<Decoder> createApple2Decoder(const DecoderProto& config)
|
||||
{
|
||||
/* Skip ID (as we know it's a APPLE2_SECTOR_RECORD). */
|
||||
readRawBits(24);
|
||||
|
||||
/* Read header. */
|
||||
|
||||
auto header = toBytes(readRawBits(8*8)).slice(0, 8);
|
||||
ByteReader br(header);
|
||||
|
||||
uint8_t volume = combine(br.read_be16());
|
||||
_sector->logicalTrack = combine(br.read_be16());
|
||||
_sector->logicalSector = combine(br.read_be16());
|
||||
uint8_t checksum = combine(br.read_be16());
|
||||
if (checksum == (volume ^ _sector->logicalTrack ^ _sector->logicalSector))
|
||||
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
void Apple2Decoder::decodeDataRecord()
|
||||
{
|
||||
/* Check ID. */
|
||||
|
||||
Bytes bytes = toBytes(readRawBits(3*8)).slice(0, 3);
|
||||
if (bytes.reader().read_be24() != APPLE2_DATA_RECORD)
|
||||
return;
|
||||
|
||||
/* Read and decode data. */
|
||||
|
||||
unsigned recordLength = APPLE2_ENCODED_SECTOR_LENGTH + 2;
|
||||
bytes = toBytes(readRawBits(recordLength*8)).slice(0, recordLength);
|
||||
|
||||
_sector->status = Sector::BAD_CHECKSUM;
|
||||
_sector->data = decode_crazy_data(&bytes[0], _sector->status);
|
||||
return std::unique_ptr<Decoder>(new Apple2Decoder(config));
|
||||
}
|
||||
|
||||
192
arch/apple2/encoder.cc
Normal file
192
arch/apple2/encoder.cc
Normal file
@@ -0,0 +1,192 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/core/utils.h"
|
||||
#include "arch/apple2/apple2.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/image.h"
|
||||
#include "fmt/format.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
#include <ctype.h>
|
||||
#include "lib/core/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 Encoder
|
||||
{
|
||||
public:
|
||||
Apple2Encoder(const EncoderProto& config):
|
||||
Encoder(config),
|
||||
_config(config.apple2())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const Apple2EncoderProto& _config;
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
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("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,
|
||||
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("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);
|
||||
|
||||
int track = sector.logicalCylinder;
|
||||
if (sector.logicalHead == 1)
|
||||
track += _config.side_one_track_offset();
|
||||
|
||||
// Write address field: APPLE2_SECTOR_RECORD + sector identifier +
|
||||
// DE AA EB
|
||||
write_bits(APPLE2_SECTOR_RECORD, 24);
|
||||
write_gcr44(volume_id);
|
||||
write_gcr44(track);
|
||||
write_gcr44(sector.logicalSector);
|
||||
write_gcr44(volume_id ^ track ^ 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<Encoder> createApple2Encoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Encoder>(new Apple2Encoder(config));
|
||||
}
|
||||
97
arch/arch.cc
Normal file
97
arch/arch.cc
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "arch/agat/agat.h"
|
||||
#include "arch/aeslanier/aeslanier.h"
|
||||
#include "arch/amiga/amiga.h"
|
||||
#include "arch/apple2/apple2.h"
|
||||
#include "arch/brother/brother.h"
|
||||
#include "arch/c64/c64.h"
|
||||
#include "arch/f85/f85.h"
|
||||
#include "arch/fb100/fb100.h"
|
||||
#include "arch/ibm/ibm.h"
|
||||
#include "arch/macintosh/macintosh.h"
|
||||
#include "arch/micropolis/micropolis.h"
|
||||
#include "arch/mx/mx.h"
|
||||
#include "arch/northstar/northstar.h"
|
||||
#include "arch/rolandd20/rolandd20.h"
|
||||
#include "arch/smaky6/smaky6.h"
|
||||
#include "arch/tartu/tartu.h"
|
||||
#include "arch/tids990/tids990.h"
|
||||
#include "arch/victor9k/victor9k.h"
|
||||
#include "arch/zilogmcz/zilogmcz.h"
|
||||
#include "arch/arch.h"
|
||||
|
||||
std::unique_ptr<Encoder> Arch::createEncoder(Config& config)
|
||||
{
|
||||
if (!config.hasEncoder())
|
||||
error("no encoder configured");
|
||||
return createEncoder(config->encoder());
|
||||
}
|
||||
|
||||
std::unique_ptr<Encoder> Arch::createEncoder(const EncoderProto& config)
|
||||
{
|
||||
static const std::map<int,
|
||||
std::function<std::unique_ptr<Encoder>(const EncoderProto&)>>
|
||||
encoders = {
|
||||
{EncoderProto::kAgat, createAgatEncoder },
|
||||
{EncoderProto::kAmiga, createAmigaEncoder },
|
||||
{EncoderProto::kApple2, createApple2Encoder },
|
||||
{EncoderProto::kBrother, createBrotherEncoder },
|
||||
{EncoderProto::kC64, createCommodore64Encoder},
|
||||
{EncoderProto::kIbm, createIbmEncoder },
|
||||
{EncoderProto::kMacintosh, createMacintoshEncoder },
|
||||
{EncoderProto::kMicropolis, createMicropolisEncoder },
|
||||
{EncoderProto::kNorthstar, createNorthstarEncoder },
|
||||
{EncoderProto::kTartu, createTartuEncoder },
|
||||
{EncoderProto::kTids990, createTids990Encoder },
|
||||
{EncoderProto::kVictor9K, createVictor9kEncoder },
|
||||
};
|
||||
|
||||
auto encoder = encoders.find(config.format_case());
|
||||
if (encoder == encoders.end())
|
||||
error("no encoder specified");
|
||||
|
||||
return (encoder->second)(config);
|
||||
}
|
||||
|
||||
std::unique_ptr<Decoder> Arch::createDecoder(Config& config)
|
||||
{
|
||||
if (!config.hasDecoder())
|
||||
error("no decoder configured");
|
||||
return createDecoder(config->decoder());
|
||||
}
|
||||
|
||||
std::unique_ptr<Decoder> Arch::createDecoder(const DecoderProto& config)
|
||||
{
|
||||
static const std::map<int,
|
||||
std::function<std::unique_ptr<Decoder>(const DecoderProto&)>>
|
||||
decoders = {
|
||||
{DecoderProto::kAgat, createAgatDecoder },
|
||||
{DecoderProto::kAeslanier, createAesLanierDecoder },
|
||||
{DecoderProto::kAmiga, createAmigaDecoder },
|
||||
{DecoderProto::kApple2, createApple2Decoder },
|
||||
{DecoderProto::kBrother, createBrotherDecoder },
|
||||
{DecoderProto::kC64, createCommodore64Decoder},
|
||||
{DecoderProto::kF85, createDurangoF85Decoder },
|
||||
{DecoderProto::kFb100, createFb100Decoder },
|
||||
{DecoderProto::kIbm, createIbmDecoder },
|
||||
{DecoderProto::kMacintosh, createMacintoshDecoder },
|
||||
{DecoderProto::kMicropolis, createMicropolisDecoder },
|
||||
{DecoderProto::kMx, createMxDecoder },
|
||||
{DecoderProto::kNorthstar, createNorthstarDecoder },
|
||||
{DecoderProto::kRolandd20, createRolandD20Decoder },
|
||||
{DecoderProto::kSmaky6, createSmaky6Decoder },
|
||||
{DecoderProto::kTartu, createTartuDecoder },
|
||||
{DecoderProto::kTids990, createTids990Decoder },
|
||||
{DecoderProto::kVictor9K, createVictor9kDecoder },
|
||||
{DecoderProto::kZilogmcz, createZilogMczDecoder },
|
||||
};
|
||||
|
||||
auto decoder = decoders.find(config.format_case());
|
||||
if (decoder == decoders.end())
|
||||
error("no decoder specified");
|
||||
|
||||
return (decoder->second)(config);
|
||||
}
|
||||
16
arch/arch.h
Normal file
16
arch/arch.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
class Encoder;
|
||||
class Decoder;
|
||||
class DecoderProto;
|
||||
class EncoderProto;
|
||||
class Config;
|
||||
|
||||
namespace Arch
|
||||
{
|
||||
std::unique_ptr<Decoder> createDecoder(Config& config);
|
||||
std::unique_ptr<Decoder> createDecoder(const DecoderProto& config);
|
||||
|
||||
std::unique_ptr<Encoder> createEncoder(Config& config);
|
||||
std::unique_ptr<Encoder> createEncoder(const EncoderProto& config);
|
||||
}
|
||||
@@ -3,37 +3,19 @@
|
||||
|
||||
/* Brother word processor format (or at least, one of them) */
|
||||
|
||||
#define BROTHER_SECTOR_RECORD 0xFFFFFD57
|
||||
#define BROTHER_DATA_RECORD 0xFFFFFDDB
|
||||
#define BROTHER_DATA_RECORD_PAYLOAD 256
|
||||
#define BROTHER_DATA_RECORD_CHECKSUM 3
|
||||
#define BROTHER_SECTOR_RECORD 0xFFFFFD57
|
||||
#define BROTHER_DATA_RECORD 0xFFFFFDDB
|
||||
#define BROTHER_DATA_RECORD_PAYLOAD 256
|
||||
#define BROTHER_DATA_RECORD_CHECKSUM 3
|
||||
#define BROTHER_DATA_RECORD_ENCODED_SIZE 415
|
||||
|
||||
#define BROTHER_TRACKS_PER_DISK 78
|
||||
#define BROTHER_SECTORS_PER_TRACK 12
|
||||
#define BROTHER_TRACKS_PER_240KB_DISK 78
|
||||
#define BROTHER_TRACKS_PER_120KB_DISK 39
|
||||
#define BROTHER_SECTORS_PER_TRACK 12
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
|
||||
class BrotherDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~BrotherDecoder() {}
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
void decodeDataRecord();
|
||||
};
|
||||
|
||||
class BrotherEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
virtual ~BrotherEncoder() {}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
|
||||
};
|
||||
|
||||
extern FlagGroup brotherEncoderFlags;
|
||||
extern std::unique_ptr<Decoder> createBrotherDecoder(
|
||||
const DecoderProto& config);
|
||||
extern std::unique_ptr<Encoder> createBrotherEncoder(
|
||||
const EncoderProto& config);
|
||||
|
||||
#endif
|
||||
|
||||
18
arch/brother/brother.proto
Normal file
18
arch/brother/brother.proto
Normal file
@@ -0,0 +1,18 @@
|
||||
syntax = "proto2";
|
||||
|
||||
message BrotherDecoderProto {}
|
||||
|
||||
enum BrotherFormat {
|
||||
BROTHER240 = 0;
|
||||
BROTHER120 = 1;
|
||||
};
|
||||
|
||||
message BrotherEncoderProto {
|
||||
optional double clock_rate_us = 1 [default = 3.83];
|
||||
optional double post_index_gap_ms = 2 [default = 1.0];
|
||||
optional double sector_spacing_ms = 3 [default = 16.2];
|
||||
optional double post_header_spacing_ms = 4 [default = 0.69];
|
||||
|
||||
optional BrotherFormat format = 6 [default = BROTHER240];
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#ifndef BROTHER_H
|
||||
#define BROTHER_H
|
||||
|
||||
/* Brother word processor format (or at least, one of them) */
|
||||
|
||||
#define BROTHER_SECTOR_RECORD 0xFFFFFD57
|
||||
#define BROTHER_DATA_RECORD 0xFFFFFDDB
|
||||
#define BROTHER_DATA_RECORD_PAYLOAD 256
|
||||
#define BROTHER_DATA_RECORD_CHECKSUM 3
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
|
||||
class BrotherDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~BrotherDecoder() {}
|
||||
|
||||
SectorVector decodeToSectors(const RawRecordVector& rawRecords);
|
||||
int recordMatcher(uint64_t fifo) const;
|
||||
};
|
||||
|
||||
extern void writeBrotherSectorHeader(std::vector<bool>& bits, unsigned& cursor,
|
||||
int track, int sector);
|
||||
extern void writeBrotherSectorData(std::vector<bool>& bits, unsigned& cursor,
|
||||
const std::vector<uint8_t>& data);
|
||||
|
||||
#endif
|
||||
@@ -1,13 +1,13 @@
|
||||
GCR_ENTRY(0x55, 0) // 00000
|
||||
GCR_ENTRY(0x57, 1) // 00001
|
||||
GCR_ENTRY(0x5b, 2) // 00010
|
||||
GCR_ENTRY(0x5d, 3) // 00011
|
||||
GCR_ENTRY(0x5f, 4) // 00100
|
||||
GCR_ENTRY(0x6b, 5) // 00101
|
||||
GCR_ENTRY(0x6d, 6) // 00110
|
||||
GCR_ENTRY(0x6f, 7) // 00111
|
||||
GCR_ENTRY(0x75, 8) // 01000
|
||||
GCR_ENTRY(0x77, 9) // 01001
|
||||
GCR_ENTRY(0x55, 0) // 00000
|
||||
GCR_ENTRY(0x57, 1) // 00001
|
||||
GCR_ENTRY(0x5b, 2) // 00010
|
||||
GCR_ENTRY(0x5d, 3) // 00011
|
||||
GCR_ENTRY(0x5f, 4) // 00100
|
||||
GCR_ENTRY(0x6b, 5) // 00101
|
||||
GCR_ENTRY(0x6d, 6) // 00110
|
||||
GCR_ENTRY(0x6f, 7) // 00111
|
||||
GCR_ENTRY(0x75, 8) // 01000
|
||||
GCR_ENTRY(0x77, 9) // 01001
|
||||
GCR_ENTRY(0x7b, 10) // 01010
|
||||
GCR_ENTRY(0x7d, 11) // 01011
|
||||
GCR_ENTRY(0x7f, 12) // 01100
|
||||
@@ -30,4 +30,3 @@ GCR_ENTRY(0xef, 28) // 11100
|
||||
GCR_ENTRY(0xf5, 29) // 11101
|
||||
GCR_ENTRY(0xf7, 30) // 11110
|
||||
GCR_ENTRY(0xfb, 31) // 11111
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
#include "globals.h"
|
||||
#include "sql.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "record.h"
|
||||
#include "brother.h"
|
||||
#include "sector.h"
|
||||
#include "bytes.h"
|
||||
#include "crc.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "arch/brother/brother.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include <ctype.h>
|
||||
|
||||
const FluxPattern SECTOR_RECORD_PATTERN(32, BROTHER_SECTOR_RECORD);
|
||||
const FluxPattern DATA_RECORD_PATTERN(32, BROTHER_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 std::vector<uint8_t> outputbuffer;
|
||||
|
||||
@@ -34,76 +34,89 @@ 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;
|
||||
};
|
||||
}
|
||||
|
||||
static int decode_header_gcr(uint16_t word)
|
||||
{
|
||||
switch (word)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case gcr: return data;
|
||||
#include "header_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
switch (word)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case gcr: \
|
||||
return data;
|
||||
#include "header_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
class BrotherDecoder : public Decoder
|
||||
{
|
||||
public:
|
||||
BrotherDecoder(const DecoderProto& config): Decoder(config) {}
|
||||
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
if (readRaw32() != BROTHER_SECTOR_RECORD)
|
||||
return;
|
||||
|
||||
const auto& rawbits = readRawBits(32);
|
||||
const auto& bytes = toBytes(rawbits).slice(0, 4);
|
||||
|
||||
ByteReader br(bytes);
|
||||
_sector->logicalCylinder = decode_header_gcr(br.read_be16());
|
||||
_sector->logicalSector = decode_header_gcr(br.read_be16());
|
||||
|
||||
/* Sanity check the values read; there's no header checksum and
|
||||
* occasionally we get garbage due to bit errors. */
|
||||
if (_sector->logicalSector > 11)
|
||||
return;
|
||||
if (_sector->logicalCylinder > 79)
|
||||
return;
|
||||
|
||||
_sector->status = Sector::DATA_MISSING;
|
||||
}
|
||||
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
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);
|
||||
|
||||
Bytes bytes;
|
||||
ByteWriter bw(bytes);
|
||||
BitWriter bitw(bw);
|
||||
for (uint8_t b : rawbytes)
|
||||
{
|
||||
uint32_t nibble = decode_data_gcr(b);
|
||||
bitw.push(nibble, 5);
|
||||
}
|
||||
bitw.flush();
|
||||
|
||||
_sector->data = bytes.slice(0, BROTHER_DATA_RECORD_PAYLOAD);
|
||||
uint32_t realCrc = crcbrother(_sector->data);
|
||||
uint32_t wantCrc =
|
||||
bytes.reader().seek(BROTHER_DATA_RECORD_PAYLOAD).read_be24();
|
||||
_sector->status =
|
||||
(realCrc == wantCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
};
|
||||
|
||||
AbstractDecoder::RecordType BrotherDecoder::advanceToNextRecord()
|
||||
std::unique_ptr<Decoder> createBrotherDecoder(const DecoderProto& config)
|
||||
{
|
||||
const FluxMatcher* matcher = nullptr;
|
||||
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
|
||||
if (matcher == &SECTOR_RECORD_PATTERN)
|
||||
return RecordType::SECTOR_RECORD;
|
||||
if (matcher == &DATA_RECORD_PATTERN)
|
||||
return RecordType::DATA_RECORD;
|
||||
return RecordType::UNKNOWN_RECORD;
|
||||
}
|
||||
|
||||
void BrotherDecoder::decodeSectorRecord()
|
||||
{
|
||||
readRawBits(32);
|
||||
const auto& rawbits = readRawBits(32);
|
||||
const auto& bytes = toBytes(rawbits).slice(0, 4);
|
||||
|
||||
ByteReader br(bytes);
|
||||
_sector->logicalTrack = decode_header_gcr(br.read_be16());
|
||||
_sector->logicalSector = decode_header_gcr(br.read_be16());
|
||||
|
||||
/* Sanity check the values read; there's no header checksum and
|
||||
* occasionally we get garbage due to bit errors. */
|
||||
if (_sector->logicalSector > 11)
|
||||
return;
|
||||
if (_sector->logicalTrack > 79)
|
||||
return;
|
||||
|
||||
_sector->status = Sector::DATA_MISSING;
|
||||
}
|
||||
|
||||
void BrotherDecoder::decodeDataRecord()
|
||||
{
|
||||
readRawBits(32);
|
||||
|
||||
const auto& rawbits = readRawBits(BROTHER_DATA_RECORD_ENCODED_SIZE*8);
|
||||
const auto& rawbytes = toBytes(rawbits).slice(0, BROTHER_DATA_RECORD_ENCODED_SIZE);
|
||||
|
||||
Bytes bytes;
|
||||
ByteWriter bw(bytes);
|
||||
BitWriter bitw(bw);
|
||||
for (uint8_t b : rawbytes)
|
||||
{
|
||||
uint32_t nibble = decode_data_gcr(b);
|
||||
bitw.push(nibble, 5);
|
||||
}
|
||||
bitw.flush();
|
||||
|
||||
_sector->data = bytes.slice(0, BROTHER_DATA_RECORD_PAYLOAD);
|
||||
uint32_t realCrc = crcbrother(_sector->data);
|
||||
uint32_t wantCrc = bytes.reader().seek(BROTHER_DATA_RECORD_PAYLOAD).read_be24();
|
||||
_sector->status = (realCrc == wantCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
return std::unique_ptr<Decoder>(new BrotherDecoder(config));
|
||||
}
|
||||
|
||||
@@ -1,167 +1,154 @@
|
||||
#include "globals.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "brother.h"
|
||||
#include "crc.h"
|
||||
#include "sectorset.h"
|
||||
#include "writer.h"
|
||||
|
||||
FlagGroup brotherEncoderFlags;
|
||||
|
||||
static DoubleFlag clockRateUs(
|
||||
{ "--clock-rate" },
|
||||
"Encoded data clock rate (microseconds).",
|
||||
3.83);
|
||||
|
||||
static DoubleFlag postIndexGapMs(
|
||||
{ "--post-index-gap" },
|
||||
"Post-index gap before first sector header (milliseconds).",
|
||||
1.0);
|
||||
|
||||
static DoubleFlag sectorSpacingMs(
|
||||
{ "--sector-spacing" },
|
||||
"Time between successive sector headers (milliseconds).",
|
||||
16.2);
|
||||
|
||||
static DoubleFlag postHeaderSpacingMs(
|
||||
{ "--post-header-spacing" },
|
||||
"Time between a sector's header and data records (milliseconds).",
|
||||
0.69);
|
||||
|
||||
static StringFlag sectorSkew(
|
||||
{ "--sector-skew" },
|
||||
"Order in which to write sectors.",
|
||||
"05a3816b4927");
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/core/utils.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "arch/brother/brother.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/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;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
switch (data)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: \
|
||||
return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void write_sector_header(std::vector<bool>& bits, unsigned& cursor,
|
||||
int track, int sector)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width)
|
||||
{
|
||||
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);
|
||||
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_data(std::vector<bool>& bits, unsigned& cursor, const Bytes& data)
|
||||
static void write_sector_header(
|
||||
std::vector<bool>& bits, unsigned& cursor, int track, int sector)
|
||||
{
|
||||
write_bits(bits, cursor, 0xffffffff, 32);
|
||||
write_bits(bits, cursor, BROTHER_DATA_RECORD, 32);
|
||||
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);
|
||||
}
|
||||
|
||||
uint16_t fifo = 0;
|
||||
int width = 0;
|
||||
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);
|
||||
|
||||
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
|
||||
Error() << "unsupported sector size";
|
||||
uint16_t fifo = 0;
|
||||
int width = 0;
|
||||
|
||||
auto write_byte = [&](uint8_t byte)
|
||||
{
|
||||
fifo |= (byte << (8 - width));
|
||||
width += 8;
|
||||
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
|
||||
error("unsupported sector size");
|
||||
|
||||
while (width >= 5)
|
||||
{
|
||||
uint8_t quintet = fifo >> 11;
|
||||
fifo <<= 5;
|
||||
width -= 5;
|
||||
auto write_byte = [&](uint8_t byte)
|
||||
{
|
||||
fifo |= (byte << (8 - width));
|
||||
width += 8;
|
||||
|
||||
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
|
||||
}
|
||||
};
|
||||
while (width >= 5)
|
||||
{
|
||||
uint8_t quintet = fifo >> 11;
|
||||
fifo <<= 5;
|
||||
width -= 5;
|
||||
|
||||
for (uint8_t byte : data)
|
||||
write_byte(byte);
|
||||
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t realCrc = crcbrother(data);
|
||||
write_byte(realCrc>>16);
|
||||
write_byte(realCrc>>8);
|
||||
write_byte(realCrc);
|
||||
write_byte(0x58); /* magic */
|
||||
for (uint8_t byte : data)
|
||||
write_byte(byte);
|
||||
|
||||
uint32_t realCrc = crcbrother(data);
|
||||
write_byte(realCrc >> 16);
|
||||
write_byte(realCrc >> 8);
|
||||
write_byte(realCrc);
|
||||
write_byte(0x58); /* magic */
|
||||
write_byte(0xd4);
|
||||
while (width != 0)
|
||||
write_byte(0);
|
||||
}
|
||||
|
||||
static int charToInt(char c)
|
||||
class BrotherEncoder : public Encoder
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
}
|
||||
public:
|
||||
BrotherEncoder(const EncoderProto& config):
|
||||
Encoder(config),
|
||||
_config(config.brother())
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> BrotherEncoder::encode(
|
||||
int physicalTrack, int physicalSide, const SectorSet& allSectors)
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
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;
|
||||
|
||||
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();
|
||||
|
||||
fillBitmapTo(bits, cursor, headerCursor, {true, false});
|
||||
write_sector_header(bits,
|
||||
cursor,
|
||||
sectorData->logicalCylinder,
|
||||
sectorData->logicalSector);
|
||||
fillBitmapTo(bits, cursor, dataCursor, {true, false});
|
||||
write_sector_data(bits, cursor, sectorData->data);
|
||||
|
||||
sectorCount++;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
const BrotherEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<Encoder> createBrotherEncoder(const EncoderProto& config)
|
||||
{
|
||||
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_DISK)
|
||||
|| (physicalSide != 0))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
const std::string& skew = sectorSkew.get();
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
for (int sectorCount=0; sectorCount<BROTHER_SECTORS_PER_TRACK; sectorCount++)
|
||||
{
|
||||
int sectorId = charToInt(skew.at(sectorCount));
|
||||
double headerMs = postIndexGapMs + sectorCount*sectorSpacingMs;
|
||||
unsigned headerCursor = headerMs*1e3 / clockRateUs;
|
||||
double dataMs = headerMs + postHeaderSpacingMs;
|
||||
unsigned dataCursor = dataMs*1e3 / clockRateUs;
|
||||
|
||||
const auto& sectorData = allSectors.get(physicalTrack, 0, sectorId);
|
||||
|
||||
fillBitmapTo(bits, cursor, headerCursor, { true, false });
|
||||
write_sector_header(bits, cursor, physicalTrack, sectorId);
|
||||
fillBitmapTo(bits, cursor, dataCursor, { true, false });
|
||||
write_sector_data(bits, cursor, sectorData->data);
|
||||
}
|
||||
|
||||
if (cursor >= bits.size())
|
||||
Error() << "track data overrun";
|
||||
fillBitmapTo(bits, cursor, bits.size(), { true, false });
|
||||
|
||||
// The pre-index gap is not normally reported.
|
||||
// std::cerr << "pre-index gap " << 200.0 - (double)cursor*clockRateUs/1e3 << std::endl;
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs*1e3);
|
||||
return fluxmap;
|
||||
return std::unique_ptr<Encoder>(new BrotherEncoder(config));
|
||||
}
|
||||
|
||||
@@ -76,4 +76,3 @@ GCR_ENTRY(0x6BAB, 74)
|
||||
GCR_ENTRY(0xAD5F, 75)
|
||||
GCR_ENTRY(0xDBED, 76)
|
||||
GCR_ENTRY(0x55BB, 77)
|
||||
|
||||
|
||||
61
arch/build.py
Normal file
61
arch/build.py
Normal file
@@ -0,0 +1,61 @@
|
||||
from build.c import cxxlibrary
|
||||
from build.protobuf import proto, protocc, protolib
|
||||
from os.path import *
|
||||
from glob import glob
|
||||
import sys
|
||||
|
||||
archs = [f for f in glob("*", root_dir="arch") if isfile(f"arch/{f}/{f}.proto")]
|
||||
|
||||
ps = []
|
||||
pls = []
|
||||
cls = []
|
||||
for a in archs:
|
||||
ps += [
|
||||
proto(
|
||||
name=f"proto_{a}",
|
||||
srcs=[f"arch/{a}/{a}.proto"],
|
||||
deps=["lib/config+common_proto"],
|
||||
)
|
||||
]
|
||||
|
||||
pls += [
|
||||
protocc(
|
||||
name=f"proto_lib_{a}",
|
||||
srcs=[f".+proto_{a}"],
|
||||
deps=["lib/config+common_proto_lib"],
|
||||
)
|
||||
]
|
||||
|
||||
cls += [
|
||||
cxxlibrary(
|
||||
name=f"arch_{a}",
|
||||
srcs=glob(f"arch/{a}/*.cc") + glob(f"arch/{a}/*.h"),
|
||||
hdrs={f"arch/{a}/{a}.h": f"arch/{a}/{a}.h"},
|
||||
deps=[
|
||||
"lib/core",
|
||||
"lib/data",
|
||||
"lib/config",
|
||||
"lib/encoders",
|
||||
"lib/decoders",
|
||||
],
|
||||
)
|
||||
]
|
||||
|
||||
protolib(
|
||||
name="proto",
|
||||
srcs=ps + ["lib/config+common_proto"],
|
||||
)
|
||||
|
||||
cxxlibrary(name="proto_lib", deps=pls)
|
||||
|
||||
cxxlibrary(
|
||||
name="arch",
|
||||
srcs=[
|
||||
"./arch.cc",
|
||||
],
|
||||
hdrs={
|
||||
"arch/arch.h": "./arch.h",
|
||||
},
|
||||
deps=cls
|
||||
+ ["lib/core", "lib/data", "lib/config", "lib/encoders", "lib/decoders"],
|
||||
)
|
||||
28
arch/c64/c64.cc
Normal file
28
arch/c64/c64.cc
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "arch/c64/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.
|
||||
*/
|
||||
|
||||
nanoseconds_t clockPeriodForC64Track(unsigned track)
|
||||
{
|
||||
constexpr double b = 8.0;
|
||||
|
||||
if (track < 17)
|
||||
return 26.0 / b;
|
||||
if (track < 24)
|
||||
return 28.0 / b;
|
||||
if (track < 30)
|
||||
return 30.0 / b;
|
||||
return 32.0 / b;
|
||||
}
|
||||
@@ -1,21 +1,37 @@
|
||||
#ifndef C64_H
|
||||
#define C64_H
|
||||
|
||||
#define C64_SECTOR_RECORD 0xffd49
|
||||
#define C64_DATA_RECORD 0xffd57
|
||||
#define C64_SECTOR_LENGTH 256
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
#define C64_SECTOR_RECORD 0xffd49
|
||||
#define C64_DATA_RECORD 0xffd57
|
||||
#define C64_SECTOR_LENGTH 256
|
||||
|
||||
class Commodore64Decoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~Commodore64Decoder() {}
|
||||
/* Source: http://www.unusedino.de/ec64/technical/formats/g64.html
|
||||
1. Header sync FF FF FF FF FF (40 'on' bits, not GCR)
|
||||
2. Header info 52 54 B5 29 4B 7A 5E 95 55 55 (10 GCR bytes)
|
||||
3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read)
|
||||
4. Data sync FF FF FF FF FF (40 'on' bits, not GCR)
|
||||
5. Data block 55...4A (325 GCR bytes)
|
||||
6. Inter-sector gap 55 55 55 55...55 55 (4 to 12 bytes, never read)
|
||||
1. Header sync (SYNC for the next sector)
|
||||
*/
|
||||
#define C64_HEADER_DATA_SYNC 0xFF
|
||||
#define C64_HEADER_BLOCK_ID 0x08
|
||||
#define C64_DATA_BLOCK_ID 0x07
|
||||
#define C64_HEADER_GAP 0x55
|
||||
#define C64_INTER_SECTOR_GAP 0x55
|
||||
#define C64_PADDING 0x0F
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
void decodeDataRecord();
|
||||
};
|
||||
#define C64_TRACKS_PER_DISK 40
|
||||
#define C64_BAM_TRACK 17
|
||||
|
||||
extern std::unique_ptr<Decoder> createCommodore64Decoder(
|
||||
const DecoderProto& config);
|
||||
extern std::unique_ptr<Encoder> createCommodore64Encoder(
|
||||
const EncoderProto& config);
|
||||
|
||||
extern nanoseconds_t clockPeriodForC64Track(unsigned track);
|
||||
|
||||
#endif
|
||||
|
||||
11
arch/c64/c64.proto
Normal file
11
arch/c64/c64.proto
Normal file
@@ -0,0 +1,11 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "lib/config/common.proto";
|
||||
|
||||
message Commodore64DecoderProto {}
|
||||
|
||||
message Commodore64EncoderProto {
|
||||
optional double post_index_gap_us = 1 [default=0.0,
|
||||
(help) = "post-index gap before first sector header."];
|
||||
}
|
||||
|
||||
@@ -1,32 +1,34 @@
|
||||
#include "globals.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "protocol.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "sector.h"
|
||||
#include "c64.h"
|
||||
#include "crc.h"
|
||||
#include "bytes.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "arch/c64/c64.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
static Bytes decode(const std::vector<bool>& bits)
|
||||
{
|
||||
@@ -38,11 +40,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);
|
||||
@@ -52,41 +54,50 @@ static Bytes decode(const std::vector<bool>& bits)
|
||||
return output;
|
||||
}
|
||||
|
||||
AbstractDecoder::RecordType Commodore64Decoder::advanceToNextRecord()
|
||||
class Commodore64Decoder : public Decoder
|
||||
{
|
||||
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;
|
||||
}
|
||||
public:
|
||||
Commodore64Decoder(const DecoderProto& config): Decoder(config) {}
|
||||
|
||||
void Commodore64Decoder::decodeSectorRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
if (readRaw20() != C64_SECTOR_RECORD)
|
||||
return;
|
||||
|
||||
const auto& bits = readRawBits(5 * 10);
|
||||
const auto& bytes = decode(bits).slice(0, 5);
|
||||
|
||||
uint8_t checksum = bytes[0];
|
||||
_sector->logicalSector = bytes[1];
|
||||
_sector->logicalHead = 0;
|
||||
_sector->logicalCylinder = bytes[2] - 1;
|
||||
if (checksum == xorBytes(bytes.slice(1, 4)))
|
||||
_sector->status =
|
||||
Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
if (readRaw20() != C64_DATA_RECORD)
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createCommodore64Decoder(const DecoderProto& config)
|
||||
{
|
||||
readRawBits(20);
|
||||
|
||||
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 */
|
||||
}
|
||||
|
||||
void Commodore64Decoder::decodeDataRecord()
|
||||
{
|
||||
readRawBits(20);
|
||||
|
||||
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;
|
||||
return std::unique_ptr<Decoder>(new Commodore64Decoder(config));
|
||||
}
|
||||
|
||||
318
arch/c64/encoder.cc
Normal file
318
arch/c64/encoder.cc
Normal file
@@ -0,0 +1,318 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/core/utils.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "arch/c64/c64.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/image.h"
|
||||
#include "fmt/format.h"
|
||||
#include "arch/c64/c64.pb.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include <ctype.h>
|
||||
#include "lib/core/bytes.h"
|
||||
|
||||
static bool lastBit;
|
||||
|
||||
static int encode_data_gcr(uint8_t data)
|
||||
{
|
||||
switch (data)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: \
|
||||
return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||
{
|
||||
for (bool bit : src) // Range-based for loop
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bit;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
|
||||
{
|
||||
cursor += width;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
unsigned pos = cursor - i - 1;
|
||||
if (pos < bits.size())
|
||||
bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<bool> encode_data(uint8_t input)
|
||||
{
|
||||
/*
|
||||
* Four 8-bit data bytes are converted to four 10-bit GCR bytes at a time by
|
||||
* the 1541 DOS. RAM is only an 8-bit storage device though. This hardware
|
||||
* limitation prevents a 10-bit GCR byte from being stored in a single
|
||||
* memory location. Four 10-bit GCR bytes total 40 bits - a number evenly
|
||||
* divisible by our overriding 8-bit constraint. Commodore sub- divides the
|
||||
* 40 GCR bits into five 8-bit bytes to solve this dilemma. This explains
|
||||
* why four 8-bit data bytes are converted to GCR form at a time. The
|
||||
* following step by step example demonstrates how this bit manipulation is
|
||||
* performed by the DOS.
|
||||
*
|
||||
* STEP 1. Four 8-bit Data Bytes
|
||||
* $08 $10 $00 $12
|
||||
*
|
||||
* STEP 2. Hexadecimal to Binary Conversion
|
||||
* 1. Binary Equivalents
|
||||
* $08 $10 $00 $12
|
||||
* 00001000 00010000 00000000 00010010
|
||||
*
|
||||
* STEP 3. Binary to GCR Conversion
|
||||
* 1. Four 8-bit Data Bytes
|
||||
* 00001000 00010000 00000000 00010010
|
||||
* 2. High and Low Nybbles
|
||||
* 0000 1000 0001 0000 0000 0000 0001 0010
|
||||
* 3. High and Low Nybble GCR Equivalents
|
||||
* 01010 01001 01011 01010 01010 01010 01011 10010
|
||||
* 4. Four 10-bit GCR Bytes
|
||||
* 0101001001 0101101010 0101001010 0101110010
|
||||
*
|
||||
* STEP 4. 10-bit GCR to 8-bit GCR Conversion
|
||||
* 1. Concatenate Four 10-bit GCR Bytes
|
||||
* 0101001001010110101001010010100101110010
|
||||
* 2. Five 8-bit Subdivisions
|
||||
* 01010010 01010110 10100101 00101001 01110010
|
||||
*
|
||||
* STEP 5. Binary to Hexadecimal Conversion
|
||||
* 1. Hexadecimal Equivalents
|
||||
* 01010010 01010110 10100101 00101001 01110010
|
||||
* $52 $56 $A5 $29 $72
|
||||
*
|
||||
* STEP 6. Four 8-bit Data Bytes are Recorded as Five 8-bit GCR Bytes
|
||||
* $08 $10 $00 $12
|
||||
*
|
||||
* are recorded as
|
||||
* $52 $56 $A5 $29 $72
|
||||
*/
|
||||
|
||||
std::vector<bool> output(10, false);
|
||||
uint8_t hi = 0;
|
||||
uint8_t lo = 0;
|
||||
uint8_t lo_GCR = 0;
|
||||
uint8_t hi_GCR = 0;
|
||||
|
||||
// Convert the byte in high and low nibble
|
||||
lo = input >> 4; // get the lo nibble shift the bits 4 to the right
|
||||
hi = input & 15; // get the hi nibble bij masking the lo bits (00001111)
|
||||
|
||||
lo_GCR = encode_data_gcr(lo); // example value: 0000 GCR = 01010
|
||||
hi_GCR = encode_data_gcr(hi); // example value: 1000 GCR = 01001
|
||||
// output = [0,1,2,3,4,5,6,7,8,9]
|
||||
// value = [0,1,0,1,0,0,1,0,0,1]
|
||||
// 01010 01001
|
||||
|
||||
int b = 4;
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
if (i < 5) // 01234
|
||||
{ // i = 0 op
|
||||
output[4 - i] = (lo_GCR & 1); // 01010
|
||||
|
||||
// 01010 -> & 00001 -> 00000 output[4] = 0
|
||||
// 00101 -> & 00001 -> 00001 output[3] = 1
|
||||
// 00010 -> & 00001 -> 00000 output[2] = 0
|
||||
// 00001 -> & 00001 -> 00001 output[1] = 1
|
||||
// 00000 -> & 00001 -> 00000 output[0] = 0
|
||||
lo_GCR >>= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
output[i + b] = (hi_GCR & 1); // 01001
|
||||
// 01001 -> & 00001 -> 00001 output[9] = 1
|
||||
// 00100 -> & 00001 -> 00000 output[8] = 0
|
||||
// 00010 -> & 00001 -> 00000 output[7] = 0
|
||||
// 00001 -> & 00001 -> 00001 output[6] = 1
|
||||
// 00000 -> & 00001 -> 00000 output[5] = 0
|
||||
hi_GCR >>= 1;
|
||||
b = b - 2;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
class Commodore64Encoder : public Encoder
|
||||
{
|
||||
public:
|
||||
Commodore64Encoder(const EncoderProto& config):
|
||||
Encoder(config),
|
||||
_config(config.c64())
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
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. it is written in every header of every sector and track.
|
||||
* headers are not stored in a d64 disk image so we have to get it from
|
||||
* track 18 which contains the BAM.
|
||||
*/
|
||||
|
||||
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);
|
||||
br.seek(162); // goto position of the first Disk ID Byte
|
||||
_formatByte1 = br.read_8();
|
||||
_formatByte2 = br.read_8();
|
||||
}
|
||||
else
|
||||
_formatByte1 = _formatByte2 = 0;
|
||||
|
||||
double clockRateUs = clockPeriodForC64Track(ltl.logicalCylinder);
|
||||
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;
|
||||
|
||||
for (const auto& sector : sectors)
|
||||
writeSector(bits, cursor, sector);
|
||||
|
||||
if (cursor >= bits.size())
|
||||
error("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, calculatePhysicalClockPeriod(clockRateUs * 1e3, 200e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
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)
|
||||
* 2. Header info 52 54 B5 29 4B 7A 5E 95 55 55 (10 GCR bytes)
|
||||
* 3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read)
|
||||
* 4. Data sync FF FF FF FF FF (40 'on' bits, not GCR)
|
||||
* 5. Data block 55...4A (325 GCR bytes)
|
||||
* 6. Inter-sector gap 55 55 55 55...55 55 (4 to 12 bytes, never read)
|
||||
* 1. Header sync (SYNC for the next sector)
|
||||
*/
|
||||
if ((sector->status == Sector::OK) or
|
||||
(sector->status == Sector::BAD_CHECKSUM))
|
||||
{
|
||||
// There is data to encode to disk.
|
||||
if ((sector->data.size() != C64_SECTOR_LENGTH))
|
||||
error("unsupported sector size {} --- you must pick 256",
|
||||
sector->data.size());
|
||||
|
||||
// 1. Write header Sync (not GCR)
|
||||
for (int i = 0; i < 6; i++)
|
||||
write_bits(
|
||||
bits, cursor, C64_HEADER_DATA_SYNC, 1 * 8); /* sync */
|
||||
|
||||
// 2. Write Header info 10 GCR bytes
|
||||
/*
|
||||
* The 10 byte header info (#2) is GCR encoded and must be decoded
|
||||
* to it's normal 8 bytes to be understood. Once decoded, its
|
||||
* breakdown is as follows:
|
||||
*
|
||||
* Byte $00 - header block ID ($08)
|
||||
* 01 - header block checksum 16 (EOR of $02-$05)
|
||||
* 02 - Sector
|
||||
* 03 - Track
|
||||
* 04 - Format ID byte #2
|
||||
* 05 - Format ID byte #1
|
||||
* 06-07 - $0F ("off" bytes)
|
||||
*/
|
||||
uint8_t encodedTrack =
|
||||
((sector->logicalCylinder) +
|
||||
1); // C64 track numbering starts with 1. Fluxengine with 0.
|
||||
uint8_t encodedSector = sector->logicalSector;
|
||||
// uint8_t formatByte1 = C64_FORMAT_ID_BYTE1;
|
||||
// uint8_t formatByte2 = C64_FORMAT_ID_BYTE2;
|
||||
uint8_t headerChecksum =
|
||||
(encodedTrack ^ encodedSector ^ _formatByte1 ^ _formatByte2);
|
||||
write_bits(bits, cursor, encode_data(C64_HEADER_BLOCK_ID));
|
||||
write_bits(bits, cursor, encode_data(headerChecksum));
|
||||
write_bits(bits, cursor, encode_data(encodedSector));
|
||||
write_bits(bits, cursor, encode_data(encodedTrack));
|
||||
write_bits(bits, cursor, encode_data(_formatByte2));
|
||||
write_bits(bits, cursor, encode_data(_formatByte1));
|
||||
write_bits(bits, cursor, encode_data(C64_PADDING));
|
||||
write_bits(bits, cursor, encode_data(C64_PADDING));
|
||||
|
||||
// 3. Write header GAP not GCR
|
||||
for (int i = 0; i < 9; i++)
|
||||
write_bits(
|
||||
bits, cursor, C64_HEADER_GAP, 1 * 8); /* header gap */
|
||||
|
||||
// 4. Write Data sync not GCR
|
||||
for (int i = 0; i < 6; i++)
|
||||
write_bits(
|
||||
bits, cursor, C64_HEADER_DATA_SYNC, 1 * 8); /* sync */
|
||||
|
||||
// 5. Write data block 325 GCR bytes
|
||||
/*
|
||||
* The 325 byte data block (#5) is GCR encoded and must be decoded
|
||||
* to its normal 260 bytes to be understood. The data block is made
|
||||
* up of the following:
|
||||
*
|
||||
* Byte $00 - data block ID ($07)
|
||||
* 01-100 - 256 bytes data
|
||||
* 101 - data block checksum (EOR of $01-100)
|
||||
* 102-103 - $00 ("off" bytes, to make the sector size a
|
||||
* multiple of 5)
|
||||
*/
|
||||
|
||||
write_bits(bits, cursor, encode_data(C64_DATA_BLOCK_ID));
|
||||
uint8_t dataChecksum = xorBytes(sector->data);
|
||||
ByteReader br(sector->data);
|
||||
int i = 0;
|
||||
for (i = 0; i < C64_SECTOR_LENGTH; i++)
|
||||
{
|
||||
uint8_t val = br.read_8();
|
||||
write_bits(bits, cursor, encode_data(val));
|
||||
}
|
||||
write_bits(bits, cursor, encode_data(dataChecksum));
|
||||
write_bits(bits, cursor, encode_data(C64_PADDING));
|
||||
write_bits(bits, cursor, encode_data(C64_PADDING));
|
||||
|
||||
// 6. Write inter-sector gap 9 - 12 bytes nor gcr
|
||||
for (int i = 0; i < 9; i++)
|
||||
write_bits(
|
||||
bits, cursor, C64_INTER_SECTOR_GAP, 1 * 8); /* sync */
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const Commodore64EncoderProto& _config;
|
||||
uint8_t _formatByte1;
|
||||
uint8_t _formatByte2;
|
||||
};
|
||||
|
||||
std::unique_ptr<Encoder> createCommodore64Encoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Encoder>(new Commodore64Encoder(config));
|
||||
}
|
||||
|
||||
// vim: sw=4 ts=4 et
|
||||
@@ -1,32 +1,34 @@
|
||||
#include "globals.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "protocol.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "sector.h"
|
||||
#include "f85.h"
|
||||
#include "crc.h"
|
||||
#include "bytes.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "arch/f85/f85.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
const FluxPattern SECTOR_RECORD_PATTERN(24, F85_SECTOR_RECORD);
|
||||
const FluxPattern DATA_RECORD_PATTERN(24, F85_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;
|
||||
};
|
||||
}
|
||||
|
||||
static Bytes decode(const std::vector<bool>& bits)
|
||||
{
|
||||
@@ -38,11 +40,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);
|
||||
@@ -52,49 +54,58 @@ static Bytes decode(const std::vector<bool>& bits)
|
||||
return output;
|
||||
}
|
||||
|
||||
AbstractDecoder::RecordType DurangoF85Decoder::advanceToNextRecord()
|
||||
class DurangoF85Decoder : public Decoder
|
||||
{
|
||||
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;
|
||||
}
|
||||
public:
|
||||
DurangoF85Decoder(const DecoderProto& config): Decoder(config) {}
|
||||
|
||||
void DurangoF85Decoder::decodeSectorRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
/* Skip sync bits and ID byte. */
|
||||
|
||||
if (readRaw24() != F85_SECTOR_RECORD)
|
||||
return;
|
||||
|
||||
/* Read header. */
|
||||
|
||||
const auto& bytes = decode(readRawBits(6 * 10));
|
||||
|
||||
_sector->logicalSector = bytes[2];
|
||||
_sector->logicalHead = 0;
|
||||
_sector->logicalCylinder = bytes[0];
|
||||
|
||||
uint16_t wantChecksum = bytes.reader().seek(4).read_be16();
|
||||
uint16_t gotChecksum = crc16(CCITT_POLY, 0xef21, bytes.slice(0, 4));
|
||||
if (wantChecksum == gotChecksum)
|
||||
_sector->status =
|
||||
Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
/* Skip sync bits ID byte. */
|
||||
|
||||
if (readRaw24() != F85_DATA_RECORD)
|
||||
return;
|
||||
|
||||
const auto& bytes = decode(readRawBits((F85_SECTOR_LENGTH + 3) * 10))
|
||||
.slice(0, F85_SECTOR_LENGTH + 3);
|
||||
ByteReader br(bytes);
|
||||
|
||||
_sector->data = br.read(F85_SECTOR_LENGTH);
|
||||
uint16_t wantChecksum = br.read_be16();
|
||||
uint16_t gotChecksum = crc16(CCITT_POLY, 0xbf84, _sector->data);
|
||||
_sector->status =
|
||||
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createDurangoF85Decoder(const DecoderProto& config)
|
||||
{
|
||||
/* Skip sync bits and ID byte. */
|
||||
|
||||
readRawBits(24);
|
||||
|
||||
/* Read header. */
|
||||
|
||||
const auto& bytes = decode(readRawBits(6*10));
|
||||
|
||||
_sector->logicalSector = bytes[2];
|
||||
_sector->logicalSide = 0;
|
||||
_sector->logicalTrack = bytes[0];
|
||||
|
||||
uint16_t wantChecksum = bytes.reader().seek(4).read_be16();
|
||||
uint16_t gotChecksum = crc16(CCITT_POLY, 0xef21, bytes.slice(0, 4));
|
||||
if (wantChecksum == gotChecksum)
|
||||
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
void DurangoF85Decoder::decodeDataRecord()
|
||||
{
|
||||
/* Skip sync bits ID byte. */
|
||||
|
||||
readRawBits(24);
|
||||
|
||||
const auto& bytes = decode(readRawBits((F85_SECTOR_LENGTH+3)*10))
|
||||
.slice(0, F85_SECTOR_LENGTH+3);
|
||||
ByteReader br(bytes);
|
||||
|
||||
_sector->data = br.read(F85_SECTOR_LENGTH);
|
||||
uint16_t wantChecksum = br.read_be16();
|
||||
uint16_t gotChecksum = crc16(CCITT_POLY, 0xbf84, _sector->data);
|
||||
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
return std::unique_ptr<Decoder>(new DurangoF85Decoder(config));
|
||||
}
|
||||
|
||||
@@ -2,20 +2,10 @@
|
||||
#define F85_H
|
||||
|
||||
#define F85_SECTOR_RECORD 0xffffce /* 1111 1111 1111 1111 1100 1110 */
|
||||
#define F85_DATA_RECORD 0xffffcb /* 1111 1111 1111 1111 1100 1101 */
|
||||
#define F85_SECTOR_LENGTH 512
|
||||
#define F85_DATA_RECORD 0xffffcb /* 1111 1111 1111 1111 1100 1101 */
|
||||
#define F85_SECTOR_LENGTH 512
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
|
||||
class DurangoF85Decoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~DurangoF85Decoder() {}
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
void decodeDataRecord();
|
||||
};
|
||||
extern std::unique_ptr<Decoder> createDurangoF85Decoder(
|
||||
const DecoderProto& config);
|
||||
|
||||
#endif
|
||||
|
||||
4
arch/f85/f85.proto
Normal file
4
arch/f85/f85.proto
Normal file
@@ -0,0 +1,4 @@
|
||||
syntax = "proto2";
|
||||
|
||||
message F85DecoderProto {}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
#include "globals.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "protocol.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "sector.h"
|
||||
#include "fb100.h"
|
||||
#include "crc.h"
|
||||
#include "bytes.h"
|
||||
#include "decoders/rawbits.h"
|
||||
#include "track.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "arch/fb100/fb100.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "lib/decoders/rawbits.h"
|
||||
#include "fmt/format.h"
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
const FluxPattern SECTOR_ID_PATTERN(16, 0xabaa);
|
||||
|
||||
/*
|
||||
/*
|
||||
* Reverse engineered from a dump of the floppy drive's ROM. I have no idea how
|
||||
* it works.
|
||||
*
|
||||
*
|
||||
* LF8BA:
|
||||
* clra
|
||||
* staa X00B0
|
||||
@@ -99,37 +98,46 @@ static uint16_t checksum(const Bytes& bytes)
|
||||
return (crchi << 8) | crclo;
|
||||
}
|
||||
|
||||
AbstractDecoder::RecordType Fb100Decoder::advanceToNextRecord()
|
||||
class Fb100Decoder : public Decoder
|
||||
{
|
||||
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;
|
||||
public:
|
||||
Fb100Decoder(const DecoderProto& config): Decoder(config) {}
|
||||
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
return seekToPattern(SECTOR_ID_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
auto rawbits = readRawBits(FB100_RECORD_SIZE * 16);
|
||||
|
||||
const Bytes bytes = decodeFmMfm(rawbits).slice(0, FB100_RECORD_SIZE);
|
||||
ByteReader br(bytes);
|
||||
br.seek(1);
|
||||
const Bytes id = br.read(FB100_ID_SIZE);
|
||||
uint16_t wantIdCrc = br.read_be16();
|
||||
uint16_t gotIdCrc = checksum(id);
|
||||
const Bytes payload = br.read(FB100_PAYLOAD_SIZE);
|
||||
uint16_t wantPayloadCrc = br.read_be16();
|
||||
uint16_t gotPayloadCrc = checksum(payload);
|
||||
|
||||
if (wantIdCrc != gotIdCrc)
|
||||
return;
|
||||
|
||||
uint8_t abssector = id[2];
|
||||
_sector->logicalCylinder = abssector >> 1;
|
||||
_sector->logicalHead = 0;
|
||||
_sector->logicalSector = abssector & 1;
|
||||
_sector->data.writer().append(id.slice(5, 12)).append(payload);
|
||||
|
||||
_sector->status = (wantPayloadCrc == gotPayloadCrc)
|
||||
? Sector::OK
|
||||
: Sector::BAD_CHECKSUM;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createFb100Decoder(const DecoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Decoder>(new Fb100Decoder(config));
|
||||
}
|
||||
|
||||
void Fb100Decoder::decodeSectorRecord()
|
||||
{
|
||||
auto rawbits = readRawBits(FB100_RECORD_SIZE*16);
|
||||
|
||||
const Bytes bytes = decodeFmMfm(rawbits).slice(0, FB100_RECORD_SIZE);
|
||||
ByteReader br(bytes);
|
||||
br.seek(1);
|
||||
const Bytes id = br.read(FB100_ID_SIZE);
|
||||
uint16_t wantIdCrc = br.read_be16();
|
||||
uint16_t gotIdCrc = checksum(id);
|
||||
const Bytes payload = br.read(FB100_PAYLOAD_SIZE);
|
||||
uint16_t wantPayloadCrc = br.read_be16();
|
||||
uint16_t gotPayloadCrc = checksum(payload);
|
||||
|
||||
if (wantIdCrc != gotIdCrc)
|
||||
return;
|
||||
|
||||
uint8_t abssector = id[2];
|
||||
_sector->logicalTrack = abssector >> 1;
|
||||
_sector->logicalSide = 0;
|
||||
_sector->logicalSector = abssector & 1;
|
||||
_sector->data.writer().append(id.slice(5, 12)).append(payload);
|
||||
|
||||
_sector->status = (wantPayloadCrc == gotPayloadCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
@@ -5,18 +5,6 @@
|
||||
#define FB100_ID_SIZE 17
|
||||
#define FB100_PAYLOAD_SIZE 0x500
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
class Track;
|
||||
|
||||
class Fb100Decoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~Fb100Decoder() {}
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
};
|
||||
extern std::unique_ptr<Decoder> createFb100Decoder(const DecoderProto& config);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
4
arch/fb100/fb100.proto
Normal file
4
arch/fb100/fb100.proto
Normal file
@@ -0,0 +1,4 @@
|
||||
syntax = "proto2";
|
||||
|
||||
message Fb100DecoderProto {}
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
#include "globals.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "ibm.h"
|
||||
#include "crc.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "sector.h"
|
||||
#include "record.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "arch/ibm/ibm.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "arch/ibm/ibm.pb.h"
|
||||
#include "lib/config/proto.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include <string.h>
|
||||
|
||||
static_assert(std::is_trivially_copyable<IbmIdam>::value,
|
||||
"IbmIdam is not trivially copyable");
|
||||
"IbmIdam is not trivially copyable");
|
||||
|
||||
/*
|
||||
* The markers at the beginning of records are special, and have
|
||||
* missing clock pulses, allowing them to be found by the logic.
|
||||
*
|
||||
*
|
||||
* IAM record:
|
||||
* flux: XXXX-XXX-XXXX-X- = 0xf77a
|
||||
* clock: X X - X - X X X = 0xd7
|
||||
* data: X X X X X X - - = 0xfc
|
||||
*
|
||||
*
|
||||
* (We just ignore this one --- it's useless and optional.)
|
||||
*/
|
||||
|
||||
/*
|
||||
/*
|
||||
* IDAM record:
|
||||
* flux: XXXX-X-X-XXXXXX- = 0xf57e
|
||||
* clock: X X - - - X X X = 0xc7
|
||||
@@ -31,7 +34,7 @@ static_assert(std::is_trivially_copyable<IbmIdam>::value,
|
||||
*/
|
||||
const FluxPattern FM_IDAM_PATTERN(16, 0xf57e);
|
||||
|
||||
/*
|
||||
/*
|
||||
* DAM1 record:
|
||||
* flux: XXXX-X-X-XX-X-X- = 0xf56a
|
||||
* clock: X X - - - X X X = 0xc7
|
||||
@@ -39,7 +42,7 @@ const FluxPattern FM_IDAM_PATTERN(16, 0xf57e);
|
||||
*/
|
||||
const FluxPattern FM_DAM1_PATTERN(16, 0xf56a);
|
||||
|
||||
/*
|
||||
/*
|
||||
* DAM2 record:
|
||||
* flux: XXXX-X-X-XX-XXXX = 0xf56f
|
||||
* clock: X X - - - X X X = 0xc7
|
||||
@@ -47,7 +50,7 @@ const FluxPattern FM_DAM1_PATTERN(16, 0xf56a);
|
||||
*/
|
||||
const FluxPattern FM_DAM2_PATTERN(16, 0xf56f);
|
||||
|
||||
/*
|
||||
/*
|
||||
* TRS80DAM1 record:
|
||||
* flux: XXXX-X-X-XX-X-XX = 0xf56b
|
||||
* clock: X X - - - X X X = 0xc7
|
||||
@@ -55,13 +58,13 @@ const FluxPattern FM_DAM2_PATTERN(16, 0xf56f);
|
||||
*/
|
||||
const FluxPattern FM_TRS80DAM1_PATTERN(16, 0xf56b);
|
||||
|
||||
/*
|
||||
/*
|
||||
* TRS80DAM2 record:
|
||||
* flux: XXXX-X-X-XX-XXX- = 0xf56c
|
||||
* flux: XXXX-X-X-XX-XXX- = 0xf56e
|
||||
* clock: X X - - - X X X = 0xc7
|
||||
* data: X X X X X - X - = 0xfa
|
||||
*/
|
||||
const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56c);
|
||||
const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56e);
|
||||
|
||||
/* MFM record separator:
|
||||
* 0xA1 is:
|
||||
@@ -71,89 +74,174 @@ const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56c);
|
||||
* ^^^^^
|
||||
* 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.
|
||||
*/
|
||||
const FluxPattern MFM_PATTERN(48, 0x448944894489LL);
|
||||
|
||||
const FluxMatchers ANY_RECORD_PATTERN(
|
||||
const FluxMatchers ANY_RECORD_PATTERN({
|
||||
&MFM_PATTERN,
|
||||
&FM_IDAM_PATTERN,
|
||||
&FM_DAM1_PATTERN,
|
||||
&FM_DAM2_PATTERN,
|
||||
&FM_TRS80DAM1_PATTERN,
|
||||
&FM_TRS80DAM2_PATTERN,
|
||||
});
|
||||
|
||||
class IbmDecoder : public Decoder
|
||||
{
|
||||
public:
|
||||
IbmDecoder(const DecoderProto& config):
|
||||
Decoder(config),
|
||||
_config(config.ibm())
|
||||
{
|
||||
&MFM_PATTERN,
|
||||
&FM_IDAM_PATTERN,
|
||||
&FM_DAM1_PATTERN,
|
||||
&FM_DAM2_PATTERN,
|
||||
&FM_TRS80DAM1_PATTERN,
|
||||
&FM_TRS80DAM2_PATTERN,
|
||||
}
|
||||
);
|
||||
|
||||
AbstractDecoder::RecordType IbmDecoder::advanceToNextRecord()
|
||||
{
|
||||
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();
|
||||
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)
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
case IBM_IDAM:
|
||||
return RecordType::SECTOR_RECORD;
|
||||
|
||||
case IBM_DAM1:
|
||||
case IBM_DAM2:
|
||||
case IBM_TRS80DAM1:
|
||||
case IBM_TRS80DAM2:
|
||||
return RecordType::DATA_RECORD;
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
return RecordType::UNKNOWN_RECORD;
|
||||
}
|
||||
|
||||
void IbmDecoder::decodeSectorRecord()
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
/* 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(bw.pos);
|
||||
|
||||
auto bits = readRawBits(IBM_IDAM_LEN * 16);
|
||||
bw += decodeFmMfm(bits).slice(0, IBM_IDAM_LEN);
|
||||
|
||||
IbmDecoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, _ltl->logicalCylinder, _ltl->logicalHead);
|
||||
|
||||
_sector->logicalCylinder = br.read_8();
|
||||
_sector->logicalHead = 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();
|
||||
if (wantCrc == gotCrc)
|
||||
_sector->status =
|
||||
Sector::DATA_MISSING; /* correct but unintuitive */
|
||||
|
||||
if (trackdata.ignore_side_byte())
|
||||
_sector->logicalHead = _ltl->logicalHead;
|
||||
_sector->logicalHead ^= trackdata.invert_side_byte();
|
||||
if (trackdata.ignore_track_byte())
|
||||
_sector->logicalCylinder = _ltl->logicalCylinder;
|
||||
|
||||
for (int sector : trackdata.ignore_sector())
|
||||
if (_sector->logicalSector == sector)
|
||||
{
|
||||
_sector->status = Sector::MISSING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
/* 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(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();
|
||||
_sector->status =
|
||||
(wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
|
||||
if (_currentSectorSize != _ltl->sectorSize)
|
||||
std::cerr << fmt::format(
|
||||
"Warning: configured sector size for t{}.h{}.s{} is {} bytes "
|
||||
"but that seen on disk is {} bytes\n",
|
||||
_sector->logicalCylinder,
|
||||
_sector->logicalHead,
|
||||
_sector->logicalSector,
|
||||
_ltl->sectorSize,
|
||||
_currentSectorSize);
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned _currentHeaderLength;
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createIbmDecoder(const DecoderProto& config)
|
||||
{
|
||||
unsigned recordSize = _currentHeaderLength + IBM_IDAM_LEN;
|
||||
auto bits = readRawBits(recordSize*16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, recordSize);
|
||||
|
||||
ByteReader br(bytes);
|
||||
br.seek(_currentHeaderLength);
|
||||
br.read_8(); /* skip ID byte */
|
||||
_sector->logicalTrack = br.read_8();
|
||||
_sector->logicalSide = br.read_8();
|
||||
_sector->logicalSector = br.read_8() - _sectorBase;
|
||||
_currentSectorSize = 1 << (br.read_8() + 7);
|
||||
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 (_ignoreSideByte)
|
||||
_sector->logicalSide = _sector->physicalSide;
|
||||
}
|
||||
|
||||
void IbmDecoder::decodeDataRecord()
|
||||
{
|
||||
unsigned recordLength = _currentHeaderLength + _currentSectorSize + 3;
|
||||
auto bits = readRawBits(recordLength*16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, recordLength);
|
||||
|
||||
ByteReader br(bytes);
|
||||
br.seek(_currentHeaderLength);
|
||||
br.read_8(); /* skip ID byte */
|
||||
|
||||
_sector->data = br.read(_currentSectorSize);
|
||||
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;
|
||||
return std::unique_ptr<Decoder>(new IbmDecoder(config));
|
||||
}
|
||||
|
||||
277
arch/ibm/encoder.cc
Normal file
277
arch/ibm/encoder.cc
Normal file
@@ -0,0 +1,277 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "arch/ibm/ibm.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/image.h"
|
||||
#include "arch/ibm/ibm.pb.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
#include "fmt/format.h"
|
||||
#include "lib/config/proto.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/* IAM record separator:
|
||||
* 0xC2 is:
|
||||
* data: 1 1 0 0 0 0 1 0 = 0xc2
|
||||
* mfm: 01 01 00 10 10 10 01 00 = 0x5254
|
||||
* special: 01 01 00 10 00 10 01 00 = 0x5224
|
||||
*/
|
||||
#define MFM_IAM_SEPARATOR 0x5224
|
||||
|
||||
/* FM IAM record:
|
||||
* flux: XXXX-XXX-XXXX-X- = 0xf77a
|
||||
* clock: X X - X - X X X = 0xd7
|
||||
* data: X X X X X X - - = 0xfc
|
||||
*/
|
||||
#define FM_IAM_RECORD 0xf77a
|
||||
|
||||
/* MFM IAM record:
|
||||
* data: 1 1 1 1 1 1 0 0 = 0xfc
|
||||
* flux: 01 01 01 01 01 01 00 10 = 0x5552
|
||||
*/
|
||||
#define MFM_IAM_RECORD 0x5552
|
||||
|
||||
/* MFM record separator:
|
||||
* 0xA1 is:
|
||||
* data: 1 0 1 0 0 0 0 1 = 0xa1
|
||||
* mfm: 01 00 01 00 10 10 10 01 = 0x44a9
|
||||
* special: 01 00 01 00 10 00 10 01 = 0x4489
|
||||
* ^^^^^
|
||||
* When shifted out of phase, the special 0xa1 byte becomes an illegal
|
||||
* encoding (you can't do 10 00). So this can't be spoofed by user data.
|
||||
*
|
||||
* shifted: 10 00 10 01 00 01 00 1
|
||||
*
|
||||
* It's repeated three times.
|
||||
*/
|
||||
#define MFM_RECORD_SEPARATOR 0x4489
|
||||
#define MFM_RECORD_SEPARATOR_BYTE 0xa1
|
||||
|
||||
/* MFM IDAM byte:
|
||||
* data: 1 1 1 1 1 1 1 0 = 0xfe
|
||||
* mfm: 01 01 01 01 01 01 01 00 = 0x5554
|
||||
*/
|
||||
|
||||
/* MFM DAM byte:
|
||||
* data: 1 1 1 1 1 0 1 1 = 0xfb
|
||||
* mfm: 01 01 01 01 01 00 01 01 = 0x5545
|
||||
*/
|
||||
|
||||
static uint8_t decodeUint16(uint16_t raw)
|
||||
{
|
||||
Bytes b;
|
||||
ByteWriter bw(b);
|
||||
bw.write_be16(raw);
|
||||
return decodeFmMfm(b.toBits())[0];
|
||||
}
|
||||
|
||||
class IbmEncoder : public Encoder
|
||||
{
|
||||
public:
|
||||
IbmEncoder(const EncoderProto& config):
|
||||
Encoder(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 getEncoderTrackData(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);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
IbmEncoderProto::TrackdataProto trackdata;
|
||||
getEncoderTrackData(trackdata, ltl.logicalCylinder, ltl.logicalHead);
|
||||
|
||||
auto writeBytes = [&](const Bytes& bytes)
|
||||
{
|
||||
if (trackdata.use_fm())
|
||||
encodeFm(_bits, _cursor, bytes);
|
||||
else
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
};
|
||||
|
||||
auto writeFillerRawBytes = [&](int count, uint16_t byte)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
writeRawBits(byte, 16);
|
||||
};
|
||||
|
||||
auto writeFillerBytes = [&](int count, uint8_t byte)
|
||||
{
|
||||
Bytes b{byte};
|
||||
for (int i = 0; i < count; i++)
|
||||
writeBytes(b);
|
||||
};
|
||||
|
||||
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 idamUnencoded = decodeUint16(trackdata.idam_byte());
|
||||
uint8_t damUnencoded = decodeUint16(trackdata.dam_byte());
|
||||
|
||||
uint8_t sectorSize = 0;
|
||||
{
|
||||
int s = ltl.sectorSize >> 7;
|
||||
while (s > 1)
|
||||
{
|
||||
s >>= 1;
|
||||
sectorSize += 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t gapFill = trackdata.gap_fill_byte();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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. */
|
||||
|
||||
{
|
||||
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->logicalCylinder);
|
||||
bw.write_8(
|
||||
sectorData->logicalHead ^ trackdata.invert_side_byte());
|
||||
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;
|
||||
}
|
||||
writeRawBits(trackdata.idam_byte(), 16);
|
||||
conventionalHeaderStart += 1;
|
||||
|
||||
writeBytes(header.slice(conventionalHeaderStart));
|
||||
}
|
||||
|
||||
writeFillerRawBytes(trackdata.gap2(), gapFill);
|
||||
|
||||
{
|
||||
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);
|
||||
|
||||
Bytes truncatedData = sectorData->data.slice(0, ltl.sectorSize);
|
||||
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;
|
||||
|
||||
writeBytes(data.slice(conventionalHeaderStart));
|
||||
}
|
||||
}
|
||||
|
||||
if (_cursor >= _bits.size())
|
||||
error("track data overrun");
|
||||
while (_cursor < _bits.size())
|
||||
writeFillerRawBytes(1, gapFill);
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(_bits,
|
||||
calculatePhysicalClockPeriod(clockRateUs * 1e3,
|
||||
trackdata.target_rotational_period_ms() * 1e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const IbmEncoderProto& _config;
|
||||
std::vector<bool> _bits;
|
||||
unsigned _cursor;
|
||||
bool _lastBit;
|
||||
};
|
||||
|
||||
std::unique_ptr<Encoder> createIbmEncoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Encoder>(new IbmEncoder(config));
|
||||
}
|
||||
@@ -1,98 +1,37 @@
|
||||
#ifndef IBM_H
|
||||
#define IBM_H
|
||||
|
||||
#include "decoders/decoders.h"
|
||||
|
||||
/* IBM format (i.e. ordinary PC floppies). */
|
||||
|
||||
#define IBM_MFM_SYNC 0xA1 /* sync byte for MFM */
|
||||
#define IBM_IAM 0xFC /* start-of-track record */
|
||||
#define IBM_IAM_LEN 1 /* plus prologue */
|
||||
#define IBM_IDAM 0xFE /* sector header */
|
||||
#define IBM_IDAM_LEN 7 /* plus prologue */
|
||||
#define IBM_DAM1 0xF8 /* sector data (type 1) */
|
||||
#define IBM_DAM2 0xFB /* sector data (type 2) */
|
||||
#define IBM_TRS80DAM1 0xF9 /* sector data (TRS-80 directory) */
|
||||
#define IBM_TRS80DAM2 0xFA /* sector data (TRS-80 directory) */
|
||||
#define IBM_DAM_LEN 1 /* plus prologue and user data */
|
||||
#define IBM_MFM_SYNC 0xA1 /* sync byte for MFM */
|
||||
#define IBM_IAM 0xFC /* start-of-track record */
|
||||
#define IBM_IAM_LEN 1 /* plus prologue */
|
||||
#define IBM_IDAM 0xFE /* sector header */
|
||||
#define IBM_IDAM_LEN 7 /* plus prologue */
|
||||
#define IBM_DAM1 0xF8 /* sector data (type 1) */
|
||||
#define IBM_DAM2 0xFB /* sector data (type 2) */
|
||||
#define IBM_TRS80DAM1 0xF9 /* sector data (TRS-80 directory) */
|
||||
#define IBM_TRS80DAM2 0xFA /* sector data (TRS-80 directory) */
|
||||
#define IBM_DAM_LEN 1 /* plus prologue and user data */
|
||||
|
||||
/* Length of a DAM record is determined by the previous sector header. */
|
||||
|
||||
struct IbmIdam
|
||||
{
|
||||
uint8_t id;
|
||||
uint8_t cylinder;
|
||||
uint8_t track;
|
||||
uint8_t side;
|
||||
uint8_t sector;
|
||||
uint8_t sectorSize;
|
||||
uint8_t crc[2];
|
||||
};
|
||||
|
||||
class IbmDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
IbmDecoder(unsigned sectorBase, bool ignoreSideByte=false):
|
||||
_sectorBase(sectorBase),
|
||||
_ignoreSideByte(ignoreSideByte)
|
||||
{}
|
||||
class Encoder;
|
||||
class Decoder;
|
||||
class DecoderProto;
|
||||
class EncoderProto;
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
void decodeDataRecord();
|
||||
|
||||
private:
|
||||
unsigned _sectorBase;
|
||||
bool _ignoreSideByte;
|
||||
unsigned _currentSectorSize;
|
||||
unsigned _currentHeaderLength;
|
||||
};
|
||||
|
||||
#if 0
|
||||
class AbstractIbmDecoder : public AbstractSoftSectorDecoder
|
||||
{
|
||||
public:
|
||||
AbstractIbmDecoder(unsigned sectorIdBase):
|
||||
_sectorIdBase(sectorIdBase)
|
||||
{}
|
||||
virtual ~AbstractIbmDecoder() {}
|
||||
|
||||
SectorVector decodeToSectors(const RawRecordVector& rawRecords, unsigned physicalTrack, unsigned physicalSide);
|
||||
|
||||
protected:
|
||||
virtual int skipHeaderBytes() const = 0;
|
||||
|
||||
private:
|
||||
unsigned _sectorIdBase;
|
||||
};
|
||||
|
||||
class IbmFmDecoder : public AbstractIbmDecoder
|
||||
{
|
||||
public:
|
||||
IbmFmDecoder(unsigned sectorIdBase):
|
||||
AbstractIbmDecoder(sectorIdBase)
|
||||
{}
|
||||
|
||||
int recordMatcher(uint64_t fifo) const;
|
||||
|
||||
protected:
|
||||
int skipHeaderBytes() const
|
||||
{ return 0; }
|
||||
};
|
||||
|
||||
class IbmMfmDecoder : public AbstractIbmDecoder
|
||||
{
|
||||
public:
|
||||
IbmMfmDecoder(unsigned sectorIdBase):
|
||||
AbstractIbmDecoder(sectorIdBase)
|
||||
{}
|
||||
|
||||
nanoseconds_t guessClock(Fluxmap& fluxmap) const;
|
||||
int recordMatcher(uint64_t fifo) const;
|
||||
|
||||
protected:
|
||||
int skipHeaderBytes() const
|
||||
{ return 3; }
|
||||
};
|
||||
#endif
|
||||
extern std::unique_ptr<Decoder> createIbmDecoder(const DecoderProto& config);
|
||||
extern std::unique_ptr<Encoder> createIbmEncoder(const EncoderProto& config);
|
||||
|
||||
#endif
|
||||
|
||||
43
arch/ibm/ibm.proto
Normal file
43
arch/ibm/ibm.proto
Normal file
@@ -0,0 +1,43 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "lib/config/common.proto";
|
||||
|
||||
message IbmDecoderProto {
|
||||
// Next: 11
|
||||
message TrackdataProto {
|
||||
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 invert_side_byte = 4 [default = false, (help) = "invert the side byte in the sector header"];
|
||||
|
||||
repeated int32 ignore_sector = 10 [(help) = "sectors with these IDs will not be read"];
|
||||
}
|
||||
|
||||
repeated TrackdataProto trackdata = 1;
|
||||
}
|
||||
|
||||
message IbmEncoderProto {
|
||||
// Next: 20
|
||||
message TrackdataProto {
|
||||
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 bool emit_iam = 3 [default=true, (help) = "whether to emit an IAM record"];
|
||||
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"];
|
||||
optional int32 gap0 = 9 [default=80, (help) = "size of gap 1 (the post-index gap)"];
|
||||
optional int32 gap1 = 10 [default=50, (help) = "size of gap 2 (the post-ID gap)"];
|
||||
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 invert_side_byte = 19 [default=false, (help) = "invert the side byte before writing"];
|
||||
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"];
|
||||
}
|
||||
|
||||
repeated TrackdataProto trackdata = 1;
|
||||
}
|
||||
|
||||
@@ -1,35 +1,38 @@
|
||||
#include "globals.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "protocol.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "sector.h"
|
||||
#include "track.h"
|
||||
#include "macintosh.h"
|
||||
#include "bytes.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include "arch/macintosh/macintosh.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
const FluxPattern SECTOR_RECORD_PATTERN(24, MAC_SECTOR_RECORD);
|
||||
const FluxPattern DATA_RECORD_PATTERN(24, MAC_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;
|
||||
};
|
||||
}
|
||||
|
||||
/* 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 decode_crazy_data(const Bytes& input, Sector::Status& status)
|
||||
{
|
||||
@@ -43,7 +46,7 @@ static Bytes decode_crazy_data(const Bytes& input, Sector::Status& status)
|
||||
uint8_t b2[LOOKUP_LEN + 1];
|
||||
uint8_t b3[LOOKUP_LEN + 1];
|
||||
|
||||
for (int i=0; i<=LOOKUP_LEN; i++)
|
||||
for (int i = 0; i <= LOOKUP_LEN; i++)
|
||||
{
|
||||
uint8_t w4 = br.read_8();
|
||||
uint8_t w1 = br.read_8();
|
||||
@@ -118,69 +121,77 @@ static Bytes decode_crazy_data(const Bytes& input, Sector::Status& status)
|
||||
uint8_t decode_side(uint8_t side)
|
||||
{
|
||||
/* Mac disks, being weird, use the side byte to encode both the side (in
|
||||
* bit 5) and also whether we're above track 0x3f (in bit 6).
|
||||
* bit 5) and also whether we're above track 0x3f (in bit 0).
|
||||
*/
|
||||
|
||||
return !!(side & 0x40);
|
||||
return !!(side & 0x20);
|
||||
}
|
||||
|
||||
AbstractDecoder::RecordType MacintoshDecoder::advanceToNextRecord()
|
||||
class MacintoshDecoder : public Decoder
|
||||
{
|
||||
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;
|
||||
}
|
||||
public:
|
||||
MacintoshDecoder(const DecoderProto& config): Decoder(config) {}
|
||||
|
||||
void MacintoshDecoder::decodeSectorRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
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 != (_ltl->logicalCylinder & 0x3f))
|
||||
return;
|
||||
|
||||
uint8_t encodedSector = decode_data_gcr(header[1]);
|
||||
uint8_t encodedSide = decode_data_gcr(header[2]);
|
||||
uint8_t formatByte = decode_data_gcr(header[3]);
|
||||
uint8_t wantedsum = decode_data_gcr(header[4]);
|
||||
|
||||
if (encodedSector > 11)
|
||||
return;
|
||||
|
||||
_sector->logicalCylinder = _ltl->logicalCylinder;
|
||||
_sector->logicalHead = decode_side(encodedSide);
|
||||
_sector->logicalSector = encodedSector;
|
||||
uint8_t gotsum =
|
||||
(encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
|
||||
if (wantedsum == gotsum)
|
||||
_sector->status =
|
||||
Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
if (readRaw24() != MAC_DATA_RECORD)
|
||||
return;
|
||||
|
||||
/* Read data. */
|
||||
|
||||
readRawBits(8); /* skip spare byte */
|
||||
auto inputbuffer = toBytes(readRawBits(MAC_ENCODED_SECTOR_LENGTH * 8))
|
||||
.slice(0, MAC_ENCODED_SECTOR_LENGTH);
|
||||
|
||||
for (unsigned i = 0; i < inputbuffer.size(); i++)
|
||||
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
|
||||
|
||||
_sector->status = Sector::BAD_CHECKSUM;
|
||||
Bytes userData = decode_crazy_data(inputbuffer, _sector->status);
|
||||
_sector->data.clear();
|
||||
_sector->data.writer()
|
||||
.append(userData.slice(12, 512))
|
||||
.append(userData.slice(0, 12));
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createMacintoshDecoder(const DecoderProto& config)
|
||||
{
|
||||
/* Skip ID (as we know it's a MAC_SECTOR_RECORD). */
|
||||
readRawBits(24);
|
||||
|
||||
/* Read header. */
|
||||
|
||||
auto header = toBytes(readRawBits(7*8)).slice(0, 7);
|
||||
|
||||
uint8_t encodedTrack = decode_data_gcr(header[0]);
|
||||
if (encodedTrack != (_track->physicalTrack & 0x3f))
|
||||
return;
|
||||
|
||||
uint8_t encodedSector = decode_data_gcr(header[1]);
|
||||
uint8_t encodedSide = decode_data_gcr(header[2]);
|
||||
uint8_t formatByte = decode_data_gcr(header[3]);
|
||||
uint8_t wantedsum = decode_data_gcr(header[4]);
|
||||
|
||||
if (encodedSector > 11)
|
||||
return;
|
||||
|
||||
_sector->logicalTrack = _track->physicalTrack;
|
||||
_sector->logicalSide = decode_side(encodedSide);
|
||||
_sector->logicalSector = encodedSector;
|
||||
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
|
||||
if (wantedsum == gotsum)
|
||||
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
void MacintoshDecoder::decodeDataRecord()
|
||||
{
|
||||
auto id = toBytes(readRawBits(24)).reader().read_be24();
|
||||
if (id != MAC_DATA_RECORD)
|
||||
return;
|
||||
|
||||
/* Read data. */
|
||||
|
||||
readRawBits(8); /* skip spare byte */
|
||||
auto inputbuffer = toBytes(readRawBits(MAC_ENCODED_SECTOR_LENGTH*8))
|
||||
.slice(0, MAC_ENCODED_SECTOR_LENGTH);
|
||||
|
||||
for (unsigned i=0; i<inputbuffer.size(); i++)
|
||||
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
|
||||
|
||||
_sector->status = Sector::BAD_CHECKSUM;
|
||||
Bytes userData = decode_crazy_data(inputbuffer, _sector->status);
|
||||
_sector->data.clear();
|
||||
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
|
||||
return std::unique_ptr<Decoder>(new MacintoshDecoder(config));
|
||||
}
|
||||
|
||||
258
arch/macintosh/encoder.cc
Normal file
258
arch/macintosh/encoder.cc
Normal file
@@ -0,0 +1,258 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/core/utils.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "arch/macintosh/macintosh.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/image.h"
|
||||
#include "fmt/format.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include "arch/macintosh/macintosh.pb.h"
|
||||
#include <ctype.h>
|
||||
|
||||
static bool lastBit;
|
||||
|
||||
static double clockRateUsForTrack(unsigned track)
|
||||
{
|
||||
if (track < 16)
|
||||
return 2.63;
|
||||
if (track < 32)
|
||||
return 2.89;
|
||||
if (track < 48)
|
||||
return 3.20;
|
||||
if (track < 64)
|
||||
return 3.57;
|
||||
return 3.98;
|
||||
}
|
||||
|
||||
static unsigned sectorsForTrack(unsigned track)
|
||||
{
|
||||
if (track < 16)
|
||||
return 12;
|
||||
if (track < 32)
|
||||
return 11;
|
||||
if (track < 48)
|
||||
return 10;
|
||||
if (track < 64)
|
||||
return 9;
|
||||
return 8;
|
||||
}
|
||||
|
||||
static int encode_data_gcr(uint8_t gcr)
|
||||
{
|
||||
switch (gcr)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: \
|
||||
return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* This is extremely inspired by the MESS implementation, written by Nathan
|
||||
* Woods and R. Belmont:
|
||||
* https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp
|
||||
*/
|
||||
static Bytes encode_crazy_data(const Bytes& input)
|
||||
{
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
ByteReader br(input);
|
||||
|
||||
uint8_t w1, w2, w3, w4;
|
||||
|
||||
static const int LOOKUP_LEN = MAC_SECTOR_LENGTH / 3;
|
||||
|
||||
uint8_t b1[LOOKUP_LEN + 1];
|
||||
uint8_t b2[LOOKUP_LEN + 1];
|
||||
uint8_t b3[LOOKUP_LEN + 1];
|
||||
|
||||
uint32_t c1 = 0;
|
||||
uint32_t c2 = 0;
|
||||
uint32_t c3 = 0;
|
||||
for (int j = 0;; j++)
|
||||
{
|
||||
c1 = (c1 & 0xff) << 1;
|
||||
if (c1 & 0x0100)
|
||||
c1++;
|
||||
|
||||
uint8_t val = br.read_8();
|
||||
c3 += val;
|
||||
if (c1 & 0x0100)
|
||||
{
|
||||
c3++;
|
||||
c1 &= 0xff;
|
||||
}
|
||||
b1[j] = (val ^ c1) & 0xff;
|
||||
|
||||
val = br.read_8();
|
||||
c2 += val;
|
||||
if (c3 > 0xff)
|
||||
{
|
||||
c2++;
|
||||
c3 &= 0xff;
|
||||
}
|
||||
b2[j] = (val ^ c3) & 0xff;
|
||||
|
||||
if (br.pos == 524)
|
||||
break;
|
||||
|
||||
val = br.read_8();
|
||||
c1 += val;
|
||||
if (c2 > 0xff)
|
||||
{
|
||||
c1++;
|
||||
c2 &= 0xff;
|
||||
}
|
||||
b3[j] = (val ^ c2) & 0xff;
|
||||
}
|
||||
uint32_t c4 = ((c1 & 0xc0) >> 6) | ((c2 & 0xc0) >> 4) | ((c3 & 0xc0) >> 2);
|
||||
b3[LOOKUP_LEN] = 0;
|
||||
|
||||
for (int i = 0; i <= LOOKUP_LEN; i++)
|
||||
{
|
||||
w1 = b1[i] & 0x3f;
|
||||
w2 = b2[i] & 0x3f;
|
||||
w3 = b3[i] & 0x3f;
|
||||
w4 = ((b1[i] & 0xc0) >> 2);
|
||||
w4 |= ((b2[i] & 0xc0) >> 4);
|
||||
w4 |= ((b3[i] & 0xc0) >> 6);
|
||||
|
||||
bw.write_8(w4);
|
||||
bw.write_8(w1);
|
||||
bw.write_8(w2);
|
||||
|
||||
if (i != LOOKUP_LEN)
|
||||
bw.write_8(w3);
|
||||
}
|
||||
|
||||
bw.write_8(c4 & 0x3f);
|
||||
bw.write_8(c3 & 0x3f);
|
||||
bw.write_8(c2 & 0x3f);
|
||||
bw.write_8(c1 & 0x3f);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||
{
|
||||
for (bool bit : src)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bit;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
|
||||
{
|
||||
cursor += width;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
unsigned pos = cursor - i - 1;
|
||||
if (pos < bits.size())
|
||||
bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t encode_side(uint8_t track, uint8_t side)
|
||||
{
|
||||
/* Mac disks, being weird, use the side byte to encode both the side (in
|
||||
* bit 5) and also whether we're above track 0x3f (in bit 0).
|
||||
*/
|
||||
|
||||
return (side ? 0x20 : 0x00) | ((track > 0x3f) ? 0x01 : 0x00);
|
||||
}
|
||||
|
||||
static void write_sector(std::vector<bool>& bits,
|
||||
unsigned& cursor,
|
||||
const 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");
|
||||
|
||||
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->logicalCylinder & 0x3f;
|
||||
uint8_t encodedSector = sector->logicalSector;
|
||||
uint8_t encodedSide =
|
||||
encode_side(sector->logicalCylinder, sector->logicalHead);
|
||||
uint8_t formatByte = MAC_FORMAT_BYTE;
|
||||
uint8_t headerChecksum =
|
||||
(encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
|
||||
|
||||
write_bits(bits, cursor, encode_data_gcr(encodedTrack), 1 * 8);
|
||||
write_bits(bits, cursor, encode_data_gcr(encodedSector), 1 * 8);
|
||||
write_bits(bits, cursor, encode_data_gcr(encodedSide), 1 * 8);
|
||||
write_bits(bits, cursor, encode_data_gcr(formatByte), 1 * 8);
|
||||
write_bits(bits, cursor, encode_data_gcr(headerChecksum), 1 * 8);
|
||||
|
||||
write_bits(bits, cursor, 0xdeaaff, 3 * 8);
|
||||
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6 * 8); /* sync */
|
||||
write_bits(bits, cursor, MAC_DATA_RECORD, 3 * 8);
|
||||
write_bits(bits, cursor, encode_data_gcr(sector->logicalSector), 1 * 8);
|
||||
|
||||
Bytes wireData;
|
||||
wireData.writer()
|
||||
.append(sector->data.slice(512, 12))
|
||||
.append(sector->data.slice(0, 512));
|
||||
for (uint8_t b : encode_crazy_data(wireData))
|
||||
write_bits(bits, cursor, encode_data_gcr(b), 1 * 8);
|
||||
|
||||
write_bits(bits, cursor, 0xdeaaff, 3 * 8);
|
||||
}
|
||||
|
||||
class MacintoshEncoder : public Encoder
|
||||
{
|
||||
public:
|
||||
MacintoshEncoder(const EncoderProto& config):
|
||||
Encoder(config),
|
||||
_config(config.macintosh())
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
double clockRateUs = clockRateUsForTrack(ltl.logicalCylinder);
|
||||
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;
|
||||
|
||||
for (const auto& sector : sectors)
|
||||
write_sector(bits, cursor, sector);
|
||||
|
||||
if (cursor >= bits.size())
|
||||
error("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, calculatePhysicalClockPeriod(clockRateUs * 1e3, 200e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const MacintoshEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<Encoder> createMacintoshEncoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Encoder>(new MacintoshEncoder(config));
|
||||
}
|
||||
@@ -1,24 +1,23 @@
|
||||
#ifndef MACINTOSH_H
|
||||
#define MACINTOSH_H
|
||||
|
||||
#define MAC_SECTOR_RECORD 0xd5aa96 /* 1101 0101 1010 1010 1001 0110 */
|
||||
#define MAC_DATA_RECORD 0xd5aaad /* 1101 0101 1010 1010 1010 1101 */
|
||||
#define MAC_SECTOR_RECORD 0xd5aa96 /* 1101 0101 1010 1010 1001 0110 */
|
||||
#define MAC_DATA_RECORD 0xd5aaad /* 1101 0101 1010 1010 1010 1101 */
|
||||
|
||||
#define MAC_SECTOR_LENGTH 524 /* yes, really */
|
||||
#define MAC_SECTOR_LENGTH 524 /* yes, really */
|
||||
#define MAC_ENCODED_SECTOR_LENGTH 703
|
||||
#define MAC_FORMAT_BYTE 0x22
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
#define MAC_TRACKS_PER_DISK 80
|
||||
|
||||
class MacintoshDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~MacintoshDecoder() {}
|
||||
class Encoder;
|
||||
class Decoder;
|
||||
class DecoderProto;
|
||||
class EncoderProto;
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
void decodeDataRecord();
|
||||
};
|
||||
extern std::unique_ptr<Decoder> createMacintoshDecoder(
|
||||
const DecoderProto& config);
|
||||
extern std::unique_ptr<Encoder> createMacintoshEncoder(
|
||||
const EncoderProto& config);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
11
arch/macintosh/macintosh.proto
Normal file
11
arch/macintosh/macintosh.proto
Normal file
@@ -0,0 +1,11 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "lib/config/common.proto";
|
||||
|
||||
message MacintoshDecoderProto {}
|
||||
|
||||
message MacintoshEncoderProto {
|
||||
optional double post_index_gap_us = 1 [default = 0.0,
|
||||
(help) = "post-index gap before first sector header (microseconds)."];
|
||||
}
|
||||
|
||||
296
arch/micropolis/decoder.cc
Normal file
296
arch/micropolis/decoder.cc
Normal file
@@ -0,0 +1,296 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include "arch/micropolis/micropolis.h"
|
||||
#include "lib/core/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.
|
||||
*
|
||||
* 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);
|
||||
|
||||
/* 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;
|
||||
while (!br.eof())
|
||||
{
|
||||
if (sum > 0xFF)
|
||||
{
|
||||
sum -= 0x100 - 1;
|
||||
}
|
||||
sum += br.read_8();
|
||||
}
|
||||
/* The last carry is ignored */
|
||||
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;
|
||||
}
|
||||
|
||||
static uint8_t b(uint32_t field, uint8_t pos)
|
||||
{
|
||||
return (field >> pos) & 1;
|
||||
}
|
||||
|
||||
static uint8_t eccNextBit(uint32_t ecc, uint8_t data_bit)
|
||||
{
|
||||
// This is 0x81932080 which is 0x0104C981 with reversed bits
|
||||
return b(ecc, 7) ^ b(ecc, 13) ^ b(ecc, 16) ^ b(ecc, 17) ^ b(ecc, 20) ^
|
||||
b(ecc, 23) ^ b(ecc, 24) ^ b(ecc, 31) ^ data_bit;
|
||||
}
|
||||
|
||||
uint32_t vectorGraphicEcc(const Bytes& bytes)
|
||||
{
|
||||
uint32_t e = 0;
|
||||
Bytes payloadBytes = bytes.slice(0, bytes.size() - 4);
|
||||
ByteReader payload(payloadBytes);
|
||||
while (!payload.eof())
|
||||
{
|
||||
uint8_t byte = payload.read_8();
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
e = (e << 1) | eccNextBit(e, byte >> 7);
|
||||
byte <<= 1;
|
||||
}
|
||||
}
|
||||
Bytes trailerBytes = bytes.slice(bytes.size() - 4);
|
||||
ByteReader trailer(trailerBytes);
|
||||
uint32_t res = e;
|
||||
while (!trailer.eof())
|
||||
{
|
||||
uint8_t byte = trailer.read_8();
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
res = (res << 1) | eccNextBit(e, byte >> 7);
|
||||
e <<= 1;
|
||||
byte <<= 1;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Fixes bytes when possible, returning true if changed. */
|
||||
static bool vectorGraphicEccFix(Bytes& bytes, uint32_t syndrome)
|
||||
{
|
||||
uint32_t ecc = syndrome;
|
||||
int pos = (MICROPOLIS_ENCODED_SECTOR_SIZE - 5) * 8 + 7;
|
||||
bool aligned = false;
|
||||
while ((ecc & 0xff000000) == 0)
|
||||
{
|
||||
pos += 8;
|
||||
ecc <<= 8;
|
||||
}
|
||||
for (; pos >= 0; pos--)
|
||||
{
|
||||
bool bit = ecc & 1;
|
||||
ecc >>= 1;
|
||||
if (bit)
|
||||
ecc ^= 0x808264c0;
|
||||
if ((ecc & 0xff07ffff) == 0)
|
||||
aligned = true;
|
||||
if (aligned && pos % 8 == 0)
|
||||
break;
|
||||
}
|
||||
if (pos < 0)
|
||||
return false;
|
||||
bytes[pos / 8] ^= ecc >> 16;
|
||||
return true;
|
||||
}
|
||||
|
||||
class MicropolisDecoder : public Decoder
|
||||
{
|
||||
public:
|
||||
MicropolisDecoder(const DecoderProto& config):
|
||||
Decoder(config),
|
||||
_config(config.micropolis())
|
||||
{
|
||||
_checksumType = _config.checksum_type();
|
||||
}
|
||||
|
||||
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. There's 570uS of slack in
|
||||
* each sector, after accounting for preamble, data, and postamble.
|
||||
*/
|
||||
if (now > (getFluxmapDuration() - 12.0e6))
|
||||
{
|
||||
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. The
|
||||
* preamble was expected to be 640uS long.
|
||||
*/
|
||||
if (_sector->headerStartTime > (getFluxmapDuration() - 11.3e6))
|
||||
{
|
||||
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);
|
||||
|
||||
bool eccPresent = bytes[274] == 0xaa;
|
||||
uint32_t ecc = 0;
|
||||
if (_config.ecc_type() == MicropolisDecoderProto::VECTOR && eccPresent)
|
||||
{
|
||||
ecc = vectorGraphicEcc(bytes.slice(0, 274));
|
||||
if (ecc != 0)
|
||||
{
|
||||
vectorGraphicEccFix(bytes, ecc);
|
||||
ecc = vectorGraphicEcc(bytes.slice(0, 274));
|
||||
}
|
||||
}
|
||||
|
||||
ByteReader br(bytes);
|
||||
|
||||
int syncByte = br.read_8(); /* sync */
|
||||
if (syncByte != 0xFF)
|
||||
return;
|
||||
|
||||
_sector->logicalCylinder = br.read_8();
|
||||
_sector->logicalHead = _ltl->logicalHead;
|
||||
_sector->logicalSector = br.read_8();
|
||||
if (_sector->logicalSector > 15)
|
||||
return;
|
||||
if (_sector->logicalCylinder > 76)
|
||||
return;
|
||||
if (_sector->logicalCylinder != _ltl->logicalCylinder)
|
||||
return;
|
||||
|
||||
br.read(10); /* OS data or padding */
|
||||
auto data = br.read(MICROPOLIS_PAYLOAD_SIZE);
|
||||
uint8_t wantChecksum = br.read_8();
|
||||
|
||||
/* 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");
|
||||
if (wantChecksum == gotChecksum && (!eccPresent || ecc == 0))
|
||||
_sector->status = Sector::OK;
|
||||
else
|
||||
_sector->status = Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
private:
|
||||
const MicropolisDecoderProto& _config;
|
||||
MicropolisDecoderProto_ChecksumType
|
||||
_checksumType; /* -1 = auto, 1 = Micropolis, 2=MZOS */
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createMicropolisDecoder(const DecoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Decoder>(new MicropolisDecoder(config));
|
||||
}
|
||||
136
arch/micropolis/encoder.cc
Normal file
136
arch/micropolis/encoder.cc
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "arch/micropolis/micropolis.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "lib/data/image.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
|
||||
static void write_sector(std::vector<bool>& bits,
|
||||
unsigned& cursor,
|
||||
const std::shared_ptr<const Sector>& sector,
|
||||
MicropolisEncoderProto::EccType eccType)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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->logicalCylinder);
|
||||
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)));
|
||||
|
||||
uint8_t eccPresent = 0;
|
||||
uint32_t ecc = 0;
|
||||
if (eccType == MicropolisEncoderProto::VECTOR)
|
||||
{
|
||||
eccPresent = 0xaa;
|
||||
ecc = vectorGraphicEcc(sectorData + Bytes(4));
|
||||
}
|
||||
writer.write_be32(ecc);
|
||||
writer.write_8(eccPresent);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
class MicropolisEncoder : public Encoder
|
||||
{
|
||||
public:
|
||||
MicropolisEncoder(const EncoderProto& config):
|
||||
Encoder(config),
|
||||
_config(config.micropolis())
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
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);
|
||||
std::vector<unsigned> indexes;
|
||||
unsigned prev_cursor = 0;
|
||||
unsigned cursor = 0;
|
||||
|
||||
for (const auto& sectorData : sectors)
|
||||
{
|
||||
indexes.push_back(cursor);
|
||||
prev_cursor = cursor;
|
||||
write_sector(bits, cursor, sectorData, _config.ecc_type());
|
||||
}
|
||||
indexes.push_back(prev_cursor + (cursor - prev_cursor) / 2);
|
||||
indexes.push_back(cursor);
|
||||
|
||||
if (cursor != bits.size())
|
||||
error("track data mismatched length");
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
nanoseconds_t clockPeriod =
|
||||
calculatePhysicalClockPeriod(_config.clock_period_us() * 1e3,
|
||||
_config.rotational_period_ms() * 1e6);
|
||||
auto pos = bits.begin();
|
||||
for (int i = 1; i < indexes.size(); i++)
|
||||
{
|
||||
auto end = bits.begin() + indexes[i];
|
||||
fluxmap->appendBits(std::vector<bool>(pos, end), clockPeriod);
|
||||
fluxmap->appendIndex();
|
||||
pos = end;
|
||||
}
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const MicropolisEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<Encoder> createMicropolisEncoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Encoder>(new MicropolisEncoder(config));
|
||||
}
|
||||
22
arch/micropolis/micropolis.h
Normal file
22
arch/micropolis/micropolis.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef MICROPOLIS_H
|
||||
#define MICROPOLIS_H
|
||||
|
||||
#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 Decoder;
|
||||
class Encoder;
|
||||
class EncoderProto;
|
||||
class DecoderProto;
|
||||
|
||||
extern std::unique_ptr<Decoder> createMicropolisDecoder(
|
||||
const DecoderProto& config);
|
||||
extern std::unique_ptr<Encoder> createMicropolisEncoder(
|
||||
const EncoderProto& config);
|
||||
|
||||
extern uint8_t micropolisChecksum(const Bytes& bytes);
|
||||
extern uint32_t vectorGraphicEcc(const Bytes& bytes);
|
||||
|
||||
#endif
|
||||
37
arch/micropolis/micropolis.proto
Normal file
37
arch/micropolis/micropolis.proto
Normal file
@@ -0,0 +1,37 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "lib/config/common.proto";
|
||||
|
||||
message MicropolisDecoderProto {
|
||||
enum ChecksumType {
|
||||
AUTO = 0;
|
||||
MICROPOLIS = 1;
|
||||
MZOS = 2;
|
||||
}
|
||||
enum EccType {
|
||||
NONE = 0;
|
||||
VECTOR = 1;
|
||||
}
|
||||
|
||||
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"];
|
||||
optional EccType ecc_type = 3 [default = NONE,
|
||||
(help) = "ECC type to use: NONE, VECTOR"];
|
||||
}
|
||||
|
||||
message MicropolisEncoderProto {
|
||||
enum EccType {
|
||||
NONE = 0;
|
||||
VECTOR = 1;
|
||||
}
|
||||
|
||||
optional double clock_period_us = 1
|
||||
[ default = 2.0, (help) = "clock rate on the real device" ];
|
||||
optional double rotational_period_ms = 2
|
||||
[ default = 200.0, (help) = "rotational period on the real device" ];
|
||||
optional EccType ecc_type = 3 [default = NONE,
|
||||
(help) = "ECC type to use for IMG data: NONE, VECTOR"];
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "globals.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "mx/mx.h"
|
||||
#include "crc.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "sector.h"
|
||||
#include "record.h"
|
||||
#include "track.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "arch/mx/mx.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include <string.h>
|
||||
|
||||
const int SECTOR_SIZE = 256;
|
||||
@@ -18,58 +18,68 @@ 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);
|
||||
|
||||
void MxDecoder::beginTrack()
|
||||
class MxDecoder : public Decoder
|
||||
{
|
||||
_currentSector = -1;
|
||||
_clock = 0;
|
||||
}
|
||||
public:
|
||||
MxDecoder(const DecoderProto& config): Decoder(config) {}
|
||||
|
||||
AbstractDecoder::RecordType MxDecoder::advanceToNextRecord()
|
||||
{
|
||||
if (_currentSector == -1)
|
||||
void beginTrack() override
|
||||
{
|
||||
/* 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)).reader().read_be16();
|
||||
}
|
||||
else if (_currentSector == 10)
|
||||
{
|
||||
/* That was the last sector on the disk. */
|
||||
return UNKNOWN_RECORD;
|
||||
}
|
||||
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;
|
||||
_clock = _sector->clock = seekToPattern(ID_PATTERN);
|
||||
_currentSector = 0;
|
||||
}
|
||||
|
||||
_currentSector++;
|
||||
return SECTOR_RECORD;
|
||||
}
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
if (_currentSector == 11)
|
||||
{
|
||||
/* That was the last sector on the disk. */
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return _clock;
|
||||
}
|
||||
|
||||
void MxDecoder::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);
|
||||
|
||||
uint16_t gotChecksum = 0;
|
||||
ByteReader br(bytes);
|
||||
for (int i = 0; i < (SECTOR_SIZE / 2); i++)
|
||||
gotChecksum += br.read_be16();
|
||||
uint16_t wantChecksum = br.read_be16();
|
||||
|
||||
_sector->logicalCylinder = _ltl->logicalCylinder;
|
||||
_sector->logicalHead = _ltl->logicalHead;
|
||||
_sector->logicalSector = _currentSector;
|
||||
_sector->data = bytes.slice(0, SECTOR_SIZE).swab();
|
||||
_sector->status =
|
||||
(gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
_currentSector++;
|
||||
}
|
||||
|
||||
private:
|
||||
nanoseconds_t _clock;
|
||||
int _currentSector;
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createMxDecoder(const DecoderProto& config)
|
||||
{
|
||||
auto bits = readRawBits((SECTOR_SIZE+2)*16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE+2).swab();
|
||||
|
||||
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();
|
||||
|
||||
_sector->logicalTrack = _logicalTrack;
|
||||
_sector->logicalSide = _track->physicalSide;
|
||||
_sector->logicalSector = _currentSector;
|
||||
_sector->data = bytes.slice(0, SECTOR_SIZE);
|
||||
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
return std::unique_ptr<Decoder>(new MxDecoder(config));
|
||||
}
|
||||
|
||||
17
arch/mx/mx.h
17
arch/mx/mx.h
@@ -1,21 +1,8 @@
|
||||
#ifndef MX_H
|
||||
#define MX_H
|
||||
|
||||
#include "decoders/decoders.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
|
||||
class MxDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~MxDecoder() {}
|
||||
|
||||
void beginTrack();
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
|
||||
private:
|
||||
nanoseconds_t _clock;
|
||||
int _currentSector;
|
||||
int _logicalTrack;
|
||||
};
|
||||
extern std::unique_ptr<Decoder> createMxDecoder(const DecoderProto& config);
|
||||
|
||||
#endif
|
||||
|
||||
4
arch/mx/mx.proto
Normal file
4
arch/mx/mx.proto
Normal file
@@ -0,0 +1,4 @@
|
||||
syntax = "proto2";
|
||||
|
||||
message MxDecoderProto {}
|
||||
|
||||
188
arch/northstar/decoder.cc
Normal file
188
arch/northstar/decoder.cc
Normal file
@@ -0,0 +1,188 @@
|
||||
/* Decoder for North Star 10-sector hard-sectored disks.
|
||||
*
|
||||
* Supports both single- and double-density. For the sector format and
|
||||
* checksum algorithm, see pp. 33 of the North Star Double Density Controller
|
||||
* manual:
|
||||
*
|
||||
* http://bitsavers.org/pdf/northstar/boards/Northstar_MDS-A-D_1978.pdf
|
||||
*
|
||||
* North Star disks do not contain any track/head/sector information
|
||||
* encoded in the sector record. For this reason, we have to be absolutely
|
||||
* sure that the hardSectorId is correct.
|
||||
*/
|
||||
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include "arch/northstar/northstar.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#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.
|
||||
*
|
||||
* This is true for most disks; however, I found a few disks, including an
|
||||
* original North Star DOS/BASIC v2.2.1 DQ disk) that uses 0xFBnn, where
|
||||
* nn is an incrementing pattern.
|
||||
*
|
||||
* 00 00 00 F B
|
||||
* 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, 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, FM_ID);
|
||||
|
||||
const FluxMatchers ANY_SECTOR_PATTERN({
|
||||
&MFM_PATTERN,
|
||||
&FM_PATTERN,
|
||||
});
|
||||
|
||||
/* Checksum is initially 0.
|
||||
* For each data byte, XOR with the current checksum.
|
||||
* Rotate checksum left, carrying bit 7 to bit 0.
|
||||
*/
|
||||
uint8_t northstarChecksum(const Bytes& bytes)
|
||||
{
|
||||
ByteReader br(bytes);
|
||||
uint8_t checksum = 0;
|
||||
|
||||
while (!br.eof())
|
||||
{
|
||||
checksum ^= br.read_8();
|
||||
checksum = ((checksum << 1) | ((checksum >> 7)));
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
class NorthstarDecoder : public Decoder
|
||||
{
|
||||
public:
|
||||
NorthstarDecoder(const DecoderProto& config):
|
||||
Decoder(config),
|
||||
_config(config.northstar())
|
||||
{
|
||||
}
|
||||
|
||||
/* Search for FM or MFM sector record */
|
||||
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() - 21e6))
|
||||
{
|
||||
seekToIndexMark();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msSinceIndex = std::round(now / 1e6);
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
nanoseconds_t clock = seekToPattern(ANY_SECTOR_PATTERN);
|
||||
_sector->headerStartTime = tell().ns();
|
||||
|
||||
/* 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 */
|
||||
if ((sectorFoundTimeRaw % 20) < 10)
|
||||
{
|
||||
sectorFoundTime = (sectorFoundTimeRaw / 20) * 20;
|
||||
}
|
||||
else
|
||||
{
|
||||
sectorFoundTime = ((sectorFoundTimeRaw + 20) / 20) * 20;
|
||||
}
|
||||
|
||||
/* Calculate the sector ID based on time since the index */
|
||||
_hardSectorId = (sectorFoundTime / 20) % 10;
|
||||
|
||||
return clock;
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
uint64_t id = toBytes(readRawBits(64)).reader().read_be64();
|
||||
unsigned recordSize, payloadSize, headerSize;
|
||||
|
||||
if (id == MFM_ID)
|
||||
{
|
||||
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_DD;
|
||||
payloadSize = NORTHSTAR_PAYLOAD_SIZE_DD;
|
||||
headerSize = NORTHSTAR_HEADER_SIZE_DD;
|
||||
}
|
||||
else
|
||||
{
|
||||
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_SD;
|
||||
payloadSize = NORTHSTAR_PAYLOAD_SIZE_SD;
|
||||
headerSize = NORTHSTAR_HEADER_SIZE_SD;
|
||||
}
|
||||
|
||||
auto rawbits = readRawBits(recordSize * 16);
|
||||
auto bytes = decodeFmMfm(rawbits).slice(0, recordSize);
|
||||
ByteReader br(bytes);
|
||||
|
||||
_sector->logicalHead = _ltl->logicalHead;
|
||||
_sector->logicalSector = _hardSectorId;
|
||||
_sector->logicalCylinder = _ltl->logicalCylinder;
|
||||
|
||||
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 - 1, payloadSize));
|
||||
_sector->status =
|
||||
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
private:
|
||||
const NorthstarDecoderProto& _config;
|
||||
uint8_t _hardSectorId;
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createNorthstarDecoder(const DecoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Decoder>(new NorthstarDecoder(config));
|
||||
}
|
||||
168
arch/northstar/encoder.cc
Normal file
168
arch/northstar/encoder.cc
Normal file
@@ -0,0 +1,168 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "arch/northstar/northstar.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "lib/data/image.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
|
||||
#define GAP_FILL_SIZE_SD 30
|
||||
#define PRE_HEADER_GAP_FILL_SIZE_SD 9
|
||||
#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 TOTAL_SECTOR_BYTES ()
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 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);
|
||||
|
||||
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 ({}); expected {}, got {}",
|
||||
sector->data.size(),
|
||||
fullSector->size(),
|
||||
fullSectorSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* sector postamble */
|
||||
for (int i = 0; i < gapFillSize; i++)
|
||||
fullSector->push_back(GAP2_FILL_BYTE);
|
||||
}
|
||||
|
||||
bool lastBit = false;
|
||||
|
||||
if (doubleDensity == true)
|
||||
{
|
||||
encodeMfm(bits, cursor, fullSector, lastBit);
|
||||
}
|
||||
else
|
||||
{
|
||||
encodeFm(bits, cursor, fullSector);
|
||||
}
|
||||
}
|
||||
|
||||
class NorthstarEncoder : public Encoder
|
||||
{
|
||||
public:
|
||||
NorthstarEncoder(const EncoderProto& config):
|
||||
Encoder(config),
|
||||
_config(config.northstar())
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution = 100000;
|
||||
double clockRateUs = _config.clock_period_us();
|
||||
|
||||
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;
|
||||
|
||||
for (const auto& sectorData : sectors)
|
||||
write_sector(bits, cursor, sectorData);
|
||||
|
||||
if (cursor > bits.size())
|
||||
error("track data overrun");
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits,
|
||||
calculatePhysicalClockPeriod(
|
||||
clockRateUs * 1e3, _config.rotational_period_ms() * 1e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const NorthstarEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<Encoder> createNorthstarEncoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Encoder>(new NorthstarEncoder(config));
|
||||
}
|
||||
42
arch/northstar/northstar.h
Normal file
42
arch/northstar/northstar.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef NORTHSTAR_H
|
||||
#define NORTHSTAR_H
|
||||
|
||||
/* Northstar floppies are 10-hard sectored disks with a sector format as
|
||||
* follows:
|
||||
*
|
||||
* |----------------------------------|
|
||||
* | SYNC Byte | Payload | Checksum |
|
||||
* |------------+----------+----------|
|
||||
* | 1 (0xFB) | 256 (SD) | 1 |
|
||||
* | 2 (0xFBFB) | 512 (DD) | |
|
||||
* |----------------------------------|
|
||||
*
|
||||
*/
|
||||
|
||||
#define NORTHSTAR_PREAMBLE_SIZE_SD (16)
|
||||
#define NORTHSTAR_PREAMBLE_SIZE_DD (32)
|
||||
#define NORTHSTAR_HEADER_SIZE_SD (1)
|
||||
#define NORTHSTAR_HEADER_SIZE_DD (2)
|
||||
#define NORTHSTAR_PAYLOAD_SIZE_SD (256)
|
||||
#define NORTHSTAR_PAYLOAD_SIZE_DD (512)
|
||||
#define NORTHSTAR_CHECKSUM_SIZE (1)
|
||||
#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)
|
||||
|
||||
class Decoder;
|
||||
class Encoder;
|
||||
class EncoderProto;
|
||||
class DecoderProto;
|
||||
|
||||
extern uint8_t northstarChecksum(const Bytes& bytes);
|
||||
|
||||
extern std::unique_ptr<Decoder> createNorthstarDecoder(
|
||||
const DecoderProto& config);
|
||||
extern std::unique_ptr<Encoder> createNorthstarEncoder(
|
||||
const EncoderProto& config);
|
||||
|
||||
#endif /* NORTHSTAR */
|
||||
13
arch/northstar/northstar.proto
Normal file
13
arch/northstar/northstar.proto
Normal file
@@ -0,0 +1,13 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "lib/config/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" ];
|
||||
}
|
||||
|
||||
52
arch/rolandd20/decoder.cc
Normal file
52
arch/rolandd20/decoder.cc
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "arch/rolandd20/rolandd20.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Sector header record:
|
||||
*
|
||||
* BF FF FF FF FF FF FE AB
|
||||
*
|
||||
* This encodes to:
|
||||
*
|
||||
* e d 5 5 5 5 5 5
|
||||
* 1110 1101 0101 0101 0101 0101 0101 0101
|
||||
* 5 5 5 5 5 5 5 5
|
||||
* 0101 0101 0101 0101 0101 0101 0101 0101
|
||||
* 5 5 5 5 5 5 5 5
|
||||
* 0101 0101 0101 0101 0101 0101 0101 0101
|
||||
* 5 5 5 4 4 4 4 5
|
||||
* 0101 0101 0101 0100 0100 0100 0100 0101
|
||||
*/
|
||||
|
||||
static const FluxPattern SECTOR_PATTERN(64, 0xed55555555555555LL);
|
||||
|
||||
class RolandD20Decoder : public Decoder
|
||||
{
|
||||
public:
|
||||
RolandD20Decoder(const DecoderProto& config): Decoder(config) {}
|
||||
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
return seekToPattern(SECTOR_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
auto rawbits = readRawBits(256);
|
||||
const auto& bytes = decodeFmMfm(rawbits);
|
||||
fmt::print("{} ", _sector->clock);
|
||||
hexdump(std::cout, bytes);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createRolandD20Decoder(const DecoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Decoder>(new RolandD20Decoder(config));
|
||||
}
|
||||
4
arch/rolandd20/rolandd20.h
Normal file
4
arch/rolandd20/rolandd20.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
extern std::unique_ptr<Decoder> createRolandD20Decoder(
|
||||
const DecoderProto& config);
|
||||
5
arch/rolandd20/rolandd20.proto
Normal file
5
arch/rolandd20/rolandd20.proto
Normal file
@@ -0,0 +1,5 @@
|
||||
syntax = "proto2";
|
||||
|
||||
message RolandD20DecoderProto {}
|
||||
|
||||
|
||||
156
arch/smaky6/decoder.cc
Normal file
156
arch/smaky6/decoder.cc
Normal file
@@ -0,0 +1,156 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "protocol.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include "arch/smaky6/smaky6.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "fmt/format.h"
|
||||
#include "lib/decoders/decoders.pb.h"
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
static const FluxPattern SECTOR_PATTERN(32, 0x54892aaa);
|
||||
|
||||
class Smaky6Decoder : public Decoder
|
||||
{
|
||||
public:
|
||||
Smaky6Decoder(const DecoderProto& config):
|
||||
Decoder(config),
|
||||
_config(config.smaky6())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
/* Returns the sector ID of the _current_ sector. */
|
||||
int advanceToNextSector()
|
||||
{
|
||||
auto previous = tell();
|
||||
seekToIndexMark();
|
||||
auto now = tell();
|
||||
if ((now.ns() - previous.ns()) < 9e6)
|
||||
{
|
||||
seekToIndexMark();
|
||||
auto next = tell();
|
||||
if ((next.ns() - now.ns()) < 9e6)
|
||||
{
|
||||
/* We just found sector 0. */
|
||||
|
||||
_sectorId = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Spurious... */
|
||||
|
||||
seek(now);
|
||||
}
|
||||
}
|
||||
|
||||
return _sectorId++;
|
||||
}
|
||||
|
||||
public:
|
||||
void beginTrack() override
|
||||
{
|
||||
/* Find the start-of-track index marks, which will be an interval
|
||||
* of about 6ms. */
|
||||
|
||||
seekToIndexMark();
|
||||
_sectorId = 99;
|
||||
for (;;)
|
||||
{
|
||||
auto pos = tell();
|
||||
advanceToNextSector();
|
||||
if (_sectorId < 99)
|
||||
{
|
||||
seek(pos);
|
||||
break;
|
||||
}
|
||||
|
||||
if (eof())
|
||||
return;
|
||||
}
|
||||
|
||||
/* Now we know where to start counting, start finding sectors. */
|
||||
|
||||
_sectorStarts.clear();
|
||||
for (;;)
|
||||
{
|
||||
auto now = tell();
|
||||
if (eof())
|
||||
break;
|
||||
|
||||
int id = advanceToNextSector();
|
||||
if (id < 16)
|
||||
_sectorStarts.push_back(std::make_pair(id, now));
|
||||
}
|
||||
|
||||
_sectorIndex = 0;
|
||||
}
|
||||
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
if (_sectorIndex == _sectorStarts.size())
|
||||
{
|
||||
seekToIndexMark();
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto& p = _sectorStarts[_sectorIndex++];
|
||||
_sectorId = p.first;
|
||||
seek(p.second);
|
||||
|
||||
nanoseconds_t clock = seekToPattern(SECTOR_PATTERN);
|
||||
_sector->headerStartTime = tell().ns();
|
||||
|
||||
return clock;
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
readRawBits(33);
|
||||
const auto& rawbits = readRawBits(SMAKY6_RECORD_SIZE * 16);
|
||||
if (rawbits.size() < SMAKY6_SECTOR_SIZE)
|
||||
return;
|
||||
const auto& rawbytes =
|
||||
toBytes(rawbits).slice(0, SMAKY6_RECORD_SIZE * 16);
|
||||
|
||||
/* The Smaky bytes are stored backwards! Backwards! */
|
||||
|
||||
const auto& bytes =
|
||||
decodeFmMfm(rawbits).slice(0, SMAKY6_RECORD_SIZE).reverseBits();
|
||||
ByteReader br(bytes);
|
||||
|
||||
uint8_t track = br.read_8();
|
||||
Bytes data = br.read(SMAKY6_SECTOR_SIZE);
|
||||
uint8_t wantedChecksum = br.read_8();
|
||||
uint8_t gotChecksum = sumBytes(data) & 0xff;
|
||||
|
||||
if (track != _ltl->logicalCylinder)
|
||||
return;
|
||||
|
||||
_sector->logicalCylinder = _ltl->physicalCylinder;
|
||||
_sector->logicalHead = _ltl->logicalHead;
|
||||
_sector->logicalSector = _sectorId;
|
||||
|
||||
_sector->data = data;
|
||||
_sector->status =
|
||||
(wantedChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
private:
|
||||
const Smaky6DecoderProto& _config;
|
||||
nanoseconds_t _startOfTrack;
|
||||
std::vector<std::pair<int, Fluxmap::Position>> _sectorStarts;
|
||||
int _sectorId;
|
||||
int _sectorIndex;
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createSmaky6Decoder(const DecoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Decoder>(new Smaky6Decoder(config));
|
||||
}
|
||||
9
arch/smaky6/smaky6.h
Normal file
9
arch/smaky6/smaky6.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef SMAKY6_H
|
||||
#define SMAKY6_H
|
||||
|
||||
#define SMAKY6_SECTOR_SIZE 256
|
||||
#define SMAKY6_RECORD_SIZE (1 + SMAKY6_SECTOR_SIZE + 1)
|
||||
|
||||
extern std::unique_ptr<Decoder> createSmaky6Decoder(const DecoderProto& config);
|
||||
|
||||
#endif
|
||||
4
arch/smaky6/smaky6.proto
Normal file
4
arch/smaky6/smaky6.proto
Normal file
@@ -0,0 +1,4 @@
|
||||
syntax = "proto2";
|
||||
|
||||
message Smaky6DecoderProto {}
|
||||
|
||||
81
arch/tartu/decoder.cc
Normal file
81
arch/tartu/decoder.cc
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "arch/tartu/tartu.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include <string.h>
|
||||
|
||||
constexpr uint64_t HEADER_BITS = 0xaaaaaaaa44895554LL;
|
||||
constexpr uint64_t DATA_BITS = 0xaaaaaaaa44895545LL;
|
||||
|
||||
static const FluxPattern HEADER_PATTERN(64, HEADER_BITS);
|
||||
static const FluxPattern DATA_PATTERN(64, DATA_BITS);
|
||||
|
||||
const FluxMatchers ANY_RECORD_PATTERN{&HEADER_PATTERN, &DATA_PATTERN};
|
||||
|
||||
class TartuDecoder : public Decoder
|
||||
{
|
||||
public:
|
||||
TartuDecoder(const DecoderProto& config):
|
||||
Decoder(config),
|
||||
_config(config.tartu())
|
||||
{
|
||||
}
|
||||
|
||||
void beginTrack() override {}
|
||||
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
if (readRaw64() != HEADER_BITS)
|
||||
return;
|
||||
|
||||
auto bits = readRawBits(16 * 4);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, 4);
|
||||
|
||||
ByteReader br(bytes);
|
||||
uint8_t track = br.read_8();
|
||||
_sector->logicalCylinder = track >> 1;
|
||||
_sector->logicalHead = track & 1;
|
||||
br.skip(1); /* seems always to be 1 */
|
||||
_sector->logicalSector = br.read_8();
|
||||
uint8_t wantChecksum = br.read_8();
|
||||
uint8_t gotChecksum = ~sumBytes(bytes.slice(0, 3));
|
||||
|
||||
if (wantChecksum == gotChecksum)
|
||||
_sector->status = Sector::DATA_MISSING;
|
||||
|
||||
_sector->status = Sector::DATA_MISSING;
|
||||
}
|
||||
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
if (readRaw64() != DATA_BITS)
|
||||
return;
|
||||
|
||||
const auto& bits = readRawBits(129 * 16);
|
||||
const auto& bytes = decodeFmMfm(bits).slice(0, 129);
|
||||
_sector->data = bytes.slice(0, 128);
|
||||
|
||||
uint8_t wantChecksum = bytes.reader().seek(128).read_8();
|
||||
uint8_t gotChecksum = ~sumBytes(_sector->data);
|
||||
_sector->status =
|
||||
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
private:
|
||||
const TartuDecoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createTartuDecoder(const DecoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Decoder>(new TartuDecoder(config));
|
||||
}
|
||||
115
arch/tartu/encoder.cc
Normal file
115
arch/tartu/encoder.cc
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "arch/tartu/tartu.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include <string.h>
|
||||
|
||||
class TartuEncoder : public Encoder
|
||||
{
|
||||
public:
|
||||
TartuEncoder(const EncoderProto& config):
|
||||
Encoder(config),
|
||||
_config(config.tartu())
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
_clockRateUs = _config.clock_period_us();
|
||||
int bitsPerRevolution =
|
||||
(_config.target_rotational_period_ms() * 1000.0) / _clockRateUs;
|
||||
|
||||
const auto& sector = *sectors.begin();
|
||||
_bits.resize(bitsPerRevolution);
|
||||
_cursor = 0;
|
||||
|
||||
writeFillerRawBitsUs(_config.gap1_us());
|
||||
bool first = true;
|
||||
for (const auto& sectorData : sectors)
|
||||
{
|
||||
if (!first)
|
||||
writeFillerRawBitsUs(_config.gap4_us());
|
||||
first = false;
|
||||
writeSector(sectorData);
|
||||
}
|
||||
|
||||
if (_cursor > _bits.size())
|
||||
error("track data overrun");
|
||||
writeFillerRawBitsUs(_config.target_rotational_period_ms() * 1000.0);
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(_bits,
|
||||
calculatePhysicalClockPeriod(_clockRateUs * 1e3,
|
||||
_config.target_rotational_period_ms() * 1e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
void writeBytes(const Bytes& bytes)
|
||||
{
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
}
|
||||
|
||||
void writeRawBits(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;
|
||||
}
|
||||
}
|
||||
|
||||
void writeFillerRawBitsUs(double us)
|
||||
{
|
||||
unsigned count = (us / _clockRateUs) / 2;
|
||||
for (int i = 0; i < count; i++)
|
||||
writeRawBits(0b10, 2);
|
||||
};
|
||||
|
||||
void writeSector(const std::shared_ptr<const Sector>& sectorData)
|
||||
{
|
||||
writeRawBits(_config.header_marker(), 64);
|
||||
{
|
||||
Bytes bytes;
|
||||
ByteWriter bw(bytes);
|
||||
bw.write_8(
|
||||
(sectorData->logicalCylinder << 1) | sectorData->logicalHead);
|
||||
bw.write_8(1);
|
||||
bw.write_8(sectorData->logicalSector);
|
||||
bw.write_8(~sumBytes(bytes.slice(0, 3)));
|
||||
writeBytes(bytes);
|
||||
}
|
||||
|
||||
writeFillerRawBitsUs(_config.gap3_us());
|
||||
writeRawBits(_config.data_marker(), 64);
|
||||
{
|
||||
Bytes bytes;
|
||||
ByteWriter bw(bytes);
|
||||
bw.append(sectorData->data);
|
||||
bw.write_8(~sumBytes(bytes.slice(0, sectorData->data.size())));
|
||||
writeBytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const TartuEncoderProto& _config;
|
||||
double _clockRateUs;
|
||||
std::vector<bool> _bits;
|
||||
unsigned _cursor;
|
||||
bool _lastBit;
|
||||
};
|
||||
|
||||
std::unique_ptr<Encoder> createTartuEncoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Encoder>(new TartuEncoder(config));
|
||||
}
|
||||
7
arch/tartu/tartu.h
Normal file
7
arch/tartu/tartu.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef TARTU_H
|
||||
#define TARTU_H
|
||||
|
||||
extern std::unique_ptr<Decoder> createTartuDecoder(const DecoderProto& config);
|
||||
extern std::unique_ptr<Encoder> createTartuEncoder(const EncoderProto& config);
|
||||
|
||||
#endif
|
||||
27
arch/tartu/tartu.proto
Normal file
27
arch/tartu/tartu.proto
Normal file
@@ -0,0 +1,27 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "lib/config/common.proto";
|
||||
|
||||
message TartuDecoderProto {}
|
||||
|
||||
message TartuEncoderProto {
|
||||
optional double clock_period_us = 1
|
||||
[ default = 2.0, (help) = "clock rate on the real device (for MFM)" ];
|
||||
optional double target_rotational_period_ms = 2
|
||||
[ default=200, (help) = "rotational period of target disk" ];
|
||||
optional double gap1_us = 3
|
||||
[ default = 1200,
|
||||
(help) = "size of gap 1 (the post-index gap)" ];
|
||||
optional double gap3_us = 4
|
||||
[ default = 150,
|
||||
(help) = "size of gap 3 (the pre-data gap)" ];
|
||||
optional double gap4_us = 5
|
||||
[ default = 180,
|
||||
(help) = "size of gap 4 (the post-data or format gap)" ];
|
||||
optional uint64 header_marker = 6
|
||||
[ default = 0xaaaaaaaa44895554,
|
||||
(help) = "64-bit raw bit pattern of header record marker" ];
|
||||
optional uint64 data_marker = 7
|
||||
[ default = 0xaaaaaaaa44895545,
|
||||
(help) = "64-bit raw bit pattern of data record marker" ];
|
||||
}
|
||||
101
arch/tids990/decoder.cc
Normal file
101
arch/tids990/decoder.cc
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "arch/tids990/tids990.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include <string.h>
|
||||
#include "fmt/format.h"
|
||||
|
||||
/* The Texas Instruments DS990 uses MFM with a scheme similar to a simplified
|
||||
* version of the IBM record scheme (it's actually easier to parse than IBM).
|
||||
* There are 26 sectors per track, each holding a rather weird 288 bytes.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Sector record:
|
||||
* data: 0 1 0 1 0 1 0 1 .0 0 0 0 1 0 1 0 = 0x550a
|
||||
* mfm: 00 01 00 01 00 01 00 01.00 10 10 10 01 00 01 00 = 0x11112a44
|
||||
* special: 00 01 00 01 00 01 00 01.00 10 00 10 01 00 01 00 = 0x11112244
|
||||
* ^^
|
||||
* When shifted out of phase, the special 0xa1 byte becomes an illegal
|
||||
* encoding (you can't do 10 00). So this can't be spoofed by user data.
|
||||
*/
|
||||
const 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 = 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});
|
||||
|
||||
class Tids990Decoder : public Decoder
|
||||
{
|
||||
public:
|
||||
Tids990Decoder(const DecoderProto& config): Decoder(config) {}
|
||||
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
_sector->logicalHead = br.read_8() >> 3;
|
||||
_sector->logicalCylinder = br.read_8();
|
||||
br.read_8(); /* number of sectors per track */
|
||||
_sector->logicalSector = br.read_8();
|
||||
br.read_be16(); /* sector size */
|
||||
uint16_t wantChecksum = br.read_be16();
|
||||
|
||||
if (wantChecksum == gotChecksum)
|
||||
_sector->status =
|
||||
Sector::DATA_MISSING; /* correct but unintuitive */
|
||||
}
|
||||
|
||||
void 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));
|
||||
|
||||
_sector->data = br.read(TIDS990_PAYLOAD_SIZE);
|
||||
uint16_t wantChecksum = br.read_be16();
|
||||
_sector->status =
|
||||
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createTids990Decoder(const DecoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Decoder>(new Tids990Decoder(config));
|
||||
}
|
||||
150
arch/tids990/encoder.cc
Normal file
150
arch/tids990/encoder.cc
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "arch/tids990/tids990.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/image.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';
|
||||
}
|
||||
|
||||
static uint8_t decodeUint16(uint16_t raw)
|
||||
{
|
||||
Bytes b;
|
||||
ByteWriter bw(b);
|
||||
bw.write_be16(raw);
|
||||
return decodeFmMfm(b.toBits())[0];
|
||||
}
|
||||
|
||||
class Tids990Encoder : public Encoder
|
||||
{
|
||||
public:
|
||||
Tids990Encoder(const EncoderProto& config):
|
||||
Encoder(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 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);
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
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());
|
||||
|
||||
writeBytes(_config.gap1_bytes(), 0x55);
|
||||
|
||||
bool first = true;
|
||||
for (const auto& sectorData : sectors)
|
||||
{
|
||||
if (!first)
|
||||
writeBytes(_config.gap3_bytes(), 0x55);
|
||||
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. */
|
||||
|
||||
{
|
||||
Bytes header;
|
||||
ByteWriter bw(header);
|
||||
|
||||
writeBytes(12, 0x55);
|
||||
bw.write_8(am1Unencoded);
|
||||
bw.write_8(sectorData->logicalHead << 3);
|
||||
bw.write_8(sectorData->logicalCylinder);
|
||||
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));
|
||||
}
|
||||
|
||||
writeBytes(_config.gap2_bytes(), 0x55);
|
||||
|
||||
{
|
||||
Bytes data;
|
||||
ByteWriter bw(data);
|
||||
|
||||
writeBytes(12, 0x55);
|
||||
bw.write_8(am2Unencoded);
|
||||
|
||||
bw += sectorData->data;
|
||||
uint16_t crc = crc16(CCITT_POLY, data);
|
||||
bw.write_be16(crc);
|
||||
|
||||
writeRawBits(_config.am2_byte(), 16);
|
||||
writeBytes(data.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
if (_cursor >= _bits.size())
|
||||
error("track data overrun");
|
||||
while (_cursor < _bits.size())
|
||||
writeBytes(1, 0x55);
|
||||
|
||||
auto fluxmap = std::make_unique<Fluxmap>();
|
||||
fluxmap->appendBits(_bits,
|
||||
calculatePhysicalClockPeriod(
|
||||
clockRateUs * 1e3, _config.rotational_period_ms() * 1e6));
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const Tids990EncoderProto& _config;
|
||||
std::vector<bool> _bits;
|
||||
unsigned _cursor;
|
||||
bool _lastBit;
|
||||
};
|
||||
|
||||
std::unique_ptr<Encoder> createTids990Encoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Encoder>(new Tids990Encoder(config));
|
||||
}
|
||||
18
arch/tids990/tids990.h
Normal file
18
arch/tids990/tids990.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef TIDS990_H
|
||||
#define TIDS990_H
|
||||
|
||||
#define TIDS990_PAYLOAD_SIZE 288 /* bytes */
|
||||
#define TIDS990_SECTOR_RECORD_SIZE 10 /* bytes */
|
||||
#define TIDS990_DATA_RECORD_SIZE (TIDS990_PAYLOAD_SIZE + 4) /* bytes */
|
||||
|
||||
class Encoder;
|
||||
class Decoder;
|
||||
class DecoderProto;
|
||||
class EncoderProto;
|
||||
|
||||
extern std::unique_ptr<Decoder> createTids990Decoder(
|
||||
const DecoderProto& config);
|
||||
extern std::unique_ptr<Encoder> createTids990Encoder(
|
||||
const EncoderProto& config);
|
||||
|
||||
#endif
|
||||
25
arch/tids990/tids990.proto
Normal file
25
arch/tids990/tids990.proto
Normal file
@@ -0,0 +1,25 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "lib/config/common.proto";
|
||||
|
||||
message Tids990DecoderProto {}
|
||||
|
||||
message Tids990EncoderProto {
|
||||
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_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" ];
|
||||
optional int32 am2_byte = 5 [ default = 0x2245,
|
||||
(help) = "16-bit RAW bit pattern to use for the AM2 ID byte" ];
|
||||
optional int32 gap1_bytes = 6 [ default = 80,
|
||||
(help) = "size of gap 1 (the post-index gap)" ];
|
||||
optional int32 gap2_bytes = 7 [ default = 21,
|
||||
(help) = "size of gap 2 (the post-ID gap)" ];
|
||||
optional int32 gap3_bytes = 8 [ default = 51,
|
||||
(help) = "size of gap 3 (the post-data or format gap)" ];
|
||||
}
|
||||
|
||||
@@ -1,33 +1,34 @@
|
||||
#include "globals.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "protocol.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "sector.h"
|
||||
#include "victor9k.h"
|
||||
#include "crc.h"
|
||||
#include "bytes.h"
|
||||
#include "track.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "arch/victor9k/victor9k.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
const FluxPattern SECTOR_RECORD_PATTERN(32, VICTOR9K_SECTOR_RECORD);
|
||||
const FluxPattern DATA_RECORD_PATTERN(32, VICTOR9K_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;
|
||||
};
|
||||
}
|
||||
|
||||
static Bytes decode(const std::vector<bool>& bits)
|
||||
{
|
||||
@@ -39,11 +40,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++;
|
||||
}
|
||||
|
||||
uint8_t decoded = decode_data_gcr(inputfifo);
|
||||
@@ -54,58 +55,65 @@ static Bytes decode(const std::vector<bool>& bits)
|
||||
return output;
|
||||
}
|
||||
|
||||
AbstractDecoder::RecordType Victor9kDecoder::advanceToNextRecord()
|
||||
class Victor9kDecoder : public Decoder
|
||||
{
|
||||
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;
|
||||
}
|
||||
public:
|
||||
Victor9kDecoder(const DecoderProto& config): Decoder(config) {}
|
||||
|
||||
void Victor9kDecoder::decodeSectorRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
return seekToPattern(ANY_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
/* Check the ID. */
|
||||
|
||||
if (readRaw32() != VICTOR9K_SECTOR_RECORD)
|
||||
return;
|
||||
|
||||
/* Read header. */
|
||||
|
||||
auto bytes = decode(readRawBits(3 * 10)).slice(0, 3);
|
||||
|
||||
uint8_t rawTrack = bytes[0];
|
||||
_sector->logicalSector = bytes[1];
|
||||
uint8_t gotChecksum = bytes[2];
|
||||
|
||||
_sector->logicalCylinder = rawTrack & 0x7f;
|
||||
_sector->logicalHead = rawTrack >> 7;
|
||||
uint8_t wantChecksum = bytes[0] + bytes[1];
|
||||
if ((_sector->logicalSector > 20) || (_sector->logicalCylinder > 85) ||
|
||||
(_sector->logicalHead > 1))
|
||||
return;
|
||||
|
||||
if (wantChecksum == gotChecksum)
|
||||
_sector->status =
|
||||
Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
/* Check the ID. */
|
||||
|
||||
if (readRaw32() != VICTOR9K_DATA_RECORD)
|
||||
return;
|
||||
|
||||
/* Read data. */
|
||||
|
||||
auto bytes = decode(readRawBits((VICTOR9K_SECTOR_LENGTH + 4) * 10))
|
||||
.slice(0, VICTOR9K_SECTOR_LENGTH + 4);
|
||||
ByteReader br(bytes);
|
||||
|
||||
_sector->data = br.read(VICTOR9K_SECTOR_LENGTH);
|
||||
uint16_t gotChecksum = sumBytes(_sector->data);
|
||||
uint16_t wantChecksum = br.read_le16();
|
||||
_sector->status =
|
||||
(gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createVictor9kDecoder(const DecoderProto& config)
|
||||
{
|
||||
/* Skip the sync marker bit. */
|
||||
readRawBits(23);
|
||||
|
||||
/* Read header. */
|
||||
|
||||
auto bytes = decode(readRawBits(4*10)).slice(0, 4);
|
||||
|
||||
uint8_t rawTrack = bytes[1];
|
||||
_sector->logicalSector = bytes[2];
|
||||
uint8_t gotChecksum = bytes[3];
|
||||
|
||||
_sector->logicalTrack = rawTrack & 0x7f;
|
||||
_sector->logicalSide = rawTrack >> 7;
|
||||
uint8_t wantChecksum = bytes[1] + bytes[2];
|
||||
if ((_sector->logicalSector > 20) || (_sector->logicalTrack > 85) || (_sector->logicalSide > 1))
|
||||
return;
|
||||
|
||||
if (wantChecksum == gotChecksum)
|
||||
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
void Victor9kDecoder::decodeDataRecord()
|
||||
{
|
||||
/* Skip the sync marker bit. */
|
||||
readRawBits(23);
|
||||
|
||||
/* Read data. */
|
||||
|
||||
auto bytes = decode(readRawBits((VICTOR9K_SECTOR_LENGTH+5)*10))
|
||||
.slice(0, VICTOR9K_SECTOR_LENGTH+5);
|
||||
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();
|
||||
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
return std::unique_ptr<Decoder>(new Victor9kDecoder(config));
|
||||
}
|
||||
|
||||
209
arch/victor9k/encoder.cc
Normal file
209
arch/victor9k/encoder.cc
Normal file
@@ -0,0 +1,209 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/core/utils.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/encoders/encoders.h"
|
||||
#include "arch/victor9k/victor9k.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/image.h"
|
||||
#include "fmt/format.h"
|
||||
#include "arch/victor9k/victor9k.pb.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include <ctype.h>
|
||||
#include "lib/core/bytes.h"
|
||||
|
||||
static bool lastBit;
|
||||
|
||||
static void write_zero_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, unsigned count)
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_one_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, unsigned count)
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
{
|
||||
ByteReader br(bytes);
|
||||
BitReader bitr(br);
|
||||
|
||||
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
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void write_byte(std::vector<bool>& bits, unsigned& cursor, uint8_t b)
|
||||
{
|
||||
write_bits(bits, cursor, encode_data_gcr(b >> 4), 5);
|
||||
write_bits(bits, cursor, encode_data_gcr(b), 5);
|
||||
}
|
||||
|
||||
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.logicalCylinder | (sector.logicalHead << 7);
|
||||
uint8_t encodedSector = sector.logicalSector;
|
||||
write_bytes(bits,
|
||||
cursor,
|
||||
Bytes{
|
||||
encodedTrack,
|
||||
encodedSector,
|
||||
(uint8_t)(encodedTrack + encodedSector),
|
||||
});
|
||||
|
||||
write_gap(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);
|
||||
|
||||
write_bytes(bits, cursor, sector.data);
|
||||
|
||||
Bytes checksum(2);
|
||||
checksum.writer().write_le16(sumBytes(sector.data));
|
||||
write_bytes(bits, cursor, checksum);
|
||||
write_gap(bits, cursor, trackdata.post_data_gap_bits());
|
||||
}
|
||||
|
||||
class Victor9kEncoder : public Encoder
|
||||
{
|
||||
public:
|
||||
Victor9kEncoder(const EncoderProto& config):
|
||||
Encoder(config),
|
||||
_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;
|
||||
|
||||
trackdata.MergeFrom(f);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
Victor9kEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, ltl.logicalCylinder, ltl.logicalHead);
|
||||
|
||||
unsigned bitsPerRevolution = (trackdata.rotational_period_ms() * 1e3) /
|
||||
trackdata.clock_period_us();
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
nanoseconds_t clockPeriod =
|
||||
calculatePhysicalClockPeriod(trackdata.clock_period_us() * 1e3,
|
||||
trackdata.rotational_period_ms() * 1e6);
|
||||
unsigned cursor = 0;
|
||||
|
||||
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("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, clockPeriod);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const Victor9kEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<Encoder> createVictor9kEncoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<Encoder>(new Victor9kEncoder(config));
|
||||
}
|
||||
|
||||
// vim: sw=4 ts=4 et
|
||||
@@ -1,22 +1,26 @@
|
||||
#ifndef VICTOR9K_H
|
||||
#define VICTOR9K_H
|
||||
|
||||
#define VICTOR9K_SECTOR_RECORD 0xfffffeab
|
||||
#define VICTOR9K_DATA_RECORD 0xfffffea4
|
||||
class Encoder;
|
||||
class Decoder;
|
||||
class EncoderProto;
|
||||
class DecoderProto;
|
||||
|
||||
/* ... 1101 0101 0111
|
||||
* ^^ ^^^^ ^^^^ ten bit IO byte */
|
||||
#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
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
|
||||
class Victor9kDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~Victor9kDecoder() {}
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
void decodeDataRecord();
|
||||
};
|
||||
extern std::unique_ptr<Decoder> createVictor9kDecoder(
|
||||
const DecoderProto& config);
|
||||
extern std::unique_ptr<Encoder> createVictor9kEncoder(
|
||||
const EncoderProto& config);
|
||||
|
||||
#endif
|
||||
|
||||
36
arch/victor9k/victor9k.proto
Normal file
36
arch/victor9k/victor9k.proto
Normal file
@@ -0,0 +1,36 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "lib/config/common.proto";
|
||||
|
||||
message Victor9kDecoderProto {}
|
||||
|
||||
// NEXT: 12
|
||||
message Victor9kEncoderProto
|
||||
{
|
||||
message TrackdataProto
|
||||
{
|
||||
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 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" ];
|
||||
}
|
||||
|
||||
repeated TrackdataProto trackdata = 1;
|
||||
}
|
||||
@@ -1,48 +1,56 @@
|
||||
#include "globals.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "protocol.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "sector.h"
|
||||
#include "zilogmcz.h"
|
||||
#include "bytes.h"
|
||||
#include "crc.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "arch/zilogmcz/zilogmcz.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "lib/core/crc.h"
|
||||
#include "fmt/format.h"
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
static const FluxPattern SECTOR_START_PATTERN(16, 0xaaab);
|
||||
|
||||
AbstractDecoder::RecordType ZilogMczDecoder::advanceToNextRecord()
|
||||
class ZilogMczDecoder : public Decoder
|
||||
{
|
||||
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;
|
||||
}
|
||||
public:
|
||||
ZilogMczDecoder(const DecoderProto& config): Decoder(config) {}
|
||||
|
||||
void ZilogMczDecoder::decodeSectorRecord()
|
||||
nanoseconds_t advanceToNextRecord() override
|
||||
{
|
||||
seekToIndexMark();
|
||||
return seekToPattern(SECTOR_START_PATTERN);
|
||||
}
|
||||
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
readRawBits(14);
|
||||
|
||||
auto rawbits = readRawBits(140 * 16);
|
||||
auto bytes = decodeFmMfm(rawbits).slice(0, 140);
|
||||
ByteReader br(bytes);
|
||||
|
||||
_sector->logicalSector = br.read_8() & 0x1f;
|
||||
_sector->logicalHead = 0;
|
||||
_sector->logicalCylinder = br.read_8() & 0x7f;
|
||||
if (_sector->logicalSector > 31)
|
||||
return;
|
||||
if (_sector->logicalCylinder > 80)
|
||||
return;
|
||||
|
||||
_sector->data = br.read(132);
|
||||
uint16_t wantChecksum = br.read_be16();
|
||||
uint16_t gotChecksum = crc16(MODBUS_POLY, 0x0000, bytes.slice(0, 134));
|
||||
|
||||
_sector->status =
|
||||
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Decoder> createZilogMczDecoder(const DecoderProto& config)
|
||||
{
|
||||
readRawBits(14);
|
||||
|
||||
auto rawbits = readRawBits(140*16);
|
||||
auto bytes = decodeFmMfm(rawbits).slice(0, 140);
|
||||
ByteReader br(bytes);
|
||||
|
||||
_sector->logicalSector = br.read_8() & 0x1f;
|
||||
_sector->logicalSide = 0;
|
||||
_sector->logicalTrack = br.read_8() & 0x7f;
|
||||
if (_sector->logicalSector > 31)
|
||||
return;
|
||||
if (_sector->logicalTrack > 80)
|
||||
return;
|
||||
|
||||
_sector->data = br.read(132);
|
||||
uint16_t wantChecksum = br.read_be16();
|
||||
uint16_t gotChecksum = crc16(MODBUS_POLY, 0x0000, bytes.slice(0, 134));
|
||||
|
||||
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
return std::unique_ptr<Decoder>(new ZilogMczDecoder(config));
|
||||
}
|
||||
|
||||
@@ -1,18 +1,7 @@
|
||||
#ifndef ZILOGMCZ_H
|
||||
#define ZILOGMCZ_H
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
|
||||
class ZilogMczDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~ZilogMczDecoder() {}
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
};
|
||||
extern std::unique_ptr<Decoder> createZilogMczDecoder(
|
||||
const DecoderProto& config);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
4
arch/zilogmcz/zilogmcz.proto
Normal file
4
arch/zilogmcz/zilogmcz.proto
Normal file
@@ -0,0 +1,4 @@
|
||||
syntax = "proto2";
|
||||
|
||||
message ZilogMczDecoderProto {}
|
||||
|
||||
121
build.py
Normal file
121
build.py
Normal file
@@ -0,0 +1,121 @@
|
||||
from build.ab import export
|
||||
from build.c import clibrary, cxxlibrary
|
||||
from build.protobuf import proto, protocc
|
||||
from build.pkg import package
|
||||
from build.utils import test
|
||||
from glob import glob
|
||||
import config
|
||||
import re
|
||||
|
||||
# Hack for building on Fedora/WSL; executables get the .exe extension,
|
||||
# but the build system detects it as Linux.
|
||||
import build.toolchain
|
||||
|
||||
toolchain.Toolchain.EXE = "$(EXT)"
|
||||
|
||||
package(name="protobuf_lib", package="protobuf")
|
||||
package(name="z_lib", package="zlib")
|
||||
package(name="fmt_lib", package="fmt", fallback="dep/fmt")
|
||||
package(name="sqlite3_lib", package="sqlite3")
|
||||
|
||||
clibrary(name="protocol", hdrs={"protocol.h": "./protocol.h"})
|
||||
|
||||
corpustests = []
|
||||
if not glob("../fluxengine-testdata/data"):
|
||||
print("fluxengine-testdata not found; skipping corpus tests")
|
||||
else:
|
||||
corpus = [
|
||||
("acorndfs", "", "--200"),
|
||||
("agat", "", ""),
|
||||
("amiga", "", ""),
|
||||
("apple2", "", "--140 --drivetype=40"),
|
||||
("atarist", "", "--360"),
|
||||
("atarist", "", "--370"),
|
||||
("atarist", "", "--400"),
|
||||
("atarist", "", "--410"),
|
||||
("atarist", "", "--720"),
|
||||
("atarist", "", "--740"),
|
||||
("atarist", "", "--800"),
|
||||
("atarist", "", "--820"),
|
||||
("bk", "", ""),
|
||||
("brother", "", "--120 --drivetype=40"),
|
||||
("brother", "", "--240"),
|
||||
(
|
||||
"commodore",
|
||||
"scripts/commodore1541_test.textpb",
|
||||
"--171 --drivetype=40",
|
||||
),
|
||||
(
|
||||
"commodore",
|
||||
"scripts/commodore1541_test.textpb",
|
||||
"--192 --drivetype=40",
|
||||
),
|
||||
("commodore", "", "--800"),
|
||||
("commodore", "", "--1620"),
|
||||
("hplif", "", "--264"),
|
||||
("hplif", "", "--608"),
|
||||
("hplif", "", "--616"),
|
||||
("hplif", "", "--770"),
|
||||
("ibm", "", "--1200"),
|
||||
("ibm", "", "--1232"),
|
||||
("ibm", "", "--1440"),
|
||||
("ibm", "", "--1680"),
|
||||
("ibm", "", "--180 --drivetype=40"),
|
||||
("ibm", "", "--160 --drivetype=40"),
|
||||
("ibm", "", "--320 --drivetype=40"),
|
||||
("ibm", "", "--360 --drivetype=40"),
|
||||
("ibm", "", "--720_96"),
|
||||
("ibm", "", "--720_135"),
|
||||
("mac", "scripts/mac400_test.textpb", "--400"),
|
||||
("mac", "scripts/mac800_test.textpb", "--800"),
|
||||
("n88basic", "", ""),
|
||||
("rx50", "", ""),
|
||||
("tartu", "", "--390 --drivetype=40"),
|
||||
("tartu", "", "--780"),
|
||||
("tids990", "", ""),
|
||||
("victor9k", "", "--612"),
|
||||
("victor9k", "", "--1224"),
|
||||
]
|
||||
|
||||
for c in corpus:
|
||||
name = re.sub(r"[^a-zA-Z0-9]", "_", "".join(c), 0)
|
||||
corpustests += [
|
||||
test(
|
||||
name=f"corpustest_{name}_{format}",
|
||||
ins=["src+fluxengine"],
|
||||
deps=["scripts/encodedecodetest.sh"],
|
||||
commands=[
|
||||
"$[deps[0]] "
|
||||
+ c[0]
|
||||
+ " "
|
||||
+ format
|
||||
+ " $[ins[0]] '"
|
||||
+ c[1]
|
||||
+ "' '"
|
||||
+ c[2]
|
||||
+ "' $[dirname(filenameof(outs[0]))] > /dev/null"
|
||||
],
|
||||
label="CORPUSTEST",
|
||||
)
|
||||
for format in ["scp", "flux"]
|
||||
]
|
||||
|
||||
export(
|
||||
name="all",
|
||||
items={
|
||||
"fluxengine$(EXT)": "src+fluxengine",
|
||||
"fluxengine-gui$(EXT)": "src/gui2",
|
||||
"brother120tool$(EXT)": "tools+brother120tool",
|
||||
"brother240tool$(EXT)": "tools+brother240tool",
|
||||
"upgrade-flux-file$(EXT)": "tools+upgrade-flux-file",
|
||||
}
|
||||
| (
|
||||
{
|
||||
"FluxEngine.pkg": "src/gui2+fluxengine_pkg",
|
||||
"FluxEngine.app.zip": "src/gui2+fluxengine_app_zip",
|
||||
}
|
||||
if config.osx
|
||||
else {}
|
||||
),
|
||||
deps=["tests", "src/formats+docs", "scripts+mkdocindex"] + corpustests,
|
||||
)
|
||||
19
build/_objectify.py
Normal file
19
build/_objectify.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import sys
|
||||
from functools import partial
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
sys.exit("Usage: %s <file> <symbol>" % sys.argv[0])
|
||||
filename = sys.argv[1]
|
||||
symbol = sys.argv[2]
|
||||
|
||||
print("const uint8_t " + symbol + "[] = {")
|
||||
n = 0
|
||||
with open(filename, "rb") as in_file:
|
||||
for c in iter(partial(in_file.read, 1), b""):
|
||||
print("0x%02X," % ord(c), end="")
|
||||
n += 1
|
||||
if n % 16 == 0:
|
||||
print()
|
||||
print("};")
|
||||
|
||||
print("const size_t " + symbol + "_len = sizeof(" + symbol + ");")
|
||||
49
build/_sandbox.py
Normal file
49
build/_sandbox.py
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
from os.path import *
|
||||
import argparse
|
||||
import os
|
||||
import shutil
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-s", "--sandbox")
|
||||
parser.add_argument("-v", "--verbose", action="store_true")
|
||||
parser.add_argument("-l", "--link", action="store_true")
|
||||
parser.add_argument("-e", "--export", action="store_true")
|
||||
parser.add_argument("files", nargs="*")
|
||||
args = parser.parse_args()
|
||||
|
||||
assert args.sandbox, "You must specify a sandbox directory"
|
||||
assert args.link ^ args.export, "You can't link and export at the same time"
|
||||
|
||||
if args.link:
|
||||
os.makedirs(args.sandbox, exist_ok=True)
|
||||
for f in args.files:
|
||||
sf = join(args.sandbox, f)
|
||||
if args.verbose:
|
||||
print("link", sf)
|
||||
os.makedirs(dirname(sf), exist_ok=True)
|
||||
try:
|
||||
os.symlink(abspath(f), sf)
|
||||
except PermissionError:
|
||||
shutil.copy(f, sf)
|
||||
|
||||
if args.export:
|
||||
for f in args.files:
|
||||
sf = join(args.sandbox, f)
|
||||
if args.verbose:
|
||||
print("export", sf)
|
||||
df = dirname(f)
|
||||
if df:
|
||||
os.makedirs(df, exist_ok=True)
|
||||
|
||||
try:
|
||||
os.remove(f)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
os.rename(sf, f)
|
||||
|
||||
|
||||
main()
|
||||
25
build/_zip.py
Executable file
25
build/_zip.py
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
from os.path import *
|
||||
import argparse
|
||||
import os
|
||||
from zipfile import ZipFile
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-z", "--zipfile")
|
||||
parser.add_argument("-v", "--verbose", action="store_true")
|
||||
parser.add_argument("-f", "--file", nargs=2, action="append")
|
||||
args = parser.parse_args()
|
||||
|
||||
assert args.zipfile, "You must specify a zipfile to create"
|
||||
|
||||
with ZipFile(args.zipfile, mode="w") as zf:
|
||||
for zipname, filename in args.file:
|
||||
if args.verbose:
|
||||
print(filename, "->", zipname)
|
||||
zf.write(filename, arcname=zipname)
|
||||
|
||||
|
||||
main()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user