mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	Compare commits
	
		
			1510 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 34fe6f0a5f | ||
|  | 76c9674f3f | ||
|  | addbabd123 | ||
|  | 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 | ||
|  | ce1daf6a2b | ||
|  | 08c1671f21 | ||
|  | 3b95e56418 | ||
|  | ce5fcaf172 | 
							
								
								
									
										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' | ||||
| ... | ||||
							
								
								
									
										143
									
								
								.github/workflows/ccpp.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										143
									
								
								.github/workflows/ccpp.yml
									
									
									
									
										vendored
									
									
								
							| @@ -2,54 +2,125 @@ name: C/C++ CI | ||||
|  | ||||
| on: [push] | ||||
|  | ||||
| concurrency:  | ||||
|   group: environment-${{ github.head_ref }} | ||||
|   cancel-in-progress: true | ||||
|  | ||||
| jobs: | ||||
|   build-linux: | ||||
|     runs-on: ubuntu-latest | ||||
|     runs-on: ubuntu-22.04 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v1 | ||||
|     - uses: actions/checkout@v4 | ||||
|       with: | ||||
|         fetch-depth: 1 | ||||
|         repository: 'davidgiven/fluxengine' | ||||
|         path: 'fluxengine' | ||||
|     - uses: actions/checkout@v4 | ||||
|       with: | ||||
|         repository: 'davidgiven/fluxengine-testdata' | ||||
|         path: 'fluxengine-testdata' | ||||
|     - name: apt | ||||
|       run: sudo apt update && sudo apt install libudev-dev libsqlite3-dev ninja-build protobuf-compiler | ||||
|       run: | | ||||
|         sudo apt install libudev-dev libsqlite3-dev protobuf-compiler libwxgtk3.0-gtk3-dev libfmt-dev libprotobuf-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-13, macos-latest] | ||||
|     runs-on: ${{ matrix.runs-on }} | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v4 | ||||
|       with: | ||||
|         fetch-depth: 1 | ||||
|         repository: 'davidgiven/fluxengine' | ||||
|         path: 'fluxengine' | ||||
|     - uses: actions/checkout@v4 | ||||
|       with: | ||||
|         repository: 'davidgiven/fluxengine-testdata' | ||||
|         path: 'fluxengine-testdata' | ||||
|     - name: brew | ||||
|       run: brew install sqlite pkg-config libusb ninja protobuf truncate | ||||
|       run: | | ||||
|         brew install sqlite pkg-config libusb protobuf wxwidgets fmt make coreutils dylibbundler libjpeg | ||||
|     - 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 | ||||
|     defaults: | ||||
|       run: | ||||
|         shell: msys2 {0} | ||||
|     steps: | ||||
|     - uses: msys2/setup-msys2@v2 | ||||
|       with: | ||||
|         update: true | ||||
|         msystem: MINGW32 | ||||
|         install: >- | ||||
|           make | ||||
|           ninja | ||||
|           mingw-w64-i686-libusb | ||||
|           mingw-w64-i686-sqlite3 | ||||
|           mingw-w64-i686-zlib | ||||
|           mingw-w64-i686-gcc | ||||
|           zip | ||||
|           mingw-w64-i686-protobuf | ||||
|           vim | ||||
|           diffutils | ||||
|     - uses: actions/checkout@v1 | ||||
|       with: | ||||
|         fetch-depth: 1 | ||||
|     - name: build | ||||
|       run: | | ||||
|         make | ||||
|  | ||||
|     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' | ||||
|  | ||||
|     - uses: actions/checkout@v4 | ||||
|       with: | ||||
|         repository: 'davidgiven/fluxengine-testdata' | ||||
|         path: 'fluxengine-testdata' | ||||
|  | ||||
|     - name: run | ||||
|       run: | | ||||
|         wsl sh -c 'make -C fluxengine 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: Upload build artifacts | ||||
|       uses: actions/upload-artifact@v4 | ||||
|       with: | ||||
|         name: ${{ github.event.repository.name }}.${{ github.sha }}.windows.zip | ||||
|         path: fluxengine/fluxengine-windows.zip | ||||
|   | ||||
							
								
								
									
										120
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										120
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,5 +1,9 @@ | ||||
| name: Autorelease | ||||
|  | ||||
| concurrency:  | ||||
|   group: environment-release-${{ github.head_ref }} | ||||
|   cancel-in-progress: true | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
| @@ -8,43 +12,57 @@ on: | ||||
| jobs: | ||||
|   dev-release: | ||||
|     runs-on: windows-latest | ||||
|     defaults: | ||||
|       run: | ||||
|         shell: msys2 {0} | ||||
|  | ||||
|     steps: | ||||
|     - uses: msys2/setup-msys2@v2 | ||||
|       with: | ||||
|         update: true | ||||
|         msystem: MINGW32 | ||||
|         install: >- | ||||
|           make | ||||
|           ninja | ||||
|           mingw-w64-i686-libusb | ||||
|           mingw-w64-i686-sqlite3 | ||||
|           mingw-w64-i686-zlib | ||||
|           mingw-w64-i686-gcc | ||||
|           zip | ||||
|           mingw-w64-i686-protobuf | ||||
|           vim | ||||
|           diffutils | ||||
|     - uses: actions/checkout@v2 | ||||
|       with: | ||||
|         fetch-depth: 1 | ||||
|     - name: build | ||||
|     - name: setup WSL | ||||
|       run: | | ||||
|         make | ||||
|         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: | | ||||
|         zip -9 fluxengine.zip fluxengine.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex | ||||
|         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: | ||||
| @@ -52,13 +70,65 @@ jobs: | ||||
|         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.zip | ||||
|           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 | ||||
|   | ||||
| @@ -1,250 +1,250 @@ | ||||
| :4000000000800020110000009D1000009D100000064A08B5136843F020031360044B1A6803F53F5302331A6001F056F8E8460040FA46004010B5054C237833B9044B13B17D | ||||
| :400040000448AFF300800123237010BD6881FF1F00000000E8380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF0000000021 | ||||
| :400080006C81FF1FE8380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8881FF1F9C | ||||
| :4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8881FF1F3F000080114BDA68196919B9FD | ||||
| :4001000001221A75597514E09969521A19698A4294BF587D00201875187D094908B1002204E0086982428CBF002201224A75DA689A611B7D13B1002002F056B9704700BF60 | ||||
| :400140008881FF1F10B5C4B2204601F087F90128FAD110BD08B572B60F4B0F49DA680132DA60DA690132C82A08BF0022DA611A6AD8690132A72A08BF00220A621B6A002BE5 | ||||
| :400180000CBF02230023002814BF184643F0010002F092FE62B608BD8881FF1F38B50446C5B2284602F0C2F8062002F0DFFA44F00200C0B202F0BAF8062002F0D7FA2846BD | ||||
| :4001C00002F0B4F8BDE83840062002F0B9BA10B5642402F0A5F828B9FFF7E0FF013CF8D1204610BD012010BD70B5C4B2054620460E4601F033F9012805D0204601F04CFAA8 | ||||
| :400200002846FFF79FFF204601F030F9314605460246204601F0ECF9204601F01FF90028FAD1284670BD000038B5044D0024285D013402F04BFA402CF9D138BDAC81FF1F30 | ||||
| :4002400008B502F065FC002002F06EFC02F080FC02F08AFC80B208BD10B50446012002F07DF8642002F06CFAFFF7EAFF2080002002F074F8642002F063FAFFF7E1FF60801C | ||||
| :4002800010BD08B502F070FD002002F079FD02F08BFD02F095FD80B208BD10B50446FFF796FF322002F04CFAFFF7EBFF20800120FFF774FF322002F043FAFFF7E2FF608072 | ||||
| :4002C00010BD0FB400B593B014AB53F8042B402102A8019302F0F0FE02A802F08CF802F096F813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF723FF6278E6 | ||||
| :400300002146BDE81040042001F000B9043A000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8643043B1002001F0EAFF85 | ||||
| :40034000002002F07DFD002384F8643010BD00BF8881FF1F38B5124D837895F8672004469A4204D0FFF7E4FF002385F86A302368C5F8653022790B4B1A71A378002B14BF31 | ||||
| :400380000220012002F05CFDE078B0FA80F0400902F050FD2079BDE8384002F087BD00BF8881FF1FED81FF1F38B50D4C94F8645065B904F16500FFF7CDFF012001F0AAFFDC | ||||
| :4003C0004FF47A7002F0BCF984F86A50E368E366012384F86430BDE8384002F0EFB900BF8881FF1FF8B5214C0546FFF7DDFF94F86A3003B15DB91E48FFF763FFFFF7E7FECE | ||||
| :400400000120002384F86A00236702F0AFF92A46216F1848FFF755FF144E0027236F9D4216D001F07DFF00B13767236F9D4205DD0120FFF7B3FE336F013305E005DA002053 | ||||
| :40044000FFF7ACFE336F013B336702F0B7F9E5E7322002F075F92A2DCCBF0020012002F031FDBDE8F8400448FFF72BBF8881FF1F113A0000183A0000353A00002DE9F04F70 | ||||
| :4004800099B062B602F004FA9E49042002F028FA9D4801F051FF9D4802F0F4FC9C4801F085FF02F0D5FB02F0A7FA002002F0C8FC01F0A0FF0221002000F086FF954C012011 | ||||
| :4004C00001F0FEF8002384F86730FFF76DFFFFF77EFE84F87400FFF72BFF012384F86730FFF762FFFFF773FE84F87500FFF720FF894B94F87400894994F875202546002AA4 | ||||
| :4005000014BF0A461A46002808BF19468448FFF7D8FE0321084602F031F9264602F04EF994F8643043B1EA6EEB689B1A41F28832934201D9FFF7FCFE00F07EFF18B9794820 | ||||
| :40054000FFF7BFFE04E000F07DFF0028F7D10BE000F072FF10B902F031F9F9E77248FFF7B0FE032001F098F8032000F077FF0128D4D16E48FFF7EEFE6D490320FFF734FE85 | ||||
| :4005800094F876106B48FFF79CFE94F87630023B142B00F2D683DFE813F01500D4031E00D4032400D4035000D4037600D403D900D403C101D4030803D4032C03D40333036C | ||||
| :4005C000D4034D0303238DF820308DF821300F238DF822302AE394F87800FFF703FF564B21E340F2DC57FFF7DFFE00232375E068227D02F0FF012AB9EB681B1ABB42F7DD44 | ||||
| :400600000B4611E083B10022174696F87810F068277594F814E0BEF1000F02D1EB681B1AF7E701329142F3DA07228DF8202004228DF82120ADF82230F8E20220FFF782FD2F | ||||
| :400640004FF000080DF1200A02F0B8F84FF480790027C9EB0803DA1907F80A200137402FF9D10220FFF76EFD3A465146022000F04DFFB9F10109EBD108F10108B8F1400F58 | ||||
| :40068000E2D12E4B38E04FF0010A4FF000080DF1200B02F093F84FF0000959460120FFF7A3FD08EB090300270493049B1BF807203B44DBB29A4209D08DE80C0041463B4641 | ||||
| :4006C0004A461F48FFF7FDFD4FF0000A0137402FEBD109F10109B9F5807FDED108F10108B8F1400FD5D151461648FFF7EAFDBAF1000F00F01B81144B1B8807A8ADF81C30AD | ||||
| :4007000095E200BF55010000F900000091000000C50000008881FF1F473A0000433A00004A3A0000623A0000753A0000ED81FF1FFE81FF1F7F3A0000EC380000EE380000BE | ||||
| :400740008E3A0000AA3A0000F0380000206FFFF749FE94F8780001F001FE94F8780001F0E5FD02F015FCB94BDFF8FC821A78002702F0FB021A701A7842F001021A701A789D | ||||
| :4007800002F0FE021A701A7802F0FE021A7002F003FC0220FFF7D6FC012141F6FF734FF48042084602F052FB84F8B60001F074FF08F807000137402FF8D1DFF8B0A200271A | ||||
| :4007C0000AF195091FFA89F80137402F14BF3A4600221AF8010F2244062392F82420402101F08EFF424646F24B419AF8000001F099FF08F14008402F1FFA88F8E4D196F859 | ||||
| :40080000793053B196F87C30336100233375237D002BFCD000233375336100234FF0FF32236062602372236894F8B600234493F8241001F0E9FE94F8B60001F0A7FE01214A | ||||
| :4008400094F8B60001F07AFE2368002BFCD0002398467360D6F80CA0012701F0AFFFE368B4F87A20CAEB030393420DD367B1042195F8B60001F0D4FE94F8B60001F0E0FEE1 | ||||
| :400880000028F9D107463072237AFBB96A682B689A4202D1002FE0D118E00220FFF752FC6968402209EB8111022000F02FFE6A68674B01321340002BBEBF03F1FF3363F091 | ||||
| :4008C0003F03013308F101086360C6E70220277AFFF738FC00221146022000F017FE0220FFF730FCFFB2FFF79FFC002001F01EFD37B15848FFF7E5FC0220FFF709FD06E06E | ||||
| :40090000554B08A81B88ADF82030FFF7EFFC227D4146237A5148FFF7D4FC15E25048FFF7D0FCD4F87A7017F03F0701D0032009E2286FFFF757FD95F8780001F00FFD95F829 | ||||
| :40094000780001F0F3FC012001F00EFD02F020FB444BDFF814811A7842F004021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F00FFB01214FF4804315 | ||||
| :4009800041F6FF72084601F0F5FC85F8B60001F083FE08F807000137402FF8D1DFF8CC90002709F195031FFA83F804930137402F14BF3A46002219F8010F2244052392F8D7 | ||||
| :4009C0002420402101F09CFE414646F2495299F8000001F0A7FE08F14008402F1FFA88F8E4D100274FF0FF33376098467360BB463B46D6F87A9037725FEA99190CBF4FF060 | ||||
| :400A0000010A4FF0000A2168114A01310A40002ABCBF02F1FF3262F03F026068B8BF013282426FD02BB1227A002A7AD12A7D002A77D12068049A059302EB8010BAF1000F36 | ||||
| :400A400016D040223F2102F003FB1CE09B6400403F000080B43A0000F2380000CE3A0000E13A000099650040AC81FF1FAB81FF1F014601370120FFF7B7FBC7EB0903D3F10C | ||||
| :400A8000000A4AEB030A2168B34A01310A40002ABEBF02F1FF3262F03F02013222606268059B01322ED12A683F2A2BD14FF00008C5F8048001F08CFC85F808806B6895F8DC | ||||
| :400AC000B6002B4493F8241001F09EFD95F8B60001F05CFD012195F8B60001F02FFD95F87E302B6185F81480237D002BFCD04FF00008012086F8148001F076FC404601F0B4 | ||||
| :400B000033FC00E023B1237A5BB92B7D4BB90123626842453FF477AF0BF1010BD5F8048071E701F05BFC012001F01EFC002001F05BFC042194F8B60001F072FD94F8B60011 | ||||
| :400B400001F07EFD80460028F8D196F8B60001F00BFD337D327A0293012303920193CDF800A05B463A4649467C48FFF7AAFBC6F81080BAF1000F0BD0FFF756FB002001F028 | ||||
| :400B8000D5FB237A63B17648FFF79BFB0220D9E0B945F1D073490120FFF726FB0137F7E77148FFF78EFB714B3DE094F8780001F0D5FB206FFFF716FC6D48FFF782FB94F8A5 | ||||
| :400BC0007930236100232375237D002BFCD0012001F00AFC00233375237D002BFCD0002001F002FC002363483361FFF76AFB624B19E0002084F86A00FFF7F4FB5F4B12E003 | ||||
| :400C000094F8743023B195F875200AB985F8782094F875201AB113B9012385F878305848FFF798FB574B1B88ADF8203008A8FFF75DFB89E0FFF77CFB02F088F8002002F0FA | ||||
| :400C40002BF82A2701F056FF002001F0F9FE3A46002108A802F0FCF917238DF820308DF8217001F0ABFD002001F054FB002002F0E7F8C82001F064FD0DF12200FFF7ECFA35 | ||||
| :400C80000DF13600FFF709FB01F098FD012002F0D7F8322001F054FD0DF12600FFF7DCFA0DF13A00FFF7F9FA012001F033FB4FF4967001F045FD01F081FD0DF12E00FFF7AC | ||||
| :400CC000CBFA0DF14200FFF7E8FA002001F022FB4FF4967001F034FD01F070FD022002F0AFF8322001F02CFD0DEB0700FFF7B4FA0DF13E00FFF7D1FA012001F00BFB4FF4FE | ||||
| :400D0000967001F01DFD01F059FD0DF13200FFF7A3FA0DF14600FFF7C0FA002001F0FAFA4FF4967001F00CFD01F048FD002002F087F8002384F86A3001F08AFF01F05CFE97 | ||||
| :400D400074E70120FFF7E4FA032000F0A5FC0E48FFF7B7FAFFF7E2BB3F000080EB3A00001B3B00004092FF1F253B0000F43800002D3B00003B3B0000F6380000F8380000ED | ||||
| :400D8000FE81FF1FFA380000483B00000F4B1A78120616D55878C0B2012814D11B79DBB2042B02D0052B05D07047094A094B5A60282203E0084A074B5A60E0221A8000F04C | ||||
| :400DC00045BE002070470120704700BF00600040FC380000C492FF1F243900002DE9F04172B68A4B61221A70A3F5F06301221A801924874A9C7092E803008033062283F8F7 | ||||
| :400E0000002283E80300522203F580731A70814B814A1B78814EDBB2137040F61802804B00251A8041F2512223F8022C33784FF4F07003F0010343EA450502F0A1F8013CE3 | ||||
| :400E400005F003052ED0032DF0D1764B4FF480721A8007221A70744A002548211570917002221D705D7103F8032C0422DA716F4A6F4C13786F4E43F00103137012F8013C9A | ||||
| :400E8000062743F0030302F8013C2378012243F0800323705D4B1A70674A137843F02003137000E0FEE707FB056300219A881868013502F0CDF8072DF5D16048604E0025F3 | ||||
| :400EC00050F8041F05F1105303F14A0221F0FF074B33C9B20B4452005B0002329A4206D012F802EC12F801CC0EF807C0F5E7B0420D44E5D1534A00231360936013619361FD | ||||
| :400F0000514B524F1A68524BDFF88C811A60514B1A6803F1784303F5D6431A604E4A137843F002031370137C43F0020313742378A2F5863243F040032370413A137843F047 | ||||
| :400F400010031370454A464B07CA03C31A80454A2833106843F8250C127903F8212C424A07CA03C31A80414AE83B07CA03C31A803F4A083307CA03C31A803E4A3E4BA2F5B0 | ||||
| :400F8000616203CBC2F8100EC2F8141E1378042043F008031370394B02F5AA521B783D78DBB298F80060EDB203F007010C321B091170F6B2537045F003033B7046F0030326 | ||||
| :400FC00088F800302E4B48221A702E4A402313702D49937013729372082382F81F3220220A7048710A72294A0A20137001F0BEFB274B88F8006044223D70264D1A7094E8E4 | ||||
| :401000000F0007C52B80BDE8F08100BF004800401C0B00480F010049A146004025420040224200400440004006400040A2430040A04300404D3B0000E8460040FCFFFF470A | ||||
| :401040009000004800760040240B0048F846004020760040280B004803500140DC0A0048C0510040E80A0048F00A0048FC0A0048080B004832510040140B0048CF01004921 | ||||
| :401080001D51004001590040235B0040585B004076580040B0430040F946004008B501F0ABFF03680C2B00D1FEE7FEE7084908B50B68084A1844821A802A01DC086005E043 | ||||
| :4010C00001F09AFF0C2303604FF0FF33184608BDCC80FF1F9093FF1F80B51148114B0025C0B1A3F1100192C922460439161BB74204D051F8046F42F8046BF7E7114653F88D | ||||
| :40110000046C8C1AA64202D041F8045BF9E701381033E5E701F076FFFFF7B0F9FEE700BF01000000E03C0000124A134B10B51A60124A134C1368134843F40073136000238F | ||||
| :40114000032B98BF54F823204FEA830188BF0E4A0133302B4250F3D10C4B1A780C4B1A700C4B084A1A60FFF739FEBDE8104001F0CFB900BF0004FA050CED00E014ED00E016 | ||||
| :40118000000000000080FF1F9D100000BC760040C080FF1F08ED00E0F8B501F0F9FE4B4A01271378022643F001031370137C484C43F001031374474B02F5E3521F700B3215 | ||||
| :4011C00003F8946C1378054603F07F031370002001F0CCFA2378404A03F0F90323701378384603F0DF03137023783B43237001F0BDFA282001F0BAFA384B30461A7802F089 | ||||
| :401200007F021A701A7802F0BF021A7023783343237001F0ABFA2378314A43F0040323700023137053702F4AFF2199540133092BFBD1284601F0B0FE0721172001F0DEFA5F | ||||
| :401240002949172001F0CCFA0721182001F0D6FA2649182001F0C4FA0721152001F0CEFA2349152001F0BCFA0721052001F0C6FA2049052001F0B4FA0721062001F0BEFA17 | ||||
| :401280001D49062001F0ACFA0721084601F0B6FA1A49072001F0A4FA0721082001F0AEFA1749082001F09CFA0021162001F0A6FA1449162001F094FA07210C2001F09EFA0C | ||||
| :4012C000BDE8F84010490C2001F08ABAA5430040944300409D60004012600040F851004084600040B592FF1F671B0000A1190000651B0000991A0000C51A0000F51A0000BA | ||||
| :401300002D1B00006D1B0000E11B0000214B224A10B5187000231370204A40201370204A0F2413701F4A13701F4A13701F4A13701F4A13701F4B4FF400021A604FF080729F | ||||
| :401340001A604FF400121A6020221A601860802018604FF480701860174804704FF480001860164B1A70933B19B91A7802F0FE0202E01A7842F001021A70114B03221A70D2 | ||||
| :40138000802203F8202C012001F0FAFD0D4B04221A7010BDD092FF1FD692FF1FD492FF1FD592FF1FD192FF1FC092FF1FD392FF1F4893FF1F00E100E09E6000409C600040AF | ||||
| :4013C000286000401260004070B5074C054623780E461BB9FFF7E0FE0123237031462846BDE87040FFF792BF8092FF1F0A4A002313700A4A13700A4A13700A4A13700A4ADE | ||||
| :4014000013700A4A13700A4A13700A4B03221A70802203F8202C7047D692FF1FD492FF1FD592FF1FD192FF1FC092FF1FD392FF1F4893FF1F28600040014B1878704700BFC1 | ||||
| :40144000D592FF1F044B1A7802F0FF001AB118780022C0B21A707047D492FF1F024A0C2303FB002040787047DC92FF1F431E072B0CD8074A064B00010344805C5B7800F0CD | ||||
| :401480000F0043EA0020023880B2704700207047FC5F00401A4A38B50C2303FB00231B79090C13F0800F00F1FF35044619BF8AB24FF480438BB24FF48042032D18D8DFE8AD | ||||
| :4014C00005F002070C110021084600F0FDFF0DE00021084600F0DCFF08E00021084600F0BBFF03E00021084600F09AFF054B1855EDB2072D03D801F0CFF8034B185538BDA0 | ||||
| :40150000DC92FF1FAC92FF1FB592FF1F431E072B2DE9F0470446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB2C3F500739D424FF00C0303FB007388BFD588DB7824 | ||||
| :4015400084BFC5F50075ADB2254A43EA15230601B354B244EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FB047200215170B9F1000F28D03DB31B4F385D00F016 | ||||
| :40158000F3FF11232946FE2218F8040001F0B8F806F5C04278321FFA89F118F8040001F0C1F8124D18F80410385D01F02DF80121385D00F0C3FF735D43F002037355735D92 | ||||
| :4015C00003F0FD037355BDE8F08703FB04746379DBB28AF80230BDE8F08700BFDC92FF1FFC5F0040B592FF1FAC92FF1F706000402DE9F047044615468846002940D0431E54 | ||||
| :40160000072B3FD8FFF732FFA84203D22046FFF72DFF05461D4E335DFF2B03D141462046FFF738FFDFF868A027011AF8040000F09BFF1223FE222946305D01F061F807F557 | ||||
| :40164000C0411FFA88F27831305D01F06BF8DFF84490315D1AF8040000F0D6FF01211AF8040000F06BFF17F8093043F0020307F8093017F8093003F0FD0307F8093002E099 | ||||
| :401680000D4600E000252846BDE8F087B592FF1FAC92FF1F70600040431E072B0AD8064A0C2303FB002300225A705A79034BD2B200011A54704700BFDC92FF1FFE5F0040C3 | ||||
| :4016C000431E072B9FBF024B000108221A547047FE5F004030B51A4A1A491B4D0878138803449BB21380194A00231488D8B2A4B27CB1082B0CD050680078C0B2E854506895 | ||||
| :401700000133013050601088013880B21080ECE718460B780E4C082B0E4A00D040B10E4D2B7883F080032B700F232370022301E0022323701370094B1870087030BD00BF12 | ||||
| :401740004C93FF1F4893FF1F00600040C492FF1FC192FF1FD692FF1FD292FF1F4993FF1F074B02221A70074B80221A70064B0F221A70064A00231370054A012013707047C2 | ||||
| :40178000D692FF1FD292FF1FC192FF1F4893FF1F4993FF1F30B5164B16491B780A8803F00F03023BDBB21A4492B20A80124C134A0020118889B279B173B15568215C013B85 | ||||
| :4017C000C9B229705168DBB20131516011880130013989B21180ECE7094A1370094A137883F080031370084B0B221A7030BD00BF296000404C93FF1F00600040C492FF1F4C | ||||
| :401800004993FF1FD292FF1FC192FF1F064A06231370064A01201370054B80221A70054B00221A70704700BFD692FF1FC192FF1FD292FF1F4993FF1F054B9A683AB19A682B | ||||
| :40184000044910709A680988518000229A607047C492FF1F4C93FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A137008BD0220FFF7E1FF0D4B1B7803F06003202BFD | ||||
| :4018800005D0402B06D043B900F012FC04E001F087FB01E0FFF77AFA10B9034B03221A7008BD00BF28600040C192FF1F0060004008B5084A084B0120197813880B449BB208 | ||||
| :4018C0001380064B00221A70FFF7B6FF044B03221A7008BD4C93FF1F4893FF1FD692FF1FC192FF1F08B50C4B1B78DBB2042B07D0062B09D0022B0DD1BDE80840FFF7D8BF34 | ||||
| :40190000BDE80840FFF746BF0320FFF795FF034B03221A7008BD00BFD692FF1FC192FF1F08B5054B002201201A70FFF785FF034B03221A7008BD00BFD692FF1FC192FF1FCE | ||||
| :4019400008B50A4B1A7832B11A78094942F080020A7000221A70074B002201201A70FFF76BFF054B03221A7008BD00BFC092FF1F08600040D692FF1FC192FF1F074B1B782C | ||||
| :40198000DBB2042B05D0062B05D0022B05D1FFF7A1BEFFF7C5BFFFF7D3BF7047D692FF1F38B51D4C2378DBB2DD0634D518060AD503F00F03012B2ED1FFF74EFF174B1B782A | ||||
| :4019C000190609D538BD5A0602D5FFF7D7FF03E09D0620D5FFF786FF23781B061BD4104B1A78104B1B7813430F4A13701278934211D10A4A0849154613782078DBB200064D | ||||
| :401A000005D41378DBB20B700B7803F00F0328788342F1D138BD38BD28600040C192FF1FD292FF1F4993FF1F29600040054A00231380054A916819B191680B7092685380CB | ||||
| :401A4000704700BF4C93FF1FC492FF1F0E4808B503889BB213B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FF094AD1799379028843EA012392B2934238BF03806C | ||||
| :401A8000FFF728FE012008BDC492FF1FD692FF1FD292FF1F00600040084B01221A700F3B9B7C074B1A7B02F00302012A1EBFDA7B82F08002DA7301225A7370470B600040D9 | ||||
| :401AC000DC92FF1F094B02221A700F3B93F82230074B1A7E02F00302012A1EBFDA7E82F08002DA7601225A76704700BF0B600040DC92FF1F0B4B04221A700F3B93F83230CF | ||||
| :401B0000094B93F8242002F00302012A1EBF93F8272082F0800283F82720012283F82520704700BF0B600040DC92FF1F0B4B08221A700F3B93F84230094B93F8302002F0F9 | ||||
| :401B40000302012A1EBF93F8332082F0800283F83320012283F83120704700BF0B600040DC92FF1F7047FFF741BC0000F0B5184B184E19780C27C9B201234FF0000C31B372 | ||||
| :401B8000CA0720D5144A4FEA031E7244947850782040C5070DD507FB03652C79240608D5147804F0FE0414706D790C4CEDB204F80E50840706D507FB036425792D0658BF05 | ||||
| :401BC00084F801C090700133DBB24908D7E7F0BD9F600040DC92FF1F70600040FE5F004000F08EBC70B50446184B88B003AA03F11006154618685968083303C5B3422A46B4 | ||||
| :401C0000F7D11B782B70FCB12223237001AD03232846637000F06CFE002220461146AB5C08AC04EB131414F8144C03F00F03847008AC234413F8143C0132082AC170037125 | ||||
| :401C4000417100F10400EAD108B070BD773B00002DE9F0431C4D01222E460C201F274FF0800E4FF0080C194B00FB02581401234418705F70164998F805902144B9F1000F62 | ||||
| :401C800007D098F8044024064CBF887081F802C001E081F802E000FB0261CC880132E4B29C71CC88092AC4F30724DC71CC88E4B21C71C988C1F307215971D4D1054BFF2213 | ||||
| :401CC0001A70BDE8F08300BFDC92FF1F70600040FC5F00400A600040064B074A1B7802EBC30253681A7C824286BF03EBC003586900207047D092FF1F9C3B00002DE9F84F64 | ||||
| :401D0000424B1A78002A7ED01878414D0138C0B2FFF7E2FFA8463F4AC3681478007ADFF800C1E4B203EBC0000C2600274FF0010E834268D01A78A24263D11CF80420597891 | ||||
| :401D400091425ED19A7893F8039002F07F0206FB02FA05EB0A01CF7093F802B009F0030981F804B093F803B005F80AB0B3F804A0A1F808A093F902A0BAF1000F0BDAB9F11C | ||||
| :401D8000010F0CBF4FF007094FF00D0981F8059081F801E009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEA02191A4906FB0282494481F802E0B2F808A0CAF330 | ||||
| :401DC000072A81F800A0B2F808A05FFA8AFA81F801A0B2F806A011495FFA8AFA494481F806A0B2F80690C9F3072981F80790B2F806905FFA89F981F80490D288C2F3072281 | ||||
| :401E00004A71083394E7BDE8F88F00BFD592FF1FDC92FF1FD192FF1FFC5F004070600040C292FF1F08B5064B18780138C0B2FFF753FF20B143681B7900EBC300406908BDDA | ||||
| :401E4000D592FF1F00212DE9F84F0B464E4E0C2707FB01F401313219092933554FF000059370494CD3701381937253705371EFD118B1464B1D70464B1D70464B1A78002AC0 | ||||
| :401E80007FD0187801250138C0B2FFF725FFA8464368DFF8F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010202F0FE021A70027AD20007FB0541C36803EB020993 | ||||
| :401EC0004B4531D093F802A00AF07F06AE4229D10E89B3F804B0B6B25E4538BFA1F808B01E7893F801B01EF80660B3451AD181F804A0DE780E7093F902A0DE78BAF1000F12 | ||||
| :401F000006F0030607DA012E0CBF07260D264E7181F8018006E0012E0CBF052609264E7181F801C00833CBE70135092DC3D1C1680A328B1C0A440C200833934209D013F814 | ||||
| :401F4000081C13F80A5C01F07F0100FB01418D72F2E7FFF767FF114B0121186000230C2000FB0142D3801289013113449BB203F00102134409299BB2F2D1BDE8F84FFFF732 | ||||
| :401F800067BEBDE8F88F00BFDC92FF1FC292FF1F4A93FF1FD592FF1FD392FF1FD892FF1F114B1B7903F07F035A1E072A19D80F490C2202FB031291781B0141F00101917098 | ||||
| :401FC0000021D170517841F002015170127912F0800F074A1A4414BF8D2389239370FFF715BC0020704700BF00600040DC92FF1FFC5F004030B4194B1A7902F07F02531EE1 | ||||
| :40200000072B27D8164B0C2404FB02339978154D01F0FE0199700021D97029461201505D114400F07F0050555A7802F0FD025A701A795B78120605D5012B01D18C7006E077 | ||||
| :402040000D2303E0012B0CBF082309238B7030BCFFF7DCBB002030BC704700BF00600040DC92FF1FFC5F004010B50D4B0D4C21791878C9B20138C0B2FFF72EFE43681B791B | ||||
| :402080008B4201D2012909D8074A0848535CDBB24354A3780120DBB2535410BD002010BDD592FF1F00600040C292FF1F4A93FF1F38B5874A874C13780021DBB221801806BC | ||||
| :4020C000517840F188800A2900F20081DFE811F05800FE00FE00FE00FE00FE000B00FE007900FE007D00D3787949012B09D17A4B1A787A4B03EBC2035B685B6863601223C5 | ||||
| :4021000010E0CB78022B12D18878FFF7E5FD002800F0DC80436863606368DA7863689B7843EA02232380BDE83840FFF78FBCCB78032B21D16A4B00228878D5B2854203D3F6 | ||||
| :40214000634A92783AB910E0187801320028F7D018780344F0E75E4A62499278097C914203D16148FFF73EFD5F4B1A78002A00F0AD801A78228018E0BDE8384000F010BF77 | ||||
| :4021800013F0030313D0022B40F0A0802380504B0C211B7903F07F02544B01FB02339A78534BD2B21A7000225A706360BBE702222280504A11784E4AC9B211705370626062 | ||||
| :4021C000B1E7012323804C4BEFE70123238013794A4A1344E9E701390A2977D8DFE801F037764F76067676760A7620009378444ADBB25AE0937803F0FF0153B93E4B1A7892 | ||||
| :4022000091425FD019703F4B01201870FFF71AFE58E0481EC0B2FFF75FFD0028EED155E0FFF722FF002851D0294A374913791279DBB2D2B20A70354A3049D25CCB5C9A4236 | ||||
| :4022400040D0304B01221A70FFF758FD3AE003F00303012B2BD009D3022B37D11C4B9B78002B33D1BDE83840FFF7C4BE184B9B78012B2BD11F4A137803F0FD0315E003F0E5 | ||||
| :402280000303012B13D008D3022B1FD1104B9B78E3B9BDE83840FFF783BE0D4B9B78012B14D1144A137843F0020313700AE0084B1A795AB998781B791549DBB2CA5C22EAEF | ||||
| :4022C0000002CA54BDE83840FFF7A0BA002038BD00600040C492FF1FD092FF1F9C3B0000003C0000733C00006893FF1FDC92FF1F8192FF1FD392FF1FD592FF1FC292FF1F96 | ||||
| :40230000C092FF1FD492FF1FD192FF1F4A93FF1FD792FF1F014B1870704700BF78650040014B1878704700BF6C640040014B1870704700BF78640040064A0123136002F698 | ||||
| :4023400088321268E0211064034A1170A2F540721360704780E100E000E400E0014B1870704700BF7A650040014B1870704700BF7F64004073B515461E460B4C0523002210 | ||||
| :40238000019200920A4601461846237000F064F932462946207800F01FF90221207800F009F9207802B070BDD080FF1F064A0423136002F688321268E0219064034A117031 | ||||
| :4023C000A2F202321360704780E100E002E400E0014B04221A60704700E100E0014B04221A60704780E100E0014B1870704700BF7E640040704738B505460078012428B1CB | ||||
| :4024000000F066FD285D0134E4B2F8E738BD08B50D2000F05DFDBDE808400A2000F058BDF7B516461F460B4C00230325019300930A4601462846257000F00EF93A46314617 | ||||
| :40244000207800F0C9F80221207800F0B3F8207803B0F0BDE080FF1FF7B516461F460B4C00230225019300930A4601462846257000F0F2F83A463146207800F0ADF829460B | ||||
| :40248000207800F097F8207803B0F0BDE180FF1FF7B516461F460B4C00230125019300930A4601462846257000F0D6F83A463146207800F091F80221207800F07BF8207844 | ||||
| :4024C00003B0F0BDE280FF1F73B515461E460B4C0023019300930A4601461846237000F0BBF832462946207800F076F80221207800F060F8207802B070BD00BFE380FF1FB2 | ||||
| :40250000024B1878C0F38010704700BF8F450040074A7F23802113705170064A013BDBB202F80839002BF9D1034A1370704700BFE480FF1FF87B00400078004017280FD877 | ||||
| :40254000084B0001C25C11B142F0200201E002F0DF02C254C25C42F00102C25400207047012070471070004017280BD8064B0001C25C02F0FE02C254C25C02F0DF02C25490 | ||||
| :4025800000207047012070471070004017280DD8074900010B4603441A7942F004021A71435C43F00103435400207047012070471070004017280BD8064A0001835C4900D2 | ||||
| :4025C00003F0F10301F00E011943815400207047012070471070004041F6FF73994208BF4FF400519A4208BF4FF4005217289FBFC00000F1804000F5EC4081809ABFC28072 | ||||
| :40260000002001207047000017289FBF034B00011954002088BF0120704700BF1970004017289FBF054B00011A5C01F007019DBF1143195400200120704700BF147000408D | ||||
| :4026400017289FBF034B0001185C00F0070088BFFF20704714700040172810B51AD8C00001F07F0100F1804441EAC21204F5EC44D2B222709DF8082003F00F0343EA021304 | ||||
| :40268000DBB263709DF80C30002003F00F03A370E07010BD012010BD10B500F079FC0A4A5378182B0AD91478013B5370E30003F1804303F5F0431B78137000E0FF2400F0DE | ||||
| :4026C0006BFC204610BD00BFE480FF1F030610B5044611D400F05CFC084AE300117803F1804303F5F04319705378147001335370BDE8104000F050BC10BD00BFE480FF1F4C | ||||
| :4027000030B504060CD411F4704509D1C40004F1804404F5F0442180A270E370284630BD012030BD03065FBFC00000F1804000F5F04081805ABFC28000200120704700000C | ||||
| :4027400038B50446084DB4F5004F05D9286800F017FCA4F50044F6E7034B58686043BDE8384000F00DBC00BFEC80FF1F024B1B7A584300F005BC00BFEC80FF1F0E4B00F01A | ||||
| :4027800003001A78490102F0FC02104318701A7801F0600142F080021A701A7802F07F021A701A7802F09F020A431A701A7842F010021A70704700BF83430040014B012277 | ||||
| :4027C0001A70704784430040044B00F00F021B6853F8220043F82210704700BF08ED00E0054A00F01F00126800F1100352F8230042F82310704700BF08ED00E000F01F0089 | ||||
| :4028000000F16040490100F56440C9B2017070470F4B10B50F4900240F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040313600A4B4FF47A72DB68B3FBF2F3084A9B | ||||
| :402840001360084B4FF400421C60C3F8E82010BD8492FF1FBD28000010E000E0EC80FF1F14E000E018E000E0024A136843F002031360704710E000E008B5FFF7F5FF034AF5 | ||||
| :40288000136843F00103136008BD00BF10E000E010B5054CA3691BB9FFF7BAFF0123A361BDE81040FFF7E8BF8492FF1F024B1868C0F30040704700BF10E000E038B5FFF723 | ||||
| :4028C000F5FF012808D1054D002455F8243003B198470134052CF8D138BD00BF8892FF1F024B03EB80035868596070478492FF1F134B144A1B78DBB20360127843EA0223E0 | ||||
| :40290000114A0360127843EA0243104A0360127843EA026303600E4B0E4A1B78DBB24360127843EA02230C4A4360127843EA02430A4A4360127843EA02634360704700BF30 | ||||
| :402940000301004904010049EC460040020100490101004900010049050100490601004910B500F015FB204A044613780A2043F002031370137C43F00203137412F80A3C43 | ||||
| :4029800043F0010302F80A3C937943F00103937102F5AB52137843F003031370134B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CBB | ||||
| :4029C000A3F597530222183B1A70094A137843F008031370FFF7CAFE064B10222046BDE810401A6000F0D8BAAB4300400E5900402F5B004080E200E008B500F0C9FA0F4A73 | ||||
| :402A0000137803F0FE031370A2F5AA521D3A137803F0FD031370137C03F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE039371BDE8084000F0AFBA00BF0859004072 | ||||
| :402A4000044A137803F03F0343EA8010C0B21070704700BF08590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F9101067 | ||||
| :402A80000A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470A590040883B00005293FF1F5493FF1F5893FF1F08B5102000F0A6F9C9 | ||||
| :402AC00007210420FFF79AFE07490420FFF788FE064A0C20137843F006031370FFF7BCFF034B00221A8008BDB12B0000095900405093FF1F10B5054C23781BB9FFF7DCFFF1 | ||||
| :402B000001232370BDE81040FFF72ABFA092FF1F044B1A7802F0FB021A701A7842F001021A7070470859004010B5084B1C7814F0010403D10028F9D0002404E02046FFF7D9 | ||||
| :402B400015FE024B1B78204610BD00BF09590040034A044B1B881088181A00B2704700BF5893FF1FA25B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B98 | ||||
| :402B80001B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475293FF1F5493FF1F5093FF1F7047000010B500F0EBF9214A0446137884 | ||||
| :402BC0000A2043F001031370137C43F00103137412F80A3C43F0020302F80A3C937943F00203937102F5AA521832137843F003031370144B18221A7013F8012C42F0400241 | ||||
| :402C000003F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222123B1A70094A137843F008031370FFF79FFD074B08222046BDE810401A6000F0ADB900BFEB | ||||
| :402C4000AB43004006590040275B004080E200E008B500F09DF90F4A137803F0FE031370A2F5AA52153A137803F0FE031370137C03F0FE03137412F80A3C03F0FD0302F8BA | ||||
| :402C80000A3C937903F0FD039371BDE8084000F083B900BF00590040044A137803F03F0343EA8010C0B21070704700BF00590040082804D00A280CBF8223C22300E04223BE | ||||
| :402CC00008380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470259004094 | ||||
| :402D0000923B00005E93FF1F6493FF1F5C93FF1F08B5102000F084F807210320FFF76EFD07490320FFF75CFD064A0C20137843F006031370FFF7BCFF034B00221A8008BD88 | ||||
| :402D4000092E0000015900406093FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF728BFA192FF1F044B1A7802F0FB021A701A7842F001021A7070470059004046 | ||||
| :402D800010B5084B1C7814F0010403D10028F9D0002404E02046FFF7E9FC024B1B78204610BD00BF01590040034A044B1B881088181A00B2704700BF5C93FF1FA05B00406B | ||||
| :402DC0000E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475E93FF1F0D | ||||
| :402E00006493FF1F6093FF1F70470000034A00F0F800137803431370704700BF02410040034A00F0F800137803431370704700BF06410040014B1870704700BF7C64004043 | ||||
| :402E4000014B1870704700BF7B64004073B515461E460B4C04230022019200920A46014618462370FFF7F8FB324629462078FFF7B3FB02212078FFF79DFB207802B070BDE9 | ||||
| :402E8000FC80FF1F074A0223136002F688321268E0215064044A11706FF440710A441360704700BF80E100E001E400E0014B1870704700BF74640040014B1870704700BFE2 | ||||
| :402EC00075640040014B1870704700BF7D640040FEB5494652465B460EB40746244909688A46244A12682448022100F071F8030020480068C018204900F06AF814388346CB | ||||
| :402F00000121C9430C460125002600F041F8814651460B7823400B705846013000F030F83800F04028400B78234003430B70584600F026F80136072EF2D900200130013812 | ||||
| :402F4000013001200B78234003430B705846043000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF30080D480FF1FF880FF1F00C2010000000000FD | ||||
| :402F80000230800803D000BF01380046FCD17047EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D113790C2103F07F02044B01FB02339B7A4A | ||||
| :402FC00000E013790020704700600040DC92FF1F002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB11217047704700BF014B1868704700BFEC | ||||
| :403000006081FF1F0E4B70B51E460E4C0025E41AA410A54204D056F8253098470135F8E700F044FE084B094C1E46E41AA4100025A54204D056F8253098470135F8E770BD98 | ||||
| :40304000B83C0000B83C0000B83C0000C03C000003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B604FF0FF301DE04FF40273ADF80C300CBFA2 | ||||
| :40308000234604F1FF33029305934FF6FF7300910491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B00221A701BB030BD000007B5009313460A464B | ||||
| :4030C000014603480068FFF7CBFF03B05DF804FB6081FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD02568096902236F1A656905EB450595FBF3F5B9 | ||||
| :403100007B1C43449D4238BF1D4653050FD5294600F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A4600F098FB064670B92169504600F0E8FAA0 | ||||
| :403140000C23CAF80030A3894FF0FF3043F04003A381BDE8F08726613E44266046466561ED1BA560464528BF464649463246206800F0B3FAA36800209B1BA36023681E44F5 | ||||
| :403180002660BDE8F08700002DE9F04F9DB003938B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F80030CDE040236B610023099320238DF86F | ||||
| :4031C0002930DFF89CB130238DF82A3037463C4614F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF771FF013000F0A780099B4B4409933B7803 | ||||
| :40320000002B00F0A08000234FF0FF3204930793059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46801A06FA00F018430490EFE7D90644BFAF | ||||
| :4032400020228DF853201A0744BF2B228DF8532022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F00200079204900BE027463B780134303B092B51 | ||||
| :4032800003D800FB02320121F5E701B107923B782E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E0002319460593781C0A2407463A780130D0 | ||||
| :4032C000303A092A03D804FB01210123F5E703B1059103223978224800F0E6F940B14023CBEB000003FA00F0049B013718430490397806221B487E1C8DF8281000F0D4F9CF | ||||
| :4033000088B1194B33B9039B073323F007030833039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A9404600F093F8B0F1FF3F824603D0099B27 | ||||
| :403340005344099342E7AB895B0601D4099801E04FF0FF301DB0BDE8F08F00BF873C00008D3C0000913C000000000000D53000002DE9F04791461F460A698B680646934279 | ||||
| :40338000B8BF1346C9F8003091F843200C46DDF8208012B10133C9F800302368990642BFD9F800300233C9F80030256815F0060510D104F1190A07E0012352463946304631 | ||||
| :4033C000C04701301AD00135E368D9F800209B1A9D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F845102244023382F8431003E04FF0FF3091 | ||||
| :40340000BDE8F08704F1430239463046C0470130F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500259342C4BF9B1AED184FF000091A344D45BF | ||||
| :4034400009D00123224639463046C0470130D5D009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9B01F1430E00F0AE8011D8632A22D009D833 | ||||
| :40348000002A00F0BB80582A40F0CA8081F84520834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B8800A6842F020020A603EE0752A24D0782A87 | ||||
| :4034C0003AD0ADE01A6801F14205111D1960136884F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F10401196002D0B2F9003000E01368002B3CDA2D228D | ||||
| :403500005B4284F8432037E021681A6811F0800F02D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A14BF0A2208221BE078225A4984F8452055 | ||||
| :403540002268186812F0800F00F104051D6003D1550601D5038800E00368D00744BF42F0200222601BB9226822F0200222601022002084F8430001E049490A226568002DF0 | ||||
| :40358000A56008DB206820F0040020602BB9002D7DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D1082A0BD12368DA0708D5236962689A42E0 | ||||
| :4035C000DEBF302305F8013C05F1FF35C5EB0E0323612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F104001860136801D0198000E019600023236173 | ||||
| :40360000754616E01A68111D1960156800216268284600F049F808B1401B6060636804E004F1420584F8422001232361002384F84330CDF800803B4603AA21463046FFF70C | ||||
| :4036400097FE013002D14FF0FF3026E023692A4639463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046C0470130E7D00135E368039A9B1A9D42D0 | ||||
| :40368000F2DBE068039B9842B8BF184605E00B7804F1420584F842308AE705B0BDE8F083773B0000983C000010B5C9B202449042034605D01C7801308C42F8D1184610BD55 | ||||
| :4036C000002010BD10B5431E0A44914204D011F8014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21AD34204D013F8014D01F8014DF8E710BD71 | ||||
| :40370000994204D011F8014B02F8014FF8E710BD38B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B96360146030E0A3420DD92268A0188342ED | ||||
| :4037400001BF18685B681218226063600C6023E0A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D110685268014419605A600DE002D90C232A | ||||
| :403780002B6009E021686018824201BF106852680918216062605C602846BDE8384000F098B838BDA892FF1F70B5CD1C25F0030508350C2D38BF0C25002D064601DBA9429D | ||||
| :4037C00002D90C23336046E000F082F8234B1C681A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B681360636018BF0C4615E00C464968E9E70D | ||||
| :40380000174C23681BB9304600F052F820602946304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B00231D20F00700C31A0ED05A42E25070BD37 | ||||
| :40384000211A304600F034F80130EBD10C233360304600F03EF8002070BD00BFA892FF1FA492FF1FF8B5074615460E4621B91146BDE8F840FFF798BF1AB9FFF749FF2846F5 | ||||
| :40388000F8BD00F027F885420ED929463846FFF78BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD38B5064C0023054608462360FDF7F6FB46 | ||||
| :4038C000431C02D1236803B12B6038BD8C93FF1F7047704751F8040C0028BEBF091851F8043CC0180438704700000000050209020B020D020F021102130215022800000013 | ||||
| :40390000000104000100000000000000000157494E55534200003030303031000000000000000000E0000000000105000100D6000000070000002A00440065007600690041 | ||||
| :403940006300650049006E0074006500720066006100630065004700550049004400730000009E0000007B00330064003200370035006300660065002D003500340033000D | ||||
| :4039800035002D0034006400640035002D0061006300630061002D003900660062003900390035006500320066003600330038007D0000007B00330064003200370035001F | ||||
| :4039C0006300660065002D0035003400330035002D0034006400640035002D0061006300630061002D003900660062003900390035006500320066003600330038007D0098 | ||||
| :403A0000000000007265706C792030782530327800686F6D696E6700626567696E6E696E67207365656B2066726F6D20256420746F2025640066696E69736865642073652D | ||||
| :403A4000656B00796573006E6F00647269766520303A20257320647269766520313A2025730057616974696E6720666F72205553422E2E2E0055534220726561647900631F | ||||
| :403A80006F6D6D616E6420307825303278006661696C2025642B25642B2564203D3D2025642C206E6F74202564007061737365643D256400756E64657272756E2061667479 | ||||
| :403AC0006572202564207061636B65747300636F756E743D256420693D256420643D256400636D645F777269746500703D25642063723D25642063773D256420663D2564F8 | ||||
| :403B000020773D256420696E6465783D256420756E64657272756E3D256400756E64657272756E2100737563636573730073746172742065726173696E670073746F702027 | ||||
| :403B400065726173696E670069646C650000510040100040510040300000000140001000140140000800400140000A004C01400002005001402000303132333435363738CF | ||||
| :403B80003941424344454600000100000004000000100001000000040000001001000000A43B000001000000733C0000000000000000000001000000BC3B00000100000084 | ||||
| :403BC000453C000004000000DE3B0000000000000000000000000000DC3B0000FF00000001024000FF00000082024000FF00000003034000FF00000084034000FF000203FC | ||||
| :403C000004030904160346006C007500780045006E00670069006E0065002A0343006F0077006C00610072006B00200054006500630068006E006F006C006F006700690036 | ||||
| :403C4000650073000009022E0001010080320904000004FF00000107050102400000070582024000000705030340000A0705840340000A12010002FF0001080912006E016F | ||||
| :403C800000020180014300232D302B2000686C4C00656667454647003031323334353637383961626364656600000000F8B500BFF8BC08BC9E4670475900000029110000DA | ||||
| :403CC000F8B500BFF8BC08BC9E46704735000000E03C0000C880FF1FA000000028120000000000009093FF1FFF000000675000400C00000007000000FFFFFFFF7F8000006F | ||||
| :403D00003F0000000000007D00FA0000400000000090D003FF0000000000000000000000000000000000000000000000000000000000000000000000853C0000000000006A | ||||
| :403D400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000081FF1F00000000A4 | ||||
| :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,51 +4098,51 @@ | ||||
| :40FF80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041 | ||||
| :40FFC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 | ||||
| :0200000480007A | ||||
| :400000000145004008520040015B004001640040020101400B0301403E04014041050140130701405508014056090140510A0140540B01405B0C0140540D0140470E01400D | ||||
| :40004000380F01400A1501400B1701404B18014059190140521A01404E1B01400B40014012410140144201400C430140044401400D4501400946014011470140084801406A | ||||
| :4000800011490140154C0140034D014006500140045101407E0208020941118218021901600C61067C4027212D0A8C40E62082608580908091029840AD01B040E008E224DC | ||||
| :4000C000E680EE020008021003020405052006020B080F041110120117041A021B011C041F0420062107220123082610270428102A082B042F04300731203208330F361052 | ||||
| :40010000371038883A303B083E453F4156085808590A5B045C995D905F0189018B028D039601A002A102A301AC01B202B303B601BE04D608D804D904DB04DC99DD90DF0157 | ||||
| :40014000000A09900A400F10100811081302164119801AA01B221C101F802142231026082A252B012F80300231443220388039023B143F90584059105F80618063046640B5 | ||||
| :4001800068046B026D206E407A107B028110822088088A108E018F4090809102968098409A209D80A502B502C00CC22BC49ECA8FCC0FCE3FD61CD81CDE0AE204E60DEA04B2 | ||||
| :4001C000EE0181108D449010918092609D449E049F80A110A402A701A904AC01B208B720E202E60AEA05EE080004020804010602080409010AF90B140D010E020F2810030B | ||||
| :4002000012FC134114041580164017011A041B4E1C801E042202234124042620274128042A102B412CFF2DF12F02310F32FF330F35F03B0A3E045804590B5B045C905F0188 | ||||
| :40024000800481078378840485088730880489808C038DFF8E0490019101920294029502970498029C029DA09F48A002A102A304A404A53FA802A908AB10AC05AF80B13F30 | ||||
| :40028000B207B33FB7C0BB80BE04D804D904DC90DF01008801020308048405020708084009220A110C040D090F0911011202132814801568170119041C202104224A230182 | ||||
| :4002C00025052714288629042C022F6430083102324035013714394A3B203F60422043045C405D206D106E407C028D01914293689420952D96909785980A99039A089B0820 | ||||
| :400300009C709D109E069FD5A042A108A280A31CA494A502A65DA702AB02AD10B220B380B610C0FFC2FFC4FFCAFFCCEBCE3FD630DE80E212E410E60AE840EA01EC80000C29 | ||||
| :4003400002F1040C06100A040E021080120C140C164018011A021C031EFC22022608280C2A202CFF34FF3E10560858045B045D905F0180088121830284C085218601870427 | ||||
| :4003800088088B108D2F8E04900291019204941A96209740980899209B019C109E209F2FA046A121A238A30EA408A808A921AB08AD0FAE80B23FB360B480B51FB640B71FD4 | ||||
| :4003C000B908BE44C203C60EC804C9FFCAFFCBFFCF83D804D904DA04DB04DC99DD09DF01000801020309050A070909080A820B040D0A0F091141120813081402152A192051 | ||||
| :400400001D041F9024022508268027082A082B102C402D402E10351437813CA03D0540804240462057405A015B415EA6600263026540661668026A406D90835089408B01C6 | ||||
| :400440008E409142920693489422953D96B197809808994B9A109CF29D309F14A040A31CA480A54AA604A702A814AC08B780C0FFC2FFC4FFCAB0CCF0CEF0D040D6F8D8F8B5 | ||||
| :40048000E280E66CE802EC1000200250061008400A3010621208146216041A031C801D01207C2201261C2A102E10306031013280341F3E043F01580B59045B045C095F01FF | ||||
| :4004C000802381C8820C85C8860189C88A108C2F8D228FCC9130922F9302942395C8960899029A409C239F48A020A1C8A202A423A604A780A880A9D4AC0FAD80AF01B01FA9 | ||||
| :40050000B161B260B380B480B51FB808B902BE10BF14C062C702C810C9FFCAFFCBFFCD20CEF0D110D804D904DA04DB04DC99DD09DF01E108E240E340E480E640E7400040C6 | ||||
| :40054000012102200505070409220A110C800E040F6010481144148016221710180419021B0C1C201D011E0420C0212122092501281029022B052E202F04311A32603504B8 | ||||
| :4005800036443842392840044A0850085F80660267806B026E807C02810188108B028D018F40900891409348951096209780980899489A109CA29D019E029F14A351A54004 | ||||
| :4005C000A604A702AB80AF40B548C07FC2FFC4FFCA0FCC1FCE0FD004D204D610DE80E008E220E680EA81012002010340070308100A080B100F10100413101408161217100D | ||||
| :400600001B1C1C1B1D401F20217C2301250227042B102D022F083001316032023418351F36043E40580B590B5B045C995F01804082308610888089018A308B028D028F0133 | ||||
| :4006400090E2920894E296049A109E03A0FCA201A420A6D0AE1CAF01B2E0B301B502B61FB928BBC0BF14D608D80BD918DB04DC99DD90DF0100110501071408800A600C019B | ||||
| :400680000D100E0A0F60121013041480162218101A801B501D401E121F0C23442409250828812A202B182F02310132403314380139203B843D905C205F80640867026A40A1 | ||||
| :4006C0006D40780279407B017C027F0189088E10C07AC2FDC4B0CA1FCC0FCE3FD630D830DE91E63080028E409240A402AD40B301E204E640EC01EE0A878092409D409F8071 | ||||
| :40070000A402A701A810B614E202EA40EC010040021105C20724082009200A110BC00D020F4810F11202139014F11604179C18101AE11B901E0E1F0320F1220823902480D4 | ||||
| :40074000261127902B902DFC2F0134F035E0360F371F3A803B20580B590B5B045C995F018001821A8411850186068C059C01A703AC05AD03B101B20CB302B410B501B603BE | ||||
| :40078000BE10BF11C006C6ECC80CC9FFCAFFCBFFD004D601D804D904DA04DB04DC99DD09DF01E2C00080010803800410050109800C420E080F201140166019681B801C02B3 | ||||
| :4007C0001E081F6020042106220223842510264029482C422F6435013640371439083A403B403D203E083F4045104604492050105180680869056A106B236F027040713001 | ||||
| :40080000728173087C127F04842091529280934894A4952596119784980299039A289B149C309D109E069FE1A042A10CA288A314A40CA522A655A722A920B4C8B640B7042C | ||||
| :40084000C05DC2F1C431CAFACCF0CE72D204DE80E202E608EE10000101120324040706080760088009800A100C0F0D700EF00F01104011421280131014801620187019069A | ||||
| :400880001A801C011D0420082104220424012708280829042A022E0F2F10318032FF3370350F39203E043F01580459045B045C905F018210838487848A108B848D168E10FE | ||||
| :4008C0008F41901C920193E49402960497849A1C9B189E03A002A101A208A306A510A610A722AA10AB84ADE7AF08B5F8B61FB707BB80D80BD90BDC99DF01008001050208F7 | ||||
| :400900000410058109080A810B040C020E0A0F2010081202130A166819081E02200821142211232425802610290A2A802B042C022E222F20329235013614395A3C123D0820 | ||||
| :400940003E083F40458046024801498058105B206A407B307C028240860190109157928193409520980299039AE09B0A9C109E02A04BA10CA28EA314A404A611AB40AF2038 | ||||
| :40098000B001C0DFC2FFC47FCAFFCCEDCEFFDE80E402EA80EE021B011F0232203302350836803B408908C630CCF0CE1030013180344037043A013F80822088028B1097407A | ||||
| :4009C0009D08A108A620AE80AF01CCF0CE60E6805108560883408508932097409A089C419D089F04A002A108A580A740AA09D460E220E610EA20EE808108844185809320A9 | ||||
| :400A000097409C41A002A580AB04E2C0E650EA8014407002C404DC015840601084028C108D20A40AA808B040D401D802E008E402EA011B01811087019A109C40A120A4082A | ||||
| :400A4000AE10C6080A040B200E200F01861487108E0297019A109D10A120A408AB01B040C20FE408E8012620932097409902A002A108B502C82052015310550870027502C3 | ||||
| :400A80008E0193209902A002A108AA20AF40D4E0DC80DE20EC80EE40051008080C080F041F105120561059206202840887049A109D10A120A202A408C001C20DC601D407CD | ||||
| :400AC000D80285209D20AF1001010B010D010F0111011D0100FF01AB02021105BF0000A09F001F000000000000000000100000004000000000000000C0000000FF0000B8F6 | ||||
| :400B00004700470000010000800000028200820000000000000707000700000027001801270018010004000000050000000000000000000000000000000000000000000002 | ||||
| :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 | ||||
| @@ -4615,12 +4615,12 @@ | ||||
| :0200000490105A | ||||
| :04000000BC90ACAF55 | ||||
| :0200000490303A | ||||
| :0200000090CF9F | ||||
| :0200000021B02D | ||||
| :0200000490402A | ||||
| :4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0 | ||||
| :400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080 | ||||
| :400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040 | ||||
| :4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | ||||
| :0200000490501A | ||||
| :0C00000000012E16106900002E30A138FF | ||||
| :0C00000000012E16106900002E3032198D | ||||
| :00000001FF | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -221,7 +221,7 @@ static int usb_read(int ep, uint8_t buffer[FRAME_SIZE]) | ||||
| static void cmd_get_version(struct any_frame* f) | ||||
| { | ||||
|     DECLARE_REPLY_FRAME(struct version_frame, F_FRAME_GET_VERSION_REPLY); | ||||
|     r.version = FLUXENGINE_VERSION; | ||||
|     r.version = FLUXENGINE_PROTOCOL_VERSION; | ||||
|     send_reply((struct any_frame*) &r); | ||||
| } | ||||
|  | ||||
| @@ -905,7 +905,6 @@ int main(void) | ||||
|     USBFS_Start(0, USBFS_DWR_VDDD_OPERATION); | ||||
|     USBFS_DisableOutEP(FLUXENGINE_DATA_OUT_EP_NUM); | ||||
|      | ||||
|     detect_drives(); | ||||
|     CyWdtStart(CYWDT_1024_TICKS, CYWDT_LPMODE_DISABLED); | ||||
|      | ||||
|     for (;;) | ||||
| @@ -926,6 +925,8 @@ int main(void) | ||||
|                 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) | ||||
|   | ||||
							
								
								
									
										166
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										166
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,84 +1,116 @@ | ||||
| PACKAGES = zlib sqlite3 protobuf | ||||
| ifeq ($(BUILDTYPE),) | ||||
|     buildtype_Darwin = osx | ||||
|     buildtype_Haiku = haiku | ||||
|     BUILDTYPE := $(buildtype_$(shell uname -s )) | ||||
|         ifeq ($(BUILDTYPE),) | ||||
|                 BUILDTYPE := unix | ||||
|         endif | ||||
| endif | ||||
| export BUILDTYPE | ||||
|  | ||||
| export CFLAGS = \ | ||||
| 	-ffunction-sections -fdata-sections | ||||
| export CXXFLAGS = $(CFLAGS) \ | ||||
| 	-x c++ --std=gnu++2a \ | ||||
| 	-Wno-deprecated-enum-enum-conversion \ | ||||
| 	-Wno-deprecated-enum-float-conversion | ||||
| export LDFLAGS = -pthread | ||||
| ifeq ($(BUILDTYPE),windows) | ||||
| 	MINGW = i686-w64-mingw32- | ||||
| 	CC = $(MINGW)gcc | ||||
| 	CXX = $(MINGW)g++ -std=c++20 | ||||
| 	CFLAGS += -g -O3 \ | ||||
| 		-Wno-unknown-warning-option \ | ||||
| 		-ffunction-sections \ | ||||
| 		-fdata-sections | ||||
| 	CXXFLAGS += \ | ||||
| 		-fext-numeric-literals \ | ||||
| 		-Wno-deprecated-enum-float-conversion \ | ||||
| 		-Wno-deprecated-enum-enum-conversion | ||||
| 	LDFLAGS += -static -Wl,--gc-sections | ||||
| 	AR = $(MINGW)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 | ||||
| 	EXT = .exe | ||||
| else | ||||
| 	CC = gcc | ||||
| 	CXX = g++ -std=c++20 | ||||
| 	CFLAGS = -g -O3 \ | ||||
| 		-Wno-unknown-warning-option | ||||
| 	CXXFLAGS += \ | ||||
| 		-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 COPTFLAGS = -Os | ||||
| export LDOPTFLAGS = -Os | ||||
| HOSTCC = gcc | ||||
| HOSTCXX = g++ -std=c++20 | ||||
| HOSTCFLAGS = -g -O3 | ||||
| HOSTLDFLAGS = | ||||
|  | ||||
| export CDBGFLAGS = -O0 -g | ||||
| export LDDBGFLAGS = -O0 -g | ||||
| REALOBJ = .obj | ||||
| OBJ = $(REALOBJ)/$(BUILDTYPE) | ||||
| DESTDIR ?= | ||||
| PREFIX ?= /usr/local | ||||
| BINDIR ?= $(PREFIX)/bin | ||||
|  | ||||
| # Special Windows settings. | ||||
|  | ||||
| ifeq ($(OS), Windows_NT) | ||||
| else | ||||
| ifeq ($(shell uname),Darwin) | ||||
| else | ||||
| 	PACKAGES += libudev | ||||
| endif | ||||
| 	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 | ||||
|  | ||||
| ifeq ($(OS), Windows_NT) | ||||
| export PROTOC = /mingw32/bin/protoc | ||||
| export CC = /mingw32/bin/gcc | ||||
| export CXX = /mingw32/bin/g++ | ||||
| export AR = /mingw32/bin/ar rc | ||||
| export RANLIB = /mingw32/bin/ranlib | ||||
| export STRIP = /mingw32/bin/strip | ||||
| export CFLAGS += -I/mingw32/include/libusb-1.0 -I/mingw32/include | ||||
| export LDFLAGS += | ||||
| export LIBS += -L/mingw32/lib -static -lz -lsqlite3 \ | ||||
| 	-lsetupapi -lwinusb -lole32 -lprotobuf -luuid | ||||
| export EXTENSION = .exe | ||||
| else | ||||
|  | ||||
| packages-exist = $(shell pkg-config --exists $(PACKAGES) && echo yes) | ||||
| ifneq ($(packages-exist),yes) | ||||
| $(warning These pkg-config packages are installed: $(shell pkg-config --list-all | sort | awk '{print $$1}')) | ||||
| $(error You must have these pkg-config packages installed: $(PACKAGES)) | ||||
| endif | ||||
|  | ||||
| export PROTOC = protoc | ||||
| export CC = gcc | ||||
| export CXX = g++ | ||||
| export AR = ar rc | ||||
| export RANLIB = ranlib | ||||
| export STRIP = strip | ||||
| export CFLAGS += $(shell pkg-config --cflags $(PACKAGES)) | ||||
| export LDFLAGS += | ||||
| export LIBS += $(shell pkg-config --libs $(PACKAGES)) | ||||
| export EXTENSION = | ||||
| # Special OSX settings. | ||||
|  | ||||
| ifeq ($(shell uname),Darwin) | ||||
| AR = ar rcS | ||||
| RANLIB += -c -no_warning_for_no_symbols | ||||
| export CC = clang | ||||
| export CXX = clang++ | ||||
| export COBJC = clang | ||||
| export LDFLAGS += -framework IOKit -framework CoreFoundation | ||||
| 	LDFLAGS += \ | ||||
| 		-framework IOKit \ | ||||
| 		-framework Foundation  | ||||
| endif | ||||
|  | ||||
| endif | ||||
| export XXD = xxd | ||||
| .PHONY: all | ||||
| all: +all README.md | ||||
|  | ||||
| CFLAGS += -Ilib -Idep/fmt -Iarch | ||||
| .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 | ||||
|  | ||||
| export OBJDIR = .obj | ||||
| .PHONY: tests | ||||
|  | ||||
| all: .obj/build.ninja | ||||
| 	@ninja -f .obj/build.ninja -k 0 | ||||
| 	@if command -v cscope > /dev/null; then cscope -bRq; fi | ||||
| clean:: | ||||
| 	$(hide) rm -rf $(REALOBJ) | ||||
|  | ||||
| clean: | ||||
| 	@echo CLEAN | ||||
| 	@rm -rf $(OBJDIR) | ||||
| include build/ab.mk | ||||
|  | ||||
| .obj/build.ninja: mkninja.sh Makefile | ||||
| 	@echo MKNINJA $@ | ||||
| 	@mkdir -p $(OBJDIR) | ||||
| 	@sh $< > $@ | ||||
| DOCKERFILES = \ | ||||
| 	debian11 \ | ||||
|     debian12 \ | ||||
|     fedora40 \ | ||||
|     fedora41 \ | ||||
| 	manjaro | ||||
|  | ||||
| docker-%: tests/docker/Dockerfile.% | ||||
| 	docker build -t $* -f $< . | ||||
|  | ||||
| .PHONY: dockertests | ||||
| dockertests: $(foreach f,$(DOCKERFILES), docker-$(strip $f) .WAIT) | ||||
|   | ||||
							
								
								
									
										193
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								README.md
									
									
									
									
									
								
							| @@ -2,12 +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 2021-05-21, the command line environment has changed | ||||
| _substantially_ (to make it more consistent and flexible, and allow some new | ||||
| backend features like multi-format IBM scheme disks, which are popular with | ||||
| CP/M). If things don't work the way you expect, please check the documentation. | ||||
| **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? | ||||
| ----- | ||||
| @@ -30,17 +32,14 @@ Don't believe me? Watch the demo reel! | ||||
| </div> | ||||
|  | ||||
| **New!** The FluxEngine client software now works with | ||||
| [GreaseWeazle](https://github.com/keirf/Greaseweazle/wiki) hardware. So, if you | ||||
| can't find a PSoC5 development kit, or don't want to use the Cypress Windows | ||||
| tools for programming it, you can use one of these instead. Very nearly all | ||||
| FluxEngine features are available with the GreaseWeazle and it works out-of-the | ||||
| box. See the [dedicated GreaseWeazle documentation page](doc/greaseweazle.md) | ||||
| for more information. | ||||
|  | ||||
| **Important note.** On 2020-04-02 I changed the bytecode format (and firmware). | ||||
| Flux files will need to be upgraded with `fluxengine upgradefluxfile`. The new | ||||
| format should be more reliable and use way, way less bandwidth. Sorry for the | ||||
| inconvenience. | ||||
| [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? | ||||
| ------ | ||||
| @@ -65,71 +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 | ||||
|  | ||||
|   - [Using GreaseWeazle hardware with the FluxEngine client | ||||
| 	software](doc/greaseweazle.md) ∾ what works ∾ what doesn't work ∾ where to | ||||
| 	go for help | ||||
|   - [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 | ||||
|     science ∾ the sector map ∾ clock detection and the histogram | ||||
|  | ||||
|   - [Checking your drive](doc/driveresponse.md) ∾ you can't do that with that ∾ | ||||
| 	measuring your drive's ability to work with exotic formats | ||||
|   - [Disk densities](doc/driveresponse.md) ∾ what's the difference between an HD | ||||
|     and DD disk? ∾ you can't do that with that ∾ measuring your drive's ability to | ||||
|     work with exotic formats ∾ I think my drive is broken | ||||
|  | ||||
| Which? | ||||
| ------ | ||||
|  | ||||
| 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](doc/disk-ibm.md)      |  🦄   |   🦄   | and compatibles (like the Atari ST) | | ||||
| | [Acorn ADFS](doc/disk-acornadfs.md)       |  🦄   |   🦖*  | single- and double- sided           | | ||||
| | [Acorn DFS](doc/disk-acorndfs.md)         |  🦄   |   🦖*  |                                     | | ||||
| | [Ampro Little Board](doc/disk-ampro.md)   |  🦖   |   🦖*  |                                     | | ||||
| | [Apple II DOS 3.3](doc/disk-apple2.md)    |  🦄   |        | doesn't do logical sector remapping | | ||||
| | [Amiga](doc/disk-amiga.md)                |  🦄   |   🦄   |                                     | | ||||
| | [Commodore 64 1541/1581](doc/disk-c64.md) |  🦄   |   🦄   | and probably the other 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 } | ||||
|  | ||||
| `*`: these formats are variations of the generic IBM format, and since the | ||||
| IBM writer is completely generic, it should be configurable for these | ||||
| formats... theoretically. I don't have the hardware to try it. | ||||
|  | ||||
| ### Even older disk formats | ||||
|  | ||||
| These formats are for particularly old, weird architectures, even by the | ||||
| 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 | | ||||
| | [VDS Eco1](doc/disk-eco1.md)             |  🦖   |        | 8" mixed format | | ||||
| | [Micropolis](doc/disk-micropolis.md)     |  🦄   |        | Micropolis 100tpi drives | | ||||
| | [Northstar(doc/disk-northstar.md)        |  🦖   |   🦖   | 5.25" hard sectors | | ||||
| | [TI DS990 FD1000](doc/disk-tids990.md)   |  🦄   |  🦄    | 8" | | ||||
| | [Victor 9000](doc/disk-victor9k.md)      |  🦖   |        | 8" | | ||||
| | [Zilog MCZ](doc/disk-zilogmcz.md)        |  🦖   |        | 8" _and_ hard sectors | | ||||
| {: .datatable } | ||||
| <!-- FORMATSEND --> | ||||
|  | ||||
| ### Notes | ||||
|  | ||||
| @@ -146,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. | ||||
|  | ||||
| @@ -200,17 +215,12 @@ 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, 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 | ||||
| @@ -228,8 +238,37 @@ 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. | ||||
| 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,9 +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) | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createAesLanierDecoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<Decoder> createAesLanierDecoder( | ||||
|     const DecoderProto& config); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -1,79 +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 "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) | ||||
| { | ||||
|     Bytes output; | ||||
|     ByteWriter bw(output); | ||||
|  | ||||
|     for (uint8_t b : input) | ||||
|         bw.write_8(reverse_bits(b)); | ||||
|     return output; | ||||
| } | ||||
|  | ||||
| class AesLanierDecoder : public AbstractDecoder | ||||
| class AesLanierDecoder : public Decoder | ||||
| { | ||||
| public: | ||||
| 	AesLanierDecoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config) | ||||
| 	{} | ||||
|     AesLanierDecoder(const DecoderProto& config): Decoder(config) {} | ||||
|  | ||||
|     RecordType advanceToNextRecord() | ||||
| 	{ | ||||
| 		_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN); | ||||
| 		if (_fmr->eof() || !_sector->clock) | ||||
| 			return UNKNOWN_RECORD; | ||||
| 		return SECTOR_RECORD; | ||||
| 	} | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         return seekToPattern(SECTOR_PATTERN); | ||||
|     } | ||||
|  | ||||
|     void decodeSectorRecord() | ||||
| 	{ | ||||
| 		/* Skip ID mark. */ | ||||
|     void decodeSectorRecord() override | ||||
|     { | ||||
|         /* Skip ID mark (we know it's a AESLANIER_RECORD_SEPARATOR). */ | ||||
|  | ||||
| 		readRawBits(16); | ||||
|         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); | ||||
|         const auto& rawbits = readRawBits(AESLANIER_RECORD_SIZE * 16); | ||||
|         const auto& bytes = | ||||
|             decodeFmMfm(rawbits).slice(0, AESLANIER_RECORD_SIZE); | ||||
|         const auto& reversed = bytes.reverseBits(); | ||||
|  | ||||
| 		_sector->logicalTrack = reversed[1]; | ||||
| 		_sector->logicalSide = 0; | ||||
| 		_sector->logicalSector = reversed[2]; | ||||
|         _sector->logicalTrack = reversed[1]; | ||||
|         _sector->logicalSide = 0; | ||||
|         _sector->logicalSector = reversed[2]; | ||||
|  | ||||
| 		/* Check header 'checksum' (which seems far too simple to mean much). */ | ||||
|         /* 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; | ||||
| 		} | ||||
|         { | ||||
|             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. */ | ||||
|         /* 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; | ||||
| 	} | ||||
|         _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<AbstractDecoder> createAesLanierDecoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createAesLanierDecoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new AesLanierDecoder(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->logicalTrack = bytes[1] >> 1; | ||||
|         _sector->logicalSector = bytes[2]; | ||||
|         _sector->logicalSide = bytes[1] & 1; | ||||
|         _sector->status = Sector::DATA_MISSING; /* unintuitive but correct */ | ||||
|     } | ||||
|  | ||||
|     void decodeDataRecord() override | ||||
|     { | ||||
|         if (readRaw64() != DATA_ID) | ||||
|             return; | ||||
|  | ||||
|         Bytes bytes = decodeFmMfm(readRawBits((AGAT_SECTOR_SIZE + 2) * 16)) | ||||
|                           .slice(0, AGAT_SECTOR_SIZE + 2); | ||||
|  | ||||
|         if (bytes[AGAT_SECTOR_SIZE + 1] != 0x5a) | ||||
|             return; | ||||
|  | ||||
|         _sector->data = bytes.slice(0, AGAT_SECTOR_SIZE); | ||||
|         uint8_t wantChecksum = bytes[AGAT_SECTOR_SIZE]; | ||||
|         uint8_t gotChecksum = agatChecksum(_sector->data); | ||||
|         _sector->status = | ||||
|             (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<Decoder> createAgatDecoder(const DecoderProto& config) | ||||
| { | ||||
|     return std::unique_ptr<Decoder>(new AgatDecoder(config)); | ||||
| } | ||||
							
								
								
									
										118
									
								
								arch/agat/encoder.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								arch/agat/encoder.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| #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(std::shared_ptr<const TrackInfo>& trackInfo, | ||||
|         const std::vector<std::shared_ptr<const Sector>>& sectors, | ||||
|         const Image& image) override | ||||
|     { | ||||
|         auto trackLayout = Layout::getLayoutOfTrack( | ||||
|             trackInfo->logicalTrack, trackInfo->logicalSide); | ||||
|  | ||||
|         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->logicalTrack << 1) | sector->logicalSide); | ||||
|             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,7 +1,7 @@ | ||||
| #include "globals.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) | ||||
| @@ -18,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++; | ||||
| @@ -81,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); | ||||
|     } | ||||
|  | ||||
| @@ -95,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,16 +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 | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createAmigaDecoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<AbstractEncoder> createAmigaEncoder(const EncoderProto& config); | ||||
| 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); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| syntax = "proto2"; | ||||
|  | ||||
| import "lib/common.proto"; | ||||
| import "lib/config/common.proto"; | ||||
|  | ||||
| message AmigaDecoderProto {} | ||||
|  | ||||
|   | ||||
| @@ -1,85 +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 "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); | ||||
|  | ||||
| class AmigaDecoder : public AbstractDecoder | ||||
| class AmigaDecoder : public Decoder | ||||
| { | ||||
| public: | ||||
| 	AmigaDecoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config), | ||||
| 		_config(config.amiga()) | ||||
| 	{} | ||||
|     AmigaDecoder(const DecoderProto& config): | ||||
|         Decoder(config), | ||||
|         _config(config.amiga()) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     RecordType advanceToNextRecord() override | ||||
| 	{ | ||||
| 		_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN); | ||||
| 		if (_fmr->eof() || !_sector->clock) | ||||
| 			return UNKNOWN_RECORD; | ||||
| 		return SECTOR_RECORD; | ||||
| 	} | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         return seekToPattern(SECTOR_PATTERN); | ||||
|     } | ||||
|  | ||||
|     void decodeSectorRecord() override | ||||
| 	{ | ||||
| 		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); | ||||
|     { | ||||
|         if (readRaw48() != AMIGA_SECTOR_RECORD) | ||||
|             return; | ||||
|  | ||||
| 		const uint8_t* ptr = bytes.begin() + 3; | ||||
|         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); | ||||
|  | ||||
| 		Bytes header = amigaDeinterleave(ptr, 4); | ||||
| 		Bytes recoveryinfo = amigaDeinterleave(ptr, 16); | ||||
|         const uint8_t* ptr = bytes.begin(); | ||||
|  | ||||
| 		_sector->logicalTrack = header[1] >> 1; | ||||
| 		_sector->logicalSide = header[1] & 1; | ||||
| 		_sector->logicalSector = header[2]; | ||||
|         Bytes header = amigaDeinterleave(ptr, 4); | ||||
|         Bytes recoveryinfo = amigaDeinterleave(ptr, 16); | ||||
|  | ||||
| 		uint32_t wantedheaderchecksum = amigaDeinterleave(ptr, 4).reader().read_be32(); | ||||
| 		uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(6, 40)); | ||||
| 		if (gotheaderchecksum != wantedheaderchecksum) | ||||
| 			return; | ||||
|         _sector->logicalTrack = header[1] >> 1; | ||||
|         _sector->logicalSide = header[1] & 1; | ||||
|         _sector->logicalSector = header[2]; | ||||
|  | ||||
| 		uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32(); | ||||
| 		uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(62, 1024)); | ||||
|         uint32_t wantedheaderchecksum = | ||||
|             amigaDeinterleave(ptr, 4).reader().read_be32(); | ||||
|         uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(0, 40)); | ||||
|         if (gotheaderchecksum != wantedheaderchecksum) | ||||
|             return; | ||||
|  | ||||
| 		Bytes data; | ||||
| 		data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo); | ||||
| 		_sector->data = data; | ||||
| 		_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
| 	} | ||||
|         uint32_t wanteddatachecksum = | ||||
|             amigaDeinterleave(ptr, 4).reader().read_be32(); | ||||
|         uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(56, 1024)); | ||||
|  | ||||
| 	std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override | ||||
| 	{ | ||||
| 		static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | ||||
| 		return sectors; | ||||
| 	} | ||||
|         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; | ||||
|     const AmigaDecoderProto& _config; | ||||
|     nanoseconds_t _clock; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractDecoder> createAmigaDecoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createAmigaDecoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new AmigaDecoder(config)); | ||||
|     return std::unique_ptr<Decoder>(new AmigaDecoder(config)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| #include "globals.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.h" | ||||
| #include "amiga.h" | ||||
| #include "crc.h" | ||||
| #include "writer.h" | ||||
| #include "image.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" | ||||
|  | ||||
| @@ -12,151 +12,138 @@ static bool lastBit; | ||||
|  | ||||
| static int charToInt(char c) | ||||
| { | ||||
| 	if (isdigit(c)) | ||||
| 		return c - '0'; | ||||
| 	return 10 + tolower(c) - 'a'; | ||||
|     if (isdigit(c)) | ||||
|         return c - '0'; | ||||
|     return 10 + tolower(c) - 'a'; | ||||
| } | ||||
|  | ||||
| static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src) | ||||
| static void write_bits( | ||||
|     std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src) | ||||
| { | ||||
| 	for (bool bit : src) | ||||
| 	{ | ||||
| 		if (cursor < bits.size()) | ||||
| 			lastBit = bits[cursor++] = bit; | ||||
| 	} | ||||
|     for (bool bit : src) | ||||
|     { | ||||
|         if (cursor < bits.size()) | ||||
|             lastBit = bits[cursor++] = bit; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width) | ||||
| static void write_bits( | ||||
|     std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width) | ||||
| { | ||||
| 	cursor += width; | ||||
| 	lastBit = data & 1; | ||||
| 	for (int i=0; i<width; i++) | ||||
| 	{ | ||||
| 		unsigned pos = cursor - i - 1; | ||||
| 		if (pos < bits.size()) | ||||
| 			bits[pos] = data & 1; | ||||
| 		data >>= 1; | ||||
| 	} | ||||
|     cursor += width; | ||||
|     lastBit = data & 1; | ||||
|     for (int i = 0; i < width; i++) | ||||
|     { | ||||
|         unsigned pos = cursor - i - 1; | ||||
|         if (pos < bits.size()) | ||||
|             bits[pos] = data & 1; | ||||
|         data >>= 1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void write_bits(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes) | ||||
| static void write_bits( | ||||
|     std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes) | ||||
| { | ||||
| 	ByteReader br(bytes); | ||||
| 	BitReader bitr(br); | ||||
|     ByteReader br(bytes); | ||||
|     BitReader bitr(br); | ||||
|  | ||||
| 	while (!bitr.eof()) | ||||
| 	{ | ||||
| 		if (cursor < bits.size()) | ||||
| 			bits[cursor++] = bitr.get(); | ||||
| 	} | ||||
|     while (!bitr.eof()) | ||||
|     { | ||||
|         if (cursor < bits.size()) | ||||
|             bits[cursor++] = bitr.get(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<Sector>& sector) | ||||
| static void write_sector(std::vector<bool>& bits, | ||||
|     unsigned& cursor, | ||||
|     const std::shared_ptr<const Sector>& sector) | ||||
| { | ||||
| 	if ((sector->data.size() != 512) && (sector->data.size() != 528)) | ||||
| 		Error() << "unsupported sector size --- you must pick 512 or 528"; | ||||
|     if ((sector->data.size() != 512) && (sector->data.size() != 528)) | ||||
|         error("unsupported sector size --- you must pick 512 or 528"); | ||||
|  | ||||
| 	uint32_t checksum = 0; | ||||
|     uint32_t checksum = 0; | ||||
|  | ||||
| 	auto write_interleaved_bytes = [&](const Bytes& bytes) | ||||
| 	{ | ||||
| 		Bytes interleaved = amigaInterleave(bytes); | ||||
| 		Bytes mfm = encodeMfm(interleaved, lastBit); | ||||
| 		checksum ^= amigaChecksum(mfm); | ||||
| 		checksum &= 0x55555555; | ||||
| 		write_bits(bits, cursor, mfm); | ||||
| 	}; | ||||
|     auto write_interleaved_bytes = [&](const Bytes& bytes) | ||||
|     { | ||||
|         Bytes interleaved = amigaInterleave(bytes); | ||||
|         Bytes mfm = encodeMfm(interleaved, lastBit); | ||||
|         checksum ^= amigaChecksum(mfm); | ||||
|         checksum &= 0x55555555; | ||||
|         write_bits(bits, cursor, mfm); | ||||
|     }; | ||||
|  | ||||
| 	auto write_interleaved_word = [&](uint32_t word) | ||||
| 	{ | ||||
| 		Bytes b(4); | ||||
| 		b.writer().write_be32(word); | ||||
| 		write_interleaved_bytes(b); | ||||
| 	}; | ||||
|     auto write_interleaved_word = [&](uint32_t word) | ||||
|     { | ||||
|         Bytes b(4); | ||||
|         b.writer().write_be32(word); | ||||
|         write_interleaved_bytes(b); | ||||
|     }; | ||||
|  | ||||
|     write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6*8); | ||||
|     write_bits(bits, cursor, 0xaaaa, 2 * 8); | ||||
|     write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6 * 8); | ||||
|  | ||||
| 	checksum = 0; | ||||
| 	Bytes header =  | ||||
| 		{ | ||||
| 			0xff, /* Amiga 1.0 format byte */ | ||||
| 			(uint8_t) ((sector->logicalTrack<<1) | sector->logicalSide), | ||||
| 			(uint8_t) sector->logicalSector, | ||||
| 			(uint8_t) (AMIGA_SECTORS_PER_TRACK - sector->logicalSector) | ||||
| 		}; | ||||
| 	write_interleaved_bytes(header); | ||||
| 	Bytes recoveryInfo(16); | ||||
| 	if (sector->data.size() == 528) | ||||
| 		recoveryInfo = sector->data.slice(512, 16); | ||||
| 	write_interleaved_bytes(recoveryInfo); | ||||
| 	write_interleaved_word(checksum); | ||||
|     checksum = 0; | ||||
|     Bytes header = {0xff, /* Amiga 1.0 format byte */ | ||||
|         (uint8_t)((sector->logicalTrack << 1) | sector->logicalSide), | ||||
|         (uint8_t)sector->logicalSector, | ||||
|         (uint8_t)(AMIGA_SECTORS_PER_TRACK - sector->logicalSector)}; | ||||
|     write_interleaved_bytes(header); | ||||
|     Bytes recoveryInfo(16); | ||||
|     if (sector->data.size() == 528) | ||||
|         recoveryInfo = sector->data.slice(512, 16); | ||||
|     write_interleaved_bytes(recoveryInfo); | ||||
|     write_interleaved_word(checksum); | ||||
|  | ||||
| 	Bytes data = sector->data.slice(0, 512); | ||||
| 	write_interleaved_word(amigaChecksum(encodeMfm(amigaInterleave(data), lastBit))); | ||||
| 	write_interleaved_bytes(data); | ||||
|     Bytes data = sector->data.slice(0, 512); | ||||
|     write_interleaved_word( | ||||
|         amigaChecksum(encodeMfm(amigaInterleave(data), lastBit))); | ||||
|     write_interleaved_bytes(data); | ||||
| } | ||||
|  | ||||
| class AmigaEncoder : public AbstractEncoder | ||||
| class AmigaEncoder : public Encoder | ||||
| { | ||||
| public: | ||||
| 	AmigaEncoder(const EncoderProto& config): | ||||
| 		AbstractEncoder(config), | ||||
| 		_config(config.amiga()) {} | ||||
|     AmigaEncoder(const EncoderProto& config): | ||||
|         Encoder(config), | ||||
|         _config(config.amiga()) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| public: | ||||
| 	std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override | ||||
| 	{ | ||||
| 		std::vector<std::shared_ptr<Sector>> sectors; | ||||
|     std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo, | ||||
|         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; | ||||
|  | ||||
| 		if ((physicalTrack >= 0) && (physicalTrack < AMIGA_TRACKS_PER_DISK)) | ||||
| 		{ | ||||
| 			for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++) | ||||
| 			{ | ||||
| 				const auto& sector = image.get(physicalTrack, physicalSide, sectorId); | ||||
| 				if (sector) | ||||
| 					sectors.push_back(sector); | ||||
| 			} | ||||
| 		} | ||||
|         fillBitmapTo(bits, | ||||
|             cursor, | ||||
|             _config.post_index_gap_ms() * 1000 / _config.clock_rate_us(), | ||||
|             {true, false}); | ||||
|         lastBit = false; | ||||
|  | ||||
| 		return sectors; | ||||
| 	} | ||||
|         for (const auto& sector : sectors) | ||||
|             write_sector(bits, cursor, sector); | ||||
|  | ||||
|     std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, | ||||
| 			const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override | ||||
| 	{ | ||||
| 		if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK)) | ||||
| 			return std::unique_ptr<Fluxmap>(); | ||||
|         if (cursor >= bits.size()) | ||||
|             error("track data overrun"); | ||||
|         fillBitmapTo(bits, cursor, bits.size(), {true, false}); | ||||
|  | ||||
| 		int bitsPerRevolution = 200000.0 / _config.clock_rate_us(); | ||||
| 		std::vector<bool> bits(bitsPerRevolution); | ||||
| 		unsigned cursor = 0; | ||||
|  | ||||
| 		fillBitmapTo(bits, cursor, _config.post_index_gap_ms() * 1000 / _config.clock_rate_us(), { true, false }); | ||||
| 		lastBit = false; | ||||
|  | ||||
| 		for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++) | ||||
| 		{ | ||||
| 			const auto& sectorData = image.get(physicalTrack, physicalSide, sectorId); | ||||
| 			if (sectorData) | ||||
| 				write_sector(bits, cursor, sectorData); | ||||
| 		} | ||||
|  | ||||
| 		if (cursor >= bits.size()) | ||||
| 			Error() << "track data overrun"; | ||||
| 		fillBitmapTo(bits, cursor, bits.size(), { true, false }); | ||||
|  | ||||
| 		std::unique_ptr<Fluxmap> fluxmap(new Fluxmap); | ||||
| 		fluxmap->appendBits(bits, _config.clock_rate_us()*1e3); | ||||
| 		return fluxmap; | ||||
| 	} | ||||
|         auto fluxmap = std::make_unique<Fluxmap>(); | ||||
|         fluxmap->appendBits(bits, | ||||
|             calculatePhysicalClockPeriod(_config.clock_rate_us() * 1e3, 200e6)); | ||||
|         return fluxmap; | ||||
|     } | ||||
|  | ||||
| private: | ||||
| 	const AmigaEncoderProto& _config; | ||||
|     const AmigaEncoderProto& _config; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractEncoder> createAmigaEncoder(const EncoderProto& config) | ||||
| std::unique_ptr<Encoder> createAmigaEncoder(const EncoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractEncoder>(new AmigaEncoder(config)); | ||||
|     return std::unique_ptr<Encoder>(new AmigaEncoder(config)); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,13 +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 | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createApple2Decoder(const DecoderProto& config); | ||||
| #define APPLE2_SECTORS 16 | ||||
|  | ||||
| extern std::unique_ptr<Decoder> createApple2Decoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<Encoder> createApple2Encoder(const EncoderProto& config); | ||||
|  | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,22 @@ | ||||
| syntax = "proto2"; | ||||
|  | ||||
| message Apple2DecoderProto {} | ||||
| 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,33 +1,39 @@ | ||||
| #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 "decoders/decoders.h" | ||||
| #include "sector.h" | ||||
| #include "apple2.h" | ||||
| #include "bytes.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/data/sector.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) | ||||
| { | ||||
| @@ -47,9 +53,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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -64,63 +72,105 @@ static uint8_t combine(uint16_t word) | ||||
|     return word & (word >> 7); | ||||
| } | ||||
|  | ||||
| class Apple2Decoder : public AbstractDecoder | ||||
| class Apple2Decoder : public Decoder | ||||
| { | ||||
| public: | ||||
| 	Apple2Decoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config) | ||||
| 	{} | ||||
|     Apple2Decoder(const DecoderProto& config): Decoder(config) {} | ||||
|  | ||||
|     RecordType advanceToNextRecord() | ||||
| 	{ | ||||
| 		const FluxMatcher* matcher = nullptr; | ||||
| 		_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); | ||||
| 		if (matcher == &SECTOR_RECORD_PATTERN) | ||||
| 			return RecordType::SECTOR_RECORD; | ||||
| 		if (matcher == &DATA_RECORD_PATTERN) | ||||
| 			return RecordType::DATA_RECORD; | ||||
| 		return RecordType::UNKNOWN_RECORD; | ||||
| 	} | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         return seekToPattern(ANY_RECORD_PATTERN); | ||||
|     } | ||||
|  | ||||
|     void decodeSectorRecord() | ||||
| 	{ | ||||
| 		/* Skip ID (as we know it's a APPLE2_SECTOR_RECORD). */ | ||||
| 		readRawBits(24); | ||||
|     void decodeSectorRecord() override | ||||
|     { | ||||
|         if (readRaw24() != APPLE2_SECTOR_RECORD) | ||||
|             return; | ||||
|  | ||||
| 		/* Read header. */ | ||||
|         /* Read header. */ | ||||
|  | ||||
| 		auto header = toBytes(readRawBits(8*8)).slice(0, 8); | ||||
| 		ByteReader br(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 */ | ||||
| 	} | ||||
|         uint8_t volume = combine(br.read_be16()); | ||||
|         _sector->logicalTrack = combine(br.read_be16()); | ||||
|         _sector->logicalSide = _sector->physicalSide; | ||||
|         _sector->logicalSector = combine(br.read_be16()); | ||||
|         uint8_t checksum = combine(br.read_be16()); | ||||
|  | ||||
|     void decodeDataRecord() | ||||
| 	{ | ||||
| 		/* Check ID. */ | ||||
|         // If the checksum is correct, upgrade the sector from MISSING | ||||
|         // to DATA_MISSING in anticipation of its data record | ||||
|         if (checksum == | ||||
|             (volume ^ _sector->logicalTrack ^ _sector->logicalSector)) | ||||
|             _sector->status = | ||||
|                 Sector::DATA_MISSING; /* unintuitive but correct */ | ||||
|  | ||||
| 		Bytes bytes = toBytes(readRawBits(3*8)).slice(0, 3); | ||||
| 		if (bytes.reader().read_be24() != APPLE2_DATA_RECORD) | ||||
| 			return; | ||||
|         if (_sector->logicalSide == 1) | ||||
|             _sector->logicalTrack -= _config.apple2().side_one_track_offset(); | ||||
|  | ||||
| 		/* Read and decode data. */ | ||||
|         /* Sanity check. */ | ||||
|  | ||||
| 		unsigned recordLength = APPLE2_ENCODED_SECTOR_LENGTH + 2; | ||||
| 		bytes = toBytes(readRawBits(recordLength*8)).slice(0, recordLength); | ||||
|         if (_sector->logicalTrack > 100) | ||||
|         { | ||||
|             _sector->status = Sector::MISSING; | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| 		_sector->status = Sector::BAD_CHECKSUM; | ||||
| 		_sector->data = decode_crazy_data(&bytes[0], _sector->status); | ||||
| 	} | ||||
|     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<AbstractDecoder> createApple2Decoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createApple2Decoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new Apple2Decoder(config)); | ||||
|     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(std::shared_ptr<const TrackInfo>& trackInfo, | ||||
|         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.logicalTrack; | ||||
|             if (sector.logicalSide == 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,17 +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_240KB_DISK    78 | ||||
| #define BROTHER_TRACKS_PER_120KB_DISK    39 | ||||
| #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 | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createBrotherDecoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<AbstractEncoder> createBrotherEncoder(const EncoderProto& config); | ||||
| extern std::unique_ptr<Decoder> createBrotherDecoder( | ||||
|     const DecoderProto& config); | ||||
| extern std::unique_ptr<Encoder> createBrotherEncoder( | ||||
|     const EncoderProto& config); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -12,9 +12,7 @@ message BrotherEncoderProto { | ||||
| 	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 string sector_skew = 5 [default = "05a3816b4927"]; | ||||
|  | ||||
| 	optional BrotherFormat format = 6 [default = BROTHER240]; | ||||
| 	optional int32 bias = 7 [default = 0]; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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,18 +1,19 @@ | ||||
| #include "globals.h" | ||||
| #include "sql.h" | ||||
| #include "fluxmap.h" | ||||
| #include "decoders/fluxmapreader.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.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; | ||||
|  | ||||
| @@ -33,91 +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 AbstractDecoder | ||||
| class BrotherDecoder : public Decoder | ||||
| { | ||||
| public: | ||||
|     BrotherDecoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config) | ||||
| 	{} | ||||
|     BrotherDecoder(const DecoderProto& config): Decoder(config) {} | ||||
|  | ||||
|     RecordType advanceToNextRecord() | ||||
| 	{ | ||||
| 		const FluxMatcher* matcher = nullptr; | ||||
| 		_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); | ||||
| 		if (matcher == &SECTOR_RECORD_PATTERN) | ||||
| 			return RecordType::SECTOR_RECORD; | ||||
| 		if (matcher == &DATA_RECORD_PATTERN) | ||||
| 			return RecordType::DATA_RECORD; | ||||
| 		return RecordType::UNKNOWN_RECORD; | ||||
| 	} | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         return seekToPattern(ANY_RECORD_PATTERN); | ||||
|     } | ||||
|  | ||||
|     void decodeSectorRecord() | ||||
| 	{ | ||||
| 		readRawBits(32); | ||||
| 		const auto& rawbits = readRawBits(32); | ||||
| 		const auto& bytes = toBytes(rawbits).slice(0, 4); | ||||
|     void decodeSectorRecord() override | ||||
|     { | ||||
|         if (readRaw32() != BROTHER_SECTOR_RECORD) | ||||
|             return; | ||||
|  | ||||
| 		ByteReader br(bytes); | ||||
| 		_sector->logicalTrack = decode_header_gcr(br.read_be16()); | ||||
| 		_sector->logicalSector = decode_header_gcr(br.read_be16()); | ||||
|         const auto& rawbits = readRawBits(32); | ||||
|         const auto& bytes = toBytes(rawbits).slice(0, 4); | ||||
|  | ||||
| 		/* 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; | ||||
|         ByteReader br(bytes); | ||||
|         _sector->logicalTrack = decode_header_gcr(br.read_be16()); | ||||
|         _sector->logicalSector = decode_header_gcr(br.read_be16()); | ||||
|  | ||||
| 		_sector->status = Sector::DATA_MISSING; | ||||
| 	} | ||||
| 	 | ||||
|     void decodeDataRecord() | ||||
| 	{ | ||||
| 		readRawBits(32); | ||||
|         /* 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; | ||||
|  | ||||
| 		const auto& rawbits = readRawBits(BROTHER_DATA_RECORD_ENCODED_SIZE*8); | ||||
| 		const auto& rawbytes = toBytes(rawbits).slice(0, BROTHER_DATA_RECORD_ENCODED_SIZE); | ||||
|         _sector->status = Sector::DATA_MISSING; | ||||
|     } | ||||
|  | ||||
| 		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(); | ||||
|     void decodeDataRecord() override | ||||
|     { | ||||
|         if (readRaw32() != BROTHER_DATA_RECORD) | ||||
|             return; | ||||
|  | ||||
| 		_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; | ||||
| 	} | ||||
|         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; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractDecoder> createBrotherDecoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createBrotherDecoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new BrotherDecoder(config)); | ||||
|     return std::unique_ptr<Decoder>(new BrotherDecoder(config)); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,212 +1,154 @@ | ||||
| #include "globals.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.h" | ||||
| #include "brother.h" | ||||
| #include "crc.h" | ||||
| #include "writer.h" | ||||
| #include "image.h" | ||||
| #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;              | ||||
|     switch (data) | ||||
|     { | ||||
| #define GCR_ENTRY(gcr, data) \ | ||||
|     case data:               \ | ||||
|         return gcr; | ||||
| #include "data_gcr.h" | ||||
| #undef GCR_ENTRY | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width) | ||||
| static void write_bits( | ||||
|     std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width) | ||||
| { | ||||
| 	cursor += width; | ||||
| 	for (int i=0; i<width; i++) | ||||
| 	{ | ||||
| 		unsigned pos = cursor - i - 1; | ||||
| 		if (pos < bits.size()) | ||||
| 			bits[pos] = data & 1; | ||||
| 		data >>= 1; | ||||
| 	} | ||||
|     cursor += width; | ||||
|     for (int i = 0; i < width; i++) | ||||
|     { | ||||
|         unsigned pos = cursor - i - 1; | ||||
|         if (pos < bits.size()) | ||||
|             bits[pos] = data & 1; | ||||
|         data >>= 1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void write_sector_header(std::vector<bool>& bits, unsigned& cursor, | ||||
| 		int track, int sector) | ||||
| static void write_sector_header( | ||||
|     std::vector<bool>& bits, unsigned& cursor, int track, int sector) | ||||
| { | ||||
| 	write_bits(bits, cursor, 0xffffffff, 31); | ||||
| 	write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32); | ||||
| 	write_bits(bits, cursor, encode_header_gcr(track), 16); | ||||
| 	write_bits(bits, cursor, encode_header_gcr(sector), 16); | ||||
| 	write_bits(bits, cursor, encode_header_gcr(0x2f), 16); | ||||
|     write_bits(bits, cursor, 0xffffffff, 31); | ||||
|     write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32); | ||||
|     write_bits(bits, cursor, encode_header_gcr(track), 16); | ||||
|     write_bits(bits, cursor, encode_header_gcr(sector), 16); | ||||
|     write_bits(bits, cursor, encode_header_gcr(0x2f), 16); | ||||
| } | ||||
|  | ||||
| static void write_sector_data(std::vector<bool>& bits, unsigned& cursor, const Bytes& data) | ||||
| static void write_sector_data( | ||||
|     std::vector<bool>& bits, unsigned& cursor, const Bytes& data) | ||||
| { | ||||
| 	write_bits(bits, cursor, 0xffffffff, 32); | ||||
| 	write_bits(bits, cursor, BROTHER_DATA_RECORD, 32); | ||||
|     write_bits(bits, cursor, 0xffffffff, 32); | ||||
|     write_bits(bits, cursor, BROTHER_DATA_RECORD, 32); | ||||
|  | ||||
| 	uint16_t fifo = 0; | ||||
| 	int width = 0; | ||||
|     uint16_t fifo = 0; | ||||
|     int width = 0; | ||||
|  | ||||
| 	if (data.size() != BROTHER_DATA_RECORD_PAYLOAD) | ||||
| 		Error() << "unsupported sector size"; | ||||
|     if (data.size() != BROTHER_DATA_RECORD_PAYLOAD) | ||||
|         error("unsupported sector size"); | ||||
|  | ||||
| 	auto write_byte = [&](uint8_t byte) | ||||
| 	{ | ||||
| 		fifo |= (byte << (8 - width)); | ||||
| 		width += 8; | ||||
|     auto write_byte = [&](uint8_t byte) | ||||
|     { | ||||
|         fifo |= (byte << (8 - width)); | ||||
|         width += 8; | ||||
|  | ||||
| 		while (width >= 5) | ||||
| 		{ | ||||
| 			uint8_t quintet = fifo >> 11; | ||||
| 			fifo <<= 5; | ||||
| 			width -= 5; | ||||
|         while (width >= 5) | ||||
|         { | ||||
|             uint8_t quintet = fifo >> 11; | ||||
|             fifo <<= 5; | ||||
|             width -= 5; | ||||
|  | ||||
| 			write_bits(bits, cursor, encode_data_gcr(quintet), 8); | ||||
| 		} | ||||
| 	}; | ||||
|             write_bits(bits, cursor, encode_data_gcr(quintet), 8); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| 	for (uint8_t byte : data) | ||||
| 		write_byte(byte); | ||||
|     for (uint8_t byte : data) | ||||
|         write_byte(byte); | ||||
|  | ||||
| 	uint32_t realCrc = crcbrother(data); | ||||
| 	write_byte(realCrc>>16); | ||||
| 	write_byte(realCrc>>8); | ||||
| 	write_byte(realCrc); | ||||
| 	write_byte(0x58); /* magic */ | ||||
|     uint32_t realCrc = crcbrother(data); | ||||
|     write_byte(realCrc >> 16); | ||||
|     write_byte(realCrc >> 8); | ||||
|     write_byte(realCrc); | ||||
|     write_byte(0x58); /* magic */ | ||||
|     write_byte(0xd4); | ||||
|     while (width != 0) | ||||
|         write_byte(0); | ||||
| } | ||||
|  | ||||
| static int charToInt(char c) | ||||
| { | ||||
| 	if (isdigit(c)) | ||||
| 		return c - '0'; | ||||
| 	return 10 + tolower(c) - 'a'; | ||||
| } | ||||
|  | ||||
| class BrotherEncoder : public AbstractEncoder | ||||
| class BrotherEncoder : public Encoder | ||||
| { | ||||
| public: | ||||
| 	BrotherEncoder(const EncoderProto& config): | ||||
| 		AbstractEncoder(config), | ||||
| 		_config(config.brother()) | ||||
| 	{} | ||||
|     BrotherEncoder(const EncoderProto& config): | ||||
|         Encoder(config), | ||||
|         _config(config.brother()) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| public: | ||||
| 	std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override | ||||
| 	{ | ||||
| 		std::vector<std::shared_ptr<Sector>> sectors; | ||||
|     std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo, | ||||
|         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 logicalTrack; | ||||
| 		if (physicalSide != 0) | ||||
| 			return sectors; | ||||
| 		physicalTrack -= _config.bias(); | ||||
| 		switch (_config.format()) | ||||
| 		{ | ||||
| 			case BROTHER120: | ||||
| 				if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2)) | ||||
| 						|| (physicalTrack & 1)) | ||||
| 					return sectors; | ||||
| 				logicalTrack = physicalTrack/2; | ||||
| 				break; | ||||
|  | ||||
| 			case BROTHER240: | ||||
| 				if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK)) | ||||
| 					return sectors; | ||||
| 				logicalTrack = physicalTrack; | ||||
| 				break; | ||||
| 		} | ||||
|  | ||||
|         for (int sectorId=0; sectorId<BROTHER_SECTORS_PER_TRACK; sectorId++) | ||||
|         int sectorCount = 0; | ||||
|         for (const auto& sectorData : sectors) | ||||
|         { | ||||
|             const auto& sector = image.get(logicalTrack, 0, sectorId); | ||||
|             if (sector) | ||||
|                 sectors.push_back(sector); | ||||
| 		} | ||||
|             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(); | ||||
|  | ||||
| 		return sectors; | ||||
| 	} | ||||
|             fillBitmapTo(bits, cursor, headerCursor, {true, false}); | ||||
|             write_sector_header(bits, | ||||
|                 cursor, | ||||
|                 sectorData->logicalTrack, | ||||
|                 sectorData->logicalSector); | ||||
|             fillBitmapTo(bits, cursor, dataCursor, {true, false}); | ||||
|             write_sector_data(bits, cursor, sectorData->data); | ||||
|  | ||||
|     std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, | ||||
| 			const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override | ||||
| 	{ | ||||
| 		int logicalTrack; | ||||
| 		if (physicalSide != 0) | ||||
| 			return std::unique_ptr<Fluxmap>(); | ||||
| 		physicalTrack -= _config.bias(); | ||||
| 		switch (_config.format()) | ||||
| 		{ | ||||
| 			case BROTHER120: | ||||
| 				if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2)) | ||||
| 						|| (physicalTrack & 1)) | ||||
| 					return std::unique_ptr<Fluxmap>(); | ||||
| 				logicalTrack = physicalTrack/2; | ||||
| 				break; | ||||
|             sectorCount++; | ||||
|         } | ||||
|  | ||||
| 			case BROTHER240: | ||||
| 				if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK)) | ||||
| 					return std::unique_ptr<Fluxmap>(); | ||||
| 				logicalTrack = physicalTrack; | ||||
| 				break; | ||||
| 		} | ||||
|         if (cursor >= bits.size()) | ||||
|             error("track data overrun"); | ||||
|         fillBitmapTo(bits, cursor, bits.size(), {true, false}); | ||||
|  | ||||
| 		int bitsPerRevolution = 200000.0 / _config.clock_rate_us(); | ||||
| 		const std::string& skew = _config.sector_skew(); | ||||
| 		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 = _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(); | ||||
|  | ||||
| 			const auto& sectorData = image.get(logicalTrack, 0, sectorId); | ||||
|  | ||||
| 			fillBitmapTo(bits, cursor, headerCursor, { true, false }); | ||||
| 			write_sector_header(bits, cursor, logicalTrack, sectorId); | ||||
| 			fillBitmapTo(bits, cursor, dataCursor, { true, false }); | ||||
| 			write_sector_data(bits, cursor, sectorData->data); | ||||
| 		} | ||||
|  | ||||
| 		if (cursor >= bits.size()) | ||||
| 			Error() << "track data overrun"; | ||||
| 		fillBitmapTo(bits, cursor, bits.size(), { true, false }); | ||||
|  | ||||
| 		// The pre-index gap is not normally reported. | ||||
| 		// std::cerr << "pre-index gap " << 200.0 - (double)cursor*clockRateUs/1e3 << std::endl; | ||||
| 		 | ||||
| 		std::unique_ptr<Fluxmap> fluxmap(new Fluxmap); | ||||
| 		fluxmap->appendBits(bits, _config.clock_rate_us()*1e3); | ||||
| 		return fluxmap; | ||||
| 	} | ||||
|         std::unique_ptr<Fluxmap> fluxmap(new Fluxmap); | ||||
|         fluxmap->appendBits(bits, _config.clock_rate_us() * 1e3); | ||||
|         return fluxmap; | ||||
|     } | ||||
|  | ||||
| private: | ||||
| 	const BrotherEncoderProto& _config; | ||||
|  | ||||
|     const BrotherEncoderProto& _config; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractEncoder> createBrotherEncoder(const EncoderProto& config) | ||||
| std::unique_ptr<Encoder> createBrotherEncoder(const EncoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractEncoder>(new BrotherEncoder(config)); | ||||
|     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,14 +1,14 @@ | ||||
| #ifndef C64_H | ||||
| #define C64_H | ||||
|  | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/encoders/encoders.h" | ||||
|  | ||||
| #define C64_SECTOR_RECORD    0xffd49 | ||||
| #define C64_DATA_RECORD      0xffd57 | ||||
| #define C64_SECTOR_LENGTH    256 | ||||
| #define C64_SECTOR_RECORD 0xffd49 | ||||
| #define C64_DATA_RECORD 0xffd57 | ||||
| #define C64_SECTOR_LENGTH 256 | ||||
|  | ||||
| /* Source: http://www.unusedino.de/ec64/technical/formats/g64.html  | ||||
| /* 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) | ||||
| @@ -17,17 +17,21 @@ | ||||
|    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 | ||||
| #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 | ||||
|  | ||||
| #define C64_TRACKS_PER_DISK         40 | ||||
| #define C64_BAM_TRACK               17 | ||||
| #define C64_TRACKS_PER_DISK 40 | ||||
| #define C64_BAM_TRACK 17 | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createCommodore64Decoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<AbstractEncoder> createCommodore64Encoder(const EncoderProto& config); | ||||
| 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 | ||||
|   | ||||
| @@ -1,13 +1,11 @@ | ||||
| syntax = "proto2"; | ||||
|  | ||||
| import "lib/common.proto"; | ||||
| 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."]; | ||||
| 	optional double clock_compensation_factor = 2 [default=1.0, | ||||
| 		(help) = "scale the output clock by this much."]; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,28 +1,31 @@ | ||||
| #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 "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; | ||||
| } | ||||
| @@ -37,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); | ||||
| @@ -51,56 +54,50 @@ static Bytes decode(const std::vector<bool>& bits) | ||||
|     return output; | ||||
| } | ||||
|  | ||||
| class Commodore64Decoder : public AbstractDecoder | ||||
| class Commodore64Decoder : public Decoder | ||||
| { | ||||
| public: | ||||
| 	Commodore64Decoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config) | ||||
| 	{} | ||||
|     Commodore64Decoder(const DecoderProto& config): Decoder(config) {} | ||||
|  | ||||
|     RecordType advanceToNextRecord() | ||||
| 	{ | ||||
| 		const FluxMatcher* matcher = nullptr; | ||||
| 		_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); | ||||
| 		if (matcher == &SECTOR_RECORD_PATTERN) | ||||
| 			return RecordType::SECTOR_RECORD; | ||||
| 		if (matcher == &DATA_RECORD_PATTERN) | ||||
| 			return RecordType::DATA_RECORD; | ||||
| 		return RecordType::UNKNOWN_RECORD; | ||||
| 	} | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         return seekToPattern(ANY_RECORD_PATTERN); | ||||
|     } | ||||
|  | ||||
|     void decodeSectorRecord() | ||||
| 	{ | ||||
| 		readRawBits(20); | ||||
|     void decodeSectorRecord() override | ||||
|     { | ||||
|         if (readRaw20() != C64_SECTOR_RECORD) | ||||
|             return; | ||||
|  | ||||
| 		const auto& bits = readRawBits(5*10); | ||||
| 		const auto& bytes = decode(bits).slice(0, 5); | ||||
|         const auto& bits = readRawBits(5 * 10); | ||||
|         const auto& bytes = decode(bits).slice(0, 5); | ||||
|  | ||||
| 		uint8_t checksum = bytes[0]; | ||||
| 		_sector->logicalSector = bytes[1]; | ||||
| 		_sector->logicalSide = 0; | ||||
| 		_sector->logicalTrack = bytes[2] - 1; | ||||
| 		if (checksum == xorBytes(bytes.slice(1, 4))) | ||||
| 			_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */ | ||||
| 	} | ||||
|         uint8_t checksum = bytes[0]; | ||||
|         _sector->logicalSector = bytes[1]; | ||||
|         _sector->logicalSide = 0; | ||||
|         _sector->logicalTrack = bytes[2] - 1; | ||||
|         if (checksum == xorBytes(bytes.slice(1, 4))) | ||||
|             _sector->status = | ||||
|                 Sector::DATA_MISSING; /* unintuitive but correct */ | ||||
|     } | ||||
|  | ||||
|     void decodeDataRecord() | ||||
| 	{ | ||||
| 		readRawBits(20); | ||||
|     void decodeDataRecord() override | ||||
|     { | ||||
|         if (readRaw20() != C64_DATA_RECORD) | ||||
|             return; | ||||
|  | ||||
| 		const auto& bits = readRawBits(259*10); | ||||
| 		const auto& bytes = decode(bits).slice(0, 259); | ||||
|         const auto& bits = readRawBits(259 * 10); | ||||
|         const auto& bytes = decode(bits).slice(0, 259); | ||||
|  | ||||
| 		_sector->data = bytes.slice(0, C64_SECTOR_LENGTH); | ||||
| 		uint8_t gotChecksum = xorBytes(_sector->data); | ||||
| 		uint8_t wantChecksum = bytes[256]; | ||||
| 		_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
| 	} | ||||
|         _sector->data = bytes.slice(0, C64_SECTOR_LENGTH); | ||||
|         uint8_t gotChecksum = xorBytes(_sector->data); | ||||
|         uint8_t wantChecksum = bytes[256]; | ||||
|         _sector->status = | ||||
|             (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractDecoder> createCommodore64Decoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createCommodore64Decoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new Commodore64Decoder(config)); | ||||
|     return std::unique_ptr<Decoder>(new Commodore64Decoder(config)); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,161 +1,104 @@ | ||||
| #include "globals.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.h" | ||||
| #include "c64.h" | ||||
| #include "crc.h" | ||||
| #include "sector.h" | ||||
| #include "writer.h" | ||||
| #include "image.h" | ||||
| #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 "bytes.h" | ||||
| #include "lib/core/bytes.h" | ||||
|  | ||||
| static bool lastBit; | ||||
|  | ||||
| static double clockRateUsForTrack(unsigned track) | ||||
| { | ||||
|     /* | ||||
|      * Track   # Sectors/Track Speed Zone  bits/rotation        | ||||
|      * 1 – 17          21          3       61,538.4 | ||||
|      * 18 – 24         19          2       57,142.8 | ||||
|      * 25 – 30         18          1       53,333.4 | ||||
|      * 31 – 35         17          0       50,000.0 | ||||
|     */ | ||||
|     if (track < 17) | ||||
|         return 200000.0/61538.4; | ||||
|     if (track < 24) | ||||
|         return 200000.0/57142.8; | ||||
|     if (track < 30) | ||||
|         return 200000.0/53333.4; | ||||
|     return 200000.0/50000.0; | ||||
|  | ||||
| } | ||||
|  | ||||
| static unsigned sectorsForTrack(unsigned track) | ||||
| { | ||||
|     /* | ||||
|      * Track   Sectors/track   # Sectors   Storage in Bytes | ||||
|      *   -----   -------------   ---------   ---------------- | ||||
|      *    1-17        21            357           7820 | ||||
|      *   18-24        19            133           7170 | ||||
|      *   25-30        18            108           6300 | ||||
|      *   31-40(*)     17             85           6020 | ||||
|      *                              --- | ||||
|      *                              683 (for a 35 track image) | ||||
|      */ | ||||
|     if (track < 17) | ||||
|         return 21; | ||||
|     if (track < 24) | ||||
|         return 19; | ||||
|     if (track < 30) | ||||
|         return 18; | ||||
|     return 17; | ||||
| } | ||||
|  | ||||
| static int encode_data_gcr(uint8_t data) | ||||
| { | ||||
|     switch (data) | ||||
|     { | ||||
|         #define GCR_ENTRY(gcr, data) \ | ||||
|             case data: return gcr; | ||||
|         #include "data_gcr.h" | ||||
|         #undef GCR_ENTRY | ||||
| #define GCR_ENTRY(gcr, data) \ | ||||
|     case data:               \ | ||||
|         return gcr; | ||||
| #include "data_gcr.h" | ||||
| #undef GCR_ENTRY | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static void write_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)  //Range-based for loop | ||||
|     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) | ||||
| static void write_bits( | ||||
|     std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width) | ||||
| { | ||||
|     cursor += width; | ||||
|     for (int i=0; i<width; i++) | ||||
|     for (int i = 0; i < width; i++) | ||||
|     { | ||||
|         unsigned pos = cursor - i - 1;               | ||||
|         unsigned pos = cursor - i - 1; | ||||
|         if (pos < bits.size()) | ||||
|             bits[pos] = data & 1; | ||||
|         data >>= 1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void bindump(std::ostream& stream, std::vector<bool>& buffer) | ||||
| { | ||||
|     size_t pos = 0; | ||||
|  | ||||
|     while ((pos < buffer.size()) and (pos <520)) | ||||
|     { | ||||
|         stream << fmt::format("{:5d} : ", pos); | ||||
|         for (int i=0; i<40; i++) | ||||
|         { | ||||
|             if ((pos+i) < buffer.size()) | ||||
|                 stream << fmt::format("{:01b}", (buffer[pos+i])); | ||||
|             else | ||||
|                 stream << "-- "; | ||||
|             if ((((pos + i + 1) % 8) == 0) and i != 0) | ||||
|                 stream << "  "; | ||||
|  | ||||
|         } | ||||
|         stream << std::endl; | ||||
|         pos += 40; | ||||
|     } | ||||
| } | ||||
| 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 | ||||
|     */ | ||||
|      * 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; | ||||
| @@ -163,124 +106,109 @@ static std::vector<bool> encode_data(uint8_t input) | ||||
|     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)      | ||||
|     // 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 | ||||
|     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 | ||||
|         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 | ||||
|             // 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   | ||||
|         } | ||||
|         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 | ||||
|             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; | ||||
|             b = b - 2; | ||||
|         } | ||||
|     } | ||||
|     return output; | ||||
| } | ||||
|  | ||||
| class Commodore64Encoder : public AbstractEncoder | ||||
| class Commodore64Encoder : public Encoder | ||||
| { | ||||
| public: | ||||
| 	Commodore64Encoder(const EncoderProto& config): | ||||
|         AbstractEncoder(config), | ||||
| 		_config(config.c64()) | ||||
| 	{} | ||||
|     Commodore64Encoder(const EncoderProto& config): | ||||
|         Encoder(config), | ||||
|         _config(config.c64()) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| public: | ||||
| 	std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override | ||||
| 	{ | ||||
| 		std::vector<std::shared_ptr<Sector>> sectors; | ||||
|  | ||||
|         if (physicalSide == 0) | ||||
|         { | ||||
|             int logicalTrack = physicalTrack / 2; | ||||
|             unsigned numSectors = sectorsForTrack(logicalTrack); | ||||
|             for (int sectorId=0; sectorId<numSectors; sectorId++) | ||||
|             { | ||||
|                 const auto& sector = image.get(logicalTrack, 0, sectorId); | ||||
|                 if (sector) | ||||
|                     sectors.push_back(sector); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| 		return sectors; | ||||
| 	} | ||||
|  | ||||
|     std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, | ||||
|             const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override | ||||
|     std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo, | ||||
|         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. | ||||
|         */ | ||||
|         /* 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. | ||||
|          */ | ||||
|  | ||||
|         if (physicalSide != 0) | ||||
|             return std::unique_ptr<Fluxmap>(); | ||||
|  | ||||
|         const auto& sectorData = image.get(C64_BAM_TRACK*2, 0, 0); //Read de BAM to get the DISK ID bytes | ||||
|         const auto& sectorData = image.get( | ||||
|             C64_BAM_TRACK, 0, 0); // Read de BAM to get the DISK ID bytes | ||||
|         if (sectorData) | ||||
|         { | ||||
|             ByteReader br(sectorData->data); | ||||
|             br.seek(162); //goto position of the first Disk ID Byte | ||||
|             br.seek(162); // goto position of the first Disk ID Byte | ||||
|             _formatByte1 = br.read_8(); | ||||
|             _formatByte2 = br.read_8(); | ||||
|         } | ||||
|         else | ||||
|             _formatByte1 = _formatByte2 = 0; | ||||
|          | ||||
|         int logicalTrack = physicalTrack / 2; | ||||
|         double clockRateUs = clockRateUsForTrack(logicalTrack) * _config.clock_compensation_factor(); | ||||
|  | ||||
|         double clockRateUs = clockPeriodForC64Track(trackInfo->logicalTrack); | ||||
|         int bitsPerRevolution = 200000.0 / clockRateUs; | ||||
|  | ||||
|         std::vector<bool> bits(bitsPerRevolution); | ||||
|         unsigned cursor = 0; | ||||
|  | ||||
|         fillBitmapTo(bits, cursor, _config.post_index_gap_us() / clockRateUs, { true, false }); | ||||
|         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() << fmt::format("track data overrun by {} bits", cursor - bits.size()); | ||||
|         fillBitmapTo(bits, cursor, bits.size(), { true, false }); | ||||
|             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, clockRateUs*1e3); | ||||
|         fluxmap->appendBits( | ||||
|             bits, calculatePhysicalClockPeriod(clockRateUs * 1e3, 200e6)); | ||||
|         return fluxmap; | ||||
|     } | ||||
|  | ||||
| private: | ||||
| 	void writeSector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<Sector>& sector) const | ||||
|     void writeSector(std::vector<bool>& bits, | ||||
|         unsigned& cursor, | ||||
|         std::shared_ptr<const Sector> sector) const | ||||
|     { | ||||
|         /* Source: http://www.unusedino.de/ec64/technical/formats/g64.html  | ||||
|         /* 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) | ||||
| @@ -288,23 +216,26 @@ private: | ||||
|          * 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)) | ||||
|          */ | ||||
|         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() << fmt::format("unsupported sector size {} --- you must pick 256", sector->data.size());     | ||||
|                 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 */ | ||||
|             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: | ||||
|              *  | ||||
|              * 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 | ||||
| @@ -313,11 +244,14 @@ private: | ||||
|              *   05 - Format ID byte #1 | ||||
|              *   06-07 - $0F ("off" bytes) | ||||
|              */ | ||||
|             uint8_t encodedTrack = ((sector->logicalTrack) + 1); // C64 track numbering starts with 1. Fluxengine with 0. | ||||
|             uint8_t encodedTrack = | ||||
|                 ((sector->logicalTrack) + | ||||
|                     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); | ||||
|             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)); | ||||
| @@ -328,22 +262,26 @@ private: | ||||
|             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 */ | ||||
|             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 */ | ||||
|             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: | ||||
|              *  | ||||
|              * 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) | ||||
|              *      102-103 - $00 ("off" bytes, to make the sector size a | ||||
|              * multiple of 5) | ||||
|              */ | ||||
|  | ||||
|             write_bits(bits, cursor, encode_data(C64_DATA_BLOCK_ID)); | ||||
| @@ -353,29 +291,28 @@ private: | ||||
|             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(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 */ | ||||
|  | ||||
|             // 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; | ||||
|     const Commodore64EncoderProto& _config; | ||||
|     uint8_t _formatByte1; | ||||
|     uint8_t _formatByte2; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractEncoder> createCommodore64Encoder(const EncoderProto& config) | ||||
| std::unique_ptr<Encoder> createCommodore64Encoder(const EncoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractEncoder>(new Commodore64Encoder(config)); | ||||
|     return std::unique_ptr<Encoder>(new Commodore64Encoder(config)); | ||||
| } | ||||
|  | ||||
| // vim: sw=4 ts=4 et | ||||
|  | ||||
|   | ||||
| @@ -1,28 +1,31 @@ | ||||
| #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 "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; | ||||
| } | ||||
| @@ -37,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); | ||||
| @@ -51,63 +54,58 @@ static Bytes decode(const std::vector<bool>& bits) | ||||
|     return output; | ||||
| } | ||||
|  | ||||
| class DurangoF85Decoder : public AbstractDecoder | ||||
| class DurangoF85Decoder : public Decoder | ||||
| { | ||||
| public: | ||||
| 	DurangoF85Decoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config) | ||||
| 	{} | ||||
|     DurangoF85Decoder(const DecoderProto& config): Decoder(config) {} | ||||
|  | ||||
|     RecordType advanceToNextRecord() | ||||
| 	{ | ||||
| 		const FluxMatcher* matcher = nullptr; | ||||
| 		_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); | ||||
| 		if (matcher == &SECTOR_RECORD_PATTERN) | ||||
| 			return RecordType::SECTOR_RECORD; | ||||
| 		if (matcher == &DATA_RECORD_PATTERN) | ||||
| 			return RecordType::DATA_RECORD; | ||||
| 		return RecordType::UNKNOWN_RECORD; | ||||
| 	} | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         return seekToPattern(ANY_RECORD_PATTERN); | ||||
|     } | ||||
|  | ||||
|     void decodeSectorRecord() | ||||
| 	{ | ||||
| 		/* Skip sync bits and ID byte. */ | ||||
|     void decodeSectorRecord() override | ||||
|     { | ||||
|         /* Skip sync bits and ID byte. */ | ||||
|  | ||||
| 		readRawBits(24); | ||||
|         if (readRaw24() != F85_SECTOR_RECORD) | ||||
|             return; | ||||
|  | ||||
| 		/* Read header. */ | ||||
|         /* Read header. */ | ||||
|  | ||||
| 		const auto& bytes = decode(readRawBits(6*10)); | ||||
|         const auto& bytes = decode(readRawBits(6 * 10)); | ||||
|  | ||||
| 		_sector->logicalSector = bytes[2]; | ||||
| 		_sector->logicalSide = 0; | ||||
| 		_sector->logicalTrack = bytes[0]; | ||||
|         _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 */ | ||||
| 	} | ||||
|         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() | ||||
| 	{ | ||||
| 		/* Skip sync bits ID byte. */ | ||||
|     void decodeDataRecord() override | ||||
|     { | ||||
|         /* Skip sync bits ID byte. */ | ||||
|  | ||||
| 		readRawBits(24); | ||||
|         if (readRaw24() != F85_DATA_RECORD) | ||||
|             return; | ||||
|  | ||||
| 		const auto& bytes = decode(readRawBits((F85_SECTOR_LENGTH+3)*10)) | ||||
| 			.slice(0, F85_SECTOR_LENGTH+3); | ||||
| 		ByteReader br(bytes); | ||||
|         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; | ||||
| 	} | ||||
|         _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<AbstractDecoder> createDurangoF85Decoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createDurangoF85Decoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new DurangoF85Decoder(config)); | ||||
|     return std::unique_ptr<Decoder>(new DurangoF85Decoder(config)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -2,9 +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 | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createDurangoF85Decoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<Decoder> createDurangoF85Decoder( | ||||
|     const DecoderProto& config); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -1,23 +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 "decoders/decoders.h" | ||||
| #include "sector.h" | ||||
| #include "fb100.h" | ||||
| #include "crc.h" | ||||
| #include "bytes.h" | ||||
| #include "decoders/rawbits.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 | ||||
| @@ -97,52 +98,46 @@ static uint16_t checksum(const Bytes& bytes) | ||||
|     return (crchi << 8) | crclo; | ||||
| } | ||||
|  | ||||
| class Fb100Decoder : public AbstractDecoder | ||||
| class Fb100Decoder : public Decoder | ||||
| { | ||||
| public: | ||||
| 	Fb100Decoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config) | ||||
| 	{} | ||||
|     Fb100Decoder(const DecoderProto& config): Decoder(config) {} | ||||
|  | ||||
|     RecordType advanceToNextRecord() | ||||
| 	{ | ||||
| 		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; | ||||
| 	} | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         return seekToPattern(SECTOR_ID_PATTERN); | ||||
|     } | ||||
|  | ||||
|     void decodeSectorRecord() | ||||
| 	{ | ||||
| 		auto rawbits = readRawBits(FB100_RECORD_SIZE*16); | ||||
|     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); | ||||
|         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; | ||||
|         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); | ||||
|         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; | ||||
| 	} | ||||
|         _sector->status = (wantPayloadCrc == gotPayloadCrc) | ||||
|                               ? Sector::OK | ||||
|                               : Sector::BAD_CHECKSUM; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractDecoder> createFb100Decoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createFb100Decoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new Fb100Decoder(config)); | ||||
|     return std::unique_ptr<Decoder>(new Fb100Decoder(config)); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,6 @@ | ||||
| #define FB100_ID_SIZE 17 | ||||
| #define FB100_PAYLOAD_SIZE 0x500 | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createFb100Decoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<Decoder> createFb100Decoder(const DecoderProto& config); | ||||
|  | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -1,30 +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 "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 "proto.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 | ||||
| @@ -32,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 | ||||
| @@ -40,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 | ||||
| @@ -48,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 | ||||
| @@ -56,7 +58,7 @@ const FluxPattern FM_DAM2_PATTERN(16, 0xf56f); | ||||
|  */ | ||||
| const FluxPattern FM_TRS80DAM1_PATTERN(16, 0xf56b); | ||||
|  | ||||
| /*  | ||||
| /* | ||||
|  * TRS80DAM2 record: | ||||
|  * flux:   XXXX-X-X-XX-XXX- = 0xf56e | ||||
|  * clock:  X X - - - X X X  = 0xc7 | ||||
| @@ -72,157 +74,178 @@ const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56e); | ||||
|  *                       ^^^^^ | ||||
|  * 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( | ||||
|     { | ||||
|         &MFM_PATTERN, | ||||
|         &FM_IDAM_PATTERN, | ||||
|         &FM_DAM1_PATTERN, | ||||
|         &FM_DAM2_PATTERN, | ||||
|         &FM_TRS80DAM1_PATTERN, | ||||
|         &FM_TRS80DAM2_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 AbstractDecoder | ||||
| class IbmDecoder : public Decoder | ||||
| { | ||||
| public: | ||||
|     IbmDecoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config), | ||||
| 		_config(config.ibm()) | ||||
|     {} | ||||
|         Decoder(config), | ||||
|         _config(config.ibm()) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     RecordType advanceToNextRecord() override | ||||
| 	{ | ||||
| 		const FluxMatcher* matcher = nullptr; | ||||
| 		_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); | ||||
|  | ||||
| 		/* If this is the MFM prefix byte, the the decoder is going to expect three | ||||
| 		 * extra bytes on the front of the header. */ | ||||
| 		_currentHeaderLength = (matcher == &MFM_PATTERN) ? 3 : 0; | ||||
|  | ||||
| 		Fluxmap::Position here = tell(); | ||||
| 		resetFluxDecoder(); | ||||
| 		if (_currentHeaderLength > 0) | ||||
| 			readRawBits(_currentHeaderLength*16); | ||||
| 		auto idbits = readRawBits(16); | ||||
| 		const Bytes idbytes = decodeFmMfm(idbits); | ||||
| 		uint8_t id = idbytes.slice(0, 1)[0]; | ||||
| 		if (eof()) | ||||
| 			return RecordType::UNKNOWN_RECORD; | ||||
| 		seek(here); | ||||
| 		 | ||||
| 		switch (id) | ||||
| 		{ | ||||
| 			case IBM_IDAM: | ||||
| 				return RecordType::SECTOR_RECORD; | ||||
|  | ||||
| 			case IBM_DAM1: | ||||
| 			case IBM_DAM2: | ||||
| 			case IBM_TRS80DAM1: | ||||
| 			case IBM_TRS80DAM2: | ||||
| 				return RecordType::DATA_RECORD; | ||||
| 		} | ||||
| 		return RecordType::UNKNOWN_RECORD; | ||||
| 	} | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         return seekToPattern(ANY_RECORD_PATTERN); | ||||
|     } | ||||
|  | ||||
|     void decodeSectorRecord() override | ||||
| 	{ | ||||
| 		unsigned recordSize = _currentHeaderLength + IBM_IDAM_LEN; | ||||
| 		auto bits = readRawBits(recordSize*16); | ||||
| 		auto bytes = decodeFmMfm(bits).slice(0, recordSize); | ||||
|     { | ||||
|         /* This is really annoying because the IBM record scheme has a | ||||
|          * variable-sized header _and_ the checksum covers this header too. So | ||||
|          * we have to read and decode a byte at a time until we know where the | ||||
|          * record itself starts, saving the bytes for the checksumming later. | ||||
|          */ | ||||
|  | ||||
| 		IbmDecoderProto::TrackdataProto trackdata; | ||||
| 		getTrackFormat(trackdata, _sector->physicalCylinder, _sector->physicalHead); | ||||
|         Bytes bytes; | ||||
|         ByteWriter bw(bytes); | ||||
|  | ||||
| 		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(); | ||||
| 		_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 */ | ||||
|         auto readByte = [&]() | ||||
|         { | ||||
|             auto bits = readRawBits(16); | ||||
|             auto bytes = decodeFmMfm(bits).slice(0, 1); | ||||
|             uint8_t byte = bytes[0]; | ||||
|             bw.write_8(byte); | ||||
|             return byte; | ||||
|         }; | ||||
|  | ||||
| 		if (trackdata.swap_sides()) | ||||
| 			_sector->logicalSide ^= 1; | ||||
| 		if (trackdata.ignore_side_byte()) | ||||
| 			_sector->logicalSide = _sector->physicalHead; | ||||
| 		if (trackdata.ignore_track_byte()) | ||||
| 			_sector->logicalTrack = _sector->physicalCylinder; | ||||
| 	} | ||||
|         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, _sector->physicalTrack, _sector->physicalSide); | ||||
|  | ||||
|         _sector->logicalTrack = br.read_8(); | ||||
|         _sector->logicalSide = br.read_8(); | ||||
|         _sector->logicalSector = br.read_8(); | ||||
|         _currentSectorSize = 1 << (br.read_8() + 7); | ||||
|  | ||||
|         uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, br.pos)); | ||||
|         uint16_t wantCrc = br.read_be16(); | ||||
|         if (wantCrc == gotCrc) | ||||
|             _sector->status = | ||||
|                 Sector::DATA_MISSING; /* correct but unintuitive */ | ||||
|  | ||||
|         if (trackdata.ignore_side_byte()) | ||||
|             _sector->logicalSide = | ||||
|                 Layout::remapSidePhysicalToLogical(_sector->physicalSide); | ||||
|         _sector->logicalSide ^= trackdata.invert_side_byte(); | ||||
|         if (trackdata.ignore_track_byte()) | ||||
|             _sector->logicalTrack = _sector->physicalTrack; | ||||
|  | ||||
|         for (int sector : trackdata.ignore_sector()) | ||||
|             if (_sector->logicalSector == sector) | ||||
|             { | ||||
|                 _sector->status = Sector::MISSING; | ||||
|                 break; | ||||
|             } | ||||
|     } | ||||
|  | ||||
|     void decodeDataRecord() override | ||||
| 	{ | ||||
| 		unsigned recordLength = _currentHeaderLength + _currentSectorSize + 3; | ||||
| 		auto bits = readRawBits(recordLength*16); | ||||
| 		auto bytes = decodeFmMfm(bits).slice(0, recordLength); | ||||
|     { | ||||
|         /* This is the same deal as the sector record. */ | ||||
|  | ||||
| 		ByteReader br(bytes); | ||||
| 		br.seek(_currentHeaderLength); | ||||
| 		br.read_8(); /* skip ID byte */ | ||||
|         Bytes bytes; | ||||
|         ByteWriter bw(bytes); | ||||
|  | ||||
| 		_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; | ||||
| 	} | ||||
|         auto readByte = [&]() | ||||
|         { | ||||
|             auto bits = readRawBits(16); | ||||
|             auto bytes = decodeFmMfm(bits).slice(0, 1); | ||||
|             uint8_t byte = bytes[0]; | ||||
|             bw.write_8(byte); | ||||
|             return byte; | ||||
|         }; | ||||
|  | ||||
| 	std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override | ||||
| 	{ | ||||
| 		IbmDecoderProto::TrackdataProto trackdata; | ||||
| 		getTrackFormat(trackdata, cylinder, head); | ||||
|         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; | ||||
|  | ||||
| 		std::set<unsigned> s; | ||||
| 		if (trackdata.has_sectors()) | ||||
| 		{ | ||||
| 			for (int sectorId : trackdata.sectors().sector()) | ||||
| 				s.insert(sectorId); | ||||
| 		} | ||||
| 		else if (trackdata.has_sector_range()) | ||||
| 		{ | ||||
| 			int sectorId = trackdata.sector_range().min_sector(); | ||||
| 			while (sectorId <= trackdata.sector_range().max_sector()) | ||||
| 			{ | ||||
| 				s.insert(sectorId); | ||||
| 				sectorId++; | ||||
| 			} | ||||
| 		} | ||||
| 		return s; | ||||
| 	} | ||||
|         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; | ||||
|  | ||||
|         auto layout = Layout::getLayoutOfTrack( | ||||
|             _sector->logicalTrack, _sector->logicalSide); | ||||
|         if (_currentSectorSize != layout->sectorSize) | ||||
|             std::cerr << fmt::format( | ||||
|                 "Warning: configured sector size for t{}.h{}.s{} is {} bytes " | ||||
|                 "but that seen on disk is {} bytes\n", | ||||
|                 _sector->logicalTrack, | ||||
|                 _sector->logicalSide, | ||||
|                 _sector->logicalSector, | ||||
|                 layout->sectorSize, | ||||
|                 _currentSectorSize); | ||||
|     } | ||||
|  | ||||
| private: | ||||
| 	void getTrackFormat(IbmDecoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head) const | ||||
| 	{ | ||||
| 		trackdata.Clear(); | ||||
| 		for (const auto& f : _config.trackdata()) | ||||
| 		{ | ||||
| 			if (f.has_cylinder() && (f.cylinder() != cylinder)) | ||||
| 				continue; | ||||
| 			if (f.has_head() && (f.head() != head)) | ||||
| 				continue; | ||||
|     void getTrackFormat(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); | ||||
| 		} | ||||
| 	} | ||||
|             trackdata.MergeFrom(f); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| private: | ||||
| 	const IbmDecoderProto& _config; | ||||
|     const IbmDecoderProto& _config; | ||||
|     unsigned _currentSectorSize; | ||||
|     unsigned _currentHeaderLength; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractDecoder> createIbmDecoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createIbmDecoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new IbmDecoder(config)); | ||||
|     return std::unique_ptr<Decoder>(new IbmDecoder(config)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,13 +1,15 @@ | ||||
| #include "globals.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.h" | ||||
| #include "ibm.h" | ||||
| #include "crc.h" | ||||
| #include "writer.h" | ||||
| #include "image.h" | ||||
| #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: | ||||
| @@ -39,9 +41,9 @@ | ||||
|  *                       ^^^^^ | ||||
|  * When shifted out of phase, the special 0xa1 byte becomes an illegal | ||||
|  * encoding (you can't do 10 00). So this can't be spoofed by user data. | ||||
|  *  | ||||
|  * | ||||
|  * shifted: 10 00 10 01 00 01 00 1 | ||||
|  *  | ||||
|  * | ||||
|  * It's repeated three times. | ||||
|  */ | ||||
| #define MFM_RECORD_SEPARATOR 0x4489 | ||||
| @@ -59,255 +61,222 @@ | ||||
|  | ||||
| static uint8_t decodeUint16(uint16_t raw) | ||||
| { | ||||
| 	Bytes b; | ||||
| 	ByteWriter bw(b); | ||||
| 	bw.write_be16(raw); | ||||
| 	return decodeFmMfm(b.toBits())[0]; | ||||
|     Bytes b; | ||||
|     ByteWriter bw(b); | ||||
|     bw.write_be16(raw); | ||||
|     return decodeFmMfm(b.toBits())[0]; | ||||
| } | ||||
|  | ||||
| class IbmEncoder : public AbstractEncoder | ||||
| class IbmEncoder : public Encoder | ||||
| { | ||||
| public: | ||||
| 	IbmEncoder(const EncoderProto& config): | ||||
| 		AbstractEncoder(config), | ||||
| 		_config(config.ibm()) | ||||
| 	{} | ||||
|     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 writeRawBits(uint32_t data, int width) | ||||
|     { | ||||
|         _cursor += width; | ||||
|         _lastBit = data & 1; | ||||
|         for (int i = 0; i < width; i++) | ||||
|         { | ||||
|             unsigned pos = _cursor - i - 1; | ||||
|             if (pos < _bits.size()) | ||||
|                 _bits[pos] = data & 1; | ||||
|             data >>= 1; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| 	void getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head) | ||||
| 	{ | ||||
| 		trackdata.Clear(); | ||||
| 		for (const auto& f : _config.trackdata()) | ||||
| 		{ | ||||
| 			if (f.has_cylinder() && (f.cylinder() != cylinder)) | ||||
| 				continue; | ||||
| 			if (f.has_head() && (f.head() != head)) | ||||
| 				continue; | ||||
|     void 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); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| private: | ||||
| 	static std::set<unsigned> getSectorIds(const IbmEncoderProto::TrackdataProto& trackdata) | ||||
| 	{ | ||||
| 		std::set<unsigned> s; | ||||
| 		if (trackdata.has_sectors()) | ||||
| 		{ | ||||
| 			for (int sectorId : trackdata.sectors().sector()) | ||||
| 				s.insert(sectorId); | ||||
| 		} | ||||
| 		else if (trackdata.has_sector_range()) | ||||
| 		{ | ||||
| 			int sectorId = trackdata.sector_range().min_sector(); | ||||
| 			while (sectorId <= trackdata.sector_range().max_sector()) | ||||
| 			{ | ||||
| 				s.insert(sectorId); | ||||
| 				sectorId++; | ||||
| 			} | ||||
| 		} | ||||
| 		return s; | ||||
| 	} | ||||
|             trackdata.MergeFrom(f); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| public: | ||||
| 	std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override | ||||
| 	{ | ||||
| 		std::vector<std::shared_ptr<Sector>> sectors; | ||||
| 		IbmEncoderProto::TrackdataProto trackdata; | ||||
| 		getTrackFormat(trackdata, physicalTrack, physicalSide); | ||||
|     std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo, | ||||
|         const std::vector<std::shared_ptr<const Sector>>& sectors, | ||||
|         const Image& image) override | ||||
|     { | ||||
|         IbmEncoderProto::TrackdataProto trackdata; | ||||
|         getEncoderTrackData( | ||||
|             trackdata, trackInfo->logicalTrack, trackInfo->logicalSide); | ||||
|  | ||||
| 		int logicalSide = physicalSide ^ trackdata.swap_sides(); | ||||
| 		for (int sectorId : getSectorIds(trackdata)) | ||||
|         auto trackLayout = Layout::getLayoutOfTrack( | ||||
|             trackInfo->logicalTrack, trackInfo->logicalSide); | ||||
|  | ||||
|         auto writeBytes = [&](const Bytes& bytes) | ||||
|         { | ||||
| 			const auto& sector = image.get(physicalTrack, logicalSide, sectorId); | ||||
| 			if (sector) | ||||
| 				sectors.push_back(sector); | ||||
|             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 = trackLayout->sectorSize >> 7; | ||||
|             while (s > 1) | ||||
|             { | ||||
|                 s >>= 1; | ||||
|                 sectorSize += 1; | ||||
|             } | ||||
|         } | ||||
|  | ||||
| 		return sectors; | ||||
| 	} | ||||
|         uint16_t gapFill = trackdata.gap_fill_byte(); | ||||
|  | ||||
|     std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, | ||||
| 			const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override | ||||
| 	{ | ||||
| 		IbmEncoderProto::TrackdataProto trackdata; | ||||
| 		getTrackFormat(trackdata, physicalTrack, physicalSide); | ||||
|         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); | ||||
|         } | ||||
|  | ||||
| 		auto writeBytes = [&](const Bytes& bytes) | ||||
| 		{ | ||||
| 			if (trackdata.use_fm()) | ||||
| 				encodeFm(_bits, _cursor, bytes); | ||||
| 			else | ||||
| 				encodeMfm(_bits, _cursor, bytes, _lastBit); | ||||
| 		}; | ||||
|         bool first = true; | ||||
|         for (const auto& sectorData : sectors) | ||||
|         { | ||||
|             if (!first) | ||||
|                 writeFillerRawBytes(trackdata.gap3(), gapFill); | ||||
|             first = false; | ||||
|  | ||||
| 		auto writeFillerRawBytes = [&](int count, uint16_t byte) | ||||
| 		{ | ||||
| 			for (int i=0; i<count; i++) | ||||
| 				writeRawBits(byte, 16); | ||||
| 		}; | ||||
|             /* 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. */ | ||||
|  | ||||
| 		auto writeFillerBytes = [&](int count, uint8_t byte) | ||||
| 		{ | ||||
| 			Bytes b { byte }; | ||||
| 			for (int i=0; i<count; i++) | ||||
| 				writeBytes(b); | ||||
| 		}; | ||||
|             { | ||||
|                 Bytes header; | ||||
|                 ByteWriter bw(header); | ||||
|  | ||||
| 		double clockRateUs = 1e3 / trackdata.clock_rate_khz(); | ||||
| 		if (!trackdata.use_fm()) | ||||
| 			clockRateUs /= 2.0; | ||||
| 		int bitsPerRevolution = (trackdata.track_length_ms() * 1000.0) / clockRateUs; | ||||
| 		_bits.resize(bitsPerRevolution); | ||||
| 		_cursor = 0; | ||||
|                 writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00); | ||||
|                 if (!trackdata.use_fm()) | ||||
|                 { | ||||
|                     for (int i = 0; i < 3; i++) | ||||
|                         bw.write_8(MFM_RECORD_SEPARATOR_BYTE); | ||||
|                 } | ||||
|                 bw.write_8(idamUnencoded); | ||||
|                 bw.write_8(sectorData->logicalTrack); | ||||
|                 bw.write_8( | ||||
|                     sectorData->logicalSide ^ trackdata.invert_side_byte()); | ||||
|                 bw.write_8(sectorData->logicalSector); | ||||
|                 bw.write_8(sectorSize); | ||||
|                 uint16_t crc = crc16(CCITT_POLY, header); | ||||
|                 bw.write_be16(crc); | ||||
|  | ||||
| 		uint8_t idamUnencoded = decodeUint16(trackdata.idam_byte()); | ||||
| 		uint8_t damUnencoded = decodeUint16(trackdata.dam_byte()); | ||||
|                 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; | ||||
|  | ||||
| 		uint8_t sectorSize = 0; | ||||
| 		{ | ||||
| 			int s = trackdata.sector_size() >> 7; | ||||
| 			while (s > 1) | ||||
| 			{ | ||||
| 				s >>= 1; | ||||
| 				sectorSize += 1; | ||||
| 			} | ||||
| 		} | ||||
|                 writeBytes(header.slice(conventionalHeaderStart)); | ||||
|             } | ||||
|  | ||||
| 		uint16_t gapFill = trackdata.gap_fill_byte(); | ||||
|             writeFillerRawBytes(trackdata.gap2(), gapFill); | ||||
|  | ||||
| 		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); | ||||
| 		} | ||||
|             { | ||||
|                 Bytes data; | ||||
|                 ByteWriter bw(data); | ||||
|  | ||||
| 		int logicalSide = physicalSide ^ trackdata.swap_sides(); | ||||
| 		bool first = true; | ||||
| 		for (int sectorId : trackdata.sectors().sector()) | ||||
| 		{ | ||||
| 			if (!first) | ||||
| 				writeFillerRawBytes(trackdata.gap3(), gapFill); | ||||
| 			first = false; | ||||
|                 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); | ||||
|  | ||||
| 			const auto& sectorData = image.get(physicalTrack, logicalSide, sectorId); | ||||
| 			if (!sectorData) | ||||
| 				continue; | ||||
|                 Bytes truncatedData = | ||||
|                     sectorData->data.slice(0, trackLayout->sectorSize); | ||||
|                 bw += truncatedData; | ||||
|                 uint16_t crc = crc16(CCITT_POLY, data); | ||||
|                 bw.write_be16(crc); | ||||
|  | ||||
| 			/* 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. */ | ||||
|                 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; | ||||
|  | ||||
| 			{ | ||||
| 				Bytes header; | ||||
| 				ByteWriter bw(header); | ||||
|                 writeBytes(data.slice(conventionalHeaderStart)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| 				writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00); | ||||
| 				if (!trackdata.use_fm()) | ||||
| 				{ | ||||
| 					for (int i=0; i<3; i++) | ||||
| 						bw.write_8(MFM_RECORD_SEPARATOR_BYTE); | ||||
| 				} | ||||
| 				bw.write_8(idamUnencoded); | ||||
| 				bw.write_8(sectorData->logicalTrack); | ||||
| 				bw.write_8(sectorData->logicalSide); | ||||
| 				bw.write_8(sectorData->logicalSector); | ||||
| 				bw.write_8(sectorSize); | ||||
| 				uint16_t crc = crc16(CCITT_POLY, header); | ||||
| 				bw.write_be16(crc); | ||||
|         if (_cursor >= _bits.size()) | ||||
|             error("track data overrun"); | ||||
|         while (_cursor < _bits.size()) | ||||
|             writeFillerRawBytes(1, gapFill); | ||||
|  | ||||
| 				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, trackdata.sector_size()); | ||||
| 				bw += truncatedData; | ||||
| 				uint16_t crc = crc16(CCITT_POLY, data); | ||||
| 				bw.write_be16(crc); | ||||
|  | ||||
| 				int conventionalHeaderStart = 0; | ||||
| 				if (!trackdata.use_fm()) | ||||
| 				{ | ||||
| 					for (int i=0; i<3; i++) | ||||
| 						writeRawBits(MFM_RECORD_SEPARATOR, 16); | ||||
| 					conventionalHeaderStart += 3; | ||||
|  | ||||
| 				} | ||||
| 				writeRawBits(trackdata.dam_byte(), 16); | ||||
| 				conventionalHeaderStart += 1; | ||||
|  | ||||
| 				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, clockRateUs*1e3); | ||||
| 		return fluxmap; | ||||
| 	} | ||||
|         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; | ||||
|     const IbmEncoderProto& _config; | ||||
|     std::vector<bool> _bits; | ||||
|     unsigned _cursor; | ||||
|     bool _lastBit; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractEncoder> createIbmEncoder(const EncoderProto& config) | ||||
| std::unique_ptr<Encoder> createIbmEncoder(const EncoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractEncoder>(new IbmEncoder(config)); | ||||
|     return std::unique_ptr<Encoder>(new IbmEncoder(config)); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -3,35 +3,35 @@ | ||||
|  | ||||
| /* 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 AbstractEncoder; | ||||
| class AbstractDecoder; | ||||
| class Encoder; | ||||
| class Decoder; | ||||
| class DecoderProto; | ||||
| class EncoderProto; | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createIbmDecoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<AbstractEncoder> createIbmEncoder(const EncoderProto& config); | ||||
| extern std::unique_ptr<Decoder> createIbmDecoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<Encoder> createIbmEncoder(const EncoderProto& config); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -1,29 +1,18 @@ | ||||
| syntax = "proto2"; | ||||
|  | ||||
| import "lib/common.proto"; | ||||
| import "lib/config/common.proto"; | ||||
|  | ||||
| message IbmDecoderProto { | ||||
| 	// Next: 10 | ||||
| 	// Next: 11 | ||||
| 	message TrackdataProto { | ||||
| 		message SectorsProto { | ||||
| 			repeated int32 sector = 1            [(help) = "require these sectors to exist for a good read"]; | ||||
| 		} | ||||
| 		message SectorRangeProto { | ||||
| 			optional int32 min_sector = 1        [(help) = "require these sectors to exist for a good read"]; | ||||
| 			optional int32 max_sector = 2        [(help) = "require these sectors to exist for a good read"]; | ||||
| 		} | ||||
|  | ||||
| 		optional int32 cylinder = 7              [(help) = "if set, the format applies only to this track"]; | ||||
| 		optional int32 track = 7              [(help) = "if set, the format applies only to this track"]; | ||||
| 		optional int32 head = 8                  [(help) = "if set, the format applies only to this head"]; | ||||
|  | ||||
| 		optional bool ignore_side_byte = 2       [default = false, (help) = "ignore side byte in sector header"]; | ||||
| 		optional bool ignore_track_byte = 6      [default = false, (help) = "ignore track byte in sector header"]; | ||||
| 		optional bool swap_sides = 4             [default = false, (help) = "put logical side 1 on physical side 0"]; | ||||
| 		optional bool invert_side_byte = 4       [default = false, (help) = "invert the side byte in the sector header"]; | ||||
|  | ||||
| 		oneof required_sectors { | ||||
| 			SectorsProto sectors = 5             [(help) = "require these sectors to exist for a good read"]; | ||||
| 			SectorRangeProto sector_range = 9    [(help) = "require these sectors to exist for a good read"]; | ||||
| 		} | ||||
| 		repeated int32 ignore_sector = 10        [(help) = "sectors with these IDs will not be read"]; | ||||
| 	} | ||||
|  | ||||
| 	repeated TrackdataProto trackdata = 1; | ||||
| @@ -32,21 +21,11 @@ message IbmDecoderProto { | ||||
| message IbmEncoderProto { | ||||
| 	// Next: 20 | ||||
| 	message TrackdataProto { | ||||
| 		message SectorsProto { | ||||
| 			repeated int32 sector = 1		[(help) = "write these sectors (in order) on each track"]; | ||||
| 		} | ||||
| 		message SectorRangeProto { | ||||
| 			optional int32 min_sector = 1   [(help) = "write these sectors (in order) on each track"]; | ||||
| 			optional int32 max_sector = 2   [(help) = "write these sectors (in order) on each track"]; | ||||
| 		} | ||||
|  | ||||
| 		optional int32 cylinder = 15        [(help) = "if set, the format applies only to this track"]; | ||||
| 		optional int32 track = 15        [(help) = "if set, the format applies only to this track"]; | ||||
| 		optional int32 head = 16            [(help) = "if set, the format applies only to this head"]; | ||||
|  | ||||
| 		optional double track_length_ms = 1 [(help) = "length of track"]; | ||||
| 		optional int32 sector_size = 2      [default=512, (help) = "number of bytes per sector"]; | ||||
| 		optional bool emit_iam = 3          [default=true, (help) = "whether to emit an IAM record"]; | ||||
| 		optional double clock_rate_khz = 5  [(help) = "data clock rate"]; | ||||
| 		optional double target_clock_period_us = 5  [default=4, (help) = "data clock rate on target disk"]; | ||||
| 		optional bool use_fm = 6            [default=false, (help) = "whether to use FM encoding rather than MFM"]; | ||||
| 		optional int32 idam_byte = 7        [default=0x5554, (help) = "16-bit raw bit pattern of IDAM byte"]; | ||||
| 		optional int32 dam_byte = 8         [default=0x5545, (help) = "16-bit raw bit pattern of DAM byte"]; | ||||
| @@ -54,13 +33,9 @@ message IbmEncoderProto { | ||||
| 		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 swap_sides = 14       [default=false, (help) = "swap side bytes when writing"]; | ||||
| 		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"]; | ||||
|  | ||||
| 		oneof required_sectors { | ||||
| 			SectorsProto sectors = 17            [(help) = "require these sectors to exist for a good read"]; | ||||
| 			SectorRangeProto sector_range = 19   [(help) = "require these sectors to exist for a good read"]; | ||||
| 		} | ||||
| 		optional double target_rotational_period_ms = 1 [default=200, (help) = "rotational period of target disk"]; | ||||
| 	} | ||||
|  | ||||
| 	repeated TrackdataProto trackdata = 1; | ||||
|   | ||||
| @@ -1,33 +1,37 @@ | ||||
| #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 "decoders/decoders.h" | ||||
| #include "sector.h" | ||||
| #include "macintosh.h" | ||||
| #include "bytes.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/data/sector.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) | ||||
| { | ||||
| @@ -41,7 +45,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(); | ||||
| @@ -122,97 +126,71 @@ uint8_t decode_side(uint8_t side) | ||||
|     return !!(side & 0x20); | ||||
| } | ||||
|  | ||||
| class MacintoshDecoder : public AbstractDecoder | ||||
| class MacintoshDecoder : public Decoder | ||||
| { | ||||
| public: | ||||
| 	MacintoshDecoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config) | ||||
| 	{} | ||||
|     MacintoshDecoder(const DecoderProto& config): Decoder(config) {} | ||||
|  | ||||
|     RecordType advanceToNextRecord() | ||||
| 	{ | ||||
| 		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; | ||||
| 	} | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         return seekToPattern(ANY_RECORD_PATTERN); | ||||
|     } | ||||
|  | ||||
|     void decodeSectorRecord() | ||||
| 	{ | ||||
| 		/* Skip ID (as we know it's a MAC_SECTOR_RECORD). */ | ||||
| 		readRawBits(24); | ||||
|     void decodeSectorRecord() override | ||||
|     { | ||||
|         if (readRaw24() != MAC_SECTOR_RECORD) | ||||
|             return; | ||||
|  | ||||
| 		/* Read header. */ | ||||
|         /* Read header. */ | ||||
|  | ||||
| 		auto header = toBytes(readRawBits(7*8)).slice(0, 7); | ||||
| 					 | ||||
| 		uint8_t encodedTrack = decode_data_gcr(header[0]); | ||||
| 		if (encodedTrack != (_sector->physicalCylinder & 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]); | ||||
|         auto header = toBytes(readRawBits(7 * 8)).slice(0, 7); | ||||
|  | ||||
| 		if (encodedSector > 11) | ||||
| 			return; | ||||
|         uint8_t encodedTrack = decode_data_gcr(header[0]); | ||||
|         if (encodedTrack != (_sector->physicalTrack & 0x3f)) | ||||
|             return; | ||||
|  | ||||
| 		_sector->logicalTrack = _sector->physicalCylinder; | ||||
| 		_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 */ | ||||
| 	} | ||||
|         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]); | ||||
|  | ||||
|     void decodeDataRecord() | ||||
| 	{ | ||||
| 		auto id = toBytes(readRawBits(24)).reader().read_be24(); | ||||
| 		if (id != MAC_DATA_RECORD) | ||||
| 			return; | ||||
|         if (encodedSector > 11) | ||||
|             return; | ||||
|  | ||||
| 		/* Read data. */ | ||||
|         _sector->logicalTrack = _sector->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 */ | ||||
|     } | ||||
|  | ||||
| 		readRawBits(8); /* skip spare byte */ | ||||
| 		auto inputbuffer = toBytes(readRawBits(MAC_ENCODED_SECTOR_LENGTH*8)) | ||||
| 			.slice(0, MAC_ENCODED_SECTOR_LENGTH); | ||||
|     void decodeDataRecord() override | ||||
|     { | ||||
|         if (readRaw24() != MAC_DATA_RECORD) | ||||
|             return; | ||||
|  | ||||
| 		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)); | ||||
| 	} | ||||
|         /* Read data. */ | ||||
|  | ||||
| 	std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const | ||||
| 	{ | ||||
| 		int count; | ||||
| 		if (cylinder < 16) | ||||
| 			count = 12; | ||||
| 		else if (cylinder < 32) | ||||
| 			count = 11; | ||||
| 		else if (cylinder < 48) | ||||
| 			count = 10; | ||||
| 		else if (cylinder < 64) | ||||
| 			count = 9; | ||||
| 		else | ||||
| 			count = 8; | ||||
|         readRawBits(8); /* skip spare byte */ | ||||
|         auto inputbuffer = toBytes(readRawBits(MAC_ENCODED_SECTOR_LENGTH * 8)) | ||||
|                                .slice(0, MAC_ENCODED_SECTOR_LENGTH); | ||||
|  | ||||
| 		std::set<unsigned> sectors; | ||||
| 		while (count--) | ||||
| 			sectors.insert(count); | ||||
| 		return sectors; | ||||
| 	} | ||||
|         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<AbstractDecoder> createMacintoshDecoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createMacintoshDecoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new MacintoshDecoder(config)); | ||||
|     return std::unique_ptr<Decoder>(new MacintoshDecoder(config)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,12 +1,13 @@ | ||||
| #include "globals.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.h" | ||||
| #include "macintosh.h" | ||||
| #include "crc.h" | ||||
| #include "writer.h" | ||||
| #include "image.h" | ||||
| #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> | ||||
|  | ||||
| @@ -14,44 +15,46 @@ static bool lastBit; | ||||
|  | ||||
| static double clockRateUsForTrack(unsigned track) | ||||
| { | ||||
| 	if (track < 16) | ||||
| 		return 2.623; | ||||
| 	if (track < 32) | ||||
| 		return 2.861; | ||||
| 	if (track < 48) | ||||
| 		return 3.148; | ||||
| 	if (track < 64) | ||||
| 		return 3.497; | ||||
| 	return 3.934; | ||||
|     if (track < 16) | ||||
|         return 2.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; | ||||
|     if (track < 16) | ||||
|         return 12; | ||||
|     if (track < 32) | ||||
|         return 11; | ||||
|     if (track < 48) | ||||
|         return 10; | ||||
|     if (track < 64) | ||||
|         return 9; | ||||
|     return 8; | ||||
| } | ||||
|  | ||||
| static int encode_data_gcr(uint8_t gcr) | ||||
| { | ||||
|     switch (gcr) | ||||
|     { | ||||
| 		#define GCR_ENTRY(gcr, data) \ | ||||
| 			case data: return gcr; | ||||
| 		#include "data_gcr.h" | ||||
| 		#undef GCR_ENTRY | ||||
| #define GCR_ENTRY(gcr, data) \ | ||||
|     case data:               \ | ||||
|         return gcr; | ||||
| #include "data_gcr.h" | ||||
| #undef GCR_ENTRY | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| /* This is extremely inspired by the MESS implementation, written by Nathan Woods | ||||
|  * and R. Belmont: https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp | ||||
| /* This is extremely inspired by the MESS implementation, written by Nathan | ||||
|  * Woods and R. Belmont: | ||||
|  * https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp | ||||
|  */ | ||||
| static Bytes encode_crazy_data(const Bytes& input) | ||||
| { | ||||
| @@ -59,7 +62,7 @@ static Bytes encode_crazy_data(const Bytes& input) | ||||
|     ByteWriter bw(output); | ||||
|     ByteReader br(input); | ||||
|  | ||||
| 	uint8_t w1, w2, w3, w4; | ||||
|     uint8_t w1, w2, w3, w4; | ||||
|  | ||||
|     static const int LOOKUP_LEN = MAC_SECTOR_LENGTH / 3; | ||||
|  | ||||
| @@ -67,92 +70,94 @@ static Bytes encode_crazy_data(const Bytes& input) | ||||
|     uint8_t b2[LOOKUP_LEN + 1]; | ||||
|     uint8_t b3[LOOKUP_LEN + 1]; | ||||
|  | ||||
| 	uint32_t c1 = 0; | ||||
| 	uint32_t c2 = 0; | ||||
| 	uint32_t c3 = 0; | ||||
| 	for (int j=0;; j++) | ||||
| 	{ | ||||
| 		c1 = (c1 & 0xff) << 1; | ||||
| 		if (c1 & 0x0100) | ||||
| 			c1++; | ||||
|     uint32_t c1 = 0; | ||||
|     uint32_t c2 = 0; | ||||
|     uint32_t c3 = 0; | ||||
|     for (int j = 0;; j++) | ||||
|     { | ||||
|         c1 = (c1 & 0xff) << 1; | ||||
|         if (c1 & 0x0100) | ||||
|             c1++; | ||||
|  | ||||
| 		uint8_t val = br.read_8(); | ||||
| 		c3 += val; | ||||
| 		if (c1 & 0x0100) | ||||
| 		{ | ||||
| 			c3++; | ||||
| 			c1 &= 0xff; | ||||
| 		} | ||||
| 		b1[j] = (val ^ c1) & 0xff; | ||||
|         uint8_t val = br.read_8(); | ||||
|         c3 += val; | ||||
|         if (c1 & 0x0100) | ||||
|         { | ||||
|             c3++; | ||||
|             c1 &= 0xff; | ||||
|         } | ||||
|         b1[j] = (val ^ c1) & 0xff; | ||||
|  | ||||
| 		val = br.read_8(); | ||||
| 		c2 += val; | ||||
| 		if (c3 > 0xff) | ||||
| 		{ | ||||
| 			c2++; | ||||
| 			c3 &= 0xff; | ||||
| 		} | ||||
| 		b2[j] = (val ^ c3) & 0xff; | ||||
|         val = br.read_8(); | ||||
|         c2 += val; | ||||
|         if (c3 > 0xff) | ||||
|         { | ||||
|             c2++; | ||||
|             c3 &= 0xff; | ||||
|         } | ||||
|         b2[j] = (val ^ c3) & 0xff; | ||||
|  | ||||
| 		if (br.pos == 524) | ||||
| 			break; | ||||
|         if (br.pos == 524) | ||||
|             break; | ||||
|  | ||||
| 		val = br.read_8(); | ||||
| 		c1 += val; | ||||
| 		if (c2 > 0xff) | ||||
| 		{ | ||||
| 			c1++; | ||||
| 			c2 &= 0xff; | ||||
| 		} | ||||
| 		b3[j] = (val ^ c2) & 0xff; | ||||
| 	} | ||||
| 	uint32_t c4 = ((c1 & 0xc0) >> 6) | ((c2 & 0xc0) >> 4) | ((c3 & 0xc0) >> 2); | ||||
| 	b3[LOOKUP_LEN] = 0; | ||||
|         val = br.read_8(); | ||||
|         c1 += val; | ||||
|         if (c2 > 0xff) | ||||
|         { | ||||
|             c1++; | ||||
|             c2 &= 0xff; | ||||
|         } | ||||
|         b3[j] = (val ^ c2) & 0xff; | ||||
|     } | ||||
|     uint32_t c4 = ((c1 & 0xc0) >> 6) | ((c2 & 0xc0) >> 4) | ((c3 & 0xc0) >> 2); | ||||
|     b3[LOOKUP_LEN] = 0; | ||||
|  | ||||
| 	for (int i = 0; i <= LOOKUP_LEN; i++) | ||||
| 	{ | ||||
| 		w1 = b1[i] & 0x3f; | ||||
| 		w2 = b2[i] & 0x3f; | ||||
| 		w3 = b3[i] & 0x3f; | ||||
| 		w4 =  ((b1[i] & 0xc0) >> 2); | ||||
| 		w4 |= ((b2[i] & 0xc0) >> 4); | ||||
| 		w4 |= ((b3[i] & 0xc0) >> 6); | ||||
|     for (int i = 0; i <= LOOKUP_LEN; i++) | ||||
|     { | ||||
|         w1 = b1[i] & 0x3f; | ||||
|         w2 = b2[i] & 0x3f; | ||||
|         w3 = b3[i] & 0x3f; | ||||
|         w4 = ((b1[i] & 0xc0) >> 2); | ||||
|         w4 |= ((b2[i] & 0xc0) >> 4); | ||||
|         w4 |= ((b3[i] & 0xc0) >> 6); | ||||
|  | ||||
| 		bw.write_8(w4); | ||||
| 		bw.write_8(w1); | ||||
| 		bw.write_8(w2); | ||||
|         bw.write_8(w4); | ||||
|         bw.write_8(w1); | ||||
|         bw.write_8(w2); | ||||
|  | ||||
| 		if (i != LOOKUP_LEN) | ||||
| 			bw.write_8(w3); | ||||
| 	} | ||||
|         if (i != LOOKUP_LEN) | ||||
|             bw.write_8(w3); | ||||
|     } | ||||
|  | ||||
| 	bw.write_8(c4 & 0x3f); | ||||
| 	bw.write_8(c3 & 0x3f); | ||||
| 	bw.write_8(c2 & 0x3f); | ||||
| 	bw.write_8(c1 & 0x3f); | ||||
|     bw.write_8(c4 & 0x3f); | ||||
|     bw.write_8(c3 & 0x3f); | ||||
|     bw.write_8(c2 & 0x3f); | ||||
|     bw.write_8(c1 & 0x3f); | ||||
|  | ||||
| 	return output; | ||||
|     return output; | ||||
| } | ||||
|  | ||||
| static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src) | ||||
| static void write_bits( | ||||
|     std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src) | ||||
| { | ||||
| 	for (bool bit : src) | ||||
| 	{ | ||||
| 		if (cursor < bits.size()) | ||||
| 			bits[cursor++] = bit; | ||||
| 	} | ||||
|     for (bool bit : src) | ||||
|     { | ||||
|         if (cursor < bits.size()) | ||||
|             bits[cursor++] = bit; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width) | ||||
| static void write_bits( | ||||
|     std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width) | ||||
| { | ||||
| 	cursor += width; | ||||
| 	for (int i=0; i<width; i++) | ||||
| 	{ | ||||
| 		unsigned pos = cursor - i - 1; | ||||
| 		if (pos < bits.size()) | ||||
| 			bits[pos] = data & 1; | ||||
| 		data >>= 1; | ||||
| 	} | ||||
|     cursor += width; | ||||
|     for (int i = 0; i < width; i++) | ||||
|     { | ||||
|         unsigned pos = cursor - i - 1; | ||||
|         if (pos < bits.size()) | ||||
|             bits[pos] = data & 1; | ||||
|         data >>= 1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static uint8_t encode_side(uint8_t track, uint8_t side) | ||||
| @@ -161,103 +166,93 @@ static uint8_t encode_side(uint8_t track, uint8_t side) | ||||
|      * bit 5) and also whether we're above track 0x3f (in bit 0). | ||||
|      */ | ||||
|  | ||||
| 	return (side ? 0x20 : 0x00) | ((track>0x3f) ? 0x01 : 0x00); | ||||
|     return (side ? 0x20 : 0x00) | ((track > 0x3f) ? 0x01 : 0x00); | ||||
| } | ||||
|  | ||||
| static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<Sector>& sector) | ||||
| static void write_sector(std::vector<bool>& bits, | ||||
|     unsigned& cursor, | ||||
|     const std::shared_ptr<const Sector>& sector) | ||||
| { | ||||
| 	if ((sector->data.size() != 512) && (sector->data.size() != 524)) | ||||
| 		Error() << "unsupported sector size --- you must pick 512 or 524"; | ||||
|     if ((sector->data.size() != 512) && (sector->data.size() != 524)) | ||||
|         error("unsupported sector size --- you must pick 512 or 524"); | ||||
|  | ||||
| 	write_bits(bits, cursor, 0xff, 1*8); /* pad byte */ | ||||
| 	for (int i=0; i<7; i++) | ||||
| 		write_bits(bits, cursor, 0xff3fcff3fcffLL, 6*8); /* sync */ | ||||
| 	write_bits(bits, cursor, MAC_SECTOR_RECORD, 3*8); | ||||
|     write_bits(bits, cursor, 0xff, 1 * 8); /* pad byte */ | ||||
|     for (int i = 0; i < 7; i++) | ||||
|         write_bits(bits, cursor, 0xff3fcff3fcffLL, 6 * 8); /* sync */ | ||||
|     write_bits(bits, cursor, MAC_SECTOR_RECORD, 3 * 8); | ||||
|  | ||||
|     uint8_t encodedTrack = sector->logicalTrack & 0x3f; | ||||
| 	uint8_t encodedSector = sector->logicalSector; | ||||
| 	uint8_t encodedSide = encode_side(sector->logicalTrack, sector->logicalSide); | ||||
| 	uint8_t formatByte = MAC_FORMAT_BYTE; | ||||
| 	uint8_t headerChecksum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f; | ||||
|     uint8_t encodedSector = sector->logicalSector; | ||||
|     uint8_t encodedSide = | ||||
|         encode_side(sector->logicalTrack, sector->logicalSide); | ||||
|     uint8_t formatByte = MAC_FORMAT_BYTE; | ||||
|     uint8_t headerChecksum = | ||||
|         (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f; | ||||
|  | ||||
| 	write_bits(bits, cursor, encode_data_gcr(encodedTrack), 1*8); | ||||
| 	write_bits(bits, cursor, encode_data_gcr(encodedSector), 1*8); | ||||
| 	write_bits(bits, cursor, encode_data_gcr(encodedSide), 1*8); | ||||
| 	write_bits(bits, cursor, encode_data_gcr(formatByte), 1*8); | ||||
| 	write_bits(bits, cursor, encode_data_gcr(headerChecksum), 1*8); | ||||
|     write_bits(bits, cursor, encode_data_gcr(encodedTrack), 1 * 8); | ||||
|     write_bits(bits, cursor, encode_data_gcr(encodedSector), 1 * 8); | ||||
|     write_bits(bits, cursor, encode_data_gcr(encodedSide), 1 * 8); | ||||
|     write_bits(bits, cursor, encode_data_gcr(formatByte), 1 * 8); | ||||
|     write_bits(bits, cursor, encode_data_gcr(headerChecksum), 1 * 8); | ||||
|  | ||||
| 	write_bits(bits, cursor, 0xdeaaff, 3*8); | ||||
| 	write_bits(bits, cursor, 0xff3fcff3fcffLL, 6*8); /* sync */ | ||||
| 	write_bits(bits, cursor, MAC_DATA_RECORD, 3*8); | ||||
| 	write_bits(bits, cursor, encode_data_gcr(sector->logicalSector), 1*8); | ||||
|     write_bits(bits, cursor, 0xdeaaff, 3 * 8); | ||||
|     write_bits(bits, cursor, 0xff3fcff3fcffLL, 6 * 8); /* sync */ | ||||
|     write_bits(bits, cursor, MAC_DATA_RECORD, 3 * 8); | ||||
|     write_bits(bits, cursor, encode_data_gcr(sector->logicalSector), 1 * 8); | ||||
|  | ||||
| 	Bytes wireData; | ||||
| 	wireData.writer().append(sector->data.slice(512, 12)).append(sector->data.slice(0, 512)); | ||||
| 	for (uint8_t b : encode_crazy_data(wireData)) | ||||
| 		write_bits(bits, cursor, encode_data_gcr(b), 1*8); | ||||
|     Bytes wireData; | ||||
|     wireData.writer() | ||||
|         .append(sector->data.slice(512, 12)) | ||||
|         .append(sector->data.slice(0, 512)); | ||||
|     for (uint8_t b : encode_crazy_data(wireData)) | ||||
|         write_bits(bits, cursor, encode_data_gcr(b), 1 * 8); | ||||
|  | ||||
| 	write_bits(bits, cursor, 0xdeaaff, 3*8); | ||||
|     write_bits(bits, cursor, 0xdeaaff, 3 * 8); | ||||
| } | ||||
|  | ||||
| class MacintoshEncoder : public AbstractEncoder | ||||
| class MacintoshEncoder : public Encoder | ||||
| { | ||||
| public: | ||||
| 	MacintoshEncoder(const EncoderProto& config): | ||||
| 		AbstractEncoder(config), | ||||
| 		_config(config.macintosh()) | ||||
| 	{} | ||||
|     MacintoshEncoder(const EncoderProto& config): | ||||
|         Encoder(config), | ||||
|         _config(config.macintosh()) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| public: | ||||
| 	std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override | ||||
| 	{ | ||||
| 		std::vector<std::shared_ptr<Sector>> sectors; | ||||
|     std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo, | ||||
|         const std::vector<std::shared_ptr<const Sector>>& sectors, | ||||
|         const Image& image) override | ||||
|     { | ||||
|         double clockRateUs = clockRateUsForTrack(trackInfo->logicalTrack); | ||||
|         int bitsPerRevolution = 200000.0 / clockRateUs; | ||||
|         std::vector<bool> bits(bitsPerRevolution); | ||||
|         unsigned cursor = 0; | ||||
|  | ||||
| 		if ((physicalTrack >= 0) && (physicalTrack < MAC_TRACKS_PER_DISK)) | ||||
|         { | ||||
|             unsigned numSectors = sectorsForTrack(physicalTrack); | ||||
|             for (int sectorId=0; sectorId<numSectors; sectorId++) | ||||
|             { | ||||
|                 const auto& sector = image.get(physicalTrack, physicalSide, sectorId); | ||||
|                 if (sector) | ||||
|                     sectors.push_back(sector); | ||||
|             } | ||||
|         } | ||||
|         fillBitmapTo(bits, | ||||
|             cursor, | ||||
|             _config.post_index_gap_us() / clockRateUs, | ||||
|             {true, false}); | ||||
|         lastBit = false; | ||||
|  | ||||
| 		return sectors; | ||||
| 	} | ||||
|         for (const auto& sector : sectors) | ||||
|             write_sector(bits, cursor, sector); | ||||
|  | ||||
|     std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, | ||||
| 			const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override | ||||
| 	{ | ||||
| 		if ((physicalTrack < 0) || (physicalTrack >= MAC_TRACKS_PER_DISK)) | ||||
| 			return std::unique_ptr<Fluxmap>(); | ||||
|         if (cursor >= bits.size()) | ||||
|             error("track data overrun by {} bits", cursor - bits.size()); | ||||
|         fillBitmapTo(bits, cursor, bits.size(), {true, false}); | ||||
|  | ||||
| 		double clockRateUs = clockRateUsForTrack(physicalTrack) * _config.clock_compensation_factor(); | ||||
| 		int bitsPerRevolution = 200000.0 / clockRateUs; | ||||
| 		std::vector<bool> bits(bitsPerRevolution); | ||||
| 		unsigned cursor = 0; | ||||
|  | ||||
| 		fillBitmapTo(bits, cursor, _config.post_index_gap_us() / clockRateUs, { true, false }); | ||||
| 		lastBit = false; | ||||
|  | ||||
| 		for (const auto& sector : sectors) | ||||
| 			write_sector(bits, cursor, sector); | ||||
|  | ||||
| 		if (cursor >= bits.size()) | ||||
| 			Error() << fmt::format("track data overrun by {} bits", cursor - bits.size()); | ||||
| 		fillBitmapTo(bits, cursor, bits.size(), { true, false }); | ||||
|  | ||||
| 		std::unique_ptr<Fluxmap> fluxmap(new Fluxmap); | ||||
| 		fluxmap->appendBits(bits, clockRateUs*1e3); | ||||
| 		return fluxmap; | ||||
| 	} | ||||
|         std::unique_ptr<Fluxmap> fluxmap(new Fluxmap); | ||||
|         fluxmap->appendBits( | ||||
|             bits, calculatePhysicalClockPeriod(clockRateUs * 1e3, 200e6)); | ||||
|         return fluxmap; | ||||
|     } | ||||
|  | ||||
| private: | ||||
| 	const MacintoshEncoderProto& _config; | ||||
|     const MacintoshEncoderProto& _config; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractEncoder> createMacintoshEncoder(const EncoderProto& config) | ||||
| std::unique_ptr<Encoder> createMacintoshEncoder(const EncoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractEncoder>(new MacintoshEncoder(config)); | ||||
|     return std::unique_ptr<Encoder>(new MacintoshEncoder(config)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,22 +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 | ||||
| #define MAC_FORMAT_BYTE 0x22 | ||||
|  | ||||
| #define MAC_TRACKS_PER_DISK 80 | ||||
|  | ||||
| class AbstractEncoder; | ||||
| class AbstractDecoder; | ||||
| class Encoder; | ||||
| class Decoder; | ||||
| class DecoderProto; | ||||
| class EncoderProto; | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createMacintoshDecoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<AbstractEncoder> createMacintoshEncoder(const EncoderProto& config); | ||||
| extern std::unique_ptr<Decoder> createMacintoshDecoder( | ||||
|     const DecoderProto& config); | ||||
| extern std::unique_ptr<Encoder> createMacintoshEncoder( | ||||
|     const EncoderProto& config); | ||||
|  | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -1,14 +1,11 @@ | ||||
| syntax = "proto2"; | ||||
|  | ||||
| import "lib/common.proto"; | ||||
| 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)."]; | ||||
|  | ||||
| 	optional double clock_compensation_factor = 2 [default = 1.0, | ||||
| 		(help) = "scale the output clock by this much."]; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,75 +1,295 @@ | ||||
| #include "globals.h" | ||||
| #include "fluxmap.h" | ||||
| #include "decoders/fluxmapreader.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "sector.h" | ||||
| #include "micropolis.h" | ||||
| #include "bytes.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/data/sector.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. */ | ||||
| static const FluxPattern SECTOR_SYNC_PATTERN(32, 0xaaaa5555); | ||||
| /* The sector has a preamble of MFM 0x00s and uses 0xFF as a sync pattern. | ||||
|  * | ||||
|  * 00        00        00        F         F | ||||
|  * 0000 0000 0000 0000 0000 0000 0101 0101 0101 0101 | ||||
|  * A    A    A    A    A    A    5    5    5    5 | ||||
|  */ | ||||
| static const FluxPattern SECTOR_SYNC_PATTERN(64, 0xAAAAAAAAAAAA5555LL); | ||||
|  | ||||
| /* Adds all bytes, with carry. */ | ||||
| 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; | ||||
| /* 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; | ||||
| } | ||||
|  | ||||
| class MicropolisDecoder : public AbstractDecoder | ||||
| /* 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): | ||||
| 		AbstractDecoder(config) | ||||
| 	{} | ||||
|     MicropolisDecoder(const DecoderProto& config): | ||||
|         Decoder(config), | ||||
|         _config(config.micropolis()) | ||||
|     { | ||||
|         _checksumType = _config.checksum_type(); | ||||
|     } | ||||
|  | ||||
| 	RecordType advanceToNextRecord() | ||||
| 	{ | ||||
| 		_fmr->seekToIndexMark(); | ||||
| 		const FluxMatcher* matcher = nullptr; | ||||
| 		_sector->clock = _fmr->seekToPattern(SECTOR_SYNC_PATTERN, matcher); | ||||
| 		if (matcher == &SECTOR_SYNC_PATTERN) { | ||||
| 			return SECTOR_RECORD; | ||||
| 		} | ||||
| 		return UNKNOWN_RECORD; | ||||
| 	} | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         nanoseconds_t now = tell().ns(); | ||||
|  | ||||
| 	void decodeSectorRecord() | ||||
| 	{ | ||||
| 		readRawBits(16); | ||||
| 		auto rawbits = readRawBits(MICROPOLIS_ENCODED_SECTOR_SIZE*16); | ||||
| 		auto bytes = decodeFmMfm(rawbits).slice(0, MICROPOLIS_ENCODED_SECTOR_SIZE); | ||||
| 		ByteReader br(bytes); | ||||
|         /* 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(); | ||||
|         } | ||||
|  | ||||
| 		br.read_8();  /* sync */ | ||||
| 		_sector->logicalTrack = br.read_8(); | ||||
| 		_sector->logicalSide = _sector->physicalHead; | ||||
| 		_sector->logicalSector = br.read_8(); | ||||
| 		if (_sector->logicalSector > 15) | ||||
| 			return; | ||||
| 		if (_sector->logicalTrack > 77) | ||||
| 			return; | ||||
|         /* 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; | ||||
|         } | ||||
|  | ||||
| 		br.read(10);  /* OS data or padding */ | ||||
| 		_sector->data = br.read(256); | ||||
| 		uint8_t wantChecksum = br.read_8(); | ||||
| 		uint8_t gotChecksum = micropolisChecksum(bytes.slice(1, 2+266)); | ||||
| 		br.read(5);  /* 4 byte ECC and ECC-present flag */ | ||||
|         nanoseconds_t clock = seekToPattern(SECTOR_SYNC_PATTERN); | ||||
|  | ||||
| 		_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
| 	} | ||||
|         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->logicalTrack = br.read_8(); | ||||
|         _sector->logicalSide = _sector->physicalSide; | ||||
|         _sector->logicalSector = br.read_8(); | ||||
|         if (_sector->logicalSector > 15) | ||||
|             return; | ||||
|         if (_sector->logicalTrack > 76) | ||||
|             return; | ||||
|         if (_sector->logicalTrack != _sector->physicalTrack) | ||||
|             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<AbstractDecoder> createMicropolisDecoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createMicropolisDecoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new MicropolisDecoder(config)); | ||||
|     return std::unique_ptr<Decoder>(new MicropolisDecoder(config)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,113 +1,136 @@ | ||||
| #include "globals.h" | ||||
| #include "micropolis.h" | ||||
| #include "sector.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.h" | ||||
| #include "image.h" | ||||
| #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<Sector>& sector) | ||||
| 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"; | ||||
|     if ((sector->data.size() != 256) && | ||||
|         (sector->data.size() != MICROPOLIS_ENCODED_SECTOR_SIZE)) | ||||
|         error("unsupported sector size --- you must pick 256 or 275"); | ||||
|  | ||||
| 	int fullSectorSize = 40 + MICROPOLIS_ENCODED_SECTOR_SIZE + 40 + 35; | ||||
| 	auto fullSector = std::make_shared<std::vector<uint8_t>>(); | ||||
| 	fullSector->reserve(fullSectorSize); | ||||
| 	/* sector preamble */ | ||||
| 	for (int i=0; i<40; i++) | ||||
| 		fullSector->push_back(0); | ||||
| 	Bytes sectorData; | ||||
| 	if (sector->data.size() == MICROPOLIS_ENCODED_SECTOR_SIZE) | ||||
| 		sectorData = sector->data; | ||||
| 	else | ||||
| 	{ | ||||
| 		ByteWriter writer(sectorData); | ||||
| 		writer.write_8(0xff); /* Sync */ | ||||
| 		writer.write_8(sector->logicalTrack); | ||||
| 		writer.write_8(sector->logicalSector); | ||||
| 		for (int i=0; i<10; i++) | ||||
| 			writer.write_8(0); /* Padding */ | ||||
| 		writer += sector->data; | ||||
| 		writer.write_8(micropolisChecksum(sectorData.slice(1))); | ||||
| 		for (int i=0; i<5; i++) | ||||
| 			writer.write_8(0); /* 4 byte ECC and ECC not present flag */ | ||||
| 	} | ||||
| 	for (uint8_t b : sectorData) | ||||
| 		fullSector->push_back(b); | ||||
| 	/* sector postamble */ | ||||
| 	for (int i=0; i<40; i++) | ||||
| 		fullSector->push_back(0); | ||||
| 	/* filler */ | ||||
| 	for (int i=0; i<35; i++) | ||||
| 		fullSector->push_back(0); | ||||
|     int fullSectorSize = 40 + MICROPOLIS_ENCODED_SECTOR_SIZE + 40 + 35; | ||||
|     auto fullSector = std::make_shared<std::vector<uint8_t>>(); | ||||
|     fullSector->reserve(fullSectorSize); | ||||
|     /* sector preamble */ | ||||
|     for (int i = 0; i < 40; i++) | ||||
|         fullSector->push_back(0); | ||||
|     Bytes sectorData; | ||||
|     if (sector->data.size() == MICROPOLIS_ENCODED_SECTOR_SIZE) | ||||
|     { | ||||
|         if (sector->data[0] != 0xFF) | ||||
|             error( | ||||
|                 "275 byte sector doesn't start with sync byte 0xFF. " | ||||
|                 "Corrupted sector"); | ||||
|         uint8_t wantChecksum = sector->data[1 + 2 + 266]; | ||||
|         uint8_t gotChecksum = | ||||
|             micropolisChecksum(sector->data.slice(1, 2 + 266)); | ||||
|         if (wantChecksum != gotChecksum) | ||||
|             std::cerr << "Warning: checksum incorrect. Sector: " | ||||
|                       << sector->logicalSector << std::endl; | ||||
|         sectorData = sector->data; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         ByteWriter writer(sectorData); | ||||
|         writer.write_8(0xff); /* Sync */ | ||||
|         writer.write_8(sector->logicalTrack); | ||||
|         writer.write_8(sector->logicalSector); | ||||
|         for (int i = 0; i < 10; i++) | ||||
|             writer.write_8(0); /* Padding */ | ||||
|         writer += sector->data; | ||||
|         writer.write_8(micropolisChecksum(sectorData.slice(1))); | ||||
|  | ||||
| 	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; | ||||
| 	} | ||||
|         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 AbstractEncoder | ||||
| class MicropolisEncoder : public Encoder | ||||
| { | ||||
| public: | ||||
| 	MicropolisEncoder(const EncoderProto& config): | ||||
| 		AbstractEncoder(config), | ||||
| 		_config(config.micropolis()) | ||||
| 	{} | ||||
|     MicropolisEncoder(const EncoderProto& config): | ||||
|         Encoder(config), | ||||
|         _config(config.micropolis()) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| 	std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override | ||||
| 	{ | ||||
| 		std::vector<std::shared_ptr<Sector>> sectors; | ||||
|     std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo, | ||||
|         const std::vector<std::shared_ptr<const Sector>>& sectors, | ||||
|         const Image& image) override | ||||
|     { | ||||
|         int bitsPerRevolution = | ||||
|             (_config.rotational_period_ms() * 1e3) / _config.clock_period_us(); | ||||
|  | ||||
| 		if ((physicalTrack >= 0) && (physicalTrack < 77)) | ||||
| 		{ | ||||
| 			for (int sectorId = 0; sectorId < 16; sectorId++) | ||||
| 			{ | ||||
| 				const auto& sector = image.get(physicalTrack, physicalSide, sectorId); | ||||
| 				if (sector) | ||||
| 					sectors.push_back(sector); | ||||
| 			} | ||||
| 		} | ||||
|         std::vector<bool> bits(bitsPerRevolution); | ||||
|         std::vector<unsigned> indexes; | ||||
|         unsigned prev_cursor = 0; | ||||
|         unsigned cursor = 0; | ||||
|  | ||||
| 		return sectors; | ||||
| 	} | ||||
|         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); | ||||
|  | ||||
| 	std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, | ||||
| 			const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override | ||||
| 	{ | ||||
| 		int bitsPerRevolution = 100000; | ||||
| 		double clockRateUs = 2.00; | ||||
|         if (cursor != bits.size()) | ||||
|             error("track data mismatched length"); | ||||
|  | ||||
| 		if ((physicalTrack < 0) || (physicalTrack >= 77) || sectors.empty()) | ||||
| 			return std::unique_ptr<Fluxmap>(); | ||||
|  | ||||
| 		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 mismatched length"; | ||||
|  | ||||
| 		std::unique_ptr<Fluxmap> fluxmap(new Fluxmap); | ||||
| 		fluxmap->appendBits(bits, clockRateUs * 1e3); | ||||
| 		return fluxmap; | ||||
| 	} | ||||
|         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; | ||||
|     const MicropolisEncoderProto& _config; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractEncoder> createMicropolisEncoder(const EncoderProto& config) | ||||
| std::unique_ptr<Encoder> createMicropolisEncoder(const EncoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractEncoder>(new MicropolisEncoder(config)); | ||||
|     return std::unique_ptr<Encoder>(new MicropolisEncoder(config)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,16 +1,22 @@ | ||||
| #ifndef MICROPOLIS_H | ||||
| #define MICROPOLIS_H | ||||
|  | ||||
| #define MICROPOLIS_ENCODED_SECTOR_SIZE (1+2+266+6) | ||||
| #define MICROPOLIS_PAYLOAD_SIZE (256) | ||||
| #define MICROPOLIS_HEADER_SIZE (1 + 2 + 10) | ||||
| #define MICROPOLIS_ENCODED_SECTOR_SIZE \ | ||||
|     (MICROPOLIS_HEADER_SIZE + MICROPOLIS_PAYLOAD_SIZE + 6) | ||||
|  | ||||
| class AbstractDecoder; | ||||
| class AbstractEncoder; | ||||
| class Decoder; | ||||
| class Encoder; | ||||
| class EncoderProto; | ||||
| class DecoderProto; | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createMicropolisDecoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<AbstractEncoder> createMicropolisEncoder(const EncoderProto& config); | ||||
| 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 | ||||
|   | ||||
| @@ -1,5 +1,37 @@ | ||||
| syntax = "proto2"; | ||||
|  | ||||
| message MicropolisDecoderProto {} | ||||
| message MicropolisEncoderProto {} | ||||
| 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,10 +1,11 @@ | ||||
| #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 "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 <string.h> | ||||
|  | ||||
| const int SECTOR_SIZE = 256; | ||||
| @@ -16,78 +17,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); | ||||
|  | ||||
| class MxDecoder : public AbstractDecoder | ||||
| class MxDecoder : public Decoder | ||||
| { | ||||
| public: | ||||
| 	MxDecoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config) | ||||
| 	{} | ||||
|     MxDecoder(const DecoderProto& config): Decoder(config) {} | ||||
|  | ||||
|     void beginTrack() | ||||
| 	{ | ||||
| 		_currentSector = -1; | ||||
| 		_clock = 0; | ||||
| 	} | ||||
|     void beginTrack() override | ||||
|     { | ||||
|         _clock = _sector->clock = seekToPattern(ID_PATTERN); | ||||
|         _currentSector = 0; | ||||
|     } | ||||
|  | ||||
|     RecordType advanceToNextRecord() | ||||
| 	{ | ||||
| 		if (_currentSector == -1) | ||||
| 		{ | ||||
| 			/* First sector in the track: look for the sync marker. */ | ||||
| 			const FluxMatcher* matcher = nullptr; | ||||
| 			_sector->clock = _clock = _fmr->seekToPattern(ID_PATTERN, matcher); | ||||
| 			readRawBits(32); /* skip the ID mark */ | ||||
| 			_logicalTrack = decodeFmMfm(readRawBits(32)).slice(0, 32).reader().read_be16(); | ||||
| 		} | ||||
| 		else if (_currentSector == 10) | ||||
| 		{ | ||||
| 			/* 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; | ||||
| 		} | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         if (_currentSector == 11) | ||||
|         { | ||||
|             /* That was the last sector on the disk. */ | ||||
|             return 0; | ||||
|         } | ||||
|         else | ||||
|             return _clock; | ||||
|     } | ||||
|  | ||||
| 		_currentSector++; | ||||
| 		return SECTOR_RECORD; | ||||
| 	} | ||||
|     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. */ | ||||
|  | ||||
|     void decodeSectorRecord() | ||||
| 	{ | ||||
| 		auto bits = readRawBits((SECTOR_SIZE+2)*16); | ||||
| 		auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE+2).swab(); | ||||
|         if (_currentSector == 0) | ||||
|             readRawBits(64); | ||||
|  | ||||
| 		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(); | ||||
|         auto bits = readRawBits((SECTOR_SIZE + 2) * 16); | ||||
|         auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE + 2); | ||||
|  | ||||
| 		_sector->logicalTrack = _logicalTrack; | ||||
| 		_sector->logicalSide = _sector->physicalHead; | ||||
| 		_sector->logicalSector = _currentSector; | ||||
| 		_sector->data = bytes.slice(0, SECTOR_SIZE); | ||||
| 		_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
| 	} | ||||
|         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->logicalTrack = _sector->physicalTrack; | ||||
|         _sector->logicalSide = _sector->physicalSide; | ||||
|         _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; | ||||
|     int _logicalTrack; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractDecoder> createMxDecoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createMxDecoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new MxDecoder(config)); | ||||
|     return std::unique_ptr<Decoder>(new MxDecoder(config)); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| #ifndef MX_H | ||||
| #define MX_H | ||||
|  | ||||
| #include "decoders/decoders.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createMxDecoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<Decoder> createMxDecoder(const DecoderProto& config); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -11,16 +11,19 @@ | ||||
|  * sure that the hardSectorId is correct. | ||||
|  */ | ||||
|  | ||||
| #include "globals.h" | ||||
| #include "fluxmap.h" | ||||
| #include "decoders/fluxmapreader.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "sector.h" | ||||
| #include "northstar.h" | ||||
| #include "bytes.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/data/sector.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. | ||||
| @@ -33,164 +36,152 @@ | ||||
|  * 0000 0000 0000 0000 0000 0000 0101 0101 0100 0101 | ||||
|  * A    A    A    A    A    A    5    5    4    5 | ||||
|  */ | ||||
| static const FluxPattern MFM_PATTERN(64, 0xAAAAAAAAAAAA5545LL); | ||||
| static const FluxPattern MFM_PATTERN(64, MFM_ID); | ||||
|  | ||||
| /* FM sectors have 16 bytes of 00's followed by 0xFB. | ||||
|  * 00        FB | ||||
|  * 0000 0000 1111 1111 1110 1111 | ||||
|  * A    A    F    F    E    F | ||||
|  */ | ||||
| static const FluxPattern FM_PATTERN(64, 0xAAAAAAAAAAAAFFEFLL); | ||||
| static const FluxPattern FM_PATTERN(64, FM_ID); | ||||
|  | ||||
| const FluxMatchers ANY_SECTOR_PATTERN( | ||||
| 	{ | ||||
| 		&MFM_PATTERN, | ||||
| 		&FM_PATTERN, | ||||
| 	} | ||||
| ); | ||||
| 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; | ||||
| 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))); | ||||
| 	} | ||||
|     while (!br.eof()) | ||||
|     { | ||||
|         checksum ^= br.read_8(); | ||||
|         checksum = ((checksum << 1) | ((checksum >> 7))); | ||||
|     } | ||||
|  | ||||
| 	return checksum; | ||||
|     return checksum; | ||||
| } | ||||
|  | ||||
| class NorthstarDecoder : public AbstractDecoder | ||||
| class NorthstarDecoder : public Decoder | ||||
| { | ||||
| public: | ||||
| 	NorthstarDecoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config), | ||||
| 		_config(config.northstar()) | ||||
| 	{} | ||||
|     NorthstarDecoder(const DecoderProto& config): | ||||
|         Decoder(config), | ||||
|         _config(config.northstar()) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| 	/* Search for FM or MFM sector record */ | ||||
| 	RecordType advanceToNextRecord() override | ||||
| 	{ | ||||
| 		nanoseconds_t now = _fmr->tell().ns(); | ||||
|     /* 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) { | ||||
| 			_fmr->seekToIndexMark(); | ||||
| 			now = _fmr->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 > (_fmr->getDuration() - 21e6)) { | ||||
| 			_fmr->seekToIndexMark(); | ||||
| 			return(UNKNOWN_RECORD); | ||||
| 		} | ||||
|         /* 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); | ||||
|         int msSinceIndex = std::round(now / 1e6); | ||||
|  | ||||
| 		const FluxMatcher* matcher = nullptr; | ||||
|         /* Note that the seekToPattern ignores the sector pulses, so if | ||||
|          * a sector is not found for some reason, the seek will advance | ||||
|          * past one or more sector pulses.  For this reason, calculate | ||||
|          * _hardSectorId after the sector header is found. | ||||
|          */ | ||||
|         nanoseconds_t clock = seekToPattern(ANY_SECTOR_PATTERN); | ||||
|         _sector->headerStartTime = tell().ns(); | ||||
|  | ||||
| 		/* Note that the seekToPattern ignores the sector pulses, so if | ||||
| 		 * a sector is not found for some reason, the seek will advance | ||||
| 		 * past one or more sector pulses.  For this reason, calculate | ||||
| 		 * _hardSectorId after the sector header is found. | ||||
| 		 */ | ||||
| 		_sector->clock = _fmr->seekToPattern(ANY_SECTOR_PATTERN, matcher); | ||||
|         /* Discard a possible partial sector. */ | ||||
|         if (_sector->headerStartTime > (getFluxmapDuration() - 21e6)) | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
| 		int sectorFoundTimeRaw = std::round((_fmr->tell().ns()) / 1e6); | ||||
| 		int sectorFoundTime; | ||||
|         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; | ||||
| 		} | ||||
|         /* 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; | ||||
|         /* Calculate the sector ID based on time since the index */ | ||||
|         _hardSectorId = (sectorFoundTime / 20) % 10; | ||||
|  | ||||
| 	//	std::cout << fmt::format( | ||||
| 	//		"Sector ID {}: hole at {}ms, sector start at {}ms", | ||||
| 	//		_hardSectorId, msSinceIndex, sectorFoundTimeRaw) << std::endl; | ||||
|         return clock; | ||||
|     } | ||||
|  | ||||
| 		if (matcher == &MFM_PATTERN) { | ||||
| 			_sectorType = SECTOR_TYPE_MFM; | ||||
| 			return SECTOR_RECORD; | ||||
| 		} | ||||
|     void decodeSectorRecord() override | ||||
|     { | ||||
|         uint64_t id = toBytes(readRawBits(64)).reader().read_be64(); | ||||
|         unsigned recordSize, payloadSize, headerSize; | ||||
|  | ||||
| 		if (matcher == &FM_PATTERN) { | ||||
| 			_sectorType = SECTOR_TYPE_FM; | ||||
| 			return SECTOR_RECORD; | ||||
| 		} | ||||
|         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; | ||||
|         } | ||||
|  | ||||
| 		return UNKNOWN_RECORD; | ||||
| 	} | ||||
|         auto rawbits = readRawBits(recordSize * 16); | ||||
|         auto bytes = decodeFmMfm(rawbits).slice(0, recordSize); | ||||
|         ByteReader br(bytes); | ||||
|  | ||||
| 	void decodeSectorRecord() override | ||||
| 	{ | ||||
| 		unsigned recordSize, payloadSize, headerSize; | ||||
|         _sector->logicalSide = _sector->physicalSide; | ||||
|         _sector->logicalSector = _hardSectorId; | ||||
|         _sector->logicalTrack = _sector->physicalTrack; | ||||
|  | ||||
| 		if (_sectorType == SECTOR_TYPE_MFM) { | ||||
| 			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; | ||||
| 		} | ||||
|         if (headerSize == NORTHSTAR_HEADER_SIZE_DD) | ||||
|         { | ||||
|             br.read_8(); /* MFM second Sync char, usually 0xFB */ | ||||
|         } | ||||
|  | ||||
| 		readRawBits(48); | ||||
|  | ||||
| 		auto rawbits = readRawBits(recordSize * 16); | ||||
| 		auto bytes = decodeFmMfm(rawbits).slice(0, recordSize); | ||||
| 		ByteReader br(bytes); | ||||
| 		uint8_t sync_char; | ||||
|  | ||||
| 		_sector->logicalSide = _sector->physicalHead; | ||||
| 		_sector->logicalSector = _hardSectorId; | ||||
| 		_sector->logicalTrack = _sector->physicalCylinder; | ||||
|  | ||||
| 		sync_char = br.read_8();	/* Sync char: 0xFB */ | ||||
| 		if (_sectorType == SECTOR_TYPE_MFM) { | ||||
| 			sync_char = br.read_8();/* MFM second Sync char, usually 0xFB */ | ||||
| 		} | ||||
|  | ||||
| 		_sector->data = br.read(payloadSize); | ||||
|  | ||||
| 		uint8_t wantChecksum = br.read_8(); | ||||
| 		uint8_t gotChecksum = northstarChecksum(bytes.slice(headerSize, payloadSize)); | ||||
|  | ||||
| 		_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
| 	} | ||||
|  | ||||
| 	std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override | ||||
| 	{ | ||||
| 		static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; | ||||
| 		return sectors; | ||||
| 	} | ||||
|         _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 _sectorType = SECTOR_TYPE_MFM; | ||||
| 	uint8_t _hardSectorId; | ||||
|     const NorthstarDecoderProto& _config; | ||||
|     uint8_t _hardSectorId; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractDecoder> createNorthstarDecoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createNorthstarDecoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new NorthstarDecoder(config)); | ||||
|     return std::unique_ptr<Decoder>(new NorthstarDecoder(config)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| #include "globals.h" | ||||
| #include "northstar.h" | ||||
| #include "sector.h" | ||||
| #include "bytes.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.h" | ||||
| #include "image.h" | ||||
| #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 | ||||
| @@ -12,155 +12,157 @@ | ||||
| #define GAP_FILL_SIZE_DD 62 | ||||
| #define PRE_HEADER_GAP_FILL_SIZE_DD 16 | ||||
|  | ||||
| #define GAP1_FILL_BYTE	(0x4F) | ||||
| #define GAP2_FILL_BYTE	(0x4F) | ||||
| #define GAP1_FILL_BYTE (0x4F) | ||||
| #define GAP2_FILL_BYTE (0x4F) | ||||
|  | ||||
| #define TOTAL_SECTOR_BYTES () | ||||
|  | ||||
| static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<Sector>& sector) | ||||
| static void write_sector(std::vector<bool>& bits, | ||||
|     unsigned& cursor, | ||||
|     const std::shared_ptr<const Sector>& sector) | ||||
| { | ||||
| 	int preambleSize = 0; | ||||
| 	int encodedSectorSize = 0; | ||||
| 	int gapFillSize = 0; | ||||
| 	int preHeaderGapFillSize = 0; | ||||
|     int preambleSize = 0; | ||||
|     int encodedSectorSize = 0; | ||||
|     int gapFillSize = 0; | ||||
|     int preHeaderGapFillSize = 0; | ||||
|  | ||||
| 	bool doubleDensity; | ||||
|     bool doubleDensity; | ||||
|  | ||||
| 	switch (sector->data.size()) { | ||||
| 	case NORTHSTAR_PAYLOAD_SIZE_SD: | ||||
| 		preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD; | ||||
| 		encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD + NORTHSTAR_ENCODED_SECTOR_SIZE_SD + GAP_FILL_SIZE_SD; | ||||
| 		gapFillSize = GAP_FILL_SIZE_SD; | ||||
| 		preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD; | ||||
| 		doubleDensity = false; | ||||
| 		break; | ||||
| 	case NORTHSTAR_PAYLOAD_SIZE_DD: | ||||
| 		preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD; | ||||
| 		encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD + NORTHSTAR_ENCODED_SECTOR_SIZE_DD + GAP_FILL_SIZE_DD; | ||||
| 		gapFillSize = GAP_FILL_SIZE_DD; | ||||
| 		preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD; | ||||
| 		doubleDensity = true; | ||||
| 		break; | ||||
| 	default: | ||||
| 		Error() << "unsupported sector size --- you must pick 256 or 512"; | ||||
| 		break; | ||||
| 	} | ||||
|     switch (sector->data.size()) | ||||
|     { | ||||
|         case NORTHSTAR_PAYLOAD_SIZE_SD: | ||||
|             preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD; | ||||
|             encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD + | ||||
|                                 NORTHSTAR_ENCODED_SECTOR_SIZE_SD + | ||||
|                                 GAP_FILL_SIZE_SD; | ||||
|             gapFillSize = GAP_FILL_SIZE_SD; | ||||
|             preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD; | ||||
|             doubleDensity = false; | ||||
|             break; | ||||
|         case NORTHSTAR_PAYLOAD_SIZE_DD: | ||||
|             preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD; | ||||
|             encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD + | ||||
|                                 NORTHSTAR_ENCODED_SECTOR_SIZE_DD + | ||||
|                                 GAP_FILL_SIZE_DD; | ||||
|             gapFillSize = GAP_FILL_SIZE_DD; | ||||
|             preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD; | ||||
|             doubleDensity = true; | ||||
|             break; | ||||
|         default: | ||||
|             error("unsupported sector size --- you must pick 256 or 512"); | ||||
|             break; | ||||
|     } | ||||
|  | ||||
| 	int fullSectorSize = preambleSize + encodedSectorSize; | ||||
| 	auto fullSector = std::make_shared<std::vector<uint8_t>>(); | ||||
| 	fullSector->reserve(fullSectorSize); | ||||
|     int fullSectorSize = preambleSize + encodedSectorSize; | ||||
|     auto fullSector = std::make_shared<std::vector<uint8_t>>(); | ||||
|     fullSector->reserve(fullSectorSize); | ||||
|  | ||||
| 	/* sector gap after index pulse */ | ||||
| 	for (int i = 0; i < preHeaderGapFillSize; i++) | ||||
| 		fullSector->push_back(GAP1_FILL_BYTE); | ||||
|     /* sector gap after index pulse */ | ||||
|     for (int i = 0; i < preHeaderGapFillSize; i++) | ||||
|         fullSector->push_back(GAP1_FILL_BYTE); | ||||
|  | ||||
| 	/* sector preamble */ | ||||
| 	for (int i = 0; i < preambleSize; i++) | ||||
| 		fullSector->push_back(0); | ||||
|     /* sector preamble */ | ||||
|     for (int i = 0; i < preambleSize; i++) | ||||
|         fullSector->push_back(0); | ||||
|  | ||||
| 	Bytes sectorData; | ||||
| 	if (sector->data.size() == encodedSectorSize) | ||||
| 		sectorData = sector->data; | ||||
| 	else { | ||||
| 		ByteWriter writer(sectorData); | ||||
| 		writer.write_8(0xFB);     /* sync character */ | ||||
| 		if (doubleDensity == true) { | ||||
| 			writer.write_8(0xFB); /* Double-density has two sync characters */ | ||||
| 		} | ||||
| 		writer += sector->data; | ||||
| 		if (doubleDensity == true) { | ||||
| 			writer.write_8(northstarChecksum(sectorData.slice(2))); | ||||
| 		} else { | ||||
| 			writer.write_8(northstarChecksum(sectorData.slice(1))); | ||||
| 		} | ||||
| 	} | ||||
| 	for (uint8_t b : sectorData) | ||||
| 		fullSector->push_back(b); | ||||
|     Bytes sectorData; | ||||
|     if (sector->data.size() == encodedSectorSize) | ||||
|         sectorData = sector->data; | ||||
|     else | ||||
|     { | ||||
|         ByteWriter writer(sectorData); | ||||
|         writer.write_8(0xFB); /* sync character */ | ||||
|         if (doubleDensity == true) | ||||
|         { | ||||
|             writer.write_8(0xFB); /* Double-density has two sync characters */ | ||||
|         } | ||||
|         writer += sector->data; | ||||
|         if (doubleDensity == true) | ||||
|         { | ||||
|             writer.write_8(northstarChecksum(sectorData.slice(2))); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             writer.write_8(northstarChecksum(sectorData.slice(1))); | ||||
|         } | ||||
|     } | ||||
|     for (uint8_t b : sectorData) | ||||
|         fullSector->push_back(b); | ||||
|  | ||||
| 	if (sector->logicalSector != 9) { | ||||
| 		/* sector postamble */ | ||||
| 		for (int i = 0; i < gapFillSize; i++) | ||||
| 			fullSector->push_back(GAP2_FILL_BYTE); | ||||
|     if (sector->logicalSector != 9) | ||||
|     { | ||||
|         /* sector postamble */ | ||||
|         for (int i = 0; i < gapFillSize; i++) | ||||
|             fullSector->push_back(GAP2_FILL_BYTE); | ||||
|  | ||||
| 		if (fullSector->size() != fullSectorSize) | ||||
| 			Error() << "sector mismatched length (" << sector->data.size() << ") expected: " << fullSector->size() << " got " << fullSectorSize; | ||||
| 	} else { | ||||
| 		/* sector postamble */ | ||||
| 		for (int i = 0; i < gapFillSize; i++) | ||||
| 			fullSector->push_back(GAP2_FILL_BYTE); | ||||
| 	} | ||||
|         if (fullSector->size() != fullSectorSize) | ||||
|             error("sector mismatched length ({}); 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; | ||||
|     bool lastBit = false; | ||||
|  | ||||
| 	if (doubleDensity == true) { | ||||
| 		encodeMfm(bits, cursor, fullSector, lastBit); | ||||
| 	} | ||||
| 	else { | ||||
| 		encodeFm(bits, cursor, fullSector); | ||||
| 	} | ||||
|     if (doubleDensity == true) | ||||
|     { | ||||
|         encodeMfm(bits, cursor, fullSector, lastBit); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         encodeFm(bits, cursor, fullSector); | ||||
|     } | ||||
| } | ||||
|  | ||||
| class NorthstarEncoder : public AbstractEncoder | ||||
| class NorthstarEncoder : public Encoder | ||||
| { | ||||
| public: | ||||
| 	NorthstarEncoder(const EncoderProto& config): | ||||
| 		AbstractEncoder(config), | ||||
| 		_config(config.northstar()) | ||||
| 	{} | ||||
|     NorthstarEncoder(const EncoderProto& config): | ||||
|         Encoder(config), | ||||
|         _config(config.northstar()) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| 	std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override | ||||
| 	{ | ||||
| 		std::vector<std::shared_ptr<Sector>> sectors; | ||||
|     std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo, | ||||
|         const std::vector<std::shared_ptr<const Sector>>& sectors, | ||||
|         const Image& image) override | ||||
|     { | ||||
|         int bitsPerRevolution = 100000; | ||||
|         double clockRateUs = _config.clock_period_us(); | ||||
|  | ||||
| 		if ((physicalTrack >= 0) && (physicalTrack < 35)) | ||||
| 		{ | ||||
| 			for (int sectorId = 0; sectorId < 10; sectorId++) | ||||
| 			{ | ||||
| 				const auto& sector = image.get(physicalTrack, physicalSide, sectorId); | ||||
| 				if (sector) | ||||
| 					sectors.push_back(sector); | ||||
| 			} | ||||
| 		} | ||||
|         const auto& sector = *sectors.begin(); | ||||
|         if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) | ||||
|             bitsPerRevolution /= 2; // FM | ||||
|         else | ||||
|             clockRateUs /= 2.00; | ||||
|  | ||||
| 		return sectors; | ||||
| 	} | ||||
|         std::vector<bool> bits(bitsPerRevolution); | ||||
|         unsigned cursor = 0; | ||||
|  | ||||
| 	std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, | ||||
| 			const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override | ||||
| 	{ | ||||
| 		int bitsPerRevolution = 100000; | ||||
| 		double clockRateUs = 4.00; | ||||
|         for (const auto& sectorData : sectors) | ||||
|             write_sector(bits, cursor, sectorData); | ||||
|  | ||||
| 		if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty()) | ||||
| 			return std::unique_ptr<Fluxmap>(); | ||||
|         if (cursor > bits.size()) | ||||
|             error("track data overrun"); | ||||
|  | ||||
| 		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, clockRateUs * 1e3); | ||||
| 		return fluxmap; | ||||
| 	} | ||||
|         std::unique_ptr<Fluxmap> fluxmap(new Fluxmap); | ||||
|         fluxmap->appendBits(bits, | ||||
|             calculatePhysicalClockPeriod( | ||||
|                 clockRateUs * 1e3, _config.rotational_period_ms() * 1e6)); | ||||
|         return fluxmap; | ||||
|     } | ||||
|  | ||||
| private: | ||||
| 	const NorthstarEncoderProto& _config; | ||||
|     const NorthstarEncoderProto& _config; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractEncoder> createNorthstarEncoder(const EncoderProto& config) | ||||
| std::unique_ptr<Encoder> createNorthstarEncoder(const EncoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config)); | ||||
|     return std::unique_ptr<Encoder>(new NorthstarEncoder(config)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,8 @@ | ||||
| #ifndef NORTHSTAR_H | ||||
| #define NORTHSTAR_H | ||||
|  | ||||
| /* Northstar floppies are 10-hard sectored disks with a sector format as follows: | ||||
| /* Northstar floppies are 10-hard sectored disks with a sector format as | ||||
|  * follows: | ||||
|  * | ||||
|  * |----------------------------------| | ||||
|  * | SYNC Byte  | Payload  | Checksum | | ||||
| @@ -12,27 +13,30 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #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) | ||||
| #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) | ||||
|  | ||||
| #define SECTOR_TYPE_MFM			(0) | ||||
| #define SECTOR_TYPE_FM				(1) | ||||
|  | ||||
| class AbstractDecoder; | ||||
| class AbstractEncoder; | ||||
| class Decoder; | ||||
| class Encoder; | ||||
| class EncoderProto; | ||||
| class DecoderProto; | ||||
|  | ||||
| extern uint8_t northstarChecksum(const Bytes& bytes); | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createNorthstarDecoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<AbstractEncoder> createNorthstarEncoder(const EncoderProto& config); | ||||
| extern std::unique_ptr<Decoder> createNorthstarDecoder( | ||||
|     const DecoderProto& config); | ||||
| extern std::unique_ptr<Encoder> createNorthstarEncoder( | ||||
|     const EncoderProto& config); | ||||
|  | ||||
| #endif /* NORTHSTAR */ | ||||
|   | ||||
| @@ -1,5 +1,13 @@ | ||||
| syntax = "proto2"; | ||||
|  | ||||
| message NorthstarDecoderProto {} | ||||
| message NorthstarEncoderProto {} | ||||
| 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 {} | ||||
|  | ||||
|  | ||||
							
								
								
									
										155
									
								
								arch/smaky6/decoder.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								arch/smaky6/decoder.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,155 @@ | ||||
| #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 "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 != _sector->physicalTrack) | ||||
|             return; | ||||
|  | ||||
|         _sector->logicalTrack = _sector->physicalTrack; | ||||
|         _sector->logicalSide = _sector->physicalSide; | ||||
|         _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->logicalTrack = track >> 1; | ||||
|         _sector->logicalSide = 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(std::shared_ptr<const TrackInfo>& trackInfo, | ||||
|         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->logicalTrack << 1) | sectorData->logicalSide); | ||||
|             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" ]; | ||||
| } | ||||
| @@ -1,13 +1,14 @@ | ||||
| #include "globals.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.h" | ||||
| #include "tids990/tids990.h" | ||||
| #include "crc.h" | ||||
| #include "fluxmap.h" | ||||
| #include "decoders/fluxmapreader.h" | ||||
| #include "sector.h" | ||||
| #include "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> | ||||
| #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). | ||||
| @@ -23,76 +24,78 @@ | ||||
|  * When shifted out of phase, the special 0xa1 byte becomes an illegal | ||||
|  * encoding (you can't do 10 00). So this can't be spoofed by user data. | ||||
|  */ | ||||
| const uint16_t SECTOR_ID = 0x550a; | ||||
| const FluxPattern SECTOR_RECORD_PATTERN(32, 0x11112244); | ||||
|  | ||||
| /* | ||||
|  * Data record: | ||||
|  * data:    0  1  0  1  0  1  0  1 .0  0  0  0  1  0  1  1  = 0x550c | ||||
|  * data:    0  1  0  1  0  1  0  1 .0  0  0  0  1  0  1  1  = 0x550b | ||||
|  * mfm:     00 01 00 01 00 01 00 01.00 10 10 10 01 00 01 01 = 0x11112a45 | ||||
|  * special: 00 01 00 01 00 01 00 01.00 10 00 10 01 00 01 01 = 0x11112245 | ||||
|  *                                        ^^ | ||||
|  * When shifted out of phase, the special 0xa1 byte becomes an illegal | ||||
|  * encoding (you can't do 10 00). So this can't be spoofed by user data. | ||||
|  */ | ||||
| const uint16_t DATA_ID = 0x550b; | ||||
| const FluxPattern DATA_RECORD_PATTERN(32, 0x11112245); | ||||
|  | ||||
| const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN }); | ||||
| const FluxMatchers ANY_RECORD_PATTERN( | ||||
|     {&SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN}); | ||||
|  | ||||
| class Tids990Decoder : public AbstractDecoder | ||||
| class Tids990Decoder : public Decoder | ||||
| { | ||||
| public: | ||||
| 	Tids990Decoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config) | ||||
| 	{} | ||||
|     Tids990Decoder(const DecoderProto& config): Decoder(config) {} | ||||
|  | ||||
|     RecordType advanceToNextRecord() | ||||
| 	{ | ||||
| 		const FluxMatcher* matcher = nullptr; | ||||
| 		_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); | ||||
| 		if (matcher == &SECTOR_RECORD_PATTERN) | ||||
| 			return RecordType::SECTOR_RECORD; | ||||
| 		if (matcher == &DATA_RECORD_PATTERN) | ||||
| 			return RecordType::DATA_RECORD; | ||||
| 		return RecordType::UNKNOWN_RECORD; | ||||
| 	} | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         return seekToPattern(ANY_RECORD_PATTERN); | ||||
|     } | ||||
|  | ||||
|     void decodeSectorRecord() | ||||
| 	{ | ||||
| 		auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE*16); | ||||
| 		auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE); | ||||
|     void decodeSectorRecord() override | ||||
|     { | ||||
|         auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE * 16); | ||||
|         auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE); | ||||
|  | ||||
| 		ByteReader br(bytes); | ||||
| 		uint16_t gotChecksum = crc16(CCITT_POLY, bytes.slice(1, TIDS990_SECTOR_RECORD_SIZE-3)); | ||||
|         ByteReader br(bytes); | ||||
|         if (br.read_be16() != SECTOR_ID) | ||||
|             return; | ||||
|  | ||||
| 		br.seek(2); | ||||
| 		_sector->logicalSide = br.read_8() >> 3; | ||||
| 		_sector->logicalTrack = br.read_8(); | ||||
| 		br.read_8(); /* number of sectors per track */ | ||||
| 		_sector->logicalSector = br.read_8(); | ||||
| 		br.read_be16(); /* sector size */ | ||||
| 		uint16_t wantChecksum = br.read_be16(); | ||||
|         uint16_t gotChecksum = | ||||
|             crc16(CCITT_POLY, bytes.slice(1, TIDS990_SECTOR_RECORD_SIZE - 3)); | ||||
|  | ||||
| 		if (wantChecksum == gotChecksum) | ||||
| 			_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */ | ||||
| 	} | ||||
|         _sector->logicalSide = br.read_8() >> 3; | ||||
|         _sector->logicalTrack = br.read_8(); | ||||
|         br.read_8(); /* number of sectors per track */ | ||||
|         _sector->logicalSector = br.read_8(); | ||||
|         br.read_be16(); /* sector size */ | ||||
|         uint16_t wantChecksum = br.read_be16(); | ||||
|  | ||||
| 	void decodeDataRecord() | ||||
| 	{ | ||||
| 		auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE*16); | ||||
| 		auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE); | ||||
|         if (wantChecksum == gotChecksum) | ||||
|             _sector->status = | ||||
|                 Sector::DATA_MISSING; /* correct but unintuitive */ | ||||
|     } | ||||
|  | ||||
| 		ByteReader br(bytes); | ||||
| 		uint16_t gotChecksum = crc16(CCITT_POLY, bytes.slice(1, TIDS990_DATA_RECORD_SIZE-3)); | ||||
|     void decodeDataRecord() override | ||||
|     { | ||||
|         auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE * 16); | ||||
|         auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE); | ||||
|  | ||||
| 		br.seek(2); | ||||
| 		_sector->data = br.read(TIDS990_PAYLOAD_SIZE); | ||||
| 		uint16_t wantChecksum = br.read_be16(); | ||||
| 		_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
| 	} | ||||
|         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<AbstractDecoder> createTids990Decoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createTids990Decoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new Tids990Decoder(config)); | ||||
|     return std::unique_ptr<Decoder>(new Tids990Decoder(config)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,168 +1,150 @@ | ||||
| #include "globals.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.h" | ||||
| #include "tids990.h" | ||||
| #include "crc.h" | ||||
| #include "writer.h" | ||||
| #include "image.h" | ||||
| #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> | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| static int charToInt(char c) | ||||
| { | ||||
| 	if (isdigit(c)) | ||||
| 		return c - '0'; | ||||
| 	return 10 + tolower(c) - 'a'; | ||||
|     if (isdigit(c)) | ||||
|         return c - '0'; | ||||
|     return 10 + tolower(c) - 'a'; | ||||
| } | ||||
|  | ||||
| static uint8_t decodeUint16(uint16_t raw) | ||||
| { | ||||
| 	Bytes b; | ||||
| 	ByteWriter bw(b); | ||||
| 	bw.write_be16(raw); | ||||
| 	return decodeFmMfm(b.toBits())[0]; | ||||
|     Bytes b; | ||||
|     ByteWriter bw(b); | ||||
|     bw.write_be16(raw); | ||||
|     return decodeFmMfm(b.toBits())[0]; | ||||
| } | ||||
|  | ||||
| class Tids990Encoder : public AbstractEncoder | ||||
| class Tids990Encoder : public Encoder | ||||
| { | ||||
| public: | ||||
| 	Tids990Encoder(const EncoderProto& config): | ||||
| 		AbstractEncoder(config), | ||||
| 		_config(config.tids990()) | ||||
| 	{} | ||||
|     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 writeRawBits(uint32_t data, int width) | ||||
|     { | ||||
|         _cursor += width; | ||||
|         _lastBit = data & 1; | ||||
|         for (int i = 0; i < width; i++) | ||||
|         { | ||||
|             unsigned pos = _cursor - i - 1; | ||||
|             if (pos < _bits.size()) | ||||
|                 _bits[pos] = data & 1; | ||||
|             data >>= 1; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| 	void writeBytes(const Bytes& bytes) | ||||
| 	{ | ||||
| 		encodeMfm(_bits, _cursor, bytes, _lastBit); | ||||
| 	} | ||||
|     void writeBytes(const Bytes& bytes) | ||||
|     { | ||||
|         encodeMfm(_bits, _cursor, bytes, _lastBit); | ||||
|     } | ||||
|  | ||||
| 	void writeBytes(int count, uint8_t byte) | ||||
| 	{ | ||||
| 		Bytes bytes = { byte }; | ||||
| 		for (int i=0; i<count; i++) | ||||
| 			writeBytes(bytes); | ||||
| 	} | ||||
|     void writeBytes(int count, uint8_t byte) | ||||
|     { | ||||
|         Bytes bytes = {byte}; | ||||
|         for (int i = 0; i < count; i++) | ||||
|             writeBytes(bytes); | ||||
|     } | ||||
|  | ||||
| public: | ||||
| 	std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override | ||||
| 	{ | ||||
| 		std::vector<std::shared_ptr<Sector>> sectors; | ||||
|     std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo, | ||||
|         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; | ||||
|  | ||||
| 		for (char sectorChar : _config.sector_skew()) | ||||
|         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) | ||||
|         { | ||||
| 			int sectorId = charToInt(sectorChar); | ||||
| 			const auto& sector = image.get(physicalTrack, physicalSide, sectorId); | ||||
| 			if (sector) | ||||
| 				sectors.push_back(sector); | ||||
|             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->logicalSide << 3); | ||||
|                 bw.write_8(sectorData->logicalTrack); | ||||
|                 bw.write_8(_config.sector_count()); | ||||
|                 bw.write_8(sectorData->logicalSector); | ||||
|                 bw.write_be16(sectorData->data.size()); | ||||
|                 uint16_t crc = crc16(CCITT_POLY, header); | ||||
|                 bw.write_be16(crc); | ||||
|  | ||||
|                 writeRawBits(_config.am1_byte(), 16); | ||||
|                 writeBytes(header.slice(1)); | ||||
|             } | ||||
|  | ||||
|             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)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| 		return sectors; | ||||
| 	} | ||||
|         if (_cursor >= _bits.size()) | ||||
|             error("track data overrun"); | ||||
|         while (_cursor < _bits.size()) | ||||
|             writeBytes(1, 0x55); | ||||
|  | ||||
|     std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, | ||||
| 			const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override | ||||
| 	{ | ||||
| 		double clockRateUs = 1e3 / _config.clock_rate_khz() / 2.0; | ||||
| 		int bitsPerRevolution = (_config.track_length_ms() * 1000.0) / clockRateUs; | ||||
| 		_bits.resize(bitsPerRevolution); | ||||
| 		_cursor = 0; | ||||
|  | ||||
| 		uint8_t am1Unencoded = decodeUint16(_config.am1_byte()); | ||||
| 		uint8_t am2Unencoded = decodeUint16(_config.am2_byte()); | ||||
|  | ||||
| 		writeBytes(_config.gap1_bytes(), 0x55); | ||||
|  | ||||
| 		bool first = true; | ||||
| 		for (char sectorChar : _config.sector_skew()) | ||||
| 		{ | ||||
| 			int sectorId = charToInt(sectorChar); | ||||
| 			if (!first) | ||||
| 				writeBytes(_config.gap3_bytes(), 0x55); | ||||
| 			first = false; | ||||
|  | ||||
| 			const auto& sectorData = image.get(physicalTrack, physicalSide, sectorId); | ||||
| 			if (!sectorData) | ||||
| 				Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId); | ||||
|  | ||||
| 			/* Writing the sector and data records are fantastically annoying. | ||||
| 			 * The CRC is calculated from the *very start* of the record, and | ||||
| 			 * include the malformed marker bytes. Our encoder doesn't know | ||||
| 			 * about this, of course, with the result that we have to construct | ||||
| 			 * the unencoded header, calculate the checksum, and then use the | ||||
| 			 * same logic to emit the bytes which require special encoding | ||||
| 			 * before encoding the rest of the header normally. */ | ||||
|  | ||||
| 			{ | ||||
| 				Bytes header; | ||||
| 				ByteWriter bw(header); | ||||
|  | ||||
| 				writeBytes(12, 0x55); | ||||
| 				bw.write_8(am1Unencoded); | ||||
| 				bw.write_8(sectorData->logicalSide << 3); | ||||
| 				bw.write_8(sectorData->logicalTrack); | ||||
| 				bw.write_8(_config.sector_count()); | ||||
| 				bw.write_8(sectorData->logicalSector); | ||||
| 				bw.write_be16(sectorData->data.size()); | ||||
| 				uint16_t crc = crc16(CCITT_POLY, header); | ||||
| 				bw.write_be16(crc); | ||||
|  | ||||
| 				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); | ||||
| 		 | ||||
| 		std::unique_ptr<Fluxmap> fluxmap(new Fluxmap); | ||||
| 		fluxmap->appendBits(_bits, clockRateUs*1e3); | ||||
| 		return fluxmap; | ||||
| 	} | ||||
|         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; | ||||
|     const Tids990EncoderProto& _config; | ||||
|     std::vector<bool> _bits; | ||||
|     unsigned _cursor; | ||||
|     bool _lastBit; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractEncoder> createTids990Encoder(const EncoderProto& config) | ||||
| std::unique_ptr<Encoder> createTids990Encoder(const EncoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractEncoder>(new Tids990Encoder(config)); | ||||
|     return std::unique_ptr<Encoder>(new Tids990Encoder(config)); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,18 +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 */ | ||||
| #define TIDS990_PAYLOAD_SIZE 288                            /* bytes */ | ||||
| #define TIDS990_SECTOR_RECORD_SIZE 10                       /* bytes */ | ||||
| #define TIDS990_DATA_RECORD_SIZE (TIDS990_PAYLOAD_SIZE + 4) /* bytes */ | ||||
|  | ||||
| class AbstractEncoder; | ||||
| class AbstractDecoder; | ||||
| class Encoder; | ||||
| class Decoder; | ||||
| class DecoderProto; | ||||
| class EncoderProto; | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createTids990Decoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<AbstractEncoder> createTids990Encoder(const EncoderProto& config); | ||||
| extern std::unique_ptr<Decoder> createTids990Decoder( | ||||
|     const DecoderProto& config); | ||||
| extern std::unique_ptr<Encoder> createTids990Encoder( | ||||
|     const EncoderProto& config); | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,14 +1,15 @@ | ||||
| syntax = "proto2"; | ||||
|  | ||||
| import "lib/common.proto"; | ||||
| import "lib/config/common.proto"; | ||||
|  | ||||
| message Tids990DecoderProto {} | ||||
|  | ||||
| message Tids990EncoderProto { | ||||
| 	optional double track_length_ms = 1 [ default = 166, | ||||
| 	optional double rotational_period_ms = 1 [ default = 166, | ||||
| 		(help) = "length of a track" ]; | ||||
| 	optional int32 sector_count = 2 [ default = 26, | ||||
| 		(help) = "number of sectors per track" ]; | ||||
| 	optional double clock_rate_khz = 3 [ default = 500, | ||||
| 	optional double clock_period_us = 3 [ default = 2, | ||||
| 		(help) = "clock rate of data to write" ]; | ||||
| 	optional int32 am1_byte = 4 [ default = 0x2244, | ||||
| 		(help) = "16-bit RAW bit pattern to use for the AM1 ID byte" ]; | ||||
| @@ -20,7 +21,5 @@ message Tids990EncoderProto { | ||||
| 		(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)" ]; | ||||
| 	optional string sector_skew = 9 [ default = "1mhc72nid83oje94pkfa50lgb6", | ||||
| 		(help) = "order to emit sectors" ]; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,28 +1,31 @@ | ||||
| #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 "decoders/decoders.h" | ||||
| #include "sector.h" | ||||
| #include "victor9k.h" | ||||
| #include "crc.h" | ||||
| #include "bytes.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; | ||||
| } | ||||
| @@ -37,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); | ||||
| @@ -52,73 +55,65 @@ static Bytes decode(const std::vector<bool>& bits) | ||||
|     return output; | ||||
| } | ||||
|  | ||||
| class Victor9kDecoder : public AbstractDecoder | ||||
| class Victor9kDecoder : public Decoder | ||||
| { | ||||
| public: | ||||
| 	Victor9kDecoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config) | ||||
| 	{} | ||||
|     Victor9kDecoder(const DecoderProto& config): Decoder(config) {} | ||||
|  | ||||
|     RecordType advanceToNextRecord() | ||||
| 	{ | ||||
| 		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; | ||||
| 	} | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         return seekToPattern(ANY_RECORD_PATTERN); | ||||
|     } | ||||
|  | ||||
|     void decodeSectorRecord() | ||||
| 	{ | ||||
| 		/* Skip the sync marker bit. */ | ||||
| 		readRawBits(22); | ||||
|     void decodeSectorRecord() override | ||||
|     { | ||||
|         /* Check the ID. */ | ||||
|  | ||||
| 		/* Read header. */ | ||||
|         if (readRaw32() != VICTOR9K_SECTOR_RECORD) | ||||
|             return; | ||||
|  | ||||
| 		auto bytes = decode(readRawBits(4*10)).slice(0, 4); | ||||
|         /* Read header. */ | ||||
|  | ||||
| 		uint8_t rawTrack = bytes[1]; | ||||
| 		_sector->logicalSector = bytes[2]; | ||||
| 		uint8_t gotChecksum = bytes[3]; | ||||
|         auto bytes = decode(readRawBits(3 * 10)).slice(0, 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 */ | ||||
| 	} | ||||
|         uint8_t rawTrack = bytes[0]; | ||||
|         _sector->logicalSector = bytes[1]; | ||||
|         uint8_t gotChecksum = bytes[2]; | ||||
|  | ||||
|     void decodeDataRecord() | ||||
| 	{ | ||||
| 		/* Skip the sync marker bit. */ | ||||
| 		readRawBits(22); | ||||
|         _sector->logicalTrack = rawTrack & 0x7f; | ||||
|         _sector->logicalSide = rawTrack >> 7; | ||||
|         uint8_t wantChecksum = bytes[0] + bytes[1]; | ||||
|         if ((_sector->logicalSector > 20) || (_sector->logicalTrack > 85) || | ||||
|             (_sector->logicalSide > 1)) | ||||
|             return; | ||||
|  | ||||
| 		/* Read data. */ | ||||
|         if (wantChecksum == gotChecksum) | ||||
|             _sector->status = | ||||
|                 Sector::DATA_MISSING; /* unintuitive but correct */ | ||||
|     } | ||||
|  | ||||
| 		auto bytes = decode(readRawBits((VICTOR9K_SECTOR_LENGTH+5)*10)) | ||||
| 			.slice(0, VICTOR9K_SECTOR_LENGTH+5); | ||||
| 		ByteReader br(bytes); | ||||
|     void decodeDataRecord() override | ||||
|     { | ||||
|         /* Check the ID. */ | ||||
|  | ||||
| 		/* Check that this is actually a data record. */ | ||||
| 		 | ||||
| 		if (br.read_8() != 8) | ||||
| 			return; | ||||
|         if (readRaw32() != VICTOR9K_DATA_RECORD) | ||||
|             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; | ||||
| 	} | ||||
|         /* 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<AbstractDecoder> createVictor9kDecoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createVictor9kDecoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new Victor9kDecoder(config)); | ||||
|     return std::unique_ptr<Decoder>(new Victor9kDecoder(config)); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,109 +1,129 @@ | ||||
| #include "globals.h" | ||||
| #include "decoders/decoders.h" | ||||
| #include "encoders/encoders.h" | ||||
| #include "victor9k.h" | ||||
| #include "crc.h" | ||||
| #include "sector.h" | ||||
| #include "writer.h" | ||||
| #include "image.h" | ||||
| #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 "bytes.h" | ||||
| #include "lib/core/bytes.h" | ||||
|  | ||||
| static bool lastBit; | ||||
|  | ||||
| static void write_zero_bits(std::vector<bool>& bits, unsigned& cursor, unsigned count) | ||||
| static void write_zero_bits( | ||||
|     std::vector<bool>& bits, unsigned& cursor, unsigned count) | ||||
| { | ||||
|     while (count--) | ||||
| 	{ | ||||
| 		if (cursor < bits.size()) | ||||
| 			lastBit = bits[cursor++] = 0; | ||||
| 	} | ||||
|     { | ||||
|         if (cursor < bits.size()) | ||||
|             lastBit = bits[cursor++] = 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void write_one_bits(std::vector<bool>& bits, unsigned& cursor, unsigned count) | ||||
| static void write_one_bits( | ||||
|     std::vector<bool>& bits, unsigned& cursor, unsigned count) | ||||
| { | ||||
|     while (count--) | ||||
| 	{ | ||||
| 		if (cursor < bits.size()) | ||||
| 			lastBit = bits[cursor++] = 1; | ||||
| 	} | ||||
|     { | ||||
|         if (cursor < bits.size()) | ||||
|             lastBit = bits[cursor++] = 1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src) | ||||
| static void write_bits( | ||||
|     std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src) | ||||
| { | ||||
| 	for (bool bit : src) | ||||
| 	{ | ||||
| 		if (cursor < bits.size()) | ||||
| 			lastBit = bits[cursor++] = bit; | ||||
| 	} | ||||
|     for (bool bit : src) | ||||
|     { | ||||
|         if (cursor < bits.size()) | ||||
|             lastBit = bits[cursor++] = bit; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width) | ||||
| static void write_bits( | ||||
|     std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width) | ||||
| { | ||||
| 	cursor += width; | ||||
| 	lastBit = data & 1; | ||||
| 	for (int i=0; i<width; i++) | ||||
| 	{ | ||||
| 		unsigned pos = cursor - i - 1; | ||||
| 		if (pos < bits.size()) | ||||
| 			bits[pos] = data & 1; | ||||
| 		data >>= 1; | ||||
| 	} | ||||
|     cursor += width; | ||||
|     lastBit = data & 1; | ||||
|     for (int i = 0; i < width; i++) | ||||
|     { | ||||
|         unsigned pos = cursor - i - 1; | ||||
|         if (pos < bits.size()) | ||||
|             bits[pos] = data & 1; | ||||
|         data >>= 1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void write_bits(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes) | ||||
| static void write_bits( | ||||
|     std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes) | ||||
| { | ||||
| 	ByteReader br(bytes); | ||||
| 	BitReader bitr(br); | ||||
|     ByteReader br(bytes); | ||||
|     BitReader bitr(br); | ||||
|  | ||||
| 	while (!bitr.eof()) | ||||
| 	{ | ||||
| 		if (cursor < bits.size()) | ||||
| 			bits[cursor++] = bitr.get(); | ||||
| 	} | ||||
|     while (!bitr.eof()) | ||||
|     { | ||||
|         if (cursor < bits.size()) | ||||
|             bits[cursor++] = bitr.get(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int encode_data_gcr(uint8_t data) | ||||
| { | ||||
|     switch (data & 0x0f) | ||||
|     { | ||||
|         #define GCR_ENTRY(gcr, data) \ | ||||
|             case data: return gcr; | ||||
|         #include "data_gcr.h" | ||||
|         #undef GCR_ENTRY | ||||
| #define GCR_ENTRY(gcr, data) \ | ||||
|     case data:               \ | ||||
|         return gcr; | ||||
| #include "data_gcr.h" | ||||
| #undef GCR_ENTRY | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static void write_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes) | ||||
| static void write_byte(std::vector<bool>& bits, unsigned& cursor, uint8_t b) | ||||
| { | ||||
|     for (uint8_t b : bytes) | ||||
|     { | ||||
|         write_bits(bits, cursor, encode_data_gcr(b>>4), 5); | ||||
|         write_bits(bits, cursor, encode_data_gcr(b),    5); | ||||
|     } | ||||
|     write_bits(bits, cursor, encode_data_gcr(b >> 4), 5); | ||||
|     write_bits(bits, cursor, encode_data_gcr(b), 5); | ||||
| } | ||||
|  | ||||
| static void write_sector(std::vector<bool>& bits, unsigned& cursor, | ||||
| 		const Victor9kEncoderProto::TrackdataProto& trackdata, | ||||
|         const Sector& sector) | ||||
| static void write_bytes( | ||||
|     std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes) | ||||
| { | ||||
|     for (uint8_t b : bytes) | ||||
|         write_byte(bits, cursor, b); | ||||
| } | ||||
|  | ||||
| static void write_gap(std::vector<bool>& bits, unsigned& cursor, int length) | ||||
| { | ||||
|     for (int i = 0; i < length / 10; i++) | ||||
|         write_byte(bits, cursor, '0'); | ||||
| } | ||||
|  | ||||
| static void write_sector(std::vector<bool>& bits, | ||||
|     unsigned& cursor, | ||||
|     const Victor9kEncoderProto::TrackdataProto& trackdata, | ||||
|     const Sector& sector) | ||||
| { | ||||
|     write_one_bits(bits, cursor, trackdata.pre_header_sync_bits()); | ||||
|     write_bits(bits, cursor, VICTOR9K_SECTOR_RECORD, 10); | ||||
|  | ||||
|     uint8_t encodedTrack = sector.logicalTrack | (sector.logicalSide<<7); | ||||
|     uint8_t encodedTrack = sector.logicalTrack | (sector.logicalSide << 7); | ||||
|     uint8_t encodedSector = sector.logicalSector; | ||||
|     write_bytes(bits, cursor, Bytes { | ||||
|         encodedTrack, | ||||
|         encodedSector, | ||||
|         (uint8_t)(encodedTrack + encodedSector), | ||||
|     }); | ||||
|     write_bytes(bits, | ||||
|         cursor, | ||||
|         Bytes{ | ||||
|             encodedTrack, | ||||
|             encodedSector, | ||||
|             (uint8_t)(encodedTrack + encodedSector), | ||||
|         }); | ||||
|  | ||||
|     write_gap(bits, cursor, trackdata.post_header_gap_bits()); | ||||
|  | ||||
|     write_zero_bits(bits, cursor, trackdata.post_header_gap_bits()); | ||||
|     write_one_bits(bits, cursor, trackdata.pre_data_sync_bits()); | ||||
|     write_bits(bits, cursor, VICTOR9K_DATA_RECORD, 10); | ||||
|  | ||||
| @@ -112,89 +132,79 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor, | ||||
|     Bytes checksum(2); | ||||
|     checksum.writer().write_le16(sumBytes(sector.data)); | ||||
|     write_bytes(bits, cursor, checksum); | ||||
|  | ||||
|     write_zero_bits(bits, cursor, trackdata.post_data_gap_bits()); | ||||
|     write_gap(bits, cursor, trackdata.post_data_gap_bits()); | ||||
| } | ||||
|  | ||||
| class Victor9kEncoder : public AbstractEncoder | ||||
| class Victor9kEncoder : public Encoder | ||||
| { | ||||
| public: | ||||
| 	Victor9kEncoder(const EncoderProto& config): | ||||
|         AbstractEncoder(config), | ||||
| 		_config(config.victor9k()) | ||||
| 	{} | ||||
|     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; | ||||
|  | ||||
| 	void getTrackFormat(Victor9kEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head) | ||||
| 	{ | ||||
| 		trackdata.Clear(); | ||||
| 		for (const auto& f : _config.trackdata()) | ||||
| 		{ | ||||
| 			if (f.has_min_cylinder() && (cylinder < f.min_cylinder())) | ||||
| 				continue; | ||||
| 			if (f.has_max_cylinder() && (cylinder > f.max_cylinder())) | ||||
| 				continue; | ||||
| 			if (f.has_head() && (head != f.head())) | ||||
| 				continue; | ||||
|  | ||||
| 			trackdata.MergeFrom(f); | ||||
| 		} | ||||
| 	} | ||||
|             trackdata.MergeFrom(f); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| public: | ||||
| 	std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override | ||||
| 	{ | ||||
| 		std::vector<std::shared_ptr<Sector>> sectors; | ||||
|  | ||||
| 		Victor9kEncoderProto::TrackdataProto trackdata; | ||||
| 		getTrackFormat(trackdata, physicalTrack, physicalSide); | ||||
|  | ||||
|         for (int i = 0; i < trackdata.sector_range().sector_count(); i++) | ||||
|         { | ||||
|             int sectorId = trackdata.sector_range().start_sector() + i; | ||||
| 			const auto& sector = image.get(physicalTrack, physicalSide, sectorId); | ||||
| 			if (sector) | ||||
| 				sectors.push_back(sector); | ||||
|         } | ||||
|  | ||||
| 		return sectors; | ||||
| 	} | ||||
|  | ||||
|     std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, | ||||
|             const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override | ||||
|     std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo, | ||||
|         const std::vector<std::shared_ptr<const Sector>>& sectors, | ||||
|         const Image& image) override | ||||
|     { | ||||
| 		Victor9kEncoderProto::TrackdataProto trackdata; | ||||
| 		getTrackFormat(trackdata, physicalTrack, physicalSide); | ||||
|         Victor9kEncoderProto::TrackdataProto trackdata; | ||||
|         getTrackFormat( | ||||
|             trackdata, trackInfo->logicalTrack, trackInfo->logicalSide); | ||||
|  | ||||
|         unsigned bitsPerRevolution = trackdata.original_data_rate_khz() * trackdata.original_period_ms(); | ||||
|         unsigned bitsPerRevolution = (trackdata.rotational_period_ms() * 1e3) / | ||||
|                                      trackdata.clock_period_us(); | ||||
|         std::vector<bool> bits(bitsPerRevolution); | ||||
|         double clockRateUs = 166666.0 / bitsPerRevolution; | ||||
|         nanoseconds_t clockPeriod = | ||||
|             calculatePhysicalClockPeriod(trackdata.clock_period_us() * 1e3, | ||||
|                 trackdata.rotational_period_ms() * 1e6); | ||||
|         unsigned cursor = 0; | ||||
|  | ||||
|         fillBitmapTo(bits, cursor, trackdata.post_index_gap_us() / clockRateUs, { true, false }); | ||||
|         fillBitmapTo(bits, | ||||
|             cursor, | ||||
|             trackdata.post_index_gap_us() * 1e3 / clockPeriod, | ||||
|             {true, false}); | ||||
|         lastBit = false; | ||||
|  | ||||
|         for (const auto& sector : sectors) | ||||
|             write_sector(bits, cursor, trackdata, *sector); | ||||
|  | ||||
|         if (cursor >= bits.size()) | ||||
|             Error() << fmt::format("track data overrun by {} bits", cursor - bits.size()); | ||||
|         fillBitmapTo(bits, cursor, bits.size(), { true, false }); | ||||
|             error("track data overrun by {} bits", cursor - bits.size()); | ||||
|         fillBitmapTo(bits, cursor, bits.size(), {true, false}); | ||||
|  | ||||
|         std::unique_ptr<Fluxmap> fluxmap(new Fluxmap); | ||||
|         fluxmap->appendBits(bits, clockRateUs*1e3); | ||||
|         fluxmap->appendBits(bits, clockPeriod); | ||||
|         return fluxmap; | ||||
|     } | ||||
|  | ||||
| private: | ||||
| 	const Victor9kEncoderProto& _config; | ||||
|     const Victor9kEncoderProto& _config; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractEncoder> createVictor9kEncoder(const EncoderProto& config) | ||||
| std::unique_ptr<Encoder> createVictor9kEncoder(const EncoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractEncoder>(new Victor9kEncoder(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 | ||||
|  | ||||
| class AbstractEncoder; | ||||
| class AbstractDecoder; | ||||
| class Encoder; | ||||
| class Decoder; | ||||
| class EncoderProto; | ||||
| class DecoderProto; | ||||
|  | ||||
| /* ... 1101 0101 0111 | ||||
|  *       ^^ ^^^^ ^^^^ ten bit IO byte */ | ||||
| #define VICTOR9K_SECTOR_RECORD 0xfffffd57  | ||||
| #define VICTOR9K_SECTOR_RECORD 0xfffffd57 | ||||
| #define VICTOR9K_HEADER_ID 0x7 | ||||
|  | ||||
| /* ... 1101 0100 1001 | ||||
|  *       ^^ ^^^^ ^^^^ ten bit IO byte */ | ||||
| #define VICTOR9K_DATA_RECORD   0xfffffd49 | ||||
| #define VICTOR9K_DATA_RECORD 0xfffffd49 | ||||
| #define VICTOR9K_DATA_ID 0x8 | ||||
|  | ||||
| #define VICTOR9K_SECTOR_LENGTH 512 | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createVictor9kDecoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<AbstractEncoder> createVictor9kEncoder(const EncoderProto& config); | ||||
| extern std::unique_ptr<Decoder> createVictor9kDecoder( | ||||
|     const DecoderProto& config); | ||||
| extern std::unique_ptr<Encoder> createVictor9kEncoder( | ||||
|     const EncoderProto& config); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -1,32 +1,36 @@ | ||||
| syntax = "proto2"; | ||||
|  | ||||
| import "lib/common.proto"; | ||||
| import "lib/config/common.proto"; | ||||
|  | ||||
| message Victor9kDecoderProto {} | ||||
|  | ||||
| // NEXT: 12 | ||||
| message Victor9kEncoderProto { | ||||
| 	message TrackdataProto { | ||||
| 		message SectorRangeProto { | ||||
| 			optional int32 start_sector = 1 [(help) = "first sector ID on track"]; | ||||
| 			optional int32 sector_count = 2 [(help) = "number of sectors on track"]; | ||||
| 		} | ||||
| message Victor9kEncoderProto | ||||
| { | ||||
|     message TrackdataProto | ||||
|     { | ||||
|         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 int32 min_cylinder = 1            [(help) = "minimum cylinder this format applies to"]; | ||||
| 		optional int32 max_cylinder = 2            [(help) = "maximum cylinder this format applies to"]; | ||||
| 		optional int32 head = 3                    [(help) = "which head this format applies to"]; | ||||
|         optional double rotational_period_ms = 4 | ||||
|             [ (help) = "original rotational period of this track" ]; | ||||
|         optional double clock_period_us = 5 | ||||
|             [ (help) = "original data rate of this track" ]; | ||||
|         optional double post_index_gap_us = 6 | ||||
|             [ (help) = "size of post-index gap" ]; | ||||
|         optional int32 pre_header_sync_bits = 10 | ||||
|             [ (help) = "number of sync bits before the sector header" ]; | ||||
|         optional int32 pre_data_sync_bits = 8 | ||||
|             [ (help) = "number of sync bits before the sector data" ]; | ||||
|         optional int32 post_data_gap_bits = 9 | ||||
|             [ (help) = "size of gap between data and the next header" ]; | ||||
|         optional int32 post_header_gap_bits = 11 | ||||
|             [ (help) = "size of gap between header and the data" ]; | ||||
|     } | ||||
|  | ||||
| 		optional double original_period_ms = 4     [(help) = "original rotational period of this cylinder"]; | ||||
| 		optional double original_data_rate_khz = 5 [(help) = "original data rate of this cylinder"]; | ||||
| 		optional double post_index_gap_us = 6      [(help) = "size of post-index gap"]; | ||||
| 		optional int32 pre_header_sync_bits = 10   [(help) = "number of sync bits before the sector header"]; | ||||
| 		optional int32 pre_data_sync_bits = 8      [(help) = "number of sync bits before the sector data"]; | ||||
| 		optional int32 post_data_gap_bits = 9      [(help) = "size of gap between data and the next header"]; | ||||
| 		optional int32 post_header_gap_bits = 11   [(help) = "size of gap between header and the data"]; | ||||
|  | ||||
| 		optional SectorRangeProto sector_range = 7 [(help) = "write these sectors on each track"]; | ||||
| 	} | ||||
|  | ||||
| 	repeated TrackdataProto trackdata = 1; | ||||
|     repeated TrackdataProto trackdata = 1; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,61 +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 "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); | ||||
|  | ||||
| class ZilogMczDecoder : public AbstractDecoder | ||||
| class ZilogMczDecoder : public Decoder | ||||
| { | ||||
| public: | ||||
| 	ZilogMczDecoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config) | ||||
| 	{} | ||||
|     ZilogMczDecoder(const DecoderProto& config): Decoder(config) {} | ||||
|  | ||||
|     RecordType advanceToNextRecord() | ||||
| 	{ | ||||
| 		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; | ||||
| 	} | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         seekToIndexMark(); | ||||
|         return seekToPattern(SECTOR_START_PATTERN); | ||||
|     } | ||||
|  | ||||
|     void decodeSectorRecord() | ||||
| 	{ | ||||
| 		readRawBits(14); | ||||
|     void decodeSectorRecord() override | ||||
|     { | ||||
|         readRawBits(14); | ||||
|  | ||||
| 		auto rawbits = readRawBits(140*16); | ||||
| 		auto bytes = decodeFmMfm(rawbits).slice(0, 140); | ||||
| 		ByteReader br(bytes); | ||||
|         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->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->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; | ||||
| 	} | ||||
|         _sector->status = | ||||
|             (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractDecoder> createZilogMczDecoder(const DecoderProto& config) | ||||
| std::unique_ptr<Decoder> createZilogMczDecoder(const DecoderProto& config) | ||||
| { | ||||
| 	return std::unique_ptr<AbstractDecoder>(new ZilogMczDecoder(config)); | ||||
|     return std::unique_ptr<Decoder>(new ZilogMczDecoder(config)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,7 @@ | ||||
| #ifndef ZILOGMCZ_H | ||||
| #define ZILOGMCZ_H | ||||
|  | ||||
| extern std::unique_ptr<AbstractDecoder> createZilogMczDecoder(const DecoderProto& config); | ||||
| extern std::unique_ptr<Decoder> createZilogMczDecoder( | ||||
|     const DecoderProto& config); | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										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/gui", | ||||
|         "brother120tool$(EXT)": "tools+brother120tool", | ||||
|         "brother240tool$(EXT)": "tools+brother240tool", | ||||
|         "upgrade-flux-file$(EXT)": "tools+upgrade-flux-file", | ||||
|     } | ||||
|     | ( | ||||
|         { | ||||
|             "FluxEngine.pkg": "src/gui+fluxengine_pkg", | ||||
|             "FluxEngine.app.zip": "src/gui+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() | ||||
							
								
								
									
										119
									
								
								build/ab.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								build/ab.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| MAKENOT4 := $(if $(findstring 3.9999, $(lastword $(sort 3.9999 $(MAKE_VERSION)))),yes,no) | ||||
|  | ||||
| ifeq ($(MAKENOT4),yes) | ||||
| $(error You need GNU Make 4.x for this (if you're on OSX, use gmake).) | ||||
| endif | ||||
|  | ||||
| OBJ ?= .obj | ||||
| PYTHON ?= python3 | ||||
| PKG_CONFIG ?= pkg-config | ||||
| HOST_PKG_CONFIG ?= $(PKG_CONFIG) | ||||
| ECHO ?= echo | ||||
| CP ?= cp | ||||
|  | ||||
| HOSTCC ?= gcc | ||||
| HOSTCXX ?= g++ | ||||
| HOSTAR ?= ar | ||||
| HOSTCFLAGS ?= -g -Og | ||||
| HOSTCXXFLAGS ?= $(HOSTCFLAGS) | ||||
| HOSTLDFLAGS ?= -g | ||||
|  | ||||
| CC ?= $(HOSTCC) | ||||
| CXX ?= $(HOSTCXX) | ||||
| AR ?= $(HOSTAR) | ||||
| CFLAGS ?= $(HOSTCFLAGS) | ||||
| CXXFLAGS ?= $(CFLAGS) | ||||
| LDFLAGS ?= $(HOSTLDFLAGS) | ||||
|  | ||||
| NINJA ?= ninja | ||||
|  | ||||
| ifdef VERBOSE | ||||
| 	hide = | ||||
| else | ||||
| 	ifdef V | ||||
| 		hide = | ||||
| 	else | ||||
| 		hide = @ | ||||
| 	endif | ||||
| endif | ||||
|  | ||||
| # If enabled, shows a nice display of how far through the build you are. This | ||||
| # doubles Make startup time. Also, on Make 4.3 and above, rebuilds don't show | ||||
| # correct progress information. | ||||
| AB_ENABLE_PROGRESS_INFO ?= true | ||||
|  | ||||
| WINDOWS := no | ||||
| OSX := no | ||||
| LINUX := no | ||||
| ifeq ($(OS),Windows_NT) | ||||
|     WINDOWS := yes | ||||
| else | ||||
|     UNAME_S := $(shell uname -s) | ||||
|     ifeq ($(UNAME_S),Linux) | ||||
| 		LINUX := yes | ||||
|     endif | ||||
|     ifeq ($(UNAME_S),Darwin) | ||||
| 		OSX := yes | ||||
|     endif | ||||
| endif | ||||
|  | ||||
| ifeq ($(OS), Windows_NT) | ||||
| 	EXT ?= .exe | ||||
| endif | ||||
| EXT ?= | ||||
|  | ||||
| CWD=$(shell pwd) | ||||
|  | ||||
| define newline | ||||
|  | ||||
|  | ||||
| endef | ||||
|  | ||||
| define check_for_command | ||||
|   $(shell command -v $1 >/dev/null || (echo "Required command '$1' missing" >/dev/stderr && kill $$PPID)) | ||||
| endef | ||||
|  | ||||
| $(call check_for_command,ninja) | ||||
| $(call check_for_command,cmp) | ||||
| $(call check_for_command,$(PYTHON)) | ||||
|  | ||||
| pkg-config-hash = $(shell ($(PKG_CONFIG) --list-all && $(HOST_PKG_CONFIG) --list-all) | md5sum) | ||||
| build-files = $(shell find . -name .obj -prune -o \( -name 'build.py' -a -type f \) -print) $(wildcard build/*.py) $(wildcard config.py) | ||||
| build-file-timestamps = $(shell ls -l $(build-files) | md5sum) | ||||
|  | ||||
| # Wipe the build file (forcing a regeneration) if the make environment is different. | ||||
| # (Conveniently, this includes the pkg-config hash calculated above.) | ||||
|  | ||||
| ignored-variables = MAKE_RESTARTS .VARIABLES MAKECMDGOALS MAKEFLAGS MFLAGS | ||||
| $(shell mkdir -p $(OBJ)) | ||||
| $(file >$(OBJ)/newvars.txt,$(foreach v,$(filter-out $(ignored-variables),$(.VARIABLES)),$(v)=$($(v))$(newline))) | ||||
| $(shell touch $(OBJ)/vars.txt) | ||||
| #$(shell diff -u $(OBJ)/vars.txt $(OBJ)/newvars.txt > /dev/stderr) | ||||
| $(shell cmp -s $(OBJ)/newvars.txt $(OBJ)/vars.txt || (rm -f $(OBJ)/build.ninja && echo "Environment changed --- regenerating" > /dev/stderr)) | ||||
| $(shell mv $(OBJ)/newvars.txt $(OBJ)/vars.txt) | ||||
|  | ||||
| .PHONY: update-ab | ||||
| update-ab: | ||||
| 	@echo "Press RETURN to update ab from the repository, or CTRL+C to cancel." \ | ||||
| 		&& read a \ | ||||
| 		&& (curl -L https://github.com/davidgiven/ab/releases/download/dev/distribution.tar.xz | tar xvJf -) \ | ||||
| 		&& echo "Done." | ||||
|  | ||||
| .PHONY: clean | ||||
| clean:: | ||||
| 	@echo CLEAN | ||||
| 	$(hide) rm -rf $(OBJ) | ||||
|  | ||||
| export PYTHONHASHSEED = 1 | ||||
| $(OBJ)/build.ninja $(OBJ)/build.targets &: | ||||
| 	@echo "AB" | ||||
| 	$(hide) $(PYTHON) -X pycache_prefix=$(OBJ)/__pycache__ build/ab.py \ | ||||
| 		-o $(OBJ) build.py \ | ||||
| 		-v $(OBJ)/vars.txt \ | ||||
| 		|| (rm -f $@ && false) | ||||
|  | ||||
| include $(OBJ)/build.targets | ||||
| .PHONY: $(ninja-targets) | ||||
| .NOTPARALLEL: | ||||
| $(ninja-targets): $(OBJ)/build.ninja | ||||
| 	+$(hide) $(NINJA) -f $(OBJ)/build.ninja $@ | ||||
							
								
								
									
										2
									
								
								build/ab.ninja
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								build/ab.ninja
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| rule rule | ||||
|     command = $command | ||||
							
								
								
									
										725
									
								
								build/ab.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										725
									
								
								build/ab.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,725 @@ | ||||
| from collections import namedtuple | ||||
| from copy import copy | ||||
| from importlib.machinery import SourceFileLoader, PathFinder, ModuleSpec | ||||
| from os.path import * | ||||
| from pathlib import Path | ||||
| from typing import Iterable | ||||
| import argparse | ||||
| import ast | ||||
| import builtins | ||||
| import functools | ||||
| import hashlib | ||||
| import importlib | ||||
| import importlib.util | ||||
| import inspect | ||||
| import os | ||||
| import re | ||||
| import string | ||||
| import sys | ||||
| import types | ||||
|  | ||||
| VERBOSE_NINJA_FILE = False | ||||
|  | ||||
| quiet = False | ||||
| cwdStack = [""] | ||||
| targets = {} | ||||
| unmaterialisedTargets = {}  # dict, not set, to get consistent ordering | ||||
| materialisingStack = [] | ||||
| defaultGlobals = {} | ||||
| outputTargets = set() | ||||
|  | ||||
| RE_FORMAT_SPEC = re.compile( | ||||
|     r"(?:(?P<fill>[\s\S])?(?P<align>[<>=^]))?" | ||||
|     r"(?P<sign>[- +])?" | ||||
|     r"(?P<pos_zero>z)?" | ||||
|     r"(?P<alt>#)?" | ||||
|     r"(?P<zero_padding>0)?" | ||||
|     r"(?P<width_str>\d+)?" | ||||
|     r"(?P<grouping>[_,])?" | ||||
|     r"(?:(?P<decimal>\.)(?P<precision_str>\d+))?" | ||||
|     r"(?P<type>[bcdeEfFgGnosxX%])?" | ||||
| ) | ||||
|  | ||||
| CommandFormatSpec = namedtuple( | ||||
|     "CommandFormatSpec", RE_FORMAT_SPEC.groupindex.keys() | ||||
| ) | ||||
|  | ||||
| sys.path += ["."] | ||||
| old_import = builtins.__import__ | ||||
|  | ||||
|  | ||||
| class Environment(types.SimpleNamespace): | ||||
|     def setdefault(self, name, value): | ||||
|         if not hasattr(self, name): | ||||
|             setattr(self, name, value) | ||||
|  | ||||
|  | ||||
| G = Environment() | ||||
|  | ||||
|  | ||||
| class PathFinderImpl(PathFinder): | ||||
|     def find_spec(self, fullname, path, target=None): | ||||
|         # The second test here is needed for Python 3.9. | ||||
|         if not path or not path[0]: | ||||
|             path = ["."] | ||||
|         if len(path) != 1: | ||||
|             return None | ||||
|  | ||||
|         try: | ||||
|             path = relpath(path[0]) | ||||
|         except ValueError: | ||||
|             return None | ||||
|  | ||||
|         realpath = fullname.replace(".", "/") | ||||
|         buildpath = realpath + ".py" | ||||
|         if isfile(buildpath): | ||||
|             spec = importlib.util.spec_from_file_location( | ||||
|                 name=fullname, | ||||
|                 location=buildpath, | ||||
|                 loader=BuildFileLoaderImpl(fullname=fullname, path=buildpath), | ||||
|                 submodule_search_locations=[], | ||||
|             ) | ||||
|             return spec | ||||
|         if isdir(realpath): | ||||
|             return ModuleSpec(fullname, None, origin=realpath, is_package=True) | ||||
|         return None | ||||
|  | ||||
|  | ||||
| class BuildFileLoaderImpl(SourceFileLoader): | ||||
|     def exec_module(self, module): | ||||
|         sourcepath = relpath(module.__file__) | ||||
|  | ||||
|         if not quiet: | ||||
|             print("loading", sourcepath) | ||||
|         cwdStack.append(dirname(sourcepath)) | ||||
|         super(SourceFileLoader, self).exec_module(module) | ||||
|         cwdStack.pop() | ||||
|  | ||||
|  | ||||
| sys.meta_path.insert(0, PathFinderImpl()) | ||||
|  | ||||
|  | ||||
| class ABException(BaseException): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| def error(message): | ||||
|     raise ABException(message) | ||||
|  | ||||
|  | ||||
| def _undo_escaped_dollar(s, op): | ||||
|     return s.replace(f"$${op}", f"${op}") | ||||
|  | ||||
|  | ||||
| class BracketedFormatter(string.Formatter): | ||||
|     def parse(self, format_string): | ||||
|         while format_string: | ||||
|             m = re.search(f"(?:[^$]|^)()\\$\\[()", format_string) | ||||
|             if not m: | ||||
|                 yield ( | ||||
|                     _undo_escaped_dollar(format_string, "["), | ||||
|                     None, | ||||
|                     None, | ||||
|                     None, | ||||
|                 ) | ||||
|                 break | ||||
|             left = format_string[: m.start(1)] | ||||
|             right = format_string[m.end(2) :] | ||||
|  | ||||
|             offset = len(right) + 1 | ||||
|             try: | ||||
|                 ast.parse(right) | ||||
|             except SyntaxError as e: | ||||
|                 if not str(e).startswith(f"unmatched ']'"): | ||||
|                     raise e | ||||
|                 offset = e.offset | ||||
|  | ||||
|             expr = right[0 : offset - 1] | ||||
|             format_string = right[offset:] | ||||
|  | ||||
|             yield ( | ||||
|                 _undo_escaped_dollar(left, "[") if left else None, | ||||
|                 expr, | ||||
|                 None, | ||||
|                 None, | ||||
|             ) | ||||
|  | ||||
|  | ||||
| class GlobalFormatter(string.Formatter): | ||||
|     def parse(self, format_string): | ||||
|         while format_string: | ||||
|             m = re.search(f"(?:[^$]|^)()\\$\\(([^)]*)\\)()", format_string) | ||||
|             if not m: | ||||
|                 yield ( | ||||
|                     format_string, | ||||
|                     None, | ||||
|                     None, | ||||
|                     None, | ||||
|                 ) | ||||
|                 break | ||||
|             left = format_string[: m.start(1)] | ||||
|             var = m[2] | ||||
|             format_string = format_string[m.end(3) :] | ||||
|  | ||||
|             yield ( | ||||
|                 left if left else None, | ||||
|                 var, | ||||
|                 None, | ||||
|                 None, | ||||
|             ) | ||||
|  | ||||
|     def get_field(self, name, a1, a2): | ||||
|         return ( | ||||
|             getattr(G, name), | ||||
|             False, | ||||
|         ) | ||||
|  | ||||
|     def format_field(self, value, format_spec): | ||||
|         if not value: | ||||
|             return "" | ||||
|         return str(value) | ||||
|  | ||||
|  | ||||
| globalFormatter = GlobalFormatter() | ||||
|  | ||||
|  | ||||
| def substituteGlobalVariables(value): | ||||
|     while True: | ||||
|         oldValue = value | ||||
|         value = globalFormatter.format(value) | ||||
|         if value == oldValue: | ||||
|             return _undo_escaped_dollar(value, "(") | ||||
|  | ||||
|  | ||||
| def Rule(func): | ||||
|     sig = inspect.signature(func) | ||||
|  | ||||
|     @functools.wraps(func) | ||||
|     def wrapper(*, name=None, replaces=None, **kwargs): | ||||
|         cwd = None | ||||
|         if "cwd" in kwargs: | ||||
|             cwd = kwargs["cwd"] | ||||
|             del kwargs["cwd"] | ||||
|  | ||||
|         if not cwd: | ||||
|             if replaces: | ||||
|                 cwd = replaces.cwd | ||||
|             else: | ||||
|                 cwd = cwdStack[-1] | ||||
|  | ||||
|         if name: | ||||
|             if name[0] != "+": | ||||
|                 name = "+" + name | ||||
|             t = Target(cwd, join(cwd, name)) | ||||
|  | ||||
|             assert ( | ||||
|                 t.name not in targets | ||||
|             ), f"target {t.name} has already been defined" | ||||
|             targets[t.name] = t | ||||
|         elif replaces: | ||||
|             t = replaces | ||||
|         else: | ||||
|             raise ABException("you must supply either 'name' or 'replaces'") | ||||
|  | ||||
|         t.cwd = cwd | ||||
|         t.types = func.__annotations__ | ||||
|         t.callback = func | ||||
|         t.traits.add(func.__name__) | ||||
|         if "args" in kwargs: | ||||
|             t.explicit_args = kwargs["args"] | ||||
|             t.args.update(t.explicit_args) | ||||
|             del kwargs["args"] | ||||
|         if "traits" in kwargs: | ||||
|             t.traits |= kwargs["traits"] | ||||
|             del kwargs["traits"] | ||||
|  | ||||
|         t.binding = sig.bind(name=name, self=t, **kwargs) | ||||
|         t.binding.apply_defaults() | ||||
|  | ||||
|         unmaterialisedTargets[t] = None | ||||
|         if replaces: | ||||
|             t.materialise(replacing=True) | ||||
|         return t | ||||
|  | ||||
|     defaultGlobals[func.__name__] = wrapper | ||||
|     return wrapper | ||||
|  | ||||
|  | ||||
| def _isiterable(xs): | ||||
|     return isinstance(xs, Iterable) and not isinstance( | ||||
|         xs, (str, bytes, bytearray) | ||||
|     ) | ||||
|  | ||||
|  | ||||
| class Target: | ||||
|     def __init__(self, cwd, name): | ||||
|         self.name = name | ||||
|         self.localname = self.name.rsplit("+")[-1] | ||||
|         self.traits = set() | ||||
|         self.dir = join(G.OBJ, name) | ||||
|         self.ins = [] | ||||
|         self.outs = [] | ||||
|         self.deps = [] | ||||
|         self.materialised = False | ||||
|         self.args = {} | ||||
|  | ||||
|     def __eq__(self, other): | ||||
|         return self.name is other.name | ||||
|  | ||||
|     def __lt__(self, other): | ||||
|         return self.name < other.name | ||||
|  | ||||
|     def __hash__(self): | ||||
|         return id(self) | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return f"Target('{self.name}')" | ||||
|  | ||||
|     def templateexpand(selfi, s): | ||||
|         class Formatter(BracketedFormatter): | ||||
|             def get_field(self, name, a1, a2): | ||||
|                 return ( | ||||
|                     eval(name, selfi.callback.__globals__, selfi.args), | ||||
|                     False, | ||||
|                 ) | ||||
|  | ||||
|             def format_field(self, value, format_spec): | ||||
|                 if not value: | ||||
|                     return "" | ||||
|                 if type(value) == str: | ||||
|                     return value | ||||
|                 if _isiterable(value): | ||||
|                     value = list(value) | ||||
|                 if type(value) != list: | ||||
|                     value = [value] | ||||
|                 return " ".join( | ||||
|                     [selfi.templateexpand(f) for f in filenamesof(value)] | ||||
|                 ) | ||||
|  | ||||
|         s = Formatter().format(s) | ||||
|         return substituteGlobalVariables(s) | ||||
|  | ||||
|     def materialise(self, replacing=False): | ||||
|         if self not in unmaterialisedTargets: | ||||
|             return | ||||
|  | ||||
|         if not replacing and self in materialisingStack: | ||||
|             print("Found dependency cycle:") | ||||
|             for i in materialisingStack: | ||||
|                 print(f"  {i.name}") | ||||
|             print(f"  {self.name}") | ||||
|             sys.exit(1) | ||||
|         materialisingStack.append(self) | ||||
|  | ||||
|         # Perform type conversion to the declared rule parameter types. | ||||
|  | ||||
|         try: | ||||
|             for k, v in self.binding.arguments.items(): | ||||
|                 if k != "kwargs": | ||||
|                     t = self.types.get(k, None) | ||||
|                     if t: | ||||
|                         v = t.convert(v, self) | ||||
|                     self.args[k] = copy(v) | ||||
|                 else: | ||||
|                     for kk, vv in v.items(): | ||||
|                         t = self.types.get(kk, None) | ||||
|                         if t: | ||||
|                             vv = t.convert(v, self) | ||||
|                         self.args[kk] = copy(vv) | ||||
|             self.args["name"] = self.name | ||||
|             self.args["dir"] = self.dir | ||||
|             self.args["self"] = self | ||||
|  | ||||
|             # Actually call the callback. | ||||
|  | ||||
|             cwdStack.append(self.cwd) | ||||
|             if "kwargs" in self.binding.arguments.keys(): | ||||
|                 # If the caller wants kwargs, return all arguments except the standard ones. | ||||
|                 cbargs = { | ||||
|                     k: v for k, v in self.args.items() if k not in {"dir"} | ||||
|                 } | ||||
|             else: | ||||
|                 # Otherwise, just call the callback with the ones it asks for. | ||||
|                 cbargs = {} | ||||
|                 for k in self.binding.arguments.keys(): | ||||
|                     if k != "kwargs": | ||||
|                         try: | ||||
|                             cbargs[k] = self.args[k] | ||||
|                         except KeyError: | ||||
|                             error( | ||||
|                                 f"invocation of {self} failed because {k} isn't an argument" | ||||
|                             ) | ||||
|             self.callback(**cbargs) | ||||
|             cwdStack.pop() | ||||
|         except BaseException as e: | ||||
|             print(f"Error materialising {self}: {self.callback}") | ||||
|             print(f"Arguments: {self.args}") | ||||
|             raise e | ||||
|  | ||||
|         if self.outs is None: | ||||
|             raise ABException(f"{self.name} didn't set self.outs") | ||||
|  | ||||
|         if self in unmaterialisedTargets: | ||||
|             del unmaterialisedTargets[self] | ||||
|         materialisingStack.pop() | ||||
|         self.materialised = True | ||||
|  | ||||
|     def convert(value, target): | ||||
|         if not value: | ||||
|             return None | ||||
|         return target.targetof(value) | ||||
|  | ||||
|     def targetof(self, value): | ||||
|         if isinstance(value, str) and (value[0] == "="): | ||||
|             value = join(self.dir, value[1:]) | ||||
|  | ||||
|         return targetof(value, self.cwd) | ||||
|  | ||||
|  | ||||
| def _filetarget(value, cwd): | ||||
|     if value in targets: | ||||
|         return targets[value] | ||||
|  | ||||
|     t = Target(cwd, value) | ||||
|     t.outs = [value] | ||||
|     targets[value] = t | ||||
|     return t | ||||
|  | ||||
|  | ||||
| def targetof(value, cwd=None): | ||||
|     if not cwd: | ||||
|         cwd = cwdStack[-1] | ||||
|     if isinstance(value, Path): | ||||
|         value = value.as_posix() | ||||
|     if isinstance(value, Target): | ||||
|         t = value | ||||
|     else: | ||||
|         assert ( | ||||
|             value[0] != "=" | ||||
|         ), "can only use = for targets associated with another target" | ||||
|  | ||||
|         if value.startswith("."): | ||||
|             # Check for local rule. | ||||
|             if value.startswith(".+"): | ||||
|                 value = normpath(join(cwd, value[1:])) | ||||
|             # Check for local path. | ||||
|             elif value.startswith("./"): | ||||
|                 value = normpath(join(cwd, value)) | ||||
|         # Explicit directories are always raw files. | ||||
|         if value.endswith("/"): | ||||
|             return _filetarget(value, cwd) | ||||
|         # Anything in .obj is a raw file. | ||||
|         elif value.startswith(outputdir) or value.startswith(G.OBJ): | ||||
|             return _filetarget(value, cwd) | ||||
|  | ||||
|         # If this is not a rule lookup... | ||||
|         if "+" not in value: | ||||
|             # ...and if the value is pointing at a directory without a trailing /, | ||||
|             # it's a shorthand rule lookup. | ||||
|             if isdir(value): | ||||
|                 value = value + "+" + basename(value) | ||||
|             # Otherwise it's an absolute file. | ||||
|             else: | ||||
|                 return _filetarget(value, cwd) | ||||
|  | ||||
|         # At this point we have the fully qualified name of a rule. | ||||
|  | ||||
|         (path, target) = value.rsplit("+", 1) | ||||
|         value = join(path, "+" + target) | ||||
|         if value not in targets: | ||||
|             # Load the new build file. | ||||
|  | ||||
|             path = join(path, "build.py") | ||||
|             try: | ||||
|                 loadbuildfile(path) | ||||
|             except ModuleNotFoundError: | ||||
|                 error( | ||||
|                     f"no such build file '{path}' while trying to resolve '{value}'" | ||||
|                 ) | ||||
|             assert ( | ||||
|                 value in targets | ||||
|             ), f"build file at '{path}' doesn't contain '+{target}' when trying to resolve '{value}'" | ||||
|  | ||||
|         t = targets[value] | ||||
|  | ||||
|     t.materialise() | ||||
|     return t | ||||
|  | ||||
|  | ||||
| class Targets: | ||||
|     def convert(value, target): | ||||
|         if not value: | ||||
|             return [] | ||||
|         assert _isiterable(value), "cannot convert non-list to Targets" | ||||
|         return [target.targetof(x) for x in flatten(value)] | ||||
|  | ||||
|  | ||||
| class TargetsMap: | ||||
|     def convert(value, target): | ||||
|         if not value: | ||||
|             return {} | ||||
|         output = {k: target.targetof(v) for k, v in value.items()} | ||||
|         for k, v in output.items(): | ||||
|             assert ( | ||||
|                 len(filenamesof([v])) == 1 | ||||
|             ), f"targets of a TargetsMap used as an argument of {target} with key '{k}' must contain precisely one output file, but was {filenamesof([v])}" | ||||
|         return output | ||||
|  | ||||
|  | ||||
| def _removesuffix(self, suffix): | ||||
|     # suffix='' should not call self[:-0]. | ||||
|     if suffix and self.endswith(suffix): | ||||
|         return self[: -len(suffix)] | ||||
|     else: | ||||
|         return self[:] | ||||
|  | ||||
|  | ||||
| def loadbuildfile(filename): | ||||
|     modulename = _removesuffix(filename.replace("/", "."), ".py") | ||||
|     if modulename not in sys.modules: | ||||
|         spec = importlib.util.spec_from_file_location( | ||||
|             name=modulename, | ||||
|             location=filename, | ||||
|             loader=BuildFileLoaderImpl(fullname=modulename, path=filename), | ||||
|             submodule_search_locations=[], | ||||
|         ) | ||||
|         module = importlib.util.module_from_spec(spec) | ||||
|         sys.modules[modulename] = module | ||||
|         spec.loader.exec_module(module) | ||||
|  | ||||
|  | ||||
| def flatten(items): | ||||
|     def generate(xs): | ||||
|         for x in xs: | ||||
|             if _isiterable(x): | ||||
|                 yield from generate(x) | ||||
|             else: | ||||
|                 yield x | ||||
|  | ||||
|     return list(generate(items)) | ||||
|  | ||||
|  | ||||
| def targetnamesof(items): | ||||
|     assert _isiterable(items), "argument of filenamesof is not a collection" | ||||
|  | ||||
|     return [t.name for t in items] | ||||
|  | ||||
|  | ||||
| def filenamesof(items): | ||||
|     assert _isiterable(items), "argument of filenamesof is not a collection" | ||||
|  | ||||
|     def generate(xs): | ||||
|         for x in xs: | ||||
|             if isinstance(x, Target): | ||||
|                 x.materialise() | ||||
|                 yield from generate(x.outs) | ||||
|             else: | ||||
|                 yield x | ||||
|  | ||||
|     return list(generate(items)) | ||||
|  | ||||
|  | ||||
| def filenameof(x): | ||||
|     xs = filenamesof(x.outs) | ||||
|     assert ( | ||||
|         len(xs) == 1 | ||||
|     ), f"tried to use filenameof() on {x} which does not have exactly one output: {x.outs}" | ||||
|     return xs[0] | ||||
|  | ||||
|  | ||||
| def emit(*args, into=None): | ||||
|     s = " ".join(args) + "\n" | ||||
|     if into is not None: | ||||
|         into += [s] | ||||
|     else: | ||||
|         ninjaFp.write(s) | ||||
|  | ||||
|  | ||||
| def shell(*args): | ||||
|     s = "".join(args) + "\n" | ||||
|     shellFp.write(s) | ||||
|  | ||||
|  | ||||
| def emit_rule(self, ins, outs, cmds=[], label=None): | ||||
|     name = self.name | ||||
|     fins = [self.templateexpand(f) for f in set(filenamesof(ins))] | ||||
|     fouts = [self.templateexpand(f) for f in filenamesof(outs)] | ||||
|  | ||||
|     global outputTargets | ||||
|     outputTargets.update(fouts) | ||||
|     outputTargets.add(name) | ||||
|  | ||||
|     emit("") | ||||
|     if VERBOSE_NINJA_FILE: | ||||
|         for k, v in self.args.items(): | ||||
|             emit(f"# {k} = {v}") | ||||
|  | ||||
|     if outs: | ||||
|         os.makedirs(self.dir, exist_ok=True) | ||||
|         rule = [] | ||||
|  | ||||
|         sandbox = join(self.dir, "sandbox") | ||||
|         emit(f"rm -rf {sandbox}", into=rule) | ||||
|         emit( | ||||
|             f"{G.PYTHON} build/_sandbox.py --link -s", sandbox, *fins, into=rule | ||||
|         ) | ||||
|         for c in cmds: | ||||
|             emit(f"(cd {sandbox} &&", c, ")", into=rule) | ||||
|         emit( | ||||
|             f"{G.PYTHON} build/_sandbox.py --export -s", | ||||
|             sandbox, | ||||
|             *fouts, | ||||
|             into=rule, | ||||
|         ) | ||||
|  | ||||
|         ruletext = "".join(rule) | ||||
|         if len(ruletext) > 7000: | ||||
|             rulehash = hashlib.sha1(ruletext.encode()).hexdigest() | ||||
|  | ||||
|             rulef = join(self.dir, f"rule-{rulehash}.sh") | ||||
|             with open(rulef, "wt") as fp: | ||||
|                 fp.write("set -e\n") | ||||
|                 fp.write(ruletext) | ||||
|  | ||||
|             emit("build", *fouts, ":rule", *fins, rulef) | ||||
|             emit(" command=sh", rulef) | ||||
|         else: | ||||
|             emit("build", *fouts, ":rule", *fins) | ||||
|             emit( | ||||
|                 " command=", | ||||
|                 "&&".join([s.strip() for s in rule]).replace("$", "$$"), | ||||
|             ) | ||||
|         if label: | ||||
|             emit(" description=", label) | ||||
|         emit("build", name, ":phony", *fouts) | ||||
|  | ||||
|     else: | ||||
|         assert len(cmds) == 0, "rules with no outputs cannot have commands" | ||||
|         emit("build", name, ":phony", *fins) | ||||
|  | ||||
|     emit("") | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def simplerule( | ||||
|     self, | ||||
|     name, | ||||
|     ins: Targets = [], | ||||
|     outs: Targets = [], | ||||
|     deps: Targets = [], | ||||
|     commands=[], | ||||
|     label="RULE", | ||||
| ): | ||||
|     self.ins = ins | ||||
|     self.outs = outs | ||||
|     self.deps = deps | ||||
|  | ||||
|     dirs = [] | ||||
|     cs = [] | ||||
|     for out in filenamesof(outs): | ||||
|         dir = dirname(out) | ||||
|         if dir and dir not in dirs: | ||||
|             dirs += [dir] | ||||
|  | ||||
|         cs = [("mkdir -p %s" % dir) for dir in dirs] | ||||
|  | ||||
|     for c in commands: | ||||
|         cs += [self.templateexpand(c)] | ||||
|  | ||||
|     emit_rule( | ||||
|         self=self, | ||||
|         ins=ins + deps, | ||||
|         outs=outs, | ||||
|         label=self.templateexpand("$[label] $[name]") if label else None, | ||||
|         cmds=cs, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def export(self, name=None, items: TargetsMap = {}, deps: Targets = []): | ||||
|     ins = [] | ||||
|     outs = [] | ||||
|     for dest, src in items.items(): | ||||
|         dest = self.targetof(dest) | ||||
|         outs += [dest] | ||||
|  | ||||
|         destf = self.templateexpand(filenameof(dest)) | ||||
|         outputTargets.update([destf]) | ||||
|  | ||||
|         srcs = filenamesof([src]) | ||||
|         assert ( | ||||
|             len(srcs) == 1 | ||||
|         ), "a dependency of an exported file must have exactly one output file" | ||||
|         srcf = self.templateexpand(srcs[0]) | ||||
|  | ||||
|         subrule = simplerule( | ||||
|             name=f"{self.localname}/{destf}", | ||||
|             cwd=self.cwd, | ||||
|             ins=[srcs[0]], | ||||
|             outs=[destf], | ||||
|             commands=["$(CP) -H %s %s" % (srcf, destf)], | ||||
|             label="EXPORT", | ||||
|         ) | ||||
|         subrule.materialise() | ||||
|  | ||||
|     self.ins = [] | ||||
|     self.outs = deps + outs | ||||
|     outputTargets.add(name) | ||||
|  | ||||
|     emit("") | ||||
|     emit( | ||||
|         "build", | ||||
|         name, | ||||
|         ":phony", | ||||
|         *[self.templateexpand(f) for f in filenamesof(outs + deps)], | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def main(): | ||||
|     parser = argparse.ArgumentParser() | ||||
|     parser.add_argument("-q", "--quiet", action="store_true") | ||||
|     parser.add_argument("-v", "--varfile") | ||||
|     parser.add_argument("-o", "--outputdir") | ||||
|     parser.add_argument("-D", "--define", action="append", default=[]) | ||||
|     parser.add_argument("files", nargs="+") | ||||
|     args = parser.parse_args() | ||||
|  | ||||
|     global quiet | ||||
|     quiet = args.quiet | ||||
|  | ||||
|     vardefs = args.define | ||||
|     if args.varfile: | ||||
|         with open(args.varfile, "rt") as fp: | ||||
|             vardefs = vardefs + list(fp) | ||||
|  | ||||
|     for line in vardefs: | ||||
|         if "=" in line: | ||||
|             name, value = line.split("=", 1) | ||||
|             G.setdefault(name.strip(), value.strip()) | ||||
|  | ||||
|     global ninjaFp, shellFp, outputdir | ||||
|     outputdir = args.outputdir | ||||
|     G.setdefault("OBJ", outputdir) | ||||
|     ninjaFp = open(outputdir + "/build.ninja", "wt") | ||||
|     ninjaFp.write(f"include build/ab.ninja\n") | ||||
|  | ||||
|     for k in ["Rule"]: | ||||
|         defaultGlobals[k] = globals()[k] | ||||
|  | ||||
|     global __name__ | ||||
|     sys.modules["build.ab"] = sys.modules[__name__] | ||||
|     __name__ = "build.ab" | ||||
|  | ||||
|     for f in args.files: | ||||
|         loadbuildfile(f) | ||||
|  | ||||
|     while unmaterialisedTargets: | ||||
|         t = next(iter(unmaterialisedTargets)) | ||||
|         t.materialise() | ||||
|  | ||||
|     with open(outputdir + "/build.targets", "wt") as fp: | ||||
|         fp.write("ninja-targets =") | ||||
|         fp.write(substituteGlobalVariables(" ".join(outputTargets))) | ||||
|  | ||||
|  | ||||
| main() | ||||
							
								
								
									
										580
									
								
								build/c.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										580
									
								
								build/c.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,580 @@ | ||||
| from build.ab import ( | ||||
|     Rule, | ||||
|     Targets, | ||||
|     TargetsMap, | ||||
|     filenameof, | ||||
|     filenamesof, | ||||
|     flatten, | ||||
|     simplerule, | ||||
|     emit, | ||||
|     G, | ||||
| ) | ||||
| from build.utils import stripext, collectattrs | ||||
| from build.toolchain import Toolchain, HostToolchain | ||||
| from os.path import * | ||||
|  | ||||
| if G.OSX != "yes": | ||||
|     G.STARTGROUP = "-Wl,--start-group" | ||||
|     G.ENDGROUP = "-Wl,--end-group" | ||||
| else: | ||||
|     G.STARTGROUP = "" | ||||
|     G.ENDGROUP = "" | ||||
|  | ||||
| Toolchain.CC = ["$(CC) -c -o $[outs[0]] $[ins[0]] $(CFLAGS) $[cflags]"] | ||||
| Toolchain.CPP = ["$(CC) -E -P -o $[outs] $[cflags] -x c $[ins]"] | ||||
| Toolchain.CXX = ["$(CXX) -c -o $[outs[0]] $[ins[0]] $(CXXFLAGS) $[cflags]"] | ||||
| Toolchain.AR = ["$(AR) cqs $[outs[0]] $[ins]"] | ||||
| Toolchain.ARXX = ["$(AR) cqs $[outs[0]] $[ins]"] | ||||
| Toolchain.CLINK = [ | ||||
|     "$(CC) -o $[outs[0]] $(STARTGROUP) $[ins] $[ldflags] $(LDFLAGS) $(ENDGROUP)" | ||||
| ] | ||||
| Toolchain.CXXLINK = [ | ||||
|     "$(CXX) -o $[outs[0]] $(STARTGROUP) $[ins] $[ldflags] $(LDFLAGS) $(ENDGROUP)" | ||||
| ] | ||||
|  | ||||
| Toolchain.is_source_file = ( | ||||
|     lambda f: f.endswith(".c") | ||||
|     or f.endswith(".cc") | ||||
|     or f.endswith(".cpp") | ||||
|     or f.endswith(".S") | ||||
|     or f.endswith(".s") | ||||
|     or f.endswith(".m") | ||||
|     or f.endswith(".mm") | ||||
| ) | ||||
|  | ||||
|  | ||||
| # Given a set of dependencies, finds the set of relevant library targets (i.e. | ||||
| # contributes *.a files) for compiling C programs.  The actual list of libraries | ||||
| # is in dep.clibrary_files. | ||||
| def _toolchain_find_library_targets(deps): | ||||
|     lib_deps = [] | ||||
|     for d in deps: | ||||
|         lib_deps = _combine(lib_deps, d.args.get("clibrary_deps", [])) | ||||
|     return lib_deps | ||||
|  | ||||
|  | ||||
| Toolchain.find_c_library_targets = _toolchain_find_library_targets | ||||
|  | ||||
|  | ||||
| # Given a set of dependencies, finds the set of relevant header targets (i.e. | ||||
| # contributes *.h files) for compiling C programs.  The actual list of libraries | ||||
| # is in dep.cheader_files. | ||||
| def _toolchain_find_header_targets(deps, initial=[]): | ||||
|     hdr_deps = initial | ||||
|     for d in deps: | ||||
|         hdr_deps = _combine(hdr_deps, d.args.get("cheader_deps", [])) | ||||
|     return hdr_deps | ||||
|  | ||||
|  | ||||
| Toolchain.find_c_header_targets = _toolchain_find_header_targets | ||||
|  | ||||
|  | ||||
| HostToolchain.CC = ["$(HOSTCC) -c -o $[outs[0]] $[ins[0]] $(HOSTCFLAGS) $[cflags]"] | ||||
| HostToolchain.CPP = ["$(HOSTCC) -E -P -o $[outs] $[cflags] -x c $[ins]"] | ||||
| HostToolchain.CXX = ["$(HOSTCXX) -c -o $[outs[0]] $[ins[0]] $(HOSTCFLAGS) $[cflags]"] | ||||
| HostToolchain.AR = ["$(HOSTAR) cqs $[outs[0]] $[ins]"] | ||||
| HostToolchain.ARXX = ["$(HOSTAR) cqs $[outs[0]] $[ins]"] | ||||
| HostToolchain.CLINK = [ | ||||
|     "$(HOSTCC) -o $[outs[0]] $(STARTGROUP) $[ins] $[ldflags] $(HOSTLDFLAGS) $(ENDGROUP)" | ||||
| ] | ||||
| HostToolchain.CXXLINK = [ | ||||
|     "$(HOSTCXX) -o $[outs[0]] $(STARTGROUP) $[ins] $[ldflags] $(HOSTLDFLAGS) $(ENDGROUP)" | ||||
| ] | ||||
|  | ||||
|  | ||||
| def _combine(list1, list2): | ||||
|     r = list(list1) | ||||
|     for i in list2: | ||||
|         if i not in r: | ||||
|             r.append(i) | ||||
|     return r | ||||
|  | ||||
|  | ||||
| def _indirect(deps, name): | ||||
|     r = [] | ||||
|     for d in deps: | ||||
|         r = _combine(r, d.args.get(name, [d])) | ||||
|     return r | ||||
|  | ||||
|  | ||||
| def cfileimpl(self, name, srcs, deps, suffix, commands, label, toolchain, cflags): | ||||
|     outleaf = "=" + stripext(basename(filenameof(srcs[0]))) + suffix | ||||
|  | ||||
|     hdr_deps = toolchain.find_c_header_targets(deps) | ||||
|     other_deps = [ | ||||
|         d | ||||
|         for d in deps | ||||
|         if ("cheader_deps" not in d.args) and ("clibrary_deps" not in d.args) | ||||
|     ] | ||||
|     hdr_files = collectattrs(targets=hdr_deps, name="cheader_files") | ||||
|     cflags = collectattrs(targets=hdr_deps, name="caller_cflags", initial=cflags) | ||||
|  | ||||
|     t = simplerule( | ||||
|         replaces=self, | ||||
|         ins=srcs, | ||||
|         deps=other_deps + hdr_files, | ||||
|         outs=[outleaf], | ||||
|         label=label, | ||||
|         commands=commands, | ||||
|         args={"cflags": cflags}, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def cfile( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     cflags=[], | ||||
|     suffix=".o", | ||||
|     toolchain=Toolchain, | ||||
|     label="CC", | ||||
| ): | ||||
|     cfileimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         suffix, | ||||
|         toolchain.CC, | ||||
|         toolchain.PREFIX + label, | ||||
|         toolchain, | ||||
|         cflags, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def cxxfile( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     cflags=[], | ||||
|     suffix=".o", | ||||
|     toolchain=Toolchain, | ||||
|     label="CXX", | ||||
| ): | ||||
|     cfileimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         suffix, | ||||
|         toolchain.CXX, | ||||
|         toolchain.PREFIX + label, | ||||
|         toolchain, | ||||
|         cflags, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def _removeprefix(self, prefix): | ||||
|     if self.startswith(prefix): | ||||
|         return self[len(prefix) :] | ||||
|     else: | ||||
|         return self[:] | ||||
|  | ||||
|  | ||||
| def findsources(self, srcs, deps, cflags, filerule, toolchain, cwd): | ||||
|     for f in filenamesof(srcs): | ||||
|         if not toolchain.is_source_file(f): | ||||
|             cflags = cflags + [f"-I{dirname(f)}"] | ||||
|             deps = deps + [f] | ||||
|  | ||||
|     objs = [] | ||||
|     for s in flatten(srcs): | ||||
|         objs += [ | ||||
|             filerule( | ||||
|                 name=join(self.localname, _removeprefix(f, G.OBJ + "/")), | ||||
|                 srcs=[f], | ||||
|                 deps=deps, | ||||
|                 cflags=sorted(set(cflags)), | ||||
|                 toolchain=toolchain, | ||||
|                 cwd=cwd, | ||||
|                 args=getattr(self, "explicit_args", {}), | ||||
|             ) | ||||
|             for f in filenamesof([s]) | ||||
|             if toolchain.is_source_file(f) | ||||
|         ] | ||||
|         if any(f.endswith(".o") for f in filenamesof([s])): | ||||
|             objs += [s] | ||||
|  | ||||
|     return objs | ||||
|  | ||||
|  | ||||
| def libraryimpl( | ||||
|     self, | ||||
|     name, | ||||
|     srcs, | ||||
|     deps, | ||||
|     hdrs, | ||||
|     caller_cflags, | ||||
|     caller_ldflags, | ||||
|     cflags, | ||||
|     ldflags, | ||||
|     toolchain, | ||||
|     commands, | ||||
|     label, | ||||
|     filerule, | ||||
| ): | ||||
|     hdr_deps = toolchain.find_c_header_targets(deps) + [self] | ||||
|     lib_deps = toolchain.find_c_library_targets(deps) + [self] | ||||
|  | ||||
|     hr = None | ||||
|     hf = [] | ||||
|     ar = None | ||||
|     if hdrs: | ||||
|         cs = [] | ||||
|         ins = hdrs.values() | ||||
|         outs = [] | ||||
|         i = 0 | ||||
|         for dest, src in hdrs.items(): | ||||
|             s = filenamesof([src]) | ||||
|             assert len(s) == 1, "the target of a header must return exactly one file" | ||||
|  | ||||
|             cs += [f"$(CP) $[ins[{i}]] $[outs[{i}]]"] | ||||
|             outs += ["=" + dest] | ||||
|             i = i + 1 | ||||
|  | ||||
|         hr = simplerule( | ||||
|             name=f"{self.localname}_hdr", | ||||
|             ins=ins, | ||||
|             outs=outs, | ||||
|             commands=cs, | ||||
|             label=toolchain.PREFIX + "CHEADERS", | ||||
|         ) | ||||
|         hr.args["cheader_deps"] = [hr] | ||||
|         hr.args["cheader_files"] = [hr] | ||||
|         hf = [f"-I{hr.dir}"] | ||||
|  | ||||
|     if srcs: | ||||
|         # Can't depend on the current target to get the library headers, because | ||||
|         # if we do it'll cause a dependency loop. | ||||
|         objs = findsources( | ||||
|             self, | ||||
|             srcs, | ||||
|             deps + ([hr] if hr else []), | ||||
|             cflags + hf, | ||||
|             filerule, | ||||
|             toolchain, | ||||
|             self.cwd, | ||||
|         ) | ||||
|  | ||||
|         ar = simplerule( | ||||
|             name=f"{self.localname}_lib", | ||||
|             ins=objs, | ||||
|             outs=[f"={self.localname}.a"], | ||||
|             deps=deps, | ||||
|             label=label, | ||||
|             commands=commands, | ||||
|         ) | ||||
|         ar.materialise() | ||||
|  | ||||
|     self.outs = ([hr] if hr else []) + ([ar] if ar else []) | ||||
|     self.deps = self.outs | ||||
|     self.args["cheader_deps"] = hdr_deps | ||||
|     self.args["clibrary_deps"] = lib_deps | ||||
|     self.args["cheader_files"] = [hr] if hr else [] | ||||
|     self.args["clibrary_files"] = [ar] if ar else [] | ||||
|     self.args["caller_cflags"] = caller_cflags + hf | ||||
|     self.args["caller_ldflags"] = caller_ldflags | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def clibrary( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     hdrs: TargetsMap = None, | ||||
|     caller_cflags=[], | ||||
|     caller_ldflags=[], | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=Toolchain, | ||||
|     label="LIB", | ||||
|     cfilerule=cfile, | ||||
| ): | ||||
|     libraryimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         hdrs, | ||||
|         caller_cflags, | ||||
|         caller_ldflags, | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         toolchain.AR, | ||||
|         toolchain.PREFIX + label, | ||||
|         cfilerule, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def hostclibrary( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     hdrs: TargetsMap = None, | ||||
|     caller_cflags=[], | ||||
|     caller_ldflags=[], | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=HostToolchain, | ||||
|     label="LIB", | ||||
|     cfilerule=cfile, | ||||
| ): | ||||
|     libraryimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         hdrs, | ||||
|         caller_cflags, | ||||
|         caller_ldflags, | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         toolchain.AR, | ||||
|         toolchain.PREFIX + label, | ||||
|         cfilerule, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def cxxlibrary( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     hdrs: TargetsMap = None, | ||||
|     caller_cflags=[], | ||||
|     caller_ldflags=[], | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=Toolchain, | ||||
|     label="CXXLIB", | ||||
|     cxxfilerule=cxxfile, | ||||
| ): | ||||
|     libraryimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         hdrs, | ||||
|         caller_cflags, | ||||
|         caller_ldflags, | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         toolchain.ARXX, | ||||
|         toolchain.PREFIX + label, | ||||
|         cxxfilerule, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def hostcxxlibrary( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     hdrs: TargetsMap = None, | ||||
|     caller_cflags=[], | ||||
|     caller_ldflags=[], | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=HostToolchain, | ||||
|     label="CXXLIB", | ||||
|     cxxfilerule=cxxfile, | ||||
| ): | ||||
|     libraryimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         hdrs, | ||||
|         caller_cflags, | ||||
|         caller_ldflags, | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         toolchain.ARXX, | ||||
|         toolchain.PREFIX + label, | ||||
|         cxxfilerule, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def programimpl( | ||||
|     self, | ||||
|     name, | ||||
|     srcs, | ||||
|     deps, | ||||
|     cflags, | ||||
|     ldflags, | ||||
|     toolchain, | ||||
|     commands, | ||||
|     label, | ||||
|     filerule, | ||||
| ): | ||||
|     cfiles = findsources(self, srcs, deps, cflags, filerule, toolchain, self.cwd) | ||||
|  | ||||
|     lib_deps = toolchain.find_c_library_targets(deps) | ||||
|     libs = collectattrs(targets=lib_deps, name="clibrary_files") | ||||
|     ldflags = collectattrs(targets=lib_deps, name="caller_ldflags", initial=ldflags) | ||||
|  | ||||
|     simplerule( | ||||
|         replaces=self, | ||||
|         ins=cfiles + libs, | ||||
|         outs=[f"={self.localname}{toolchain.EXE}"], | ||||
|         deps=deps, | ||||
|         label=label, | ||||
|         commands=commands, | ||||
|         args={"ldflags": ldflags}, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def cprogram( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=Toolchain, | ||||
|     label="CLINK", | ||||
|     cfilerule=cfile, | ||||
| ): | ||||
|     programimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         toolchain.CLINK, | ||||
|         toolchain.PREFIX + label, | ||||
|         cfilerule, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def hostcprogram( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=HostToolchain, | ||||
|     label="CLINK", | ||||
|     cfilerule=cfile, | ||||
| ): | ||||
|     programimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         toolchain.CLINK, | ||||
|         toolchain.PREFIX + label, | ||||
|         cfilerule, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def cxxprogram( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=Toolchain, | ||||
|     label="CXXLINK", | ||||
|     cxxfilerule=cxxfile, | ||||
| ): | ||||
|     programimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         toolchain.CXXLINK, | ||||
|         toolchain.PREFIX + label, | ||||
|         cxxfilerule, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def hostcxxprogram( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=HostToolchain, | ||||
|     label="CXXLINK", | ||||
|     cxxfilerule=cxxfile, | ||||
| ): | ||||
|     programimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         toolchain.CXXLINK, | ||||
|         toolchain.PREFIX + label, | ||||
|         cxxfilerule, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def _cppfileimpl(self, name, srcs, deps, cflags, toolchain): | ||||
|     hdr_deps = _indirect(deps, "cheader_deps") | ||||
|     cflags = collectattrs(targets=hdr_deps, name="caller_cflags", initial=cflags) | ||||
|  | ||||
|     simplerule( | ||||
|         replaces=self, | ||||
|         ins=srcs, | ||||
|         outs=[f"={self.localname}"], | ||||
|         deps=deps, | ||||
|         commands=toolchain.CPP, | ||||
|         args={"cflags": cflags}, | ||||
|         label=toolchain.PREFIX + "CPPFILE", | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def cppfile( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = [], | ||||
|     deps: Targets = [], | ||||
|     cflags=[], | ||||
|     toolchain=Toolchain, | ||||
| ): | ||||
|     _cppfileimpl(self, name, srcs, deps, cflags, toolchain) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def hostcppfile( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = [], | ||||
|     deps: Targets = [], | ||||
|     cflags=[], | ||||
|     toolchain=HostToolchain, | ||||
| ): | ||||
|     _cppfileimpl(self, name, srcs, deps, cflags, toolchain) | ||||
							
								
								
									
										83
									
								
								build/pkg.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								build/pkg.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| from build.ab import Rule, Target, G | ||||
| import os | ||||
| import subprocess | ||||
|  | ||||
|  | ||||
| class _PkgConfig: | ||||
|     package_present = set() | ||||
|     package_properties = {} | ||||
|     pkgconfig = None | ||||
|  | ||||
|     def __init__(self, cmd): | ||||
|         assert cmd, "no pkg-config environment variable supplied" | ||||
|         self.pkgconfig = cmd | ||||
|  | ||||
|         r = subprocess.run(f"{cmd} --list-all", shell=True, capture_output=True) | ||||
|         ps = r.stdout.decode("utf-8") | ||||
|         self.package_present = {l.split(" ", 1)[0] for l in ps.splitlines()} | ||||
|  | ||||
|     def has_package(self, name): | ||||
|         return name in self.package_present | ||||
|  | ||||
|     def get_property(self, name, flag): | ||||
|         p = f"{name}.{flag}" | ||||
|         if p not in self.package_properties: | ||||
|             r = subprocess.run( | ||||
|                 f"{self.pkgconfig} {flag} {name}", | ||||
|                 shell=True, | ||||
|                 capture_output=True, | ||||
|             ) | ||||
|             self.package_properties[p] = r.stdout.decode("utf-8").strip() | ||||
|         return self.package_properties[p] | ||||
|  | ||||
|  | ||||
| TargetPkgConfig = _PkgConfig(G.PKG_CONFIG) | ||||
| HostPkgConfig = _PkgConfig(G.HOST_PKG_CONFIG) | ||||
|  | ||||
|  | ||||
| def _package(self, name, package, fallback, pkgconfig): | ||||
|     if pkgconfig.has_package(package): | ||||
|         cflags = pkgconfig.get_property(package, "--cflags") | ||||
|         ldflags = pkgconfig.get_property(package, "--libs") | ||||
|  | ||||
|         if cflags: | ||||
|             self.args["caller_cflags"] = [cflags] | ||||
|         if ldflags: | ||||
|             self.args["caller_ldflags"] = [ldflags] | ||||
|         self.args["clibrary_deps"] = [self] | ||||
|         self.args["cheader_deps"] = [self] | ||||
|         self.traits.update({"clibrary", "cxxlibrary"}) | ||||
|         return | ||||
|  | ||||
|     assert fallback, f"Required package '{package}' not installed" | ||||
|  | ||||
|     if "cheader_deps" in fallback.args: | ||||
|         self.args["cheader_deps"] = fallback.args["cheader_deps"] | ||||
|     if "clibrary_deps" in fallback.args: | ||||
|         self.args["clibrary_deps"] = fallback.args["clibrary_deps"] | ||||
|     if "cheader_files" in fallback.args: | ||||
|         self.args["cheader_files"] = fallback.args["cheader_files"] | ||||
|     if "clibrary_files" in fallback.args: | ||||
|         self.args["clibrary_files"] = fallback.args["clibrary_files"] | ||||
|     self.ins = fallback.ins | ||||
|     self.outs = fallback.outs | ||||
|     self.deps = fallback.deps | ||||
|     self.traits = fallback.traits | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def package(self, name, package=None, fallback: Target = None): | ||||
|     _package(self, name, package, fallback, TargetPkgConfig) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def hostpackage(self, name, package=None, fallback: Target = None): | ||||
|     _package(self, name, package, fallback, HostPkgConfig) | ||||
|  | ||||
|  | ||||
| def has_package(name): | ||||
|     return TargetPkgConfig.has_package(name) | ||||
|  | ||||
|  | ||||
| def has_host_package(name): | ||||
|     return HostPkgConfig.has_package(name) | ||||
							
								
								
									
										180
									
								
								build/protobuf.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								build/protobuf.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,180 @@ | ||||
| from build.ab import Rule, Targets, emit, simplerule, filenamesof, G | ||||
| from build.utils import filenamesmatchingof, collectattrs | ||||
| from os.path import join, abspath, dirname, relpath | ||||
| from build.pkg import has_package | ||||
|  | ||||
| G.setdefault("PROTOC", "protoc") | ||||
| G.setdefault("HOSTPROTOC", "hostprotoc") | ||||
|  | ||||
| assert has_package("protobuf"), "required package 'protobuf' not installed" | ||||
|  | ||||
|  | ||||
| def _getprotodeps(deps): | ||||
|     r = set() | ||||
|     for d in deps: | ||||
|         r.update(d.args.get("protodeps", {d})) | ||||
|     return sorted(r) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def proto(self, name, srcs: Targets = [], deps: Targets = []): | ||||
|     protodeps = _getprotodeps(deps) | ||||
|     descriptorlist = ":".join( | ||||
|         [ | ||||
|             relpath(f, start=self.dir) | ||||
|             for f in filenamesmatchingof(protodeps, "*.descriptor") | ||||
|         ] | ||||
|     ) | ||||
|  | ||||
|     dirs = sorted({"$[dir]/" + dirname(f) for f in filenamesof(srcs)}) | ||||
|     simplerule( | ||||
|         replaces=self, | ||||
|         ins=srcs, | ||||
|         outs=[f"={self.localname}.descriptor"], | ||||
|         deps=protodeps, | ||||
|         commands=( | ||||
|             ["mkdir -p " + (" ".join(dirs))] | ||||
|             + [f"$(CP) {f} $[dir]/{f}" for f in filenamesof(srcs)] | ||||
|             + [ | ||||
|                 "cd $[dir] && " | ||||
|                 + ( | ||||
|                     " ".join( | ||||
|                         [ | ||||
|                             "$(PROTOC)", | ||||
|                             "--proto_path=.", | ||||
|                             "--include_source_info", | ||||
|                             f"--descriptor_set_out={self.localname}.descriptor", | ||||
|                         ] | ||||
|                         + ( | ||||
|                             [f"--descriptor_set_in={descriptorlist}"] | ||||
|                             if descriptorlist | ||||
|                             else [] | ||||
|                         ) | ||||
|                         + ["$[ins]"] | ||||
|                     ) | ||||
|                 ) | ||||
|             ] | ||||
|         ), | ||||
|         label="PROTO", | ||||
|         args={ | ||||
|             "protosrcs": filenamesof(srcs), | ||||
|             "protodeps": set(protodeps) | {self}, | ||||
|         }, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def protolib(self, name, srcs: Targets = []): | ||||
|     simplerule( | ||||
|         replaces=self, | ||||
|         label="PROTOLIB", | ||||
|         args={ | ||||
|             "protosrcs": collectattrs(targets=srcs, name="protosrcs"), | ||||
|             "protodeps": set(_getprotodeps(srcs)), | ||||
|         }, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def protocc(self, name, srcs: Targets = [], deps: Targets = []): | ||||
|     outs = [] | ||||
|     protos = [] | ||||
|  | ||||
|     allsrcs = collectattrs(targets=srcs, name="protosrcs") | ||||
|     assert allsrcs, "no sources provided" | ||||
|     for f in filenamesmatchingof(allsrcs, "*.proto"): | ||||
|         cc = f.replace(".proto", ".pb.cc") | ||||
|         h = f.replace(".proto", ".pb.h") | ||||
|         protos += [f] | ||||
|         outs += ["=" + cc, "=" + h] | ||||
|  | ||||
|     protodeps = _getprotodeps(deps + srcs) | ||||
|     descriptorlist = ":".join( | ||||
|         [ | ||||
|             relpath(f, start=self.dir) | ||||
|             for f in filenamesmatchingof(protodeps, "*.descriptor") | ||||
|         ] | ||||
|     ) | ||||
|  | ||||
|     r = simplerule( | ||||
|         name=f"{self.localname}_srcs", | ||||
|         cwd=self.cwd, | ||||
|         ins=srcs, | ||||
|         outs=outs, | ||||
|         deps=protodeps, | ||||
|         commands=[ | ||||
|             "cd $[dir] && " | ||||
|             + ( | ||||
|                 " ".join( | ||||
|                     [ | ||||
|                         "$(PROTOC)", | ||||
|                         "--proto_path=.", | ||||
|                         "--cpp_out=.", | ||||
|                         f"--descriptor_set_in={descriptorlist}", | ||||
|                     ] | ||||
|                     + protos | ||||
|                 ) | ||||
|             ) | ||||
|         ], | ||||
|         label="PROTOCC", | ||||
|     ) | ||||
|  | ||||
|     headers = {f[1:]: join(r.dir, f[1:]) for f in outs if f.endswith(".pb.h")} | ||||
|  | ||||
|     from build.c import cxxlibrary | ||||
|  | ||||
|     cxxlibrary( | ||||
|         replaces=self, | ||||
|         srcs=[r], | ||||
|         deps=deps, | ||||
|         hdrs=headers, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def protojava(self, name, srcs: Targets = [], deps: Targets = []): | ||||
|     outs = [] | ||||
|  | ||||
|     allsrcs = collectattrs(targets=srcs, name="protosrcs") | ||||
|     assert allsrcs, "no sources provided" | ||||
|     protos = [] | ||||
|     for f in filenamesmatchingof(allsrcs, "*.proto"): | ||||
|         protos += [f] | ||||
|         srcs += [f] | ||||
|  | ||||
|     descriptorlist = ":".join( | ||||
|         [abspath(f) for f in filenamesmatchingof(srcs + deps, "*.descriptor")] | ||||
|     ) | ||||
|  | ||||
|     r = simplerule( | ||||
|         name=f"{self.localname}_srcs", | ||||
|         cwd=self.cwd, | ||||
|         ins=protos, | ||||
|         outs=[f"={self.localname}.srcjar"], | ||||
|         deps=srcs + deps, | ||||
|         commands=[ | ||||
|             "mkdir -p $[dir]/srcs", | ||||
|             "cd $[dir]/srcs && " | ||||
|             + ( | ||||
|                 " ".join( | ||||
|                     [ | ||||
|                         "$(PROTOC)", | ||||
|                         "--proto_path=.", | ||||
|                         "--java_out=.", | ||||
|                         f"--descriptor_set_in={descriptorlist}", | ||||
|                     ] | ||||
|                     + protos | ||||
|                 ) | ||||
|             ), | ||||
|             "$(JAR) cf $[outs[0]] -C $[dir]/srcs .", | ||||
|         ], | ||||
|         traits={"srcjar"}, | ||||
|         label="PROTOJAVA", | ||||
|     ) | ||||
|  | ||||
|     from build.java import javalibrary | ||||
|  | ||||
|     javalibrary( | ||||
|         replaces=self, | ||||
|         deps=[r] + deps, | ||||
|     ) | ||||
							
								
								
									
										11
									
								
								build/toolchain.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								build/toolchain.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| import platform | ||||
|  | ||||
| _is_windows = (platform.system() == "Windows") | ||||
|  | ||||
| class Toolchain: | ||||
|     PREFIX = "" | ||||
|     EXE = ".exe" if _is_windows else "" | ||||
|  | ||||
|  | ||||
| class HostToolchain(Toolchain): | ||||
|     PREFIX = "HOST" | ||||
							
								
								
									
										105
									
								
								build/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								build/utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| from build.ab import ( | ||||
|     Rule, | ||||
|     Target, | ||||
|     Targets, | ||||
|     filenameof, | ||||
|     filenamesof, | ||||
|     cwdStack, | ||||
|     error, | ||||
|     simplerule, | ||||
|     G | ||||
| ) | ||||
| from os.path import relpath, splitext, join, basename, isfile | ||||
| from glob import iglob | ||||
| import fnmatch | ||||
| import subprocess | ||||
| import shutil | ||||
|  | ||||
|  | ||||
| def filenamesmatchingof(xs, pattern): | ||||
|     return fnmatch.filter(filenamesof(xs), pattern) | ||||
|  | ||||
|  | ||||
| def stripext(path): | ||||
|     return splitext(path)[0] | ||||
|  | ||||
|  | ||||
| def targetswithtraitsof(xs, trait): | ||||
|     return [t for t in xs if trait in t.traits] | ||||
|  | ||||
|  | ||||
| def collectattrs(*, targets, name, initial=[]): | ||||
|     s = set(initial) | ||||
|     for a in [t.args.get(name, []) for t in targets]: | ||||
|         s.update(a) | ||||
|     return sorted(s) | ||||
|  | ||||
|  | ||||
| def itemsof(pattern, root=None, cwd=None): | ||||
|     if not cwd: | ||||
|         cwd = cwdStack[-1] | ||||
|     if not root: | ||||
|         root = "." | ||||
|  | ||||
|     pattern = join(cwd, pattern) | ||||
|     root = join(cwd, root) | ||||
|  | ||||
|     result = {} | ||||
|     for f in iglob(pattern, recursive=True): | ||||
|         try: | ||||
|             if isfile(f): | ||||
|                 result[relpath(f, root)] = f | ||||
|         except ValueError: | ||||
|             error(f"file '{f}' is not in root '{root}'") | ||||
|     return result | ||||
|  | ||||
|  | ||||
| def does_command_exist(cmd): | ||||
|     basecmd = cmd.strip().split()[0] | ||||
|     return shutil.which(basecmd) | ||||
|  | ||||
|  | ||||
| def shell(cmd): | ||||
|     r = subprocess.check_output([G.SHELL, "-c", cmd]) | ||||
|     return r.decode("utf-8").strip() | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def objectify(self, name, src: Target, symbol): | ||||
|     simplerule( | ||||
|         replaces=self, | ||||
|         ins=["build/_objectify.py", src], | ||||
|         outs=[f"={basename(filenameof(src))}.h"], | ||||
|         commands=["$(PYTHON) $[ins[0]] $[ins[1]] " + symbol + " > $[outs]"], | ||||
|         label="OBJECTIFY", | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def test( | ||||
|     self, | ||||
|     name, | ||||
|     command: Target = None, | ||||
|     commands=None, | ||||
|     ins: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     label="TEST", | ||||
| ): | ||||
|     if command: | ||||
|         simplerule( | ||||
|             replaces=self, | ||||
|             ins=[command], | ||||
|             outs=["=sentinel"], | ||||
|             commands=["$[ins[0]]", "touch $[outs[0]]"], | ||||
|             deps=deps, | ||||
|             label=label, | ||||
|         ) | ||||
|     else: | ||||
|         simplerule( | ||||
|             replaces=self, | ||||
|             ins=ins, | ||||
|             outs=["=sentinel"], | ||||
|             commands=commands + ["touch $[outs[0]]"], | ||||
|             deps=deps, | ||||
|             label=label, | ||||
|         ) | ||||
							
								
								
									
										25
									
								
								build/zip.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								build/zip.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| from build.ab import ( | ||||
|     Rule, | ||||
|     simplerule, | ||||
|     TargetsMap, | ||||
|     filenameof, | ||||
| ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def zip(self, name, flags="", items: TargetsMap = {}, extension="zip", label="ZIP"): | ||||
|     cs = ["$(PYTHON) build/_zip.py -z $[outs]"] | ||||
|  | ||||
|     ins = [] | ||||
|     for k, v in items.items(): | ||||
|         cs += [f"-f {k} {filenameof(v)}"] | ||||
|         ins += [v] | ||||
|  | ||||
|     simplerule( | ||||
|         replaces=self, | ||||
|         ins=ins, | ||||
|         deps=["build/_zip.py"], | ||||
|         outs=[f"={self.localname}." + extension], | ||||
|         commands=[" ".join(cs)], | ||||
|         label=label, | ||||
|     ) | ||||
							
								
								
									
										11
									
								
								config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								config.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| import platform | ||||
| import os | ||||
|  | ||||
| if os.getenv("BUILDTYPE") == "windows": | ||||
|     windows = True | ||||
|     osx = False | ||||
|     unix = False | ||||
| else: | ||||
|     windows = False | ||||
|     osx = platform.system() == "Darwin" | ||||
|     unix = True | ||||
							
								
								
									
										45
									
								
								dep/adflib/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								dep/adflib/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| # Object files | ||||
| *.o | ||||
| *.lo | ||||
|  | ||||
| # Libraries | ||||
| .libs | ||||
| *.lib | ||||
| *.a | ||||
| *.la | ||||
|  | ||||
| # Shared objects (inc. Windows DLLs) | ||||
| *.dll | ||||
| *.so | ||||
| *.so.* | ||||
| *.dylib | ||||
|  | ||||
| # Executables | ||||
| *.exe | ||||
| *.out | ||||
| *.app | ||||
|  | ||||
| # generated by autogen.sh | ||||
| INSTALL | ||||
| Makefile.in | ||||
| aclocal.m4 | ||||
| autom4te.cache | ||||
| compile | ||||
| config.guess | ||||
| config.h.in | ||||
| config.sub | ||||
| configure | ||||
| depcomp | ||||
| install-sh | ||||
| ltmain.sh | ||||
| missing | ||||
|  | ||||
| # generated by configure | ||||
| .deps | ||||
| Makefile | ||||
| adflib.pc | ||||
| config.h | ||||
| config.log | ||||
| config.status | ||||
| libtool | ||||
| stamp-h1 | ||||
							
								
								
									
										13
									
								
								dep/adflib/AUTHORS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								dep/adflib/AUTHORS
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
|  | ||||
| The main developper is  | ||||
|  Laurent Clévy (laurent.clevy@club-internet.fr) | ||||
|  | ||||
| Contributors are: | ||||
|  Bjarne Viksoe  | ||||
|    (C++ wrapper, lot of bug fixes) | ||||
|  Gary Harris  | ||||
|    (bug fixes and W32 support) | ||||
|  Dan Sutherland  | ||||
|    (bug fixes and W32 support) | ||||
|  | ||||
| See CHANGES.txt for detailed contributions. | ||||
							
								
								
									
										339
									
								
								dep/adflib/COPYING
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										339
									
								
								dep/adflib/COPYING
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,339 @@ | ||||
| 		    GNU GENERAL PUBLIC LICENSE | ||||
| 		       Version 2, June 1991 | ||||
|  | ||||
|  Copyright (C) 1989, 1991 Free Software Foundation, Inc., | ||||
|  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  Everyone is permitted to copy and distribute verbatim copies | ||||
|  of this license document, but changing it is not allowed. | ||||
|  | ||||
| 			    Preamble | ||||
|  | ||||
|   The licenses for most software are designed to take away your | ||||
| freedom to share and change it.  By contrast, the GNU General Public | ||||
| License is intended to guarantee your freedom to share and change free | ||||
| software--to make sure the software is free for all its users.  This | ||||
| General Public License applies to most of the Free Software | ||||
| Foundation's software and to any other program whose authors commit to | ||||
| using it.  (Some other Free Software Foundation software is covered by | ||||
| the GNU Lesser General Public License instead.)  You can apply it to | ||||
| your programs, too. | ||||
|  | ||||
|   When we speak of free software, we are referring to freedom, not | ||||
| price.  Our General Public Licenses are designed to make sure that you | ||||
| have the freedom to distribute copies of free software (and charge for | ||||
| this service if you wish), that you receive source code or can get it | ||||
| if you want it, that you can change the software or use pieces of it | ||||
| in new free programs; and that you know you can do these things. | ||||
|  | ||||
|   To protect your rights, we need to make restrictions that forbid | ||||
| anyone to deny you these rights or to ask you to surrender the rights. | ||||
| These restrictions translate to certain responsibilities for you if you | ||||
| distribute copies of the software, or if you modify it. | ||||
|  | ||||
|   For example, if you distribute copies of such a program, whether | ||||
| gratis or for a fee, you must give the recipients all the rights that | ||||
| you have.  You must make sure that they, too, receive or can get the | ||||
| source code.  And you must show them these terms so they know their | ||||
| rights. | ||||
|  | ||||
|   We protect your rights with two steps: (1) copyright the software, and | ||||
| (2) offer you this license which gives you legal permission to copy, | ||||
| distribute and/or modify the software. | ||||
|  | ||||
|   Also, for each author's protection and ours, we want to make certain | ||||
| that everyone understands that there is no warranty for this free | ||||
| software.  If the software is modified by someone else and passed on, we | ||||
| want its recipients to know that what they have is not the original, so | ||||
| that any problems introduced by others will not reflect on the original | ||||
| authors' reputations. | ||||
|  | ||||
|   Finally, any free program is threatened constantly by software | ||||
| patents.  We wish to avoid the danger that redistributors of a free | ||||
| program will individually obtain patent licenses, in effect making the | ||||
| program proprietary.  To prevent this, we have made it clear that any | ||||
| patent must be licensed for everyone's free use or not licensed at all. | ||||
|  | ||||
|   The precise terms and conditions for copying, distribution and | ||||
| modification follow. | ||||
|  | ||||
| 		    GNU GENERAL PUBLIC LICENSE | ||||
|    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||
|  | ||||
|   0. This License applies to any program or other work which contains | ||||
| a notice placed by the copyright holder saying it may be distributed | ||||
| under the terms of this General Public License.  The "Program", below, | ||||
| refers to any such program or work, and a "work based on the Program" | ||||
| means either the Program or any derivative work under copyright law: | ||||
| that is to say, a work containing the Program or a portion of it, | ||||
| either verbatim or with modifications and/or translated into another | ||||
| language.  (Hereinafter, translation is included without limitation in | ||||
| the term "modification".)  Each licensee is addressed as "you". | ||||
|  | ||||
| Activities other than copying, distribution and modification are not | ||||
| covered by this License; they are outside its scope.  The act of | ||||
| running the Program is not restricted, and the output from the Program | ||||
| is covered only if its contents constitute a work based on the | ||||
| Program (independent of having been made by running the Program). | ||||
| Whether that is true depends on what the Program does. | ||||
|  | ||||
|   1. You may copy and distribute verbatim copies of the Program's | ||||
| source code as you receive it, in any medium, provided that you | ||||
| conspicuously and appropriately publish on each copy an appropriate | ||||
| copyright notice and disclaimer of warranty; keep intact all the | ||||
| notices that refer to this License and to the absence of any warranty; | ||||
| and give any other recipients of the Program a copy of this License | ||||
| along with the Program. | ||||
|  | ||||
| You may charge a fee for the physical act of transferring a copy, and | ||||
| you may at your option offer warranty protection in exchange for a fee. | ||||
|  | ||||
|   2. You may modify your copy or copies of the Program or any portion | ||||
| of it, thus forming a work based on the Program, and copy and | ||||
| distribute such modifications or work under the terms of Section 1 | ||||
| above, provided that you also meet all of these conditions: | ||||
|  | ||||
|     a) You must cause the modified files to carry prominent notices | ||||
|     stating that you changed the files and the date of any change. | ||||
|  | ||||
|     b) You must cause any work that you distribute or publish, that in | ||||
|     whole or in part contains or is derived from the Program or any | ||||
|     part thereof, to be licensed as a whole at no charge to all third | ||||
|     parties under the terms of this License. | ||||
|  | ||||
|     c) If the modified program normally reads commands interactively | ||||
|     when run, you must cause it, when started running for such | ||||
|     interactive use in the most ordinary way, to print or display an | ||||
|     announcement including an appropriate copyright notice and a | ||||
|     notice that there is no warranty (or else, saying that you provide | ||||
|     a warranty) and that users may redistribute the program under | ||||
|     these conditions, and telling the user how to view a copy of this | ||||
|     License.  (Exception: if the Program itself is interactive but | ||||
|     does not normally print such an announcement, your work based on | ||||
|     the Program is not required to print an announcement.) | ||||
|  | ||||
| These requirements apply to the modified work as a whole.  If | ||||
| identifiable sections of that work are not derived from the Program, | ||||
| and can be reasonably considered independent and separate works in | ||||
| themselves, then this License, and its terms, do not apply to those | ||||
| sections when you distribute them as separate works.  But when you | ||||
| distribute the same sections as part of a whole which is a work based | ||||
| on the Program, the distribution of the whole must be on the terms of | ||||
| this License, whose permissions for other licensees extend to the | ||||
| entire whole, and thus to each and every part regardless of who wrote it. | ||||
|  | ||||
| Thus, it is not the intent of this section to claim rights or contest | ||||
| your rights to work written entirely by you; rather, the intent is to | ||||
| exercise the right to control the distribution of derivative or | ||||
| collective works based on the Program. | ||||
|  | ||||
| In addition, mere aggregation of another work not based on the Program | ||||
| with the Program (or with a work based on the Program) on a volume of | ||||
| a storage or distribution medium does not bring the other work under | ||||
| the scope of this License. | ||||
|  | ||||
|   3. You may copy and distribute the Program (or a work based on it, | ||||
| under Section 2) in object code or executable form under the terms of | ||||
| Sections 1 and 2 above provided that you also do one of the following: | ||||
|  | ||||
|     a) Accompany it with the complete corresponding machine-readable | ||||
|     source code, which must be distributed under the terms of Sections | ||||
|     1 and 2 above on a medium customarily used for software interchange; or, | ||||
|  | ||||
|     b) Accompany it with a written offer, valid for at least three | ||||
|     years, to give any third party, for a charge no more than your | ||||
|     cost of physically performing source distribution, a complete | ||||
|     machine-readable copy of the corresponding source code, to be | ||||
|     distributed under the terms of Sections 1 and 2 above on a medium | ||||
|     customarily used for software interchange; or, | ||||
|  | ||||
|     c) Accompany it with the information you received as to the offer | ||||
|     to distribute corresponding source code.  (This alternative is | ||||
|     allowed only for noncommercial distribution and only if you | ||||
|     received the program in object code or executable form with such | ||||
|     an offer, in accord with Subsection b above.) | ||||
|  | ||||
| The source code for a work means the preferred form of the work for | ||||
| making modifications to it.  For an executable work, complete source | ||||
| code means all the source code for all modules it contains, plus any | ||||
| associated interface definition files, plus the scripts used to | ||||
| control compilation and installation of the executable.  However, as a | ||||
| special exception, the source code distributed need not include | ||||
| anything that is normally distributed (in either source or binary | ||||
| form) with the major components (compiler, kernel, and so on) of the | ||||
| operating system on which the executable runs, unless that component | ||||
| itself accompanies the executable. | ||||
|  | ||||
| If distribution of executable or object code is made by offering | ||||
| access to copy from a designated place, then offering equivalent | ||||
| access to copy the source code from the same place counts as | ||||
| distribution of the source code, even though third parties are not | ||||
| compelled to copy the source along with the object code. | ||||
|  | ||||
|   4. You may not copy, modify, sublicense, or distribute the Program | ||||
| except as expressly provided under this License.  Any attempt | ||||
| otherwise to copy, modify, sublicense or distribute the Program is | ||||
| void, and will automatically terminate your rights under this License. | ||||
| However, parties who have received copies, or rights, from you under | ||||
| this License will not have their licenses terminated so long as such | ||||
| parties remain in full compliance. | ||||
|  | ||||
|   5. You are not required to accept this License, since you have not | ||||
| signed it.  However, nothing else grants you permission to modify or | ||||
| distribute the Program or its derivative works.  These actions are | ||||
| prohibited by law if you do not accept this License.  Therefore, by | ||||
| modifying or distributing the Program (or any work based on the | ||||
| Program), you indicate your acceptance of this License to do so, and | ||||
| all its terms and conditions for copying, distributing or modifying | ||||
| the Program or works based on it. | ||||
|  | ||||
|   6. Each time you redistribute the Program (or any work based on the | ||||
| Program), the recipient automatically receives a license from the | ||||
| original licensor to copy, distribute or modify the Program subject to | ||||
| these terms and conditions.  You may not impose any further | ||||
| restrictions on the recipients' exercise of the rights granted herein. | ||||
| You are not responsible for enforcing compliance by third parties to | ||||
| this License. | ||||
|  | ||||
|   7. If, as a consequence of a court judgment or allegation of patent | ||||
| infringement or for any other reason (not limited to patent issues), | ||||
| conditions are imposed on you (whether by court order, agreement or | ||||
| otherwise) that contradict the conditions of this License, they do not | ||||
| excuse you from the conditions of this License.  If you cannot | ||||
| distribute so as to satisfy simultaneously your obligations under this | ||||
| License and any other pertinent obligations, then as a consequence you | ||||
| may not distribute the Program at all.  For example, if a patent | ||||
| license would not permit royalty-free redistribution of the Program by | ||||
| all those who receive copies directly or indirectly through you, then | ||||
| the only way you could satisfy both it and this License would be to | ||||
| refrain entirely from distribution of the Program. | ||||
|  | ||||
| If any portion of this section is held invalid or unenforceable under | ||||
| any particular circumstance, the balance of the section is intended to | ||||
| apply and the section as a whole is intended to apply in other | ||||
| circumstances. | ||||
|  | ||||
| It is not the purpose of this section to induce you to infringe any | ||||
| patents or other property right claims or to contest validity of any | ||||
| such claims; this section has the sole purpose of protecting the | ||||
| integrity of the free software distribution system, which is | ||||
| implemented by public license practices.  Many people have made | ||||
| generous contributions to the wide range of software distributed | ||||
| through that system in reliance on consistent application of that | ||||
| system; it is up to the author/donor to decide if he or she is willing | ||||
| to distribute software through any other system and a licensee cannot | ||||
| impose that choice. | ||||
|  | ||||
| This section is intended to make thoroughly clear what is believed to | ||||
| be a consequence of the rest of this License. | ||||
|  | ||||
|   8. If the distribution and/or use of the Program is restricted in | ||||
| certain countries either by patents or by copyrighted interfaces, the | ||||
| original copyright holder who places the Program under this License | ||||
| may add an explicit geographical distribution limitation excluding | ||||
| those countries, so that distribution is permitted only in or among | ||||
| countries not thus excluded.  In such case, this License incorporates | ||||
| the limitation as if written in the body of this License. | ||||
|  | ||||
|   9. The Free Software Foundation may publish revised and/or new versions | ||||
| of the General Public License from time to time.  Such new versions will | ||||
| be similar in spirit to the present version, but may differ in detail to | ||||
| address new problems or concerns. | ||||
|  | ||||
| Each version is given a distinguishing version number.  If the Program | ||||
| specifies a version number of this License which applies to it and "any | ||||
| later version", you have the option of following the terms and conditions | ||||
| either of that version or of any later version published by the Free | ||||
| Software Foundation.  If the Program does not specify a version number of | ||||
| this License, you may choose any version ever published by the Free Software | ||||
| Foundation. | ||||
|  | ||||
|   10. If you wish to incorporate parts of the Program into other free | ||||
| programs whose distribution conditions are different, write to the author | ||||
| to ask for permission.  For software which is copyrighted by the Free | ||||
| Software Foundation, write to the Free Software Foundation; we sometimes | ||||
| make exceptions for this.  Our decision will be guided by the two goals | ||||
| of preserving the free status of all derivatives of our free software and | ||||
| of promoting the sharing and reuse of software generally. | ||||
|  | ||||
| 			    NO WARRANTY | ||||
|  | ||||
|   11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | ||||
| FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN | ||||
| OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | ||||
| PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | ||||
| OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||
| MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS | ||||
| TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE | ||||
| PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | ||||
| REPAIR OR CORRECTION. | ||||
|  | ||||
|   12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | ||||
| WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | ||||
| REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, | ||||
| INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING | ||||
| OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED | ||||
| TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY | ||||
| YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | ||||
| PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | ||||
| POSSIBILITY OF SUCH DAMAGES. | ||||
|  | ||||
| 		     END OF TERMS AND CONDITIONS | ||||
|  | ||||
| 	    How to Apply These Terms to Your New Programs | ||||
|  | ||||
|   If you develop a new program, and you want it to be of the greatest | ||||
| possible use to the public, the best way to achieve this is to make it | ||||
| free software which everyone can redistribute and change under these terms. | ||||
|  | ||||
|   To do so, attach the following notices to the program.  It is safest | ||||
| to attach them to the start of each source file to most effectively | ||||
| convey the exclusion of warranty; and each file should have at least | ||||
| the "copyright" line and a pointer to where the full notice is found. | ||||
|  | ||||
|     <one line to give the program's name and a brief idea of what it does.> | ||||
|     Copyright (C) <year>  <name of author> | ||||
|  | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
|  | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
|  | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  | ||||
| Also add information on how to contact you by electronic and paper mail. | ||||
|  | ||||
| If the program is interactive, make it output a short notice like this | ||||
| when it starts in an interactive mode: | ||||
|  | ||||
|     Gnomovision version 69, Copyright (C) year name of author | ||||
|     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | ||||
|     This is free software, and you are welcome to redistribute it | ||||
|     under certain conditions; type `show c' for details. | ||||
|  | ||||
| The hypothetical commands `show w' and `show c' should show the appropriate | ||||
| parts of the General Public License.  Of course, the commands you use may | ||||
| be called something other than `show w' and `show c'; they could even be | ||||
| mouse-clicks or menu items--whatever suits your program. | ||||
|  | ||||
| You should also get your employer (if you work as a programmer) or your | ||||
| school, if any, to sign a "copyright disclaimer" for the program, if | ||||
| necessary.  Here is a sample; alter the names: | ||||
|  | ||||
|   Yoyodyne, Inc., hereby disclaims all copyright interest in the program | ||||
|   `Gnomovision' (which makes passes at compilers) written by James Hacker. | ||||
|  | ||||
|   <signature of Ty Coon>, 1 April 1989 | ||||
|   Ty Coon, President of Vice | ||||
|  | ||||
| This General Public License does not permit incorporating your program into | ||||
| proprietary programs.  If your program is a subroutine library, you may | ||||
| consider it more useful to permit linking proprietary applications with the | ||||
| library.  If this is what you want to do, use the GNU Lesser General | ||||
| Public License instead of this License. | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user