Compare commits

...

35 Commits

Author SHA1 Message Date
David Given
cbcf457ce3 Merge pull request #182 from davidgiven/usb
Add support for multiple simultaneously connected FluxEngines.
2020-08-10 23:32:16 +02:00
David Given
4855f825e2 Show serial number on device connection, and improve the device listing a bit. 2020-08-10 23:16:04 +02:00
David Given
85bc1637f2 Document the use of multiple FluxEngines. 2020-08-10 23:12:33 +02:00
David Given
73398b83a9 Add support for specifying which FluxEngine you want to use with the --devices
parameter.
2020-08-10 22:36:47 +02:00
David Given
2727e66d40 Allow multiple USB implementations. 2020-08-09 22:33:54 +02:00
David Given
8b6be5a501 Move usb.{cc,h} into its own directory. 2020-08-09 21:14:09 +02:00
David Given
4fee29307c Refactor the USB stuff to allow for multiple USB implementations. 2020-08-08 14:52:11 +01:00
David Given
35f8249c67 Merge pull request #178 from davidgiven/mac
Add support for exporting to DiskCopy 4.2 Mac disk image
2020-08-02 17:42:48 +01:00
David Given
d1467a14b8 Merge. 2020-08-02 18:24:15 +02:00
David Given
3e6b9eb74d Document the .diskcopy file format. 2020-08-02 18:22:30 +02:00
David Given
ce2e8fb4b5 First draft of the DiskCopy 4.2 image writer. 2020-07-30 20:21:49 +01:00
David Given
7eaa75c05d Merge pull request #177 from davidgiven/mac
Make Mac double-sided disks work.
2020-07-29 00:38:28 +02:00
David Given
e86de4483a Fix stupid bug where the mac decoder was looking at the wrong bit for the side
data.
2020-07-28 01:25:50 +02:00
David Given
203a74713f Merge pull request #175 from davidgiven/scp
Make SCP import and export properly handle single-sided images.
2020-06-30 11:19:48 +02:00
David Given
59ed2a6793 Make SCP import and export properly handle single-sided images. 2020-06-26 20:25:56 +02:00
David Given
a03283ce64 Merge pull request #174 from davidgiven/scp
Fix the SCP exporter.
2020-06-26 15:40:41 +02:00
David Given
984cdaeb03 Make nanoseconds_t a double to prevent overflow on very large numbers of
revolutions (I've just seen a flux file with 50).
2020-06-26 14:47:25 +02:00
David Given
a1ed4a9171 Fill out the SCP checksum correctly, and add a --indexed mode which causes data
prior to the first index mark to be discarded and sets the INDEX bit in the
header.
2020-06-26 12:20:49 +02:00
David Given
93caf8e549 Merge pull request #172 from davidgiven/b169
Fix a crash when decoding MX disks if advanceToNextRecord finds no records in a track.
2020-06-25 22:15:00 +02:00
David Given
3841942153 Fix a crash when decoding MX disks if advanceToNextRecord finds no records in a
track.
2020-06-25 21:56:33 +02:00
David Given
5706877b67 Merge pull request #171 from davidgiven/newsampler
Manually merge in jboone's new sampler from #164
2020-06-25 21:16:11 +02:00
David Given
d60900262b Remove the pulse converters from the sampler (the new sampler doesn't require
them). Update firmware.
2020-06-25 21:07:58 +02:00
David Given
54ea34400b Merge in jboone's updated sampler branch. 2020-06-25 21:01:35 +02:00
Jared Boone
db2ab8841a Update Sampler.v, moving clock domain crossing to FIFO interface.
Hopefully, I unscrewed the tab/space and line ending mismatches to minimize the diff.
2020-05-30 21:31:17 -07:00
David Given
adba93ae0a Merge pull request #163 from davidgiven/brother120
Untested Brother 120kB write support.
2020-05-24 01:32:28 +02:00
David Given
98587d04a7 Merge from trunk. 2020-05-24 00:56:17 +02:00
David Given
0051b64648 Merge pull request #167 from vanbogaertetienne/trs80fix
Typo fix in FM_TRS80DAM2_PATTERN
2020-05-24 00:47:12 +02:00
vanbogaertetienne
603009ba15 Typo fix in FM_TRS80DAM2_PATTERN 2020-05-22 20:46:48 +02:00
Jared Boone
adb9809692 Simplify Sampler. 2020-05-20 11:34:29 -07:00
David Given
06eb10d2a0 Merge. 2020-05-16 10:38:17 +02:00
David Given
2244299bd9 Add a bias parameter to allow the entire Brother format to be moved around on
the disk.
2020-05-16 10:37:48 +02:00
David Given
6ca06ecafb Fix several embarassingly stupid bugs in the brother 120kB encoder code path. 2020-05-14 10:41:25 +02:00
David Given
9a5958f80b Prevent OOB when appending no bytes to a fluxmap. 2020-05-14 10:40:49 +02:00
David Given
2b53ac057c Fix some bugs which allow erasing tracks with F_FRAME_WRITE_CMD to work again.
(F_FRAME_ERASE_CMD always worked.)
2020-05-13 23:45:58 +02:00
David Given
5deba8af41 Untested Brother 120kB write support. 2020-05-13 14:49:06 +02:00
39 changed files with 1204 additions and 765 deletions

View File

@@ -1,243 +1,243 @@
:400000000080002011000000990F0000990F0000064A08B5136843F020031360044B1A6803F53F5302331A6000F0D4FFE8460040FA46004010B5054C237833B9044B13B103
:400040000448AFF300800123237010BD6881FF1F0000000018380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF00000000F1
:400080006C81FF1F18380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8881FF1F6C
:400000000080002011000000A10F0000A10F0000064A08B5136843F020031360044B1A6803F53F5302331A6000F0D8FFE8460040FA46004010B5054C237833B9044B13B1EF
:400040000448AFF300800123237010BD6881FF1F0000000020380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF00000000E9
:400080006C81FF1F20380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8881FF1F64
:4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8881FF1F3F000080024B012200205A7293
:4001000002F00EB98881FF1F10B5C4B2204601F023F90128FAD110BD08B572B60F4B0F49DA680132DA601A690132C82A08BF00221A615A6918690132A72A08BF00224A6142
:400140005B69002B0CBF02230023002814BF184643F0010002F04CFE62B608BD8881FF1F38B50446C5B2284602F07CF8062002F099FA44F00200C0B202F074F8062002F065
:4001800091FA284602F06EF8BDE83840062002F073BA10B5642402F05FF830B90120FFF7DFFF013CF7D1204610BD012010BD70B5C4B2054620460E4601F0CEF8012805D09D
:4001C000204601F0E7F92846FFF79EFF204601F0CBF8314605460246204601F087F9204601F0BAF80028FAD1284670BD38B5044D0024285D013402F005FA402CF9D138BDFD
:40020000A081FF1F08B502F01FFC002002F028FC02F03AFC02F044FC80B208BD10B50446012002F037F8642002F026FAFFF7EAFF2080002002F02EF8642002F01DFAFFF70D
:40024000E1FF608010BD08B502F02AFD002002F033FD02F045FD02F04FFD80B208BD10B50446FFF796FF322002F006FAFFF7EBFF20800020FFF774FF322002F0FDF9FFF759
:40028000E2FF608010BD0FB400B593B014AB53F8042B402102A8019302F0A6FE02A802F046F802F050F813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF737
:4002C00023FF62782146BDE81040042001F09CB82C38000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8583043B10020EF
:4003000001F0A4FF002002F037FD002384F8583010BD00BF8881FF1F38B5104D837895F85B2004469A4204D0FFF7E4FF002385F85E302368C5F859302279094B1A71A37855
:40034000002B14BF0220012002F016FDE07802F00DFD2079BDE8384002F044BD8881FF1FE181FF1F38B50D4C94F8585065B904F15900FFF7D1FF012001F068FF4FF47A7041
:4003800002F07AF984F85E50E3682366012384F85830BDE8384002F0ADB900BF8881FF1FF8B51E4C0646FFF7DDFF94F85E3003B15EB91B48FFF767FFFFF7EBFE012000235B
:4003C00084F85E00636602F06DF93246616E1548FFF759FF114D0027636E9E4216D001F03BFF00B16F66636E9E4205DD0020FFF7B7FE6B6E013305E005DA0120FFF7B0FE4F
:400400006B6E013B6B6602F075F9E5E7322002F033F9BDE8F8400448FFF735BF8881FF1F39380000403800005D3800002DE9F04F99B062B602F0C8F99949042002F0ECF93C
:40044000984801F015FF984802F0B8FC974801F049FF02F099FB02F06BFA002002F08CFC01F064FF0221002000F02CFF904C012001F0A4F8002384F85B30FFF773FFFFF7BE
:4001000002F012B98881FF1F10B5C4B2204601F027F90128FAD110BD08B572B60F4B0F49DA680132DA601A690132C82A08BF00221A615A6918690132A72A08BF00224A613A
:400140005B69002B0CBF02230023002814BF184643F0010002F050FE62B608BD8881FF1F38B50446C5B2284602F080F8062002F09DFA44F00200C0B202F078F8062002F055
:4001800095FA284602F072F8BDE83840062002F077BA10B5642402F063F830B90120FFF7DFFF013CF7D1204610BD012010BD70B5C4B2054620460E4601F0D2F8012805D089
:4001C000204601F0EBF92846FFF79EFF204601F0CFF8314605460246204601F08BF9204601F0BEF80028FAD1284670BD38B5044D0024285D013402F009FA402CF9D138BDE9
:40020000A081FF1F08B502F023FC002002F02CFC02F03EFC02F048FC80B208BD10B50446012002F03BF8642002F02AFAFFF7EAFF2080002002F032F8642002F021FAFFF7ED
:40024000E1FF608010BD08B502F02EFD002002F037FD02F049FD02F053FD80B208BD10B50446FFF796FF322002F00AFAFFF7EBFF20800020FFF774FF322002F001FAFFF740
:40028000E2FF608010BD0FB400B593B014AB53F8042B402102A8019302F0AAFE02A802F04AF802F054F813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF72B
:4002C00023FF62782146BDE81040042001F0A0B83438000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8583043B10020E3
:4003000001F0A8FF002002F03BFD002384F8583010BD00BF8881FF1F38B5104D837895F85B2004469A4204D0FFF7E4FF002385F85E302368C5F859302279094B1A71A3784D
:40034000002B14BF0220012002F01AFDE07802F011FD2079BDE8384002F048BD8881FF1FE181FF1F38B50D4C94F8585065B904F15900FFF7D1FF012001F06CFF4FF47A7031
:4003800002F07EF984F85E50E3682366012384F85830BDE8384002F0B1B900BF8881FF1FF8B51E4C0646FFF7DDFF94F85E3003B15EB91B48FFF767FFFFF7EBFE0120002353
:4003C00084F85E00636602F071F93246616E1548FFF759FF114D0027636E9E4216D001F03FFF00B16F66636E9E4205DD0020FFF7B7FE6B6E013305E005DA0120FFF7B0FE47
:400400006B6E013B6B6602F079F9E5E7322002F037F9BDE8F8400448FFF735BF8881FF1F4138000048380000653800002DE9F04F99B062B602F0CCF99949042002F0F0F914
:40044000984801F019FF984802F0BCFC974801F04DFF02F09DFB02F06FFA002002F090FC01F068FF0221002000F030FF904C012001F0A8F8002384F85B30FFF773FFFFF79A
:4004800088FE84F86800FFF735FF012384F85B30FFF768FFFFF77DFE84F86900FFF72AFF844B94F86800844994F869202546002A14BF0A461A46002808BF19467F48FFF7E3
:4004C000E2FE0321084602F0F5F8264602F012F994F8583043B12A6EEB689B1A41F28832934201D9FFF706FF00F024FF18B97448FFF7C9FE04E000F023FF0028F7D10BE0F5
:4005000000F018FF10B902F0F5F8F9E76D48FFF7BAFE032001F03EF8032000F01DFF0128D4D16948FFF7F8FE68490320FFF73FFE94F86A106648FFF7A6FE94F86A30023B2D
:40054000142B00F2A783DFE813F01500A5031E00A5032400A5034600A5036C00A503CF00A503AA01A503DE02A503FD02A5030403A5031E0303238DF820308DF821300D233B
:400580008DF82230FBE294F86C00FFF709FF514BF2E2FFF7E7FE00236372E068627A02F0FF0132B9EB681B1AB3F57A7FF6DD0B4608E03BB100227272F168627A12B9EB68D3
:4005C0005B1AFAE707228DF8202004228DF82120ADF82230D3E20220FFF796FD4FF000080DF1200A02F086F84FF480790027C9EB0803DA1907F80A200137402FF9D10220C4
:40060000FFF782FD3A465146022000F0FDFEB9F10109EBD108F10108B8F1400FE2D12E4B38E04FF0010A4FF000080DF1200B02F061F84FF0000959460120FFF7B8FD08EBC8
:4004C000E2FE0321084602F0F9F8264602F016F994F8583043B12A6EEB689B1A41F28832934201D9FFF706FF00F028FF18B97448FFF7C9FE04E000F027FF0028F7D10BE0E5
:4005000000F01CFF10B902F0F9F8F9E76D48FFF7BAFE032001F042F8032000F021FF0128D4D16948FFF7F8FE68490320FFF73FFE94F86A106648FFF7A6FE94F86A30023B1D
:40054000142B00F2AB83DFE813F01500A9031E00A9032400A9034600A9036C00A903CF00A903AA01A903E202A9030103A9030803A903220303238DF820308DF821300E23FD
:400580008DF82230FFE294F86C00FFF709FF514BF6E2FFF7E7FE00236372E068627A02F0FF0132B9EB681B1AB3F57A7FF6DD0B4608E03BB100227272F168627A12B9EB68CB
:4005C0005B1AFAE707228DF8202004228DF82120ADF82230D7E20220FFF796FD4FF000080DF1200A02F08AF84FF480790027C9EB0803DA1907F80A200137402FF9D10220BC
:40060000FFF782FD3A465146022000F001FFB9F10109EBD108F10108B8F1400FE2D12E4B38E04FF0010A4FF000080DF1200B02F065F84FF0000959460120FFF7B8FD08EBBF
:40064000090300270493049B1BF807203B44DBB2934209D08DE80C0041463B464A461F48FFF711FE4FF0000A0137402FEBD109F10109B9F5807FDED108F10108B8F1400F92
:40068000D5D151461648FFF7FEFDBAF1000F00F00E81144B1B8807A8ADF81C3070E200BF19010000F900000091000000C50000008881FF1F6F3800006B380000723800003E
:4006C0008A3800009D380000E181FF1FF281FF1FA73800001C3800001E380000B6380000D23800002038000094F86C0001F0D2FD606EFFF755FE02F0E3FBB04BDFF8D882AE
:400700001A78002702F0FB021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F0D1FB0220FFF7EEFC012141F6FF734FF48042084602F024FB84F8AA0005
:4007400001F046FF08F807000137402FF8D1DFF88CA200270AF199091FFA89F80137402F14BF3A4600221AF8010F22440623127E402101F061FF424646F244519AF8000073
:4007800001F06CFF08F14008402F1FFA88F8E5D196F86D3033B100237372637A002BFCD00023737200234FF0FF32236062602372236894F8AA002344197E01F0C1FE94F883
:4007C000AA0001F07FFE012194F8AA0001F052FE2368002BFCD0002398467360D6F80CA0012701F087FFE368B4F86E20CAEB030393420DD367B1042195F8AA0001F0ACFED5
:4008000094F8AA0001F0B8FE0028F9D107463072237AFBB96A682B689A4202D1002FE0D118E00220FFF770FC6968402209EB8111022000F0E9FD6A68614B01321340002B59
:40084000BEBF03F1FF3363F03F03013308F101086360C6E70220277AFFF756FC00221146022000F0D1FDFFB20220FFF74DFCFFF7BDFC37B15348FFF706FD0220FFF72AFDBA
:4008800006E0514B08A81B88ADF82030FFF710FD627A4146237A4D48FFF7F5FCFDE14C48FFF7F1FCD4F86E7017F03F0701D00320F1E1012001F0F4FC95F86C0001F0EAFC0B
:4008C00002F0FEFA434BDFF810811A7842F004021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F0EDFA686EFFF756FD01214FF4804341F6FF72084641
:4009000001F0D4FC85F8AA0001F062FE08F807000137402FF8D1DFF8C490002709F199031FFA83F804930137402F14BF3A46002219F8010F22440523127E402101F07CFECD
:40094000414646F24B4299F8000001F087FE08F14008402F1FFA88F8E5D100274FF0FF33376098467360BB46BA463B46D6F86E9037724FEA99192168114A01310A40002AD9
:40098000BCBF02F1FF3262F03F026068B8BF013282426BD02BB1227A002A76D16A7A002A73D12068049A059302EB8010BAF1000F16D040223F2102F0E3FA1CE094650040B8
:4009C0003F000080DC38000022380000F6380000093900009B640040A081FF1F9F81FF1F014601370120FFF7E2FBC7EB0903D3F1000A4AEB030A2168AB4A01310A40002A39
:400A0000BEBF02F1FF3262F03F02013222606268059B01322AD12A683F2A27D14FF00008C5F8048001F070FC85F808806B6895F8AA002B44197E01F083FD95F8AA0001F0AF
:400A400041FD012195F8AA0001F014FD85F80980637A002BFCD04FF00008012086F8098001F05EFC404601F01BFC00E023B1237A5BB96B7A4BB90123626842453FF47BAF9B
:400A80000BF1010BD5F8048075E701F043FC012001F006FC002001F043FC042194F8AA0001F05AFD94F8AA0001F066FD0028F9D196F8AA0001F0F4FC737A327A0293012363
:400AC00003920193CDF800A05B463A4649467748FFF7D9FBBAF1000F08D0FFF787FB237A63B17348FFF7CFFB0220D4E0B945F4D070490120FFF75BFB0137F7E76E48FFF779
:400B0000C2FB6E4B38E094F86C0001F0C3FB606EFFF746FC6A48FFF7B6FB00236372637A002BFCD0012001F0FBFB00237372637A002BFCD0002001F0F3FB6248FFF7A3FB34
:400B4000614B19E0002084F85E00FFF729FC5F4B12E094F8683023B195F869200AB985F86C2094F869201AB113B9012385F86C305748FFF7D1FB574B1B88ADF8203008A811
:400B8000FFF796FB89E0FFF7B5FB02F07BF8002002F01EF82A2701F049FF002001F0ECFE3A46002108A802F0EBF917238DF820308DF8217001F09EFD002001F047FB002042
:400BC00002F0DAF8C82001F057FD0DF12200FFF725FB0DF13600FFF742FB01F08BFD012002F0CAF8322001F047FD0DF12600FFF715FB0DF13A00FFF732FB012001F026FBDA
:400C00004FF4967001F038FD01F074FD0DF12E00FFF704FB0DF14200FFF721FB002001F015FB4FF4967001F027FD01F063FD022002F0A2F8322001F01FFD0DEB0700FFF79E
:400C4000EDFA0DF13E00FFF70AFB012001F0FEFA4FF4967001F010FD01F04CFD0DF13200FFF7DCFA0DF14600FFF7F9FA002001F0EDFA4FF4967001F0FFFC01F03BFD00202C
:400C800002F07AF8002384F85E3001F07DFF01F04FFE74E70120FFF71DFB032000F07AFC0D48FFF7F0FA11E43F00008013390000433900003892FF1F4D390000243800006A
:400CC00055390000633900002638000028380000F281FF1F2A380000703900002DE9F04172B6884B61221A70A3F5F06301221A801924854A9C7092E803008033062283F88E
:400D0000002283E80300522203F580731A707F4B7F4A1B787F4EDBB2137040F618027E4B00251A8041F2512223F8022C33784FF4F07003F0010343EA450502F0B9F8013CD4
:400D400005F003052ED0032DF0D1744B4FF480721A8007221A70724A002548211570917002221D705D7103F8032C0422DA716D4A6D4C13786D4E43F00103137012F8013CA5
:400D8000062743F0030302F8013C2378012243F0800323705B4B1A70654A137843F02003137000E0FEE707FB056300219A881868013502F0E5F8072DF5D15E485E4E0025E4
:400DC00050F8041F05F1105303F14A0221F0FF074B33C9B20B4452005B0002329A4206D012F802EC12F801CC0EF807C0F5E7B0420D44E5D1514A0023136093601361936100
:400E00004F4B504F1A68504BDFF888811A604F4B1A684F4B1A604F4A137843F002031370137C43F0020313742378A2F5863243F040032370413A137843F010031370464A53
:400E4000464B07CA03C31A80454A2833106843F8250C127903F8212C424A07CA03C31A80414AE83B07CA03C31A80404A083307CA03C31A803E4A3F4BA2F5616203CBC2F889
:400E8000100EC2F8141E1378042043F008031370394B02F5AA521B783D78DBB298F80060EDB203F007010C321B091170F6B2537045F003033B7046F0030388F800302F4B48
:400EC00048221A702E4A402313702E49937013729372082382F81F3220220A7048710A72294A0A20137001F0DDFB284B88F8006044223D70264D1A7094E80F0007C52B8067
:400F0000BDE8F08100480040140C00480F010049A146004025420040224200400440004006400040A2430040A043004075390000E8460040FCFFFF479000004800760040A3
:400F40001C0C0048F846004020760040200C00482876004003500140D40B0048C0510040E00B0048E80B0048F40B0048000C0048325100400C0C0048CF0100491D5100405C
:400F800001590040235B0040585B004076580040B0430040F946004008B501F0C5FF03680C2B00D1FEE7FEE7084908B50B68084A1844821A802A01DC086005E001F0B4FF34
:400FC0000C2303604FF0FF33184608BDCC80FF1F8893FF1F80B51148114B0025C0B1A3F1100192C922460439161BB74204D051F8046F42F8046BF7E7114653F8046C8C1A0A
:40100000A64202D041F8045BF9E701381033E5E701F090FFFFF70AFAFEE700BF01000000443B0000124A134B10B51A60124A134C1368134843F4007313600023032B98BF49
:4010400054F823204FEA830188BF0E4A0133302B4250F3D10C4B1A780C4B1A700C4B084A1A60FFF73BFEBDE8104001F0EDB900BF0004FA050CED00E014ED00E0000000007C
:401080000080FF1F990F0000BC760040C080FF1F08ED00E0F8B501F013FF4B4A01271378022643F001031370137C484C43F001031374474B02F5E3521F700B3203F8946C05
:4010C0001378054603F07F031370002001F0EAFA2378404A03F0F90323701378384603F0DF03137023783B43237001F0DBFA282001F0D8FA384B30461A7802F07F021A7020
:401100001A7802F0BF021A7023783343237001F0C9FA2378314A43F0040323700023137053702F4AFF2199540133092BFBD1284601F0CAFE0721172001F0FCFA294917206C
:4011400001F0EAFA0721182001F0F4FA2649182001F0E2FA0721152001F0ECFA2349152001F0DAFA0721052001F0E4FA2049052001F0D2FA0721062001F0DCFA1D49062045
:4011800001F0CAFA0721084601F0D4FA1A49072001F0C2FA0721082001F0CCFA1749082001F0BAFA0021162001F0C4FA1449162001F0B2FA07210C2001F0BCFABDE8F840CC
:4011C00010490C2001F0A8BAA5430040944300409D60004012600040F851004084600040AD92FF1F631A00009D180000611A000095190000C1190000F1190000291A00005D
:40120000691A0000DD1A0000214B224A10B5187000231370204A40201370204A0F2413701F4A13701F4A13701F4A13701F4A13701F4B4FF400021A604FF080721A604FF435
:4012400000121A6020221A601860802018604FF480701860174804704FF480001860164B1A70933B19B91A7802F0FE0202E01A7842F001021A70114B03221A70802203F8F3
:40128000202C012001F014FE0D4B04221A7010BDC892FF1FCE92FF1FCC92FF1FCD92FF1FC992FF1FB892FF1FCB92FF1F4093FF1F00E100E09E6000409C60004028600040AA
:4012C0001260004070B5074C054623780E461BB9FFF7E0FE0123237031462846BDE87040FFF792BF7892FF1F0A4A002313700A4A13700A4A13700A4A13700A4A13700A4AD8
:4013000013700A4A13700A4B03221A70802203F8202C7047CE92FF1FCC92FF1FCD92FF1FC992FF1FB892FF1FCB92FF1F4093FF1F28600040014B1878704700BFCD92FF1F54
:40134000044B1A7802F0FF001AB118780022C0B21A707047CC92FF1F024A0C2303FB002040787047D492FF1F431E072B0CD8074A064B00010344805C5B7800F00F0043EA27
:401380000020023880B2704700207047FC5F00401A4A38B50C2303FB00231B79090C13F0800F00F1FF35044619BF8AB24FF480438BB24FF48042032D18D8DFE805F00207EC
:4013C0000C110021084601F01BF80DE00021084600F0FAFF08E00021084600F0D9FF03E00021084600F0B8FF054B1855EDB2072D03D801F0EDF8034B185538BDD492FF1F8B
:40140000A492FF1FAD92FF1F431E072B2DE9F0470446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB2C3F500739D424FF00C0303FB007388BFD588DB7884BFC5F5C4
:401440000075ADB2254A43EA15230601B354B244EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FB047200215170B9F1000F28D03DB31B4F385D01F011F81123D6
:401480002946FE2218F8040001F0D6F806F5C04278321FFA89F118F8040001F0DFF8124D18F80410385D01F04BF80121385D00F0E1FF735D43F002037355735D03F0FD034E
:4014C0007355BDE8F08703FB04746379DBB28AF80230BDE8F08700BFD492FF1FFC5F0040AD92FF1FA492FF1F706000402DE9F047044615468846002940D0431E072B3FD817
:40150000FFF732FFA84203D22046FFF72DFF05461D4E335DFF2B03D141462046FFF738FFDFF868A027011AF8040000F0B9FF1223FE222946305D01F07FF807F5C0411FFA4B
:4015400088F27831305D01F089F8DFF84490315D1AF8040000F0F4FF01211AF8040000F089FF17F8093043F0020307F8093017F8093003F0FD0307F8093002E00D4600E027
:4015800000252846BDE8F087AD92FF1FA492FF1F70600040431E072B0AD8064A0C2303FB002300225A705A79034BD2B200011A54704700BFD492FF1FFE5F0040431E072B7C
:4015C0009FBF024B000108221A547047FE5F004030B51A4A1A491B4D0878138803449BB21380194A00231488D8B2A4B27CB1082B0CD050680078C0B2E854506801330130C4
:4016000050601088013880B21080ECE718460B780E4C082B0E4A00D040B10E4D2B7883F080032B700F232370022301E0022323701370094B1870087030BD00BF4493FF1F83
:401640004093FF1F00600040BC92FF1FB992FF1FCE92FF1FCA92FF1F4193FF1F074B02221A70074B80221A70064B0F221A70064A00231370054A012013707047CE92FF1F72
:40168000CA92FF1FB992FF1F4093FF1F4193FF1F30B5164B16491B780A8803F00F03023BDBB21A4492B20A80124C134A0020118889B279B173B15568215C013BC9B2297018
:4016C0005168DBB20131516011880130013989B21180ECE7094A1370094A137883F080031370084B0B221A7030BD00BF296000404493FF1F00600040BC92FF1F4193FF1F7F
:40170000CA92FF1FB992FF1F064A06231370064A01201370054B80221A70054B00221A70704700BFCE92FF1FB992FF1FCA92FF1F4193FF1F054B9A683AB19A680449107089
:401740009A680988518000229A607047BC92FF1F4493FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A137008BD0220FFF7E1FF0D4B1B7803F06003202B05D0402B9B
:4017800006D043B900F012FC04E001F0A1FB01E000F046FD10B9034B03221A7008BD00BF28600040B992FF1F0060004008B5084A084B0120197813880B449BB21380064B8A
:4017C00000221A70FFF7B6FF044B03221A7008BD4493FF1F4093FF1FCE92FF1FB992FF1F08B50C4B1B78DBB2042B07D0062B09D0022B0DD1BDE80840FFF7D8BFBDE808404C
:40180000FFF746BF0320FFF795FF034B03221A7008BD00BFCE92FF1FB992FF1F08B5054B002201201A70FFF785FF034B03221A7008BD00BFCE92FF1FB992FF1F08B50A4BCA
:401840001A7832B11A78094942F080020A7000221A70074B002201201A70FFF76BFF054B03221A7008BD00BFB892FF1F08600040CE92FF1FB992FF1F074B1B78DBB2042B9B
:4018800005D0062B05D0022B05D1FFF7A1BEFFF7C5BFFFF7D3BF7047CE92FF1F38B51D4C2378DBB2DD0634D518060AD503F00F03012B2ED1FFF74EFF174B1B78190609D5F2
:4018C00038BD5A0602D5FFF7D7FF03E09D0620D5FFF786FF23781B061BD4104B1A78104B1B7813430F4A13701278934211D10A4A0849154613782078DBB2000605D41378E7
:40190000DBB20B700B7803F00F0328788342F1D138BD38BD28600040B992FF1FCA92FF1F4193FF1F29600040054A00231380054A916819B191680B7092685380704700BFD2
:401940004493FF1FBC92FF1F0E4808B503889BB213B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FF094AD1799379028843EA012392B2934238BF0380FFF728FED7
:40198000012008BDBC92FF1FCE92FF1FCA92FF1F00600040084B01221A700F3B9B7C074B1A7B02F00302012A1EBFDA7B82F08002DA7301225A7370470B600040D492FF1F8A
:4019C000094B02221A700F3B93F82230074B1A7E02F00302012A1EBFDA7E82F08002DA7601225A76704700BF0B600040D492FF1F0B4B04221A700F3B93F83230094B93F885
:401A0000242002F00302012A1EBF93F8272082F0800283F82720012283F82520704700BF0B600040D492FF1F0B4B08221A700F3B93F84230094B93F8302002F00302012AB1
:401A40001EBF93F8332082F0800283F83320012283F83120704700BF0B600040D492FF1F7047FFF741BC0000F0B5184B184E19780C27C9B201234FF0000C31B3CA0720D5E5
:401A8000144A4FEA031E7244947850782040C5070DD507FB03652C79240608D5147804F0FE0414706D790C4CEDB204F80E50840706D507FB036425792D0658BF84F801C08F
:401AC00090700133DBB24908D7E7F0BD9F600040D492FF1F70600040FE5F004000F0ACBC70B50446184B88B003AA03F11006154618685968083303C5B3422A46F7D11B7881
:401B00002B70FCB12223237001AD03232846637000F08AFE002220461146AB5C08AC04EB131414F8144C03F00F03847008AC234413F8143C0132082AC1700371417100F1C0
:401B40000400EAD108B070BD9F3900002DE9F0431C4D01222E460C201F274FF0800E4FF0080C194B00FB02581401234418705F70164998F805902144B9F1000F07D098F879
:401B8000044024064CBF887081F802C001E081F802E000FB0261CC880132E4B29C71CC88092AC4F30724DC71CC88E4B21C71C988C1F307215971D4D1054BFF221A70BDE84C
:401BC000F08300BFD492FF1F70600040FC5F00400A600040064B074A1B7802EBC30253681A7C824286BF03EBC003586900207047C892FF1F003A00002DE9F84F424B1A7822
:401C0000002A7ED01878414D0138C0B2FFF7E2FFA8463F4AC3681478007ADFF800C1E4B203EBC0000C2600274FF0010E834268D01A78A24263D11CF80420597891425ED1AF
:401C40009A7893F8039002F07F0206FB02FA05EB0A01CF7093F802B009F0030981F804B093F803B005F80AB0B3F804A0A1F808A093F902A0BAF1000F0BDAB9F1010F0CBF44
:401C80004FF007094FF00D0981F8059081F801E009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEA02191A4906FB0282494481F802E0B2F808A0CAF3072A81F862
:401CC00000A0B2F808A05FFA8AFA81F801A0B2F806A011495FFA8AFA494481F806A0B2F80690C9F3072981F80790B2F806905FFA89F981F80490D288C2F307224A71083336
:401D000094E7BDE8F88F00BFCD92FF1FD492FF1FC992FF1FFC5F004070600040BA92FF1F08B5064B18780138C0B2FFF753FF20B143681B7900EBC300406908BDCD92FF1F74
:401D400000212DE9F84F0B464E4E0C2707FB01F401313219092933554FF000059370494CD3701381937253705371EFD118B1464B1D70464B1D70464B1A78002A7FD0187867
:401D800001250138C0B2FFF725FFA8464368DFF8F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010202F0FE021A70027AD20007FB0541C36803EB02094B4531D0E2
:401DC00093F802A00AF07F06AE4229D10E89B3F804B0B6B25E4538BFA1F808B01E7893F801B01EF80660B3451AD181F804A0DE780E7093F902A0DE78BAF1000F06F00306A5
:401E000007DA012E0CBF07260D264E7181F8018006E0012E0CBF052609264E7181F801C00833CBE70135092DC3D1C1680A328B1C0A440C200833934209D013F8081C13F8E5
:401E40000A5C01F07F0100FB01418D72F2E7FFF767FF114B0121186000230C2000FB0142D3801289013113449BB203F00102134409299BB2F2D1BDE8F84FFFF767BEBDE898
:401E8000F88F00BFD492FF1FBA92FF1F4293FF1FCD92FF1FCB92FF1FD092FF1F114B1B7903F07F035A1E072A19D80F490C2202FB031291781B0141F0010191700021D17031
:401EC000517841F002015170127912F0800F074A1A4414BF8D2389239370FFF715BC0020704700BF00600040D492FF1FFC5F004030B4194B1A7902F07F02531E072B27D81B
:401F0000164B0C2404FB02339978154D01F0FE0199700021D97029461201505D114400F07F0050555A7802F0FD025A701A795B78120605D5012B01D18C7006E00D2303E096
:401F4000012B0CBF082309238B7030BCFFF7DCBB002030BC704700BF00600040D492FF1FFC5F004010B50D4B0D4C21791878C9B20138C0B2FFF72EFE43681B798B4201D297
:401F8000012909D8074A0848535CDBB24354A3780120DBB2535410BD002010BDCD92FF1F00600040BA92FF1F4293FF1F38B58A4A8A4C13780021DBB221801806517840F175
:401FC0008D800A2900F20581DFE811F05D00030103010301030103010B0003017E0003018200D3787C49012B09D17D4B1A787D4B03EBC2035B685B686360122310E0CB7841
:40200000022B12D18878FFF7E5FD002800F0E180436863606368DA7863689B7843EA02232380BDE83840FFF78FBCCB78032B26D16D4B00228878D5B2854209D3664A91785E
:402040006A4AEE2908BF1346634A917881B106E0187801320028F1D018780344EAE764499278097C914203D16248FFF739FD614B1A78002A00F0AD801A78228018E0BDE844
:40208000384000F025BF13F0030313D0022B40F0A0802380504B0C211B7903F07F02564B01FB02339A78554BD2B21A7000225A706360B6E702222280514A11784F4AC9B21C
:4020C000117053706260ACE7012323804D4BEFE70123238013794C4A1344E9E701390A2977D8DFE801F037764F76067676760A7620009378454ADBB25AE0937803F0FF01B5
:4021000053B9404B1A7891425FD01970404B01201870FFF715FE58E0481EC0B2FFF75AFD0028EED155E0FFF71DFF002851D02A4A384913791279DBB2D2B20A70364A324948
:40214000D25CCB5C9A4240D0314B01221A70FFF753FD3AE003F00303012B2BD009D3022B37D11D4B9B78002B33D1BDE83840FFF7BFBE194B9B78012B2BD1214A137803F0A2
:40218000FD0315E003F00303012B13D008D3022B1FD1114B9B78E3B9BDE83840FFF77EBE0D4B9B78012B14D1154A137843F0020313700AE0084B1A795AB998781B791749C8
:4021C000DBB2CA5C22EA0002CA54BDE83840FFF79BBA002038BD00BF00600040BC92FF1FC892FF1F003A0000643A0000EC390000D73A00006093FF1FD492FF1F7992FF1F74
:40220000CB92FF1FCD92FF1FBA92FF1FB892FF1FCC92FF1FC992FF1F4293FF1FCF92FF1F074B1A78120609D55B78012B06D1054B054A5A6012781A80FFF786BB0020704732
:4022400000600040BC92FF1FC4390000014B1870704700BF78650040014B1878704700BF68650040014B1870704700BF76640040064A0123136002F688321268E0211064A3
:40228000034A1170A2F540721360704780E100E000E400E0014B1870704700BF77640040014B1870704700BF7A64004073B515461E460B4C05230022019200920A460146C7
:4022C0001846237000F064F932462946207800F01FF90221207800F009F9207802B070BDD080FF1F064A0423136002F688321268E0219064034A1170A2F2023213607047BC
:4023000080E100E002E400E0014B04221A60704700E100E0014B04221A60704780E100E0014B1870704700BF74650040704738B505460078012428B100F062FD285D01347D
:40234000E4B2F8E738BD08B50D2000F059FDBDE808400A2000F054BDF7B516461F460B4C00230325019300930A4601462846257000F00EF93A463146207800F0C9F8022181
:40238000207800F0B3F8207803B0F0BDE080FF1FF7B516461F460B4C00230225019300930A4601462846257000F0F2F83A463146207800F0ADF82946207800F097F8207889
:4023C00003B0F0BDE180FF1FF7B516461F460B4C00230125019300930A4601462846257000F0D6F83A463146207800F091F80221207800F07BF8207803B0F0BDE280FF1FD4
:4024000073B515461E460B4C0023019300930A4601461846237000F0BBF832462946207800F076F80221207800F060F8207802B070BD00BFE380FF1F024B1878C0F3801032
:40244000704700BF8F450040074A7F23802113705170064A013BDBB202F80839002BF9D1034A1370704700BFE480FF1FF87B00400078004017280FD8084B0001C25C11B124
:4024800042F0200201E002F0DF02C254C25C42F00102C25400207047012070471070004017280BD8064B0001C25C02F0FE02C254C25C02F0DF02C2540020704701207047D6
:4024C0001070004017280DD8074900010B4603441A7942F004021A71435C43F00103435400207047012070471070004017280BD8064A0001835C490003F0F10301F00E015B
:402500001943815400207047012070471070004041F6FF73994208BF4FF400519A4208BF4FF4005217289FBFC00000F1804000F5EC4081809ABFC280002001207047000021
:4025400017289FBF034B00011954002088BF0120704700BF1970004017289FBF054B00011A5C01F007019DBF1143195400200120704700BF1470004017289FBF034B00015A
:40258000185C00F0070088BFFF20704714700040172810B51AD8C00001F07F0100F1804441EAC21204F5EC44D2B222709DF8082003F00F0343EA0213DBB263709DF80C3080
:4025C000002003F00F03A370E07010BD012010BD10B500F075FC0A4A5378182B0AD91478013B5370E30003F1804303F5F0431B78137000E0FF2400F067FC204610BD00BF7F
:40260000E480FF1F030610B5044611D400F058FC084AE300117803F1804303F5F04319705378147001335370BDE8104000F04CBC10BD00BFE480FF1F30B504060CD411F499
:40264000704509D1C40004F1804404F5F0442180A270E370284630BD012030BD03065FBFC00000F1804000F5F04081805ABFC280002001207047000038B50446084DB4F56C
:40268000004F05D9286800F013FCA4F50044F6E7034B58686043BDE8384000F009BC00BFEC80FF1F024B1B7A584300F001BC00BFEC80FF1F0E4B00F003001A78490102F04B
:4026C000FC02104318701A7801F0600142F080021A701A7802F07F021A701A7802F09F020A431A701A7842F010021A70704700BF83430040014B01221A70704784430040C1
:40270000044B00F00F021B6853F8220043F82210704700BF08ED00E0054A00F01F00126800F1100352F8230042F82310704700BF08ED00E000F01F0000F16040490100F5C1
:402740006440C9B2017070470F4B10B50F4900240F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040313600A4B4FF47A72DB68B3FBF2F3084A1360084B4FF40042E1
:402780001C60C3F8E82010BD7C92FF1FF527000010E000E0EC80FF1F14E000E018E000E0024A136843F002031360704710E000E008B5FFF7F5FF034A136843F001031360AD
:4027C00008BD00BF10E000E010B5054CA3691BB9FFF7BAFF0123A361BDE81040FFF7E8BF7C92FF1F024B1868C0F30040704700BF10E000E038B5FFF7F5FF012808D1054DC9
:40280000002455F8243003B198470134052CF8D138BD00BF8092FF1F024B03EB80035868596070477C92FF1F134B144A1B78DBB20360127843EA0223114A0360127843EA83
:402840000243104A0360127843EA026303600E4B0E4A1B78DBB24360127843EA02230C4A4360127843EA02430A4A4360127843EA02634360704700BF0301004904010049CB
:40288000EC460040020100490101004900010049050100490601004910B500F011FB204A044613780A2043F002031370137C43F00203137412F80A3C43F0010302F80A3C2C
:4028C000937943F00103937102F5AB52137843F003031370134B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222183BFA
:402900001A70094A137843F008031370FFF7CAFE064B10222046BDE810401A6000F0D4BAAB4300400E5900402F5B004080E200E008B500F0C5FA0F4A137803F0FE03137032
:40294000A2F5AA521D3A137803F0FD031370137C03F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE039371BDE8084000F0ABBA00BF08590040044A137803F03F032B
:4029800043EA8010C0B21070704700BF08590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A80A9
:4029C00042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470A590040B03900004A93FF1F4C93FF1F5093FF1F08B5102000F0A6F907210420FFF79AFE2F
:402A000007490420FFF788FE064A0C20137843F006031370FFF7BCFF034B00221A8008BDE92A0000095900404893FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040B0
:402A4000FFF72ABF9892FF1F044B1A7802F0FB021A701A7842F001021A7070470859004010B5084B1C7814F0010403D10028F9D0002404E02046FFF715FE024B1B782046F5
:402A800010BD00BF09590040034A044B1B881088181A00B2704700BF5093FF1FA25B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107386
:402AC00000B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270474A93FF1F4C93FF1F4893FF1F7047000010B500F0E7F9214A044613780A2043F001031370B1
:402B0000137C43F00103137412F80A3C43F0020302F80A3C937943F00203937102F5AA521832137843F003031370144B18221A7013F8012C42F0400203F8012C13F8012C85
:402B400002F0FC0203F8012CCE2203F8062CA3F597530222123B1A70094A137843F008031370FFF79FFD074B08222046BDE810401A6000F0A9B900BFAB4300400659004043
:402B8000275B004080E200E008B500F099F90F4A137803F0FE031370A2F5AA52153A137803F0FE031370137C03F0FE03137412F80A3C03F0FD0302F80A3C937903F0FD0307
:402BC0009371BDE8084000F07FB900BF00590040044A137803F03F0343EA8010C0B21070704700BF00590040082804D00A280CBF8223C22300E0422308380E4AC0B2042892
:402C0000137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A80704702590040BA3900005693FF1F90
:402C40005C93FF1F5493FF1F08B5102000F084F807210320FFF76EFD07490320FFF75CFD064A0C20137843F006031370FFF7BCFF034B00221A8008BD412D0000015900402D
:402C80005893FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF728BF9992FF1F044B1A7802F0FB021A701A7842F001021A7070470059004010B5084B1C7814F038
:402CC000010403D10028F9D0002404E02046FFF7E9FC024B1B78204610BD00BF01590040034A044B1B881088181A00B2704700BF5493FF1FA05B00400E4A13881BB223B150
:402D000011880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475693FF1F5C93FF1F5893FF1F53
:402D400070470000034A00F0F800137803431370704700BF02410040034A00F0F800137803431370704700BF06410040014B1870704700BF7F640040014B1870704700BFDD
:402D80007B65004073B515461E460B4C04230022019200920A46014618462370FFF7F8FB324629462078FFF7B3FB02212078FFF79DFB207802B070BDFC80FF1F074A0223E3
:402DC000136002F688321268E0215064044A11706FF440710A441360704700BF80E100E001E400E0014B1870704700BF7A650040014B1870704700BF79650040000000008E
:402E0000FEB5494652465B460EB40746244909688A46244A12682448022100F071F8030020480068C018204900F06AF8143883460121C9430C460125002600F041F8814653
:402E400051460B7823400B705846013000F030F83800F04028400B78234003430B70584600F026F80136072EF2D9002001300138013001200B78234003430B7058460430C4
:402E800000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF30080D480FF1FF880FF1F00C20100000000000230800803D000BF01380046FCD170473A
:402EC000EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D113790C2103F07F02044B01FB02339B7A00E013790020704700600040D492FF1FF3
:402F0000002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB11217047704700BF014B1868704700BF6081FF1F0E4B70B51E460E4C0025E41ABD
:402F4000A410A54204D056F8253098470135F8E700F0DEFD084B094C1E46E41AA4100025A54204D056F8253098470135F8E770BD1C3B00001C3B00001C3B0000243B0000BA
:402F800003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B604FF0FF301DE04FF40273ADF80C300CBF234604F1FF33029305934FF6FF73009136
:402FC0000491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B00221A701BB030BD000007B5009313460A46014603480068FFF7CBFF03B05DF804FB50
:403000006081FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD02568096902236F1A656905EB450595FBF3F57B1C43449D4238BF1D4653050FD5294638
:4030400000F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A4600F098FB064670B92169504600F0E8FA0C23CAF80030A3894FF0FF3043F0400332
:40308000A381BDE8F08726613E44266046466561ED1BA560464528BF464649463246206800F0B3FAA36800209B1BA36023681E442660BDE8F08700002DE9F04F9DB003930D
:4030C0008B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F80030CDE040236B610023099320238DF82930DFF89CB130238DF82A3037463C465C
:4031000014F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF771FF013000F0A780099B4B4409933B78002B00F0A08000234FF0FF320493079372
:40314000059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46801A06FA00F018430490EFE7D90644BF20228DF853201A0744BF2B228DF85320CC
:4031800022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F00200079204900BE027463B780134303B092B03D800FB02320121F5E701B107923B78AF
:4031C0002E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E0002319460593781C0A2407463A780130303A092A03D804FB01210123F5E703B14A
:40320000059103223978224800F0E6F940B14023CBEB000003FA00F0049B013718430490397806221B487E1C8DF8281000F0D4F988B1194B33B9039B073323F00703083323
:40324000039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A9404600F093F8B0F1FF3F824603D0099B5344099342E7AB895B0601D4099801E059
:403280004FF0FF301DB0BDE8F08F00BFEB3A0000F13A0000F53A000000000000053000002DE9F04791461F460A698B6806469342B8BF1346C9F8003091F843200C46DDF858
:4032C000208012B10133C9F800302368990642BFD9F800300233C9F80030256815F0060510D104F1190A07E00123524639463046C04701301AD00135E368D9F800209B1A7D
:403300009D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F845102244023382F8431003E04FF0FF30BDE8F08704F1430239463046C047013017
:40334000F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500259342C4BF9B1AED184FF000091A344D4509D00123224639463046C0470130D5D0CC
:4033800009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9B01F1430E00F0AE8011D8632A22D009D8002A00F0BB80582A40F0CA8081F84520FC
:4033C000834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B8800A6842F020020A603EE0752A24D0782A3AD0ADE01A6801F14205111D1960136803
:4034000084F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F10401196002D0B2F9003000E01368002B3CDA2D225B4284F8432037E021681A6811F0800F93
:4034400002D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A14BF0A2208221BE078225A4984F845202268186812F0800F00F104051D6003D15E
:40348000550601D5038800E00368D00744BF42F0200222601BB9226822F0200222601022002084F8430001E049490A226568002DA56008DB206820F0040020602BB9002D82
:4034C0007DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D1082A0BD12368DA0708D5236962689A42DEBF302305F8013C05F1FF35C5EB0E03A1
:4035000023612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F104001860136801D0198000E0196000232361754616E01A68111D196015680021626806
:40354000284600F049F808B1401B6060636804E004F1420584F8422001232361002384F84330CDF800803B4603AA21463046FFF797FE013002D14FF0FF3026E023692A4606
:4035800039463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046C0470130E7D00135E368039A9B1A9D42F2DBE068039B9842B8BF184605E00B78D0
:4035C00004F1420584F842308AE705B0BDE8F0839F390000FC3A000010B5C9B202449042034605D01C7801308C42F8D1184610BD002010BD10B5431E0A44914204D011F847
:40360000014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21AD34204D013F8014D01F8014DF8E710BD994204D011F8014B02F8014FF8E710BD48
:4036400038B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B96360146030E0A3420DD92268A018834201BF18685B681218226063600C6023E0C7
:40368000A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D110685268014419605A600DE002D90C232B6009E021686018824201BF10685268A1
:4036C0000918216062605C602846BDE8384000F098B838BDA092FF1F70B5CD1C25F0030508350C2D38BF0C25002D064601DBA94202D90C23336046E000F082F8234B1C6872
:403700001A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B681360636018BF0C4615E00C464968E9E7174C23681BB9304600F052F8206029468B
:40374000304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B00231D20F00700C31A0ED05A42E25070BD211A304600F034F80130EBD10C233360DD
:40378000304600F03EF8002070BD00BFA092FF1F9C92FF1FF8B5074615460E4621B91146BDE8F840FFF798BF1AB9FFF749FF2846F8BD00F027F885420ED929463846FFF7ED
:4037C0008BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD38B5064C0023054608462360FDF7DCFB431C02D1236803B12B6038BD8493FF1F50
:403800007047704751F8040C0028BEBF091851F8043CC0180438704700000000050209020B020D020F021102130215027265706C792030782530327800686F6D696E6700B4
:40384000626567696E6E696E67207365656B2066726F6D20256420746F2025640066696E6973686564207365656B00796573006E6F00647269766520303A20257320647214
:4038800069766520313A2025730057616974696E6720666F72205553422E2E2E0055534220726561647900636F6D6D616E6420307825303278006661696C2025642B2564F5
:4038C0002B2564203D3D2025642C206E6F74202564007061737365643D256400756E64657272756E206166746572202564207061636B65747300636F756E743D2564206974
:403900003D256420643D256400636D645F777269746500703D25642063723D25642063773D256420663D256420773D256420696E6465783D256420756E64657272756E3D91
:40394000256400756E64657272756E2100737563636573730073746172742065726173696E670073746F702065726173696E670069646C65000051004010004051004030EB
:403980000000000140001000140140000800400140000A004C01400002005001402000303132333435363738394142434445460000010000000400000010000100000004D2
:4039C0000000001028000000000104000100000000000000000157494E5553420000303030303100000000000000000012034D0053004600540031003000300001000000DE
:403A000001000000083A000001000000D73A0000000000000000000001000000203A000001000000A93A000004000000423A0000000000000000000000000000403A0000F8
:403A4000FF00000001024000FF00000082024000FF00000003034000FF00000084034000FF00020304030904160346006C007500780045006E00670069006E0065002A03E3
:403A800043006F0077006C00610072006B00200054006500630068006E006F006C006F0067006900650073000009022E0001010080320904000004FF0000010705010240E2
:403AC0000000070582024000000705030340000A0705840340000A12010002FF0001080912006E0100020180014300232D302B2000686C4C0065666745464700303132339A
:403B000034353637383961626364656600000000F8B500BFF8BC08BC9E4670475900000025100000F8B500BFF8BC08BC9E46704735000000483B0000C880FF1FA00000009F
:403B40002012000000000000000000008893FF1FFF000000675000400C00000007000000FFFFFFFF7F8000003F0000000000007D00FA0000400000000090D003FF0000007E
:403B80000000000000000000000000000000000000000000000000000000000000000000E93A000000000000000000000000000000000000000000000000000000000000E2
:403BC00000000000000000000000000000000000000000000000000000000000000000000081FF1F0000000000000000000000000000000000000000000000000000000026
:40068000D5D151461648FFF7FEFDBAF1000F00F00E81144B1B8807A8ADF81C3074E200BF19010000F900000091000000C50000008881FF1F77380000733800007A38000022
:4006C00092380000A5380000E181FF1FF281FF1FAF3800002438000026380000BE380000DA3800002838000094F86C0001F0D6FD606EFFF755FE02F0E7FBB24BDFF8E0825C
:400700001A78002702F0FB021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F0D5FB0220FFF7EEFC012141F6FF734FF48042084602F028FB84F8AA00FD
:4007400001F04AFF08F807000137402FF8D1DFF894A200270AF199091FFA89F80137402F14BF3A4600221AF8010F22440623127E402101F065FF424646F24A519AF800005D
:4007800001F070FF08F14008402F1FFA88F8E5D196F86D3033B100237372637A002BFCD00023737200234FF0FF32236062602372236894F8AA002344197E01F0C5FE94F87B
:4007C000AA0001F083FE012194F8AA0001F056FE2368002BFCD0002398467360D6F80CA0012701F08BFFE368B4F86E20CAEB030393420DD367B1042195F8AA0001F0B0FEC5
:4008000094F8AA0001F0BCFE0028F9D107463072237AFBB96A682B689A4202D1002FE0D118E00220FFF770FC6968402209EB8111022000F0EDFD6A68634B01321340002B4F
:40084000BEBF03F1FF3363F03F03013308F101086360C6E70220277AFFF756FC00221146022000F0D5FDFFB20220FFF74DFCFFF7BDFC37B15548FFF706FD0220FFF72AFDB4
:4008800006E0534B08A81B88ADF82030FFF710FD627A4146237A4F48FFF7F5FC01E24E48FFF7F1FCD4F86E7017F03F0701D00320F5E1012001F0F8FC95F86C0001F0EEFCF4
:4008C00002F002FB454BDFF818811A7842F004021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F0F1FA686EFFF756FD01214FF4804341F6FF7208462E
:4009000001F0D8FC85F8AA0001F066FE08F807000137402FF8D1DFF8CC90002709F199031FFA83F804930137402F14BF3A46002219F8010F22440523127E402101F080FEB9
:40094000414646F24C4299F8000001F08BFE08F14008402F1FFA88F8E5D100274FF0FF33376098467360BB463B46D6F86E9037725FEA99190CBF4FF0010A4FF0000A216867
:40098000114A01310A40002ABCBF02F1FF3262F03F026068B8BF013282426BD02BB1227A002A76D16A7A002A73D12068049A059302EB8010BAF1000F16D040223F2102F0C9
:4009C000E3FA1CE09A6500403F000080E43800002A380000FE380000113900009C640040A081FF1F9F81FF1F014601370120FFF7DEFBC7EB0903D3F1000A4AEB030A21689F
:400A0000AB4A01310A40002ABEBF02F1FF3262F03F02013222606268059B01322AD12A683F2A27D14FF00008C5F8048001F070FC85F808806B6895F8AA002B44197E01F0BC
:400A400083FD95F8AA0001F041FD012195F8AA0001F014FD85F80980637A002BFCD04FF00008012086F8098001F05EFC404601F01BFC00E023B1237A5BB96B7A4BB90123A1
:400A8000626842453FF47BAF0BF1010BD5F8048075E701F043FC012001F006FC002001F043FC042194F8AA0001F05AFD94F8AA0001F066FD0028F9D196F8AA0001F0F4FC07
:400AC000737A327A0293012303920193CDF800A05B463A4649467748FFF7D5FBBAF1000F08D0FFF783FB237A63B17348FFF7CBFB0220D4E0B945F4D070490120FFF757FBF9
:400B00000137F7E76E48FFF7BEFB6E4B38E094F86C0001F0C3FB606EFFF742FC6A48FFF7B2FB00236372637A002BFCD0012001F0FBFB00237372637A002BFCD0002001F0AA
:400B4000F3FB6248FFF79FFB614B19E0002084F85E00FFF725FC5F4B12E094F8683023B195F869200AB985F86C2094F869201AB113B9012385F86C305748FFF7CDFB574B39
:400B80001B88ADF8203008A8FFF792FB89E0FFF7B1FB02F07BF8002002F01EF82A2701F049FF002001F0ECFE3A46002108A802F0EBF917238DF820308DF8217001F09EFD75
:400BC000002001F047FB002002F0DAF8C82001F057FD0DF12200FFF721FB0DF13600FFF73EFB01F08BFD012002F0CAF8322001F047FD0DF12600FFF711FB0DF13A00FFF7D3
:400C00002EFB012001F026FB4FF4967001F038FD01F074FD0DF12E00FFF700FB0DF14200FFF71DFB002001F015FB4FF4967001F027FD01F063FD022002F0A2F8322001F05B
:400C40001FFD0DEB0700FFF7E9FA0DF13E00FFF706FB012001F0FEFA4FF4967001F010FD01F04CFD0DF13200FFF7D8FA0DF14600FFF7F5FA002001F0EDFA4FF4967001F06F
:400C8000FFFC01F03BFD002002F07AF8002384F85E3001F07DFF01F04FFE74E70120FFF719FB032000F07AFC0D48FFF7ECFA0DE43F0000801B3900004B3900003892FF1F04
:400CC000553900002C3800005D3900006B3900002E38000030380000F281FF1F32380000783900002DE9F04172B6884B61221A70A3F5F06301221A801924854A9C7092E8C5
:400D000003008033062283F8002283E80300522203F580731A707F4B7F4A1B787F4EDBB2137040F618027E4B00251A8041F2512223F8022C33784FF4F07003F0010343EAA5
:400D4000450502F0B9F8013C05F003052ED0032DF0D1744B4FF480721A8007221A70724A002548211570917002221D705D7103F8032C0422DA716D4A6D4C13786D4E43F049
:400D80000103137012F8013C062743F0030302F8013C2378012243F0800323705B4B1A70654A137843F02003137000E0FEE707FB056300219A881868013502F0E5F8072D53
:400DC000F5D15E485E4E002550F8041F05F1105303F1440221F0FF074533C9B20B4452005B0002329A4206D012F802EC12F801CC0EF807C0F5E7B0420D44E5D1514A00239D
:400E000013609360136193614F4B504F1A68504BDFF888811A604F4B1A684F4B1A604F4A137843F002031370137C43F0020313742378A2F5863243F040032370413A1378DE
:400E400043F010031370464A464B07CA03C31A80454A2833106843F8250C127903F8212C424A07CA03C31A80414AE83B07CA03C31A80404A083307CA03C31A803E4A3F4B12
:400E8000A2F5616203CBC2F8100EC2F8141E1378042043F008031370394B02F5AA521B783D78DBB298F80060EDB203F007010C321B091170F6B2537045F003033B7046F096
:400EC000030388F800302F4B48221A702E4A402313702E49937013729372082382F81F3220220A7048710A72294A0A20137001F0DDFB284B88F8006044223D70264D1A7039
:400F000094E80F0007C52B80BDE8F08100480040B00900480F010049A146004025420040224200400440004006400040A2430040A04300407D390000E8460040FCFFFF478E
:400F40008400004800760040B8090048F846004020760040BC090048287600400350014070090048C05100407C09004884090048900900489C09004832510040A8090048D5
:400F8000CF0100491D51004001590040235B0040585B004076580040B0430040F946004008B501F0C5FF03680C2B00D1FEE7FEE7084908B50B68084A1844821A802A01DC5E
:400FC000086005E001F0B4FF0C2303604FF0FF33184608BDCC80FF1F8893FF1F80B51148114B0025C0B1A3F1100192C922460439161BB74204D051F8046F42F8046BF7E7D1
:40100000114653F8046C8C1AA64202D041F8045BF9E701381033E5E701F090FFFFF706FAFEE700BF010000004C3B0000124A134B10B51A60124A134C1368134843F40073A8
:4010400013600023032B98BF54F823204FEA830188BF0E4A0133302B4250F3D10C4B1A780C4B1A700C4B084A1A60FFF73BFEBDE8104001F0EDB900BF0004FA050CED00E042
:4010800014ED00E0000000000080FF1FA10F0000BC760040C080FF1F08ED00E0F8B501F013FF4B4A01271378022643F001031370137C484C43F001031374474B02F5E352E3
:4010C0001F700B3203F8946C1378054603F07F031370002001F0EAFA2378404A03F0F90323701378384603F0DF03137023783B43237001F0DBFA282001F0D8FA384B3046E8
:401100001A7802F07F021A701A7802F0BF021A7023783343237001F0C9FA2378314A43F0040323700023137053702F4AFF2199540133092BFBD1284601F0CAFE072117206D
:4011400001F0FCFA2949172001F0EAFA0721182001F0F4FA2649182001F0E2FA0721152001F0ECFA2349152001F0DAFA0721052001F0E4FA2049052001F0D2FA0721062008
:4011800001F0DCFA1D49062001F0CAFA0721084601F0D4FA1A49072001F0C2FA0721082001F0CCFA1749082001F0BAFA0021162001F0C4FA1449162001F0B2FA07210C20FD
:4011C00001F0BCFABDE8F84010490C2001F0A8BAA5430040944300409D60004012600040F851004084600040AD92FF1F6B1A0000A5180000691A00009D190000C9190000FE
:40120000F9190000311A0000711A0000E51A0000214B224A10B5187000231370204A40201370204A0F2413701F4A13701F4A13701F4A13701F4A13701F4B4FF400021A60B6
:401240004FF080721A604FF400121A6020221A601860802018604FF480701860174804704FF480001860164B1A70933B19B91A7802F0FE0202E01A7842F001021A70114B51
:4012800003221A70802203F8202C012001F014FE0D4B04221A7010BDC892FF1FCE92FF1FCC92FF1FCD92FF1FC992FF1FB892FF1FCB92FF1F4093FF1F00E100E09E60004062
:4012C0009C600040286000401260004070B5074C054623780E461BB9FFF7E0FE0123237031462846BDE87040FFF792BF7892FF1F0A4A002313700A4A13700A4A13700A4A82
:4013000013700A4A13700A4A13700A4A13700A4B03221A70802203F8202C7047CE92FF1FCC92FF1FCD92FF1FC992FF1FB892FF1FCB92FF1F4093FF1F28600040014B187899
:40134000704700BFCD92FF1F044B1A7802F0FF001AB118780022C0B21A707047CC92FF1F024A0C2303FB002040787047D492FF1F431E072B0CD8074A064B00010344805C33
:401380005B7800F00F0043EA0020023880B2704700207047FC5F00401A4A38B50C2303FB00231B79090C13F0800F00F1FF35044619BF8AB24FF480438BB24FF48042032DA2
:4013C00018D8DFE805F002070C110021084601F01BF80DE00021084600F0FAFF08E00021084600F0D9FF03E00021084600F0B8FF054B1855EDB2072D03D801F0EDF8034BBC
:40140000185538BDD492FF1FA492FF1FAD92FF1F431E072B2DE9F0470446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB2C3F500739D424FF00C0303FB007388BF8B
:40144000D588DB7884BFC5F50075ADB2254A43EA15230601B354B244EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FB047200215170B9F1000F28D03DB31B4FEC
:40148000385D01F011F811232946FE2218F8040001F0D6F806F5C04278321FFA89F118F8040001F0DFF8124D18F80410385D01F04BF80121385D00F0E1FF735D43F0020316
:4014C0007355735D03F0FD037355BDE8F08703FB04746379DBB28AF80230BDE8F08700BFD492FF1FFC5F0040AD92FF1FA492FF1F706000402DE9F047044615468846002946
:4015000040D0431E072B3FD8FFF732FFA84203D22046FFF72DFF05461D4E335DFF2B03D141462046FFF738FFDFF868A027011AF8040000F0B9FF1223FE222946305D01F01E
:401540007FF807F5C0411FFA88F27831305D01F089F8DFF84490315D1AF8040000F0F4FF01211AF8040000F089FF17F8093043F0020307F8093017F8093003F0FD0307F8E8
:40158000093002E00D4600E000252846BDE8F087AD92FF1FA492FF1F70600040431E072B0AD8064A0C2303FB002300225A705A79034BD2B200011A54704700BFD492FF1F5E
:4015C000FE5F0040431E072B9FBF024B000108221A547047FE5F004030B51A4A1A491B4D0878138803449BB21380194A00231488D8B2A4B27CB1082B0CD050680078C0B2ED
:40160000E85450680133013050601088013880B21080ECE718460B780E4C082B0E4A00D040B10E4D2B7883F080032B700F232370022301E0022323701370094B18700870CB
:4016400030BD00BF4493FF1F4093FF1F00600040BC92FF1FB992FF1FCE92FF1FCA92FF1F4193FF1F074B02221A70074B80221A70064B0F221A70064A00231370054A012089
:4016800013707047CE92FF1FCA92FF1FB992FF1F4093FF1F4193FF1F30B5164B16491B780A8803F00F03023BDBB21A4492B20A80124C134A0020118889B279B173B155682D
:4016C000215C013BC9B229705168DBB20131516011880130013989B21180ECE7094A1370094A137883F080031370084B0B221A7030BD00BF296000404493FF1F0060004010
:40170000BC92FF1F4193FF1FCA92FF1FB992FF1F064A06231370064A01201370054B80221A70054B00221A70704700BFCE92FF1FB992FF1FCA92FF1F4193FF1F054B9A68E5
:401740003AB19A68044910709A680988518000229A607047BC92FF1F4493FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A137008BD0220FFF7E1FF0D4B1B7803F0CF
:401780006003202B05D0402B06D043B900F012FC04E001F0A1FB01E000F046FD10B9034B03221A7008BD00BF28600040B992FF1F0060004008B5084A084B0120197813881C
:4017C0000B449BB21380064B00221A70FFF7B6FF044B03221A7008BD4493FF1F4093FF1FCE92FF1FB992FF1F08B50C4B1B78DBB2042B07D0062B09D0022B0DD1BDE8084046
:40180000FFF7D8BFBDE80840FFF746BF0320FFF795FF034B03221A7008BD00BFCE92FF1FB992FF1F08B5054B002201201A70FFF785FF034B03221A7008BD00BFCE92FF1FCB
:40184000B992FF1F08B50A4B1A7832B11A78094942F080020A7000221A70074B002201201A70FFF76BFF054B03221A7008BD00BFB892FF1F08600040CE92FF1FB992FF1FC1
:40188000074B1B78DBB2042B05D0062B05D0022B05D1FFF7A1BEFFF7C5BFFFF7D3BF7047CE92FF1F38B51D4C2378DBB2DD0634D518060AD503F00F03012B2ED1FFF74EFF43
:4018C000174B1B78190609D538BD5A0602D5FFF7D7FF03E09D0620D5FFF786FF23781B061BD4104B1A78104B1B7813430F4A13701278934211D10A4A0849154613782078EC
:40190000DBB2000605D41378DBB20B700B7803F00F0328788342F1D138BD38BD28600040B992FF1FCA92FF1F4193FF1F29600040054A00231380054A916819B191680B701E
:4019400092685380704700BF4493FF1FBC92FF1F0E4808B503889BB213B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FF094AD1799379028843EA012392B293422A
:4019800038BF0380FFF728FE012008BDBC92FF1FCE92FF1FCA92FF1F00600040084B01221A700F3B9B7C074B1A7B02F00302012A1EBFDA7B82F08002DA7301225A73704723
:4019C0000B600040D492FF1F094B02221A700F3B93F82230074B1A7E02F00302012A1EBFDA7E82F08002DA7601225A76704700BF0B600040D492FF1F0B4B04221A700F3B22
:401A000093F83230094B93F8242002F00302012A1EBF93F8272082F0800283F82720012283F82520704700BF0B600040D492FF1F0B4B08221A700F3B93F84230094B93F857
:401A4000302002F00302012A1EBF93F8332082F0800283F83320012283F83120704700BF0B600040D492FF1F7047FFF741BC0000F0B5184B184E19780C27C9B201234FF029
:401A8000000C31B3CA0720D5144A4FEA031E7244947850782040C5070DD507FB03652C79240608D5147804F0FE0414706D790C4CEDB204F80E50840706D507FB0364257960
:401AC0002D0658BF84F801C090700133DBB24908D7E7F0BD9F600040D492FF1F70600040FE5F004000F0ACBC70B50446184B88B003AA03F11006154618685968083303C5BA
:401B0000B3422A46F7D11B782B70FCB12223237001AD03232846637000F08AFE002220461146AB5C08AC04EB131414F8144C03F00F03847008AC234413F8143C0132082A48
:401B4000C1700371417100F10400EAD108B070BDA73900002DE9F0431C4D01222E460C201F274FF0800E4FF0080C194B00FB02581401234418705F70164998F80590214449
:401B8000B9F1000F07D098F8044024064CBF887081F802C001E081F802E000FB0261CC880132E4B29C71CC88092AC4F30724DC71CC88E4B21C71C988C1F307215971D4D1CC
:401BC000054BFF221A70BDE8F08300BFD492FF1F70600040FC5F00400A600040064B074A1B7802EBC30253681A7C824286BF03EBC003586900207047C892FF1F083A0000F6
:401C00002DE9F84F424B1A78002A7ED01878414D0138C0B2FFF7E2FFA8463F4AC3681478007ADFF800C1E4B203EBC0000C2600274FF0010E834268D01A78A24263D11CF82A
:401C40000420597891425ED19A7893F8039002F07F0206FB02FA05EB0A01CF7093F802B009F0030981F804B093F803B005F80AB0B3F804A0A1F808A093F902A0BAF1000FB7
:401C80000BDAB9F1010F0CBF4FF007094FF00D0981F8059081F801E009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEA02191A4906FB0282494481F802E0B2F807
:401CC00008A0CAF3072A81F800A0B2F808A05FFA8AFA81F801A0B2F806A011495FFA8AFA494481F806A0B2F80690C9F3072981F80790B2F806905FFA89F981F80490D288FB
:401D0000C2F307224A71083394E7BDE8F88F00BFCD92FF1FD492FF1FC992FF1FFC5F004070600040BA92FF1F08B5064B18780138C0B2FFF753FF20B143681B7900EBC3008B
:401D4000406908BDCD92FF1F00212DE9F84F0B464E4E0C2707FB01F401313219092933554FF000059370494CD3701381937253705371EFD118B1464B1D70464B1D70464B17
:401D80001A78002A7FD0187801250138C0B2FFF725FFA8464368DFF8F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010202F0FE021A70027AD20007FB0541C368D1
:401DC00003EB02094B4531D093F802A00AF07F06AE4229D10E89B3F804B0B6B25E4538BFA1F808B01E7893F801B01EF80660B3451AD181F804A0DE780E7093F902A0DE78D4
:401E0000BAF1000F06F0030607DA012E0CBF07260D264E7181F8018006E0012E0CBF052609264E7181F801C00833CBE70135092DC3D1C1680A328B1C0A440C20083393423F
:401E400009D013F8081C13F80A5C01F07F0100FB01418D72F2E7FFF767FF114B0121186000230C2000FB0142D3801289013113449BB203F00102134409299BB2F2D1BDE88C
:401E8000F84FFFF767BEBDE8F88F00BFD492FF1FBA92FF1F4293FF1FCD92FF1FCB92FF1FD092FF1F114B1B7903F07F035A1E072A19D80F490C2202FB031291781B0141F08F
:401EC000010191700021D170517841F002015170127912F0800F074A1A4414BF8D2389239370FFF715BC0020704700BF00600040D492FF1FFC5F004030B4194B1A7902F0D9
:401F00007F02531E072B27D8164B0C2404FB02339978154D01F0FE0199700021D97029461201505D114400F07F0050555A7802F0FD025A701A795B78120605D5012B01D168
:401F40008C7006E00D2303E0012B0CBF082309238B7030BCFFF7DCBB002030BC704700BF00600040D492FF1FFC5F004010B50D4B0D4C21791878C9B20138C0B2FFF72EFE81
:401F800043681B798B4201D2012909D8074A0848535CDBB24354A3780120DBB2535410BD002010BDCD92FF1F00600040BA92FF1F4293FF1F38B58A4A8A4C13780021DBB24F
:401FC00021801806517840F18D800A2900F20581DFE811F05D00030103010301030103010B0003017E0003018200D3787C49012B09D17D4B1A787D4B03EBC2035B685B68B3
:402000006360122310E0CB78022B12D18878FFF7E5FD002800F0E180436863606368DA7863689B7843EA02232380BDE83840FFF78FBCCB78032B26D16D4B00228878D5B28F
:40204000854209D3664A91786A4AEE2908BF1346634A917881B106E0187801320028F1D018780344EAE764499278097C914203D16248FFF739FD614B1A78002A00F0AD80B9
:402080001A78228018E0BDE8384000F025BF13F0030313D0022B40F0A0802380504B0C211B7903F07F02564B01FB02339A78554BD2B21A7000225A706360B6E70222228083
:4020C000514A11784F4AC9B2117053706260ACE7012323804D4BEFE70123238013794C4A1344E9E701390A2977D8DFE801F037764F76067676760A7620009378454ADBB2B5
:402100005AE0937803F0FF0153B9404B1A7891425FD01970404B01201870FFF715FE58E0481EC0B2FFF75AFD0028EED155E0FFF71DFF002851D02A4A384913791279DBB209
:40214000D2B20A70364A3249D25CCB5C9A4240D0314B01221A70FFF753FD3AE003F00303012B2BD009D3022B37D11D4B9B78002B33D1BDE83840FFF7BFBE194B9B78012B8E
:402180002BD1214A137803F0FD0315E003F00303012B13D008D3022B1FD1114B9B78E3B9BDE83840FFF77EBE0D4B9B78012B14D1154A137843F0020313700AE0084B1A79FA
:4021C0005AB998781B791749DBB2CA5C22EA0002CA54BDE83840FFF79BBA002038BD00BF00600040BC92FF1FC892FF1F083A00006C3A0000F4390000DF3A00006093FF1FEA
:40220000D492FF1F7992FF1FCB92FF1FCD92FF1FBA92FF1FB892FF1FCC92FF1FC992FF1F4293FF1FCF92FF1F074B1A78120609D55B78012B06D1054B054A5A6012781A8093
:40224000FFF786BB0020704700600040BC92FF1FCC390000014B1870704700BF78640040014B1878704700BF6B650040014B1870704700BF79640040064A0123136002F631
:4022800088321268E0211064034A1170A2F540721360704780E100E000E400E0014B1870704700BF79650040014B1870704700BF7D64004073B515461E460B4C05230022D4
:4022C000019200920A4601461846237000F064F932462946207800F01FF90221207800F009F9207802B070BDD080FF1F064A0423136002F688321268E0219064034A1170F2
:40230000A2F202321360704780E100E002E400E0014B04221A60704700E100E0014B04221A60704780E100E0014B1870704700BF7B650040704738B505460078012428B18D
:4023400000F062FD285D0134E4B2F8E738BD08B50D2000F059FDBDE808400A2000F054BDF7B516461F460B4C00230325019300930A4601462846257000F00EF93A463146E4
:40238000207800F0C9F80221207800F0B3F8207803B0F0BDE080FF1FF7B516461F460B4C00230225019300930A4601462846257000F0F2F83A463146207800F0ADF82946CC
:4023C000207800F097F8207803B0F0BDE180FF1FF7B516461F460B4C00230125019300930A4601462846257000F0D6F83A463146207800F091F80221207800F07BF8207805
:4024000003B0F0BDE280FF1F73B515461E460B4C0023019300930A4601461846237000F0BBF832462946207800F076F80221207800F060F8207802B070BD00BFE380FF1F72
:40244000024B1878C0F38010704700BF8F450040074A7F23802113705170064A013BDBB202F80839002BF9D1034A1370704700BFE480FF1FF87B00400078004017280FD838
:40248000084B0001C25C11B142F0200201E002F0DF02C254C25C42F00102C25400207047012070471070004017280BD8064B0001C25C02F0FE02C254C25C02F0DF02C25451
:4024C00000207047012070471070004017280DD8074900010B4603441A7942F004021A71435C43F00103435400207047012070471070004017280BD8064A0001835C490093
:4025000003F0F10301F00E011943815400207047012070471070004041F6FF73994208BF4FF400519A4208BF4FF4005217289FBFC00000F1804000F5EC4081809ABFC28032
:40254000002001207047000017289FBF034B00011954002088BF0120704700BF1970004017289FBF054B00011A5C01F007019DBF1143195400200120704700BF147000404E
:4025800017289FBF034B0001185C00F0070088BFFF20704714700040172810B51AD8C00001F07F0100F1804441EAC21204F5EC44D2B222709DF8082003F00F0343EA0213C5
:4025C000DBB263709DF80C30002003F00F03A370E07010BD012010BD10B500F075FC0A4A5378182B0AD91478013B5370E30003F1804303F5F0431B78137000E0FF2400F0A3
:4026000067FC204610BD00BFE480FF1F030610B5044611D400F058FC084AE300117803F1804303F5F04319705378147001335370BDE8104000F04CBC10BD00BFE480FF1F18
:4026400030B504060CD411F4704509D1C40004F1804404F5F0442180A270E370284630BD012030BD03065FBFC00000F1804000F5F04081805ABFC2800020012070470000CD
:4026800038B50446084DB4F5004F05D9286800F013FCA4F50044F6E7034B58686043BDE8384000F009BC00BFEC80FF1F024B1B7A584300F001BC00BFEC80FF1F0E4B00F0E7
:4026C00003001A78490102F0FC02104318701A7801F0600142F080021A701A7802F07F021A701A7802F09F020A431A701A7842F010021A70704700BF83430040014B012238
:402700001A70704784430040044B00F00F021B6853F8220043F82210704700BF08ED00E0054A00F01F00126800F1100352F8230042F82310704700BF08ED00E000F01F0049
:4027400000F16040490100F56440C9B2017070470F4B10B50F4900240F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040313600A4B4FF47A72DB68B3FBF2F3084A5C
:402780001360084B4FF400421C60C3F8E82010BD7C92FF1FFD27000010E000E0EC80FF1F14E000E018E000E0024A136843F002031360704710E000E008B5FFF7F5FF034A7F
:4027C000136843F00103136008BD00BF10E000E010B5054CA3691BB9FFF7BAFF0123A361BDE81040FFF7E8BF7C92FF1F024B1868C0F30040704700BF10E000E038B5FFF7EC
:40280000F5FF012808D1054D002455F8243003B198470134052CF8D138BD00BF8092FF1F024B03EB80035868596070477C92FF1F134B144A1B78DBB20360127843EA0223B0
:40284000114A0360127843EA0243104A0360127843EA026303600E4B0E4A1B78DBB24360127843EA02230C4A4360127843EA02430A4A4360127843EA02634360704700BFF1
:402880000301004904010049EC460040020100490101004900010049050100490601004910B500F011FB204A044613780A2043F002031370137C43F00203137412F80A3C08
:4028C00043F0010302F80A3C937943F00103937102F5AB52137843F003031370134B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062C7C
:40290000A3F597530222183B1A70094A137843F008031370FFF7CAFE064B10222046BDE810401A6000F0D4BAAB4300400E5900402F5B004080E200E008B500F0C5FA0F4A3B
:40294000137803F0FE031370A2F5AA521D3A137803F0FD031370137C03F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE039371BDE8084000F0ABBA00BF0859004037
:40298000044A137803F03F0343EA8010C0B21070704700BF08590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F9101028
:4029C0000A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470A590040B83900004A93FF1F4C93FF1F5093FF1F08B5102000F0A6F974
:402A000007210420FFF79AFE07490420FFF788FE064A0C20137843F006031370FFF7BCFF034B00221A8008BDF12A0000095900404893FF1F10B5054C23781BB9FFF7DCFF7A
:402A400001232370BDE81040FFF72ABF9892FF1F044B1A7802F0FB021A701A7842F001021A7070470859004010B5084B1C7814F0010403D10028F9D0002404E02046FFF7A2
:402A800015FE024B1B78204610BD00BF09590040034A044B1B881088181A00B2704700BF5093FF1FA25B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B61
:402AC0001B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270474A93FF1F4C93FF1F4893FF1F7047000010B500F0E7F9214A0446137861
:402B00000A2043F001031370137C43F00103137412F80A3C43F0020302F80A3C937943F00203937102F5AA521832137843F003031370144B18221A7013F8012C42F0400201
:402B400003F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222123B1A70094A137843F008031370FFF79FFD074B08222046BDE810401A6000F0A9B900BFB0
:402B8000AB43004006590040275B004080E200E008B500F099F90F4A137803F0FE031370A2F5AA52153A137803F0FE031370137C03F0FE03137412F80A3C03F0FD0302F87F
:402BC0000A3C937903F0FD039371BDE8084000F07FB900BF00590040044A137803F03F0343EA8010C0B21070704700BF00590040082804D00A280CBF8223C22300E0422383
:402C000008380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470259004054
:402C4000C23900005693FF1F5C93FF1F5493FF1F08B5102000F084F807210320FFF76EFD07490320FFF75CFD064A0C20137843F006031370FFF7BCFF034B00221A8008BD33
:402C8000492D0000015900405893FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF728BF9992FF1F044B1A7802F0FB021A701A7842F001021A70704700590040D8
:402CC00010B5084B1C7814F0010403D10028F9D0002404E02046FFF7E9FC024B1B78204610BD00BF01590040034A044B1B881088181A00B2704700BF5493FF1FA05B004034
:402D00000E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475693FF1FD5
:402D40005C93FF1F5893FF1F70470000034A00F0F800137803431370704700BF02410040034A00F0F800137803431370704700BF06410040014B1870704700BF7A65004015
:402D8000014B1870704700BF7E64004073B515461E460B4C04230022019200920A46014618462370FFF7F8FB324629462078FFF7B3FB02212078FFF79DFB207802B070BDA7
:402DC000FC80FF1F074A0223136002F688321268E0215064044A11706FF440710A441360704700BF80E100E001E400E0014B1870704700BF7A640040014B1870704700BF9D
:402E00007B64004000000000FEB5494652465B460EB40746244909688A46244A12682448022100F071F8030020480068C018204900F06AF8143883460121C9430C4601254A
:402E4000002600F041F8814651460B7823400B705846013000F030F83800F04028400B78234003430B70584600F026F80136072EF2D9002001300138013001200B78234041
:402E800003430B705846043000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF30080D480FF1FF880FF1F00C20100000000000230800803D000BFAA
:402EC00001380046FCD17047EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D113790C2103F07F02044B01FB02339B7A00E013790020704714
:402F000000600040D492FF1F002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB11217047704700BF014B1868704700BF6081FF1F0E4B70B57A
:402F40001E460E4C0025E41AA410A54204D056F8253098470135F8E700F0DEFD084B094C1E46E41AA4100025A54204D056F8253098470135F8E770BD243B0000243B00007F
:402F8000243B00002C3B000003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B604FF0FF301DE04FF40273ADF80C300CBF234604F1FF33029350
:402FC00005934FF6FF7300910491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B00221A701BB030BD000007B5009313460A46014603480068FFF741
:40300000CBFF03B05DF804FB6081FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD02568096902236F1A656905EB450595FBF3F57B1C43449D4238BF75
:403040001D4653050FD5294600F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A4600F098FB064670B92169504600F0E8FA0C23CAF80030A38908
:403080004FF0FF3043F04003A381BDE8F08726613E44266046466561ED1BA560464528BF464649463246206800F0B3FAA36800209B1BA36023681E442660BDE8F087000061
:4030C0002DE9F04F9DB003938B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F80030CDE040236B610023099320238DF82930DFF89CB1302302
:403100008DF82A3037463C4614F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF771FF013000F0A780099B4B4409933B78002B00F0A080002335
:403140004FF0FF3204930793059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46801A06FA00F018430490EFE7D90644BF20228DF853201A0773
:4031800044BF2B228DF8532022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F00200079204900BE027463B780134303B092B03D800FB0232012141
:4031C000F5E701B107923B782E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E0002319460593781C0A2407463A780130303A092A03D804FB46
:4032000001210123F5E703B1059103223978224800F0E6F940B14023CBEB000003FA00F0049B013718430490397806221B487E1C8DF8281000F0D4F988B1194B33B9039BDF
:40324000073323F007030833039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A9404600F093F8B0F1FF3F824603D0099B5344099342E7AB897F
:403280005B0601D4099801E04FF0FF301DB0BDE8F08F00BFF33A0000F93A0000FD3A0000000000000D3000002DE9F04791461F460A698B6806469342B8BF1346C9F8003093
:4032C00091F843200C46DDF8208012B10133C9F800302368990642BFD9F800300233C9F80030256815F0060510D104F1190A07E00123524639463046C04701301AD001355B
:40330000E368D9F800209B1A9D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F845102244023382F8431003E04FF0FF30BDE8F08704F1430253
:4033400039463046C0470130F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500259342C4BF9B1AED184FF000091A344D4509D0012322463946F2
:403380003046C0470130D5D009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9B01F1430E00F0AE8011D8632A22D009D8002A00F0BB80582A01
:4033C00040F0CA8081F84520834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B8800A6842F020020A603EE0752A24D0782A3AD0ADE01A6801F114
:403400004205111D1960136884F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F10401196002D0B2F9003000E01368002B3CDA2D225B4284F8432037E0C5
:4034400021681A6811F0800F02D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A14BF0A2208221BE078225A4984F845202268186812F0800F0E
:4034800000F104051D6003D1550601D5038800E00368D00744BF42F0200222601BB9226822F0200222601022002084F8430001E049490A226568002DA56008DB206820F0CC
:4034C000040020602BB9002D7DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D1082A0BD12368DA0708D5236962689A42DEBF302305F8013CF7
:4035000005F1FF35C5EB0E0323612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F104001860136801D0198000E0196000232361754616E01A68111DFC
:403540001960156800216268284600F049F808B1401B6060636804E004F1420584F8422001232361002384F84330CDF800803B4603AA21463046FFF797FE013002D14FF056
:40358000FF3026E023692A4639463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046C0470130E7D00135E368039A9B1A9D42F2DBE068039B9842DC
:4035C000B8BF184605E00B7804F1420584F842308AE705B0BDE8F083A7390000043B000010B5C9B202449042034605D01C7801308C42F8D1184610BD002010BD10B5431EF7
:403600000A44914204D011F8014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21AD34204D013F8014D01F8014DF8E710BD994204D011F8014B40
:4036400002F8014FF8E710BD38B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B96360146030E0A3420DD92268A018834201BF18685B68121885
:40368000226063600C6023E0A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D110685268014419605A600DE002D90C232B6009E021686018A3
:4036C000824201BF106852680918216062605C602846BDE8384000F098B838BDA092FF1F70B5CD1C25F0030508350C2D38BF0C25002D064601DBA94202D90C23336046E018
:4037000000F082F8234B1C681A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B681360636018BF0C4615E00C464968E9E7174C23681BB9304658
:4037400000F052F820602946304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B00231D20F00700C31A0ED05A42E25070BD211A304600F034F863
:403780000130EBD10C233360304600F03EF8002070BD00BFA092FF1F9C92FF1FF8B5074615460E4621B91146BDE8F840FFF798BF1AB9FFF749FF2846F8BD00F027F8854208
:4037C0000ED929463846FFF78BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD38B5064C0023054608462360FDF7DCFB431C02D1236803B13B
:403800002B6038BD8493FF1F7047704751F8040C0028BEBF091851F8043CC0180438704700000000050209020B020D020F021102130215027265706C792030782530327881
:4038400000686F6D696E6700626567696E6E696E67207365656B2066726F6D20256420746F2025640066696E6973686564207365656B00796573006E6F00647269766520AA
:40388000303A20257320647269766520313A2025730057616974696E6720666F72205553422E2E2E0055534220726561647900636F6D6D616E64203078253032780066610F
:4038C000696C2025642B25642B2564203D3D2025642C206E6F74202564007061737365643D256400756E64657272756E206166746572202564207061636B65747300636FE8
:40390000756E743D256420693D256420643D256400636D645F777269746500703D25642063723D25642063773D256420663D256420773D256420696E6465783D2564207526
:403940006E64657272756E3D256400756E64657272756E2100737563636573730073746172742065726173696E670073746F702065726173696E670069646C650000510001
:4039800040100040510040300000000140001000140140000800400140000A004C014000020050014020003031323334353637383941424344454600000100000004000096
:4039C00000100001000000040000001028000000000104000100000000000000000157494E5553420000303030303100000000000000000012034D0053004600540031002A
:403A0000300030000100000001000000103A000001000000DF3A0000000000000000000001000000283A000001000000B13A0000040000004A3A00000000000000000000E9
:403A400000000000483A0000FF00000001024000FF00000082024000FF00000003034000FF00000084034000FF00020304030904160346006C007500780045006E006700CA
:403A800069006E0065002A0343006F0077006C00610072006B00200054006500630068006E006F006C006F0067006900650073000009022E0001010080320904000004FFC9
:403AC00000000107050102400000070582024000000705030340000A0705840340000A12010002FF0001080912006E0100020180014300232D302B2000686C4C00656667E2
:403B0000454647003031323334353637383961626364656600000000F8B500BFF8BC08BC9E467047590000002D100000F8B500BFF8BC08BC9E46704735000000503B0000FD
:403B4000C880FF1FA00000002012000000000000000000008893FF1FFF000000675000400C00000007000000FFFFFFFF7F8000003F0000000000007D00FA000040000000DA
:403B80000090D003FF0000000000000000000000000000000000000000000000000000000000000000000000F13A0000000000000000000000000000000000000000000078
:403BC000000000000000000000000000000000000000000000000000000000000000000000000000000000000081FF1F000000000000000000000000000000000000000026
:403C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084
:403C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044
:403C80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004
@@ -4098,55 +4098,55 @@
:40FF80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041
:40FFC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
:0200000480007A
:400000000145004008520040015B0040016400400103014008050140440601404807014051080140520901404B0A01404E0B0140650C0140450D0140580E0140350F0140DA
:40004000471401405B150140221701403F18014056190140481A0140561B01400A4001400E4101400E420140084301400244014009450140064601400B4701400F48014016
:4000800011490140174C0140094D014007500140045101407E0208020901100211821902601661157C4027212D0AE21029022B4088408CA88E808F05E628EA08003F032039
:4000C0000538062007010A380B200E070F3811041220131016201707183819021A011B381C041D041E101F082220232024042608272028022A382B202D3F2E20353F363F03
:40010000580459045B045C995F01822086208A078C3F910292209438950196019A209C049E10A220A404A608A802AA38AE38B102B301B43FBF05D608D804D904DB04DC0965
:40014000DD90DF01012A030205490604082009080A010B800C100D040E010F801210136114021690172019301D402040220427042C042D082F4232083301358036023724A4
:400180003D043E203F815A605B08600862486A806C046D8085108728880689018B408C0891089301960898A099029AA0A008A344A440A540AB40AD04AE20B140B204C0FFAF
:4001C000C2FFC4FFCAF0CCF3CEF0D60ED80EE20BEA0CEE20020206040760080C09070A200B100C800D770E0C10FF112013101403152016FC1A081B071C0C1E101F08200CD3
:40020000210122402306240125012602270229012A022B042C0C2D012EF1310F334036FF37303B803E40580459045B045C905F0181108204860489148A078B218C078D7F65
:40024000920495099604981899019A029B0E9D089E109F01A008A604A750A909AA01AD01AE04AF22B21FB330B50FB740BE04D804D904DC90DF01009001010310040A052049
:400280000701081009100A820B200E420F141190120A13091404150A174019041D402019211822102308250126842741280829082A802C882E042F02325033043540360469
:4002C0003721381139403E403F186C026D40850888048B088D088E048F04908A910A9244933494309531980499309A049B309C109E80A088A203A302A446A509A680A70142
:40030000AB10AD20B020C0FFC2FFC4FFCAFECCFECE7DE040E229E680E88000010104033909051120160119341B0A1D0528022904311033203401350C360237033E503F047C
:40034000580459045B045C905F018077831186E4871188168A218B778D778E1490119288930E96779A149BE19E14A214A410A511A642A744A911AA14AB22AC01AD11AE0EF7
:40038000AF88B10FB20FB3F0B4F0BA28BB0AC006C60EC70CC814C9FFCAFFCBFFD004D601D804D904DA04DB04DC99DD09DF01E2C0000104A406800A200C020D180F02120442
:4003C00014121501171019401B101C041D341F1020012104221923202480260227102B042C122D1032513602371038013B103C983F024F2057845C205D806AC06B016C893C
:400400006D806E046F027649878088848C028D148E0290089102931494029508964499A09A049D409E499F21A201A488A541A688A702AE40C0F8C2F4C4F2CA74CCADCEF509
:40044000D220D630E250E610EA08EE02004001120220047E0512083E09080A400B130D121112120114401610184019111A081C401E041F02230424802710288029122A7E0E
:400480002C402D182E0231083310350736FF39203E403F05580459045C905F01808081248204852486048804891A8A208BE48E0290FF916C93809404950A9610971098035A
:4004C00099249AFC9D409E029F84A004A208A301A404A524A6F9A801A924AA02AC04AE40AF10B1F9B2FFB307B90ABE04C035C702C810C9FFCAFFCBFFCD20CEF0D110D80495
:40050000D904DA04DC90DD09DF01E108E240E340E480E640E74000040120028103400402062809480A680C880E440F801102120813441603173819081F802101220623502F
:40054000249C26102708281029202B422D202E402FA0329A3410369139013B143D033E203F5440104940520483018C4093069408950A9644975099A09D089E429F59A108DA
:40058000A201A320A488A501A6CAA702C07FC2FFC4EFCAFFCCBFCEF7D002D208E210E404004001010280030E04020501060C07040872090F0A800C080E041080122014047E
:4005C0001608170F18101C101D011F022010210F24102710280E29012AF02B082D012E0134FF351F3820580459045C995F018201831086108708881089078BF88C1F8DFF45
:4006000090409118934094099612970799189A069B209C109D809E089F18A010A118A3E0A40CA501A601A706A901AA20AB02AD01AE10AF04B040B238B3FFB407B638BE018F
:40064000BF04D608D804D904DB04DC09DD90DF010120028003120408052006410701080409800B440C020E0A0F10110212011318150A176019201D881E0A1FA12304272093
:40068000280429082A012B022C0C2D042E042F82308032083520368A39043A023B103D023E403F145E8065806C02C0FFC2FFC4FFCAFFCCFACEF7D610D810000A080D0F01A1
:4006C00012051C011D04201029012D02301031033403360C37043E013F40420345E0480249FF4AFF4BFF4F83580A59045A045B045C995D095F01801182888320840185381E
:40070000860E870188108A228B208E148F20913F921495049614970899029A049B389EE49F07A214A338A477A720AA77AB20AC16AD04AE41AF10B13FB20FB4F0B6F0BAA8F4
:40074000D804D904DC99DF010120024005050602070809020A020C080E010F8811021525160119201A021B021D041E011F60204021022280274029202A442B202C0A2E028D
:400780002F2032403501376838403D053E203F804108484059125B44608061646B017808810186018810892C8B208D0291219280950496029780988899519A639B2C9C104A
:4007C0009E049F01A00AA344A440A508A680A702A802AA01AD88AE02AF08B201B440B640C0FAC2F9C4F8CAFCCCF8CEF8D002D60FD80FDE02E008E212E609EA0CEE0A8202FC
:40080000869087028A01904091219280950496419780988899519A609B249C109D889E149F01A008A344A440A508A602A901AB02B404B610E008E243E408E614E840EA0CDF
:40084000EE08021006040A040E041007150216071A021E041F0120082104260428182A012D072E0433043502361F37013E40580459045B045C905F018101822085F1862038
:4008800088048A018B448C018E028F22900191CC923E932295AA962097449A109E20A240A311A620AA08AC7FAD88B03FB50FB640B7F0D608D804D909DB04DC99DD90DF01DE
:4008C00000900101031004820510071009500A800B200D290F221002120813091488160217281A821B011F802305242225302608272429012A402C022F80300833013480CD
:40090000350439503B0558405D80624069506A106E807B04815085808720884089208B408E108F20909091209320942095019682970199149B189C089E809F04A002A1482A
:40094000A30CA440A510A608A701A802B708C0FFC2FFC4FFCA99CC53CE0FD618D808DE04E240E408E690E810EA08EE0802010320070708100B200E020F20100C11FF1202C4
:400980001504160417901B201D041F48200821F823012401250227F8280A2A042B382F20300F31E03210333F340E3E14580459045B045C995F0182078404860889068A20DB
:4009C0008B098D098E388F06903F940296389A209E209F0CA220A404A610AA20AC38AE01B104B308B501B63FB702B9AABF55D608D804D904DB04DC09DD90DF01014203085E
:400A00000481051007200A900C020D100F2210081240130415011794190B1B201C011F8020A021052380240825042682288029102B11308233143640388039293D904080E9
:400A4000410142805F40640268086A806B016D90800285018682885089018A028C048D048F809080916092109344950399109A409B349D099E829F01A082A240A319A44806
:400A8000A720A920AC80AE08AF01B080B108C0FBC2FCC4FECA0FCC1FCE3FD610D810E080E270E6A0E860EE421B011F0831103308368037023B40C630CCF0CE103002338003
:400AC000358037043B083D808F0297409D109F02AE80AF01CCF0CE60500857108704894097489D109F04A402A540A780AD80D460E610EA10830497409D109F04A402A790B8
:400B0000B008E2801420C4045F80630883048F889820D604D802E208E4081B0898209C06A708AC06C608080809200C040F01830186018D0898209C06C20FE4082680838010
:400B40008D108E0297409D10A680A710AC02AF80B680C820E620EA10EE105202538078407D80844085408F10A202A380A710AF40D4A0DC80DE20E240E610EE40052008023A
:400B80000E411F105280550858406302870188208920960198209C02A108AC08AD20C001C20DC601D407D602E20577108F10AF10B040B6C0DE04E402E803EE0201010B01AE
:400BC0000D010F0111011B011D0100FF01AB020211050000BF0000A09F001F000000000000000000100000004000000000000000C0000000FF0000B8470047000001000053
:400C0000800000028200820000000000000303000300000027001801270018010004000000050000000000000000000000000000000000000000000000000000000000009C
:400000000145004008520040015B0040016400400101014003050140030701404E0801404A0901406C0A0140550B01405A0C0140490D01405A0E0140340F0140041701403E
:40004000471801405A1901404F1A01404C1B01400C4001400A4101400F4201400D430140044401400C450140064601400C4701400F4801400B490140194C0140074D0140FA
:4000800005500140045101407E02080509411080118219026006610A7C4027212A0AE280E640EA05EE10E620EA01EE8C00180220041806E009020A100C010E02100111076F
:4000C00012041407150216F8170118801A181B011C181E4020FF260828012A062E072F0630FF330335043B083E01580459045B045C905F0181408210833085E18680870885
:4001000088018A248B108C7C8E828F1091FC92039302961097039B109D209E109FDCA180A210A330A401A648A9E1AA1CAB04AE10B01FB31FB4F0B7E0BA20D608D80BD90B5D
:40014000DB04DC99DD90DF01012003A20404055208040AA10B010D080EA80F0210201280135015041680175919011A801B301E402180228424202514260429202B802F0167
:400180003180321433023614370138083B513D2058105980600A68026B046D4078018410884091209358942C9540970199729B599C019F04A240A301A58CA628A792A901F2
:4001C000AA10B010C0FFC2FDC4FFCA15CCEFCE2FD60CD80CDE01E622E804EE8C004801400311044805F10708081009100A200BE10C5A0E200F0E10C01201144818481C02F7
:400200001D201E041F112180224023112448288629F12A782B022DF12E042F04303F310F328033F0344038023B023E1440254520480249FF4AFF4BFF4D204EF0511058045A
:40024000590B5A045B045C995D095F01610862406340648066406740800481018220830E8404861088808A048BC08D018E0291EF92029403952F96FC974099019A049B04BE
:400280009C049D809E409F2FA0FFA101A302A404A6F9A710A804A901AA08AB08AC01AE02AF20B4FFB51FB7E0BE10D804D904DB04DC90DF01004401020204042405020606B2
:4002C000084809200A800B280D280E800F021180128813081408174918401A041B051F2023502680270829202B822C802E802F28318034403608375138083B513D903E02AA
:4003000042044620470849014A0268056E507801830489108A808E409120928093209540965099729A829B519C019D019E209F04A090A160A242A308A405A50CA628A7130E
:40034000A840AA01B540C0FFC2FFC4FFCAFDCCF8CEBFD004D208DE01E404E640EE1000800102027E044006100702083E09010A400C400E020F041040111812201320147E22
:400380001703188019021C401D081E04210222012502284029022A082D202F1830FF310833203507371039203E013F05400247EC483049FF4AFF4BFF5004560158045904C1
:4003C0005A045B045C905D095F0162C080368209842D8612880789028B018C3F9209961299019B029C24A104A480A640A840AA80AEC0B040B104B238B301B407B680B7021B
:40040000B882B988BE41BF45D80BD904DB04DC09DF01001A010403820408060A07400A700B410C800D280E80102011501711189019A01C80202021402202253026802760C1
:4004400029012D022E202F11302036A939103C803D283F02472056056A806B016C946D806EA06F18768A780189C08A1091309323942895049620974099129E249F45A0205B
:40048000A201A480A55CA668A79BA820A950AA80B080B640C0FFC2FDC457CAF1CCF4CEF4D020DE0100020140020C04100550072008040A080E010F10107211041280130203
:4004C000140E150816F018101B201C801E20201021602310244025022680270528102C082D072E0430FF310133703506370838023F445608580459045B045C995D905F0174
:40050000810C82078340840185808604870C88018A028B048C408EA08F02900191FF9457950397FC9840990C9A909B109C019D019E069F02A057A302A50CA7F1A840AB0883
:40054000AD0CAE08AF20B030B1FFB40FB6C0BF01D804D904DC09DF010220034904280540074009180A410C800D080E881004118812801604178119021A401D501E021F114A
:400580002112224427802B922C842E602F103102321033043628374139043B413DA83E013F025880630169908A048E048F01C0FFC2FFC4DFCAFBCCF7CEFBD608D808E280A8
:4005C000E404E680EA08EE0B011406410714080109140A120B080CF10E040F201241150A16411714180119041A281C801D0A1E01211422412504264E29042B102D182F0158
:40060000320F3307353836F039083A085608580859045B045C995D905F018210846088018A048C7C8D018E02921C96039A109C409D04A210A401A608AA10AD02AE10B1016E
:40064000B21FB304B440B502B620BF15D80BD904DB04DC09DF01012803810524070108100A120B800C200E060F50102011011280135014101501170418081A161E021F885A
:4006800020202101221024042518262828022D062E802F0430043240330C35103608374039803D283E403F0258405A205D505F086401650867066A806C016F0378017F01D2
:4006C000814083088501912492489358940C96019702984299339A809B409C059D80A104A280A301A460A508A628A782AC02B048C0EFC2FFC43FCAF8CC78CEF8D67CD870F3
:40070000DE11E204E402E680EE0B01FC02900302042005C106C0072808C10A240B900CFC0E020F901120120313C018011A481B901E901F032290239C2690279029012A9012
:400740002B442E9C2F9031E034E0361F371F3A203B02420245E0480249FF4AFF4BFF4F83580B590B5A045B045C995D095F01821084618501860888618A04921096109A0394
:400780009C409E30A07CA202A61CA820AA50AC80B080B101B260B41FB660BE01BF01D808D904DB04DC09DF01012002C0036204080520074008020A050C400E0A0F50105066
:4007C0001350141015401608170519401B301C041D041E491F4022422780284029242A4830103242380A3B503F044110484059015A985F80600661406202632069806D40BA
:4008000078017F018240900A9120921C9356958096019780980199219B059D04A281A302A440AD10B010B580C07FC2FBC4FFCA0FCC0DCE4FD004D61FD80FDE11E004EE085F
:400840001A401F083328360437103A406A406B108B20C630CCF0CE1032043380364037023A013E809E40A604CCF0CE60502055108D2096089EC09F02A020A110A644A7809D
:40088000A910AA01B420D460EAA08A809EC09F02A002A120A202A640A780AA06AE04B002EA80EE3014107310C404DC01530164108B1090109710B010B410D401D801E4017C
:4008C000E808EA041B048301901097029C10C60808080B080E020F408710903097089C10A810AB04AF04C20F240280018A409A209E40A002A120A202AB02AE60AF80C820D3
:40090000E420EA10EED051205202722074029A20A002A120A202D4A0DC80DE2005400B200C800E401F40501056205C106120824084108B40903097209C10AA01AC08AF40A1
:40094000C001C20DC601D406D604D802E60182209E20AD40B120B480EC01EE0201010D010F0111011D0100FF01AB020211050000BF0000A09F001F0000000000000000004A
:40098000100000004000000000000000C0000000FF0000B84700470000010000800000028200820000000000000303000300000027001801270018010004000000050000C9
:4009C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F7
:400A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B6
:400A40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076
:400A80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036
:400AC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F6
:400B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B5
:400B40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075
:400B80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035
:400BC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F5
:400C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B4
:400C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074
:400C80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034
:400CC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F4
@@ -4615,12 +4615,12 @@
:0200000490105A
:04000000BC90ACAF55
:0200000490303A
:0200000043BA01
:02000000861365
:0200000490402A
:4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0
:400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
:400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
:4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
:0200000490501A
:0C00000000012E16106900002E30542361
:0C00000000012E16106900002E2F967CC7
:00000001FF

View File

@@ -1,7 +1,6 @@
//`#start header` -- edit after this line, do not edit this line
`include "cypress.v"
`include "../SuperCounter/SuperCounter.v"
//`#end` -- edit above this line, do not edit this line
// Generated on 12/11/2019 at 21:18
@@ -9,7 +8,7 @@
module Sampler (
output [2:0] debug_state,
output reg [7:0] opcode,
output req,
output reg req,
input clock,
input index,
input rdata,
@@ -19,77 +18,62 @@ module Sampler (
//`#start body` -- edit after this line, do not edit this line
localparam STATE_WAITING = 0;
localparam STATE_OPCODE = 1;
// NOTE: Reset pulse is used in both clock domains, and must be long enough
// to be detected in both.
reg [0:0] state;
reg [5:0] counter;
reg oldsampleclock;
reg oldindex;
reg oldrdata;
reg index_q;
reg rdata_q;
reg sampleclocked;
reg indexed;
reg rdataed;
reg index_edge;
reg rdata_edge;
assign req = (state == STATE_OPCODE);
reg req_toggle;
always @(posedge clock)
always @(posedge sampleclock)
begin
if (reset)
begin
state <= STATE_WAITING;
opcode <= 0;
sampleclocked <= 0;
indexed <= 0;
rdataed <= 0;
oldsampleclock <= 0;
oldindex <= 0;
oldrdata <= 0;
index_edge <= 0;
rdata_edge <= 0;
index_q <= 0;
rdata_q <= 0;
counter <= 0;
req_toggle <= 0;
end
else
begin
/* Remember positive egdes for sampleclock, index and rdata. */
/* Both index and rdata are active high -- positive-going edges
* indicate the start of an index pulse and read pulse, respectively.
*/
index_edge <= index && !index_q;
index_q <= index;
if (sampleclock && !oldsampleclock)
sampleclocked <= 1;
oldsampleclock <= sampleclock;
rdata_edge <= rdata && !rdata_q;
rdata_q <= rdata;
if (index && !oldindex)
indexed <= 1;
oldindex <= index;
if (rdata && !oldrdata)
rdataed <= 1;
oldrdata <= rdata;
case (state)
STATE_WAITING:
begin
if (sampleclocked)
begin
if (rdataed || indexed || (counter == 6'h3f))
begin
opcode <= {rdataed, indexed, counter};
rdataed <= 0;
indexed <= 0;
counter <= 1; /* remember to count this tick */
state <= STATE_OPCODE;
end
else
counter <= counter + 1;
sampleclocked <= 0;
end
end
STATE_OPCODE: /* opcode sent here */
begin
state <= STATE_WAITING;
end
endcase
if (rdata_edge || index_edge || (counter == 6'h3f)) begin
opcode <= { rdata_edge, index_edge, counter };
req_toggle <= ~req_toggle;
counter <= 1; /* remember to count this tick */
end else begin
counter <= counter + 1;
end
end
end
reg req_toggle_q;
always @(posedge clock)
begin
if (reset) begin
req_toggle_q <= 0;
req <= 0;
end else begin
req_toggle_q <= req_toggle;
req <= (req_toggle != req_toggle_q);
end
end

View File

Binary file not shown.

View File

@@ -537,14 +537,14 @@ static void cmd_write(struct write_frame* f)
init_replay_dma();
bool writing = false; /* to the disk */
bool finished = false;
int packets = f->bytes_to_write / FRAME_SIZE;
bool finished = (packets == 0);
int count_written = 0;
int count_read = 0;
dma_writing_to_td = 0;
dma_reading_from_td = -1;
dma_underrun = false;
int old_reading_from_td = -1;
for (;;)
{

View File

@@ -39,7 +39,7 @@ CFLAGS += -Ilib -Idep/fmt -Iarch
export OBJDIR = .obj
all: .obj/build.ninja
@ninja -f .obj/build.ninja -v
@ninja -f .obj/build.ninja
clean:
@echo CLEAN

View File

@@ -9,7 +9,8 @@
#define BROTHER_DATA_RECORD_CHECKSUM 3
#define BROTHER_DATA_RECORD_ENCODED_SIZE 415
#define BROTHER_TRACKS_PER_DISK 78
#define BROTHER_TRACKS_PER_240KB_DISK 78
#define BROTHER_TRACKS_PER_120KB_DISK 39
#define BROTHER_SECTORS_PER_TRACK 12
class Sector;
@@ -28,8 +29,16 @@ public:
class BrotherEncoder : public AbstractEncoder
{
public:
BrotherEncoder(int format, int bias):
_format(format),
_bias(bias)
{}
virtual ~BrotherEncoder() {}
private:
int _format;
int _bias;
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
};

View File

@@ -129,9 +129,25 @@ static int charToInt(char c)
std::unique_ptr<Fluxmap> BrotherEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_DISK)
|| (physicalSide != 0))
int logicalTrack;
if (physicalSide != 0)
return std::unique_ptr<Fluxmap>();
physicalTrack -= _bias;
switch (_format)
{
case 120:
if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2))
|| (physicalTrack & 1))
return std::unique_ptr<Fluxmap>();
logicalTrack = physicalTrack/2;
break;
case 240:
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
return std::unique_ptr<Fluxmap>();
logicalTrack = physicalTrack;
break;
}
int bitsPerRevolution = 200000.0 / clockRateUs;
const std::string& skew = sectorSkew.get();
@@ -146,10 +162,10 @@ std::unique_ptr<Fluxmap> BrotherEncoder::encode(
double dataMs = headerMs + postHeaderSpacingMs;
unsigned dataCursor = dataMs*1e3 / clockRateUs;
const auto& sectorData = allSectors.get(physicalTrack, 0, sectorId);
const auto& sectorData = allSectors.get(logicalTrack, 0, sectorId);
fillBitmapTo(bits, cursor, headerCursor, { true, false });
write_sector_header(bits, cursor, physicalTrack, sectorId);
write_sector_header(bits, cursor, logicalTrack, sectorId);
fillBitmapTo(bits, cursor, dataCursor, { true, false });
write_sector_data(bits, cursor, sectorData->data);
}

View File

@@ -57,11 +57,11 @@ const FluxPattern FM_TRS80DAM1_PATTERN(16, 0xf56b);
/*
* TRS80DAM2 record:
* flux: XXXX-X-X-XX-XXX- = 0xf56c
* flux: XXXX-X-X-XX-XXX- = 0xf56e
* clock: X X - - - X X X = 0xc7
* data: X X X X X - X - = 0xfa
*/
const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56c);
const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56e);
/* MFM record separator:
* 0xA1 is:

View File

@@ -205,6 +205,7 @@ std::unique_ptr<Fluxmap> IbmEncoder::encode(
Bytes truncatedData = sectorData->data.slice(0, _parameters.sectorSize);
bw += truncatedData;
hexdump(std::cout, data.slice(0, 64));
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);

View File

@@ -121,7 +121,7 @@ uint8_t decode_side(uint8_t side)
* bit 5) and also whether we're above track 0x3f (in bit 6).
*/
return !!(side & 0x40);
return !!(side & 0x20);
}
AbstractDecoder::RecordType MacintoshDecoder::advanceToNextRecord()

View File

@@ -37,7 +37,7 @@ AbstractDecoder::RecordType MxDecoder::advanceToNextRecord()
const FluxMatcher* matcher = nullptr;
_sector->clock = _clock = _fmr->seekToPattern(ID_PATTERN, matcher);
readRawBits(32); /* skip the ID mark */
_logicalTrack = decodeFmMfm(readRawBits(32)).reader().read_be16();
_logicalTrack = decodeFmMfm(readRawBits(32)).slice(0, 32).reader().read_be16();
}
else if (_currentSector == 10)
{

View File

@@ -45,22 +45,20 @@ Reading discs
Just do:
```
fluxengine read mac
fluxengine read mac -o mac.diskcopy
```
You should end up with an `mac.img` which is 1001888 bytes long (for a normal
DD disk). If you want the single-sided variety, use `-s :s=0`.
You should end up with a `mac.diskcopy` file which is compatible with DiskCopy
4.2, which most Mac emulators support.
**Big warning!** The image may not work in an emulator. Mac disk images are
complicated due to the way the tracks are different sizes and the odd sector
size. FluxEngine chooses to store them in a simple 524 x 12 x 2 x 80 layout,
with holes where missing sectors should be. This was easiest. If anyone can
suggest a better way, please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new).
The 12 bytes of metadata _follow_ the 512 bytes of user payload in the sector
image. If you don't want it, specify a geometry in the output file with a
512-byte sectore size like `-o mac.img:c=80:h=1:s=12:b=512`.
**Big warning!** Mac disk images are complicated due to the way the tracks are
different sizes and the odd sector size. If you use a normal `.img` file, then
FluxEngine will store them in a simple 524 x 12 x 2 x 80 layout, with holes
where missing sectors should be; this was easiest, but is unlikely to work with
most Mac emulators and other software. In these files, the The 12 bytes of
metadata _follow_ the 512 bytes of user payload in the sector image. If you
don't want it, specify a geometry in the output file with a 512-byte sectore
size like `-o mac.img:c=80:h=1:s=12:b=512`.
Useful references
-----------------
@@ -74,3 +72,7 @@ Useful references
- [Les Disquettes et le drive Disk II](http://www.hackzapple.com/DISKII/DISKIITECH.HTM), an
epicly detailed writeup of the Apple II disk format (which is closely related)
- [The DiskCopy 4.2
format](https://www.discferret.com/wiki/Apple_DiskCopy_4.2), described on
the DiskFerret website.

View File

@@ -34,6 +34,21 @@ interchangeably: you can specify either at (very nearly) any time. A very
common workflow is to read a disk to a flux file, and then reread from the flux
file while changing the decoder options, to save disk wear. It's also much faster.
### Connecting it up
To use, simply plug your FluxEngine into your computer and run the client. If a
single device is plugged in, it will be automatically detected and used.
If _more_ than one device is plugged in, you need to specify which one to use
with the `--device` parameter, which takes the device serial number as a
parameter. You can find out the serial numbers by running the command without
the `--device` parameter, and if more than one device is attached they will be
listed. The serial number is also shown whenever a connection is made.
You _can_ work with more than one FluxEngine at the same time, using different
invocations of the client; but be careful of USB bandwidth. If the devices are
connected via the same hub, the bandwidth will be shared.
### Source and destination specifiers
When reading from or writing _flux_ (either from or to a real disk, or a flux
@@ -117,6 +132,10 @@ exact format varies according to the extension:
FluxEngine's D64 support is currently limited to write only. It will work
with up to 40 logical tracks.
- `.diskcopy`: a Macintosh DiskCopy 4.2 file. This is a special-purpose
format due to the weird layout of Mac GCR disks, but it can also support
720kB and 1440kB IBM disks (although there's no real benefit).
### High density disks
High density disks use a different magnetic medium to low and double density

View File

@@ -1,6 +1,8 @@
#ifndef BYTES_H
#define BYTES_H
#include <string.h>
class ByteReader;
class ByteWriter;
@@ -10,6 +12,7 @@ public:
Bytes();
Bytes(unsigned size);
Bytes(const uint8_t* ptr, size_t len);
Bytes(const std::string& data);
Bytes(std::initializer_list<uint8_t> data);
Bytes(std::shared_ptr<std::vector<uint8_t>> data);
Bytes(std::shared_ptr<std::vector<uint8_t>> data, unsigned start, unsigned end);
@@ -271,6 +274,16 @@ public:
ByteWriter& operator += (std::istream& stream);
ByteWriter& append(const char* data)
{
return *this += Bytes((const uint8_t*)data, strlen(data));
}
ByteWriter& append(const std::string& data)
{
return *this += Bytes(data);
}
ByteWriter& append(const Bytes data)
{
return *this += data;

View File

@@ -4,6 +4,9 @@
Fluxmap& Fluxmap::appendBytes(const Bytes& bytes)
{
if (bytes.size() == 0)
return *this;
return appendBytes(&bytes[0], bytes.size());
}

View File

@@ -1,10 +1,12 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "usb.h"
#include "usb/usb.h"
#include "fluxsink/fluxsink.h"
FlagGroup hardwareFluxSinkFlags;
FlagGroup hardwareFluxSinkFlags = {
&usbFlags,
};
static bool high_density = false;

View File

@@ -1,11 +1,13 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "usb.h"
#include "usb/usb.h"
#include "fluxsource/fluxsource.h"
#include "fmt/format.h"
FlagGroup hardwareFluxSourceFlags;
FlagGroup hardwareFluxSourceFlags = {
&usbFlags
};
static DoubleFlag revolutions(
{ "--revolutions" },

View File

@@ -12,7 +12,7 @@
#include <set>
#include <cassert>
typedef int nanoseconds_t;
typedef double nanoseconds_t;
class Bytes;
extern double getCurrentTime();

View File

@@ -5,12 +5,15 @@
#include "sectorset.h"
#include "imagereader/imagereader.h"
#include "fmt/format.h"
#include <algorithm>
#include <ctype.h>
std::map<std::string, ImageReader::Constructor> ImageReader::formats =
{
{".adf", ImageReader::createImgImageReader},
{".d81", ImageReader::createImgImageReader},
{".img", ImageReader::createImgImageReader},
{".ima", ImageReader::createImgImageReader},
};
static bool ends_with(const std::string& value, const std::string& ending)

View File

@@ -0,0 +1,169 @@
#include "globals.h"
#include "flags.h"
#include "dataspec.h"
#include "sector.h"
#include "sectorset.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
#include "ldbs.h"
#include <algorithm>
#include <iostream>
#include <fstream>
static const char LABEL[] = "FluxEngine image";
static void write_and_update_checksum(ByteWriter& bw, uint32_t& checksum, const Bytes& data)
{
ByteReader br(data);
while (!br.eof())
{
uint32_t i = br.read_be16();
checksum += i;
checksum = (checksum >> 1) | (checksum << 31);
bw.write_be16(i);
}
}
class DiskCopyImageWriter : public ImageWriter
{
public:
DiskCopyImageWriter(const SectorSet& sectors, const ImageSpec& spec):
ImageWriter(sectors, spec)
{}
void writeImage()
{
bool mfm = false;
if (spec.bytes == 524)
{
/* GCR disk */
}
else if (spec.bytes == 512)
{
/* MFM disk */
mfm = true;
}
else
Error() << "this image is not compatible with the DiskCopy 4.2 format";
std::cout << "writing DiskCopy 4.2 image\n"
<< fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector; {}\n",
spec.cylinders, spec.heads, spec.sectors, spec.bytes,
mfm ? "MFM" : "GCR");
auto sectors_per_track = [&](int track) -> int
{
if (mfm)
return spec.sectors;
if (track < 16)
return 12;
if (track < 32)
return 11;
if (track < 48)
return 10;
if (track < 64)
return 9;
return 8;
};
Bytes image;
ByteWriter bw(image);
/* Write the actual sectr data. */
uint32_t dataChecksum = 0;
uint32_t tagChecksum = 0;
uint32_t offset = 0x54;
uint32_t sectorDataStart = offset;
for (int track = 0; track < spec.cylinders; track++)
{
for (int head = 0; head < spec.heads; head++)
{
int sectorCount = sectors_per_track(track);
for (int sectorId = 0; sectorId < sectorCount; sectorId++)
{
const auto& sector = sectors.get(track, head, sectorId);
if (sector)
{
bw.seek(offset);
write_and_update_checksum(bw, dataChecksum, sector->data.slice(0, 512));
}
offset += 512;
}
}
}
uint32_t sectorDataEnd = offset;
if (!mfm)
{
for (int track = 0; track < spec.cylinders; track++)
{
for (int head = 0; head < spec.heads; head++)
{
int sectorCount = sectors_per_track(track);
for (int sectorId = 0; sectorId < sectorCount; sectorId++)
{
const auto& sector = sectors.get(track, head, sectorId);
if (sector)
{
bw.seek(offset);
write_and_update_checksum(bw, tagChecksum, sector->data.slice(512, 12));
}
offset += 12;
}
}
}
}
uint32_t tagDataEnd = offset;
/* Write the header. */
uint8_t encoding;
uint8_t format;
if (mfm)
{
format = 0x22;
if (spec.sectors == 18)
encoding = 3;
else
encoding = 2;
}
else
{
if (spec.heads == 2)
{
encoding = 1;
format = 0x22;
}
else
{
encoding = 0;
format = 0x02;
}
}
bw.seek(0);
bw.write_8(sizeof(LABEL));
bw.append(LABEL);
bw.seek(0x40);
bw.write_be32(sectorDataEnd - sectorDataStart); /* data size */
bw.write_be32(tagDataEnd - sectorDataEnd); /* tag size */
bw.write_be32(dataChecksum); /* data checksum */
bw.write_be32(tagChecksum); /* tag checksum */
bw.write_8(encoding); /* encoding */
bw.write_8(format); /* format byte */
bw.write_be16(0x0100); /* magic number */
image.writeToFile(spec.filename);
}
};
std::unique_ptr<ImageWriter> ImageWriter::createDiskCopyImageWriter(
const SectorSet& sectors, const ImageSpec& spec)
{
return std::unique_ptr<ImageWriter>(new DiskCopyImageWriter(sectors, spec));
}

View File

@@ -13,6 +13,7 @@ std::map<std::string, ImageWriter::Constructor> ImageWriter::formats =
{".adf", ImageWriter::createImgImageWriter},
{".d64", ImageWriter::createD64ImageWriter},
{".d81", ImageWriter::createImgImageWriter},
{".diskcopy", ImageWriter::createDiskCopyImageWriter},
{".img", ImageWriter::createImgImageWriter},
{".ldbs", ImageWriter::createLDBSImageWriter},
};

View File

@@ -29,6 +29,8 @@ private:
const SectorSet& sectors, const ImageSpec& spec);
static std::unique_ptr<ImageWriter> createD64ImageWriter(
const SectorSet& sectors, const ImageSpec& spec);
static std::unique_ptr<ImageWriter> createDiskCopyImageWriter(
const SectorSet& sectors, const ImageSpec& spec);
static Constructor findConstructor(const ImageSpec& spec);

View File

@@ -1,6 +1,6 @@
#include "globals.h"
#include "flags.h"
#include "usb.h"
#include "usb/usb.h"
#include "fluxsource/fluxsource.h"
#include "fluxsink/fluxsink.h"
#include "reader.h"
@@ -107,7 +107,7 @@ void Track::readFluxmap()
fluxmap = fluxsource->readFlux(physicalTrack, physicalSide);
std::cout << fmt::format(
"{0} ms in {1} bytes\n",
int(fluxmap->duration()/1e6),
fluxmap->duration()/1e6,
fluxmap->bytes());
if (outputFluxSink)
outputFluxSink->writeFlux(physicalTrack, physicalSide, *fluxmap);

View File

@@ -1,341 +0,0 @@
#include "globals.h"
#include "usb.h"
#include "protocol.h"
#include "fluxmap.h"
#include "bytes.h"
#include <libusb.h>
#include "fmt/format.h"
#define TIMEOUT 5000
static libusb_device_handle* device;
static uint8_t buffer[FRAME_SIZE];
static std::string usberror(int i)
{
return libusb_strerror((libusb_error) i);
}
static void usb_init()
{
if (device)
return;
int i = libusb_init(NULL);
if (i < 0)
Error() << "could not start libusb: " << usberror(i);
device = libusb_open_device_with_vid_pid(NULL, FLUXENGINE_VID, FLUXENGINE_PID);
if (!device)
Error() << "cannot find the FluxEngine (is it plugged in?)";
int cfg = -1;
libusb_get_configuration(device, &cfg);
if (cfg != 1)
{
i = libusb_set_configuration(device, 1);
if (i < 0)
Error() << "the FluxEngine would not accept configuration: " << usberror(i);
}
i = libusb_claim_interface(device, 0);
if (i < 0)
Error() << "could not claim interface: " << usberror(i);
int version = usbGetVersion();
if (version != FLUXENGINE_VERSION)
Error() << "your FluxEngine firmware is at version " << version
<< " but the client is for version " << FLUXENGINE_VERSION
<< "; please upgrade";
}
static int usb_cmd_send(void* ptr, int len)
{
//std::cerr << "send:\n";
//hexdump(std::cerr, Bytes((const uint8_t*)ptr, len));
int i = libusb_interrupt_transfer(device, FLUXENGINE_CMD_OUT_EP,
(uint8_t*) ptr, len, &len, TIMEOUT);
if (i < 0)
Error() << "failed to send command: " << usberror(i);
return len;
}
void usb_cmd_recv(void* ptr, int len)
{
int i = libusb_interrupt_transfer(device, FLUXENGINE_CMD_IN_EP,
(uint8_t*) ptr, len, &len, TIMEOUT);
if (i < 0)
Error() << "failed to receive command reply: " << usberror(i);
//std::cerr << "recv:\n";
//hexdump(std::cerr, Bytes((const uint8_t*)ptr, len));
}
static void bad_reply(void)
{
struct error_frame* f = (struct error_frame*) buffer;
if (f->f.type != F_FRAME_ERROR)
Error() << fmt::format("bad USB reply 0x{:2x}", f->f.type);
switch (f->error)
{
case F_ERROR_BAD_COMMAND:
Error() << "device did not understand command";
case F_ERROR_UNDERRUN:
Error() << "USB underrun (not enough bandwidth)";
default:
Error() << fmt::format("unknown device error {}", f->error);
}
}
template <typename T>
static T* await_reply(int desired)
{
for (;;)
{
usb_cmd_recv(buffer, sizeof(buffer));
struct any_frame* r = (struct any_frame*) buffer;
if (r->f.type == F_FRAME_DEBUG)
{
std::cout << "dev: " << ((struct debug_frame*)r)->payload << std::endl;
continue;
}
if (r->f.type != desired)
bad_reply();
return (T*) r;
}
}
int usbGetVersion(void)
{
usb_init();
struct any_frame f = { .f = {.type = F_FRAME_GET_VERSION_CMD, .size = sizeof(f)} };
usb_cmd_send(&f, f.f.size);
auto r = await_reply<struct version_frame>(F_FRAME_GET_VERSION_REPLY);
return r->version;
}
void usbSeek(int track)
{
usb_init();
struct seek_frame f = {
{ .type = F_FRAME_SEEK_CMD, .size = sizeof(f) },
.track = (uint8_t) track
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_SEEK_REPLY);
}
void usbRecalibrate()
{
usb_init();
struct any_frame f = {
{ .type = F_FRAME_RECALIBRATE_CMD, .size = sizeof(f) },
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_RECALIBRATE_REPLY);
}
nanoseconds_t usbGetRotationalPeriod(void)
{
usb_init();
struct any_frame f = { .f = {.type = F_FRAME_MEASURE_SPEED_CMD, .size = sizeof(f)} };
usb_cmd_send(&f, f.f.size);
auto r = await_reply<struct speed_frame>(F_FRAME_MEASURE_SPEED_REPLY);
return r->period_ms * 1000000;
}
static int large_bulk_transfer(int ep, Bytes& bytes)
{
int len;
int i = libusb_bulk_transfer(device, ep, bytes.begin(), bytes.size(), &len, TIMEOUT);
if (i < 0)
Error() << fmt::format("data transfer failed at {} bytes: {}", len, usberror(i));
return len;
}
void usbTestBulkWrite()
{
usb_init();
struct any_frame f = { .f = {.type = F_FRAME_BULK_WRITE_TEST_CMD, .size = sizeof(f)} };
usb_cmd_send(&f, f.f.size);
/* These must match the device. */
const int XSIZE = 64;
const int YSIZE = 256;
const int ZSIZE = 64;
Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
double start_time = getCurrentTime();
large_bulk_transfer(FLUXENGINE_DATA_IN_EP, bulk_buffer);
double elapsed_time = getCurrentTime() - start_time;
std::cout << "Transferred "
<< bulk_buffer.size()
<< " bytes from FluxEngine -> PC in "
<< int(elapsed_time * 1000.0)
<< " ms ("
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
<< " kB/s)"
<< std::endl;
for (int x=0; x<XSIZE; x++)
for (int y=0; y<YSIZE; y++)
for (int z=0; z<ZSIZE; z++)
{
int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
if (bulk_buffer[offset] != uint8_t(x+y+z))
Error() << "data transfer corrupted at 0x"
<< std::hex << offset << std::dec
<< " "
<< x << '.' << y << '.' << z << '.';
}
await_reply<struct any_frame>(F_FRAME_BULK_WRITE_TEST_REPLY);
}
void usbTestBulkRead()
{
usb_init();
struct any_frame f = { .f = {.type = F_FRAME_BULK_READ_TEST_CMD, .size = sizeof(f)} };
usb_cmd_send(&f, f.f.size);
/* These must match the device. */
const int XSIZE = 64;
const int YSIZE = 256;
const int ZSIZE = 64;
Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
for (int x=0; x<XSIZE; x++)
for (int y=0; y<YSIZE; y++)
for (int z=0; z<ZSIZE; z++)
{
int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
bulk_buffer[offset] = uint8_t(x+y+z);
}
double start_time = getCurrentTime();
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, bulk_buffer);
double elapsed_time = getCurrentTime() - start_time;
std::cout << "Transferred "
<< bulk_buffer.size()
<< " bytes from PC -> FluxEngine in "
<< int(elapsed_time * 1000.0)
<< " ms ("
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
<< " kB/s)"
<< std::endl;
await_reply<struct any_frame>(F_FRAME_BULK_READ_TEST_REPLY);
}
Bytes usbRead(int side, bool synced, nanoseconds_t readTime)
{
struct read_frame f = {
.f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
.synced = (uint8_t) synced
};
uint16_t milliseconds = readTime / 1e6;
((uint8_t*)&f.milliseconds)[0] = milliseconds;
((uint8_t*)&f.milliseconds)[1] = milliseconds >> 8;
usb_cmd_send(&f, f.f.size);
auto fluxmap = std::unique_ptr<Fluxmap>(new Fluxmap);
Bytes buffer(1024*1024);
int len = large_bulk_transfer(FLUXENGINE_DATA_IN_EP, buffer);
buffer.resize(len);
await_reply<struct any_frame>(F_FRAME_READ_REPLY);
return buffer;
}
void usbWrite(int side, const Bytes& bytes)
{
unsigned safelen = bytes.size() & ~(FRAME_SIZE-1);
Bytes safeBytes = bytes.slice(0, safelen);
struct write_frame f = {
.f = { .type = F_FRAME_WRITE_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
};
((uint8_t*)&f.bytes_to_write)[0] = safelen;
((uint8_t*)&f.bytes_to_write)[1] = safelen >> 8;
((uint8_t*)&f.bytes_to_write)[2] = safelen >> 16;
((uint8_t*)&f.bytes_to_write)[3] = safelen >> 24;
usb_cmd_send(&f, f.f.size);
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, safeBytes);
await_reply<struct any_frame>(F_FRAME_WRITE_REPLY);
}
void usbErase(int side)
{
struct erase_frame f = {
.f = { .type = F_FRAME_ERASE_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_ERASE_REPLY);
}
void usbSetDrive(int drive, bool high_density, int index_mode)
{
usb_init();
struct set_drive_frame f = {
{ .type = F_FRAME_SET_DRIVE_CMD, .size = sizeof(f) },
.drive = (uint8_t) drive,
.high_density = high_density,
.index_mode = (uint8_t) index_mode
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_SET_DRIVE_REPLY);
}
/* Hacky: the board always operates in little-endian mode. */
static uint16_t read_short_from_usb(uint16_t usb)
{
uint8_t* p = (uint8_t*)&usb;
return p[0] | (p[1] << 8);
}
static void convert_voltages_from_usb(const struct voltages& vin, struct voltages& vout)
{
vout.logic0_mv = read_short_from_usb(vin.logic0_mv);
vout.logic1_mv = read_short_from_usb(vin.logic1_mv);
}
void usbMeasureVoltages(struct voltages_frame* voltages)
{
usb_init();
struct any_frame f = {
{ .type = F_FRAME_MEASURE_VOLTAGES_CMD, .size = sizeof(f) },
};
usb_cmd_send(&f, f.f.size);
struct voltages_frame* r = await_reply<struct voltages_frame>(F_FRAME_MEASURE_VOLTAGES_REPLY);
convert_voltages_from_usb(r->input_both_off, voltages->input_both_off);
convert_voltages_from_usb(r->input_drive_0_selected, voltages->input_drive_0_selected);
convert_voltages_from_usb(r->input_drive_1_selected, voltages->input_drive_1_selected);
convert_voltages_from_usb(r->input_drive_0_running, voltages->input_drive_0_running);
convert_voltages_from_usb(r->input_drive_1_running, voltages->input_drive_1_running);
convert_voltages_from_usb(r->output_both_off, voltages->output_both_off);
convert_voltages_from_usb(r->output_drive_0_selected, voltages->output_drive_0_selected);
convert_voltages_from_usb(r->output_drive_1_selected, voltages->output_drive_1_selected);
convert_voltages_from_usb(r->output_drive_0_running, voltages->output_drive_0_running);
convert_voltages_from_usb(r->output_drive_1_running, voltages->output_drive_1_running);
}

View File

@@ -1,19 +0,0 @@
#ifndef USB_H
#define USB_H
class Fluxmap;
class Bytes;
extern int usbGetVersion();
extern void usbRecalibrate();
extern void usbSeek(int track);
extern nanoseconds_t usbGetRotationalPeriod();
extern void usbTestBulkWrite();
extern void usbTestBulkRead();
extern Bytes usbRead(int side, bool synced, nanoseconds_t readTime);
extern void usbWrite(int side, const Bytes& bytes);
extern void usbErase(int side);
extern void usbSetDrive(int drive, bool high_density, int index_mode);
extern void usbMeasureVoltages(struct voltages_frame* voltages);
#endif

326
lib/usb/fluxengineusb.cc Normal file
View File

@@ -0,0 +1,326 @@
#include "globals.h"
#include "usb.h"
#include "protocol.h"
#include "fluxmap.h"
#include "bytes.h"
#include <libusb.h>
#include "fmt/format.h"
#define TIMEOUT 5000
/* Hacky: the board always operates in little-endian mode. */
static uint16_t read_short_from_usb(uint16_t usb)
{
uint8_t* p = (uint8_t*)&usb;
return p[0] | (p[1] << 8);
}
class FluxEngineUsb : public USB
{
private:
uint8_t _buffer[FRAME_SIZE];
int usb_cmd_send(void* ptr, int len)
{
//std::cerr << "send:\n";
//hexdump(std::cerr, Bytes((const uint8_t*)ptr, len));
int i = libusb_interrupt_transfer(_device, FLUXENGINE_CMD_OUT_EP,
(uint8_t*) ptr, len, &len, TIMEOUT);
if (i < 0)
Error() << "failed to send command: " << usberror(i);
return len;
}
void usb_cmd_recv(void* ptr, int len)
{
int i = libusb_interrupt_transfer(_device, FLUXENGINE_CMD_IN_EP,
(uint8_t*) ptr, len, &len, TIMEOUT);
if (i < 0)
Error() << "failed to receive command reply: " << usberror(i);
//std::cerr << "recv:\n";
//hexdump(std::cerr, Bytes((const uint8_t*)ptr, len));
}
int large_bulk_transfer(int ep, Bytes& bytes)
{
if (bytes.size() == 0)
return 0;
int len;
int i = libusb_bulk_transfer(_device, ep, bytes.begin(), bytes.size(), &len, TIMEOUT);
if (i < 0)
Error() << fmt::format("data transfer failed at {} bytes: {}", len, usberror(i));
return len;
}
public:
FluxEngineUsb(libusb_device_handle* device)
{
_device = device;
int i;
int cfg = -1;
libusb_get_configuration(_device, &cfg);
if (cfg != 1)
{
i = libusb_set_configuration(_device, 1);
if (i < 0)
Error() << "the FluxEngine would not accept configuration: " << usberror(i);
}
i = libusb_claim_interface(_device, 0);
if (i < 0)
Error() << "could not claim interface: " << usberror(i);
int version = getVersion();
if (version != FLUXENGINE_VERSION)
Error() << "your FluxEngine firmware is at version " << version
<< " but the client is for version " << FLUXENGINE_VERSION
<< "; please upgrade";
}
private:
void bad_reply(void)
{
struct error_frame* f = (struct error_frame*) _buffer;
if (f->f.type != F_FRAME_ERROR)
Error() << fmt::format("bad USB reply 0x{:2x}", f->f.type);
switch (f->error)
{
case F_ERROR_BAD_COMMAND:
Error() << "device did not understand command";
case F_ERROR_UNDERRUN:
Error() << "USB underrun (not enough bandwidth)";
default:
Error() << fmt::format("unknown device error {}", f->error);
}
}
template <typename T>
T* await_reply(int desired)
{
for (;;)
{
usb_cmd_recv(_buffer, sizeof(_buffer));
struct any_frame* r = (struct any_frame*) _buffer;
if (r->f.type == F_FRAME_DEBUG)
{
std::cout << "dev: " << ((struct debug_frame*)r)->payload << std::endl;
continue;
}
if (r->f.type != desired)
bad_reply();
return (T*) r;
}
}
public:
int getVersion()
{
struct any_frame f = { .f = {.type = F_FRAME_GET_VERSION_CMD, .size = sizeof(f)} };
usb_cmd_send(&f, f.f.size);
auto r = await_reply<struct version_frame>(F_FRAME_GET_VERSION_REPLY);
return r->version;
}
void seek(int track)
{
struct seek_frame f = {
{ .type = F_FRAME_SEEK_CMD, .size = sizeof(f) },
.track = (uint8_t) track
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_SEEK_REPLY);
}
void recalibrate()
{
struct any_frame f = {
{ .type = F_FRAME_RECALIBRATE_CMD, .size = sizeof(f) },
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_RECALIBRATE_REPLY);
}
nanoseconds_t getRotationalPeriod(void)
{
struct any_frame f = { .f = {.type = F_FRAME_MEASURE_SPEED_CMD, .size = sizeof(f)} };
usb_cmd_send(&f, f.f.size);
auto r = await_reply<struct speed_frame>(F_FRAME_MEASURE_SPEED_REPLY);
return r->period_ms * 1000000;
}
void testBulkWrite()
{
struct any_frame f = { .f = {.type = F_FRAME_BULK_WRITE_TEST_CMD, .size = sizeof(f)} };
usb_cmd_send(&f, f.f.size);
/* These must match the device. */
const int XSIZE = 64;
const int YSIZE = 256;
const int ZSIZE = 64;
Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
double start_time = getCurrentTime();
large_bulk_transfer(FLUXENGINE_DATA_IN_EP, bulk_buffer);
double elapsed_time = getCurrentTime() - start_time;
std::cout << "Transferred "
<< bulk_buffer.size()
<< " bytes from FluxEngine -> PC in "
<< int(elapsed_time * 1000.0)
<< " ms ("
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
<< " kB/s)"
<< std::endl;
for (int x=0; x<XSIZE; x++)
for (int y=0; y<YSIZE; y++)
for (int z=0; z<ZSIZE; z++)
{
int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
if (bulk_buffer[offset] != uint8_t(x+y+z))
Error() << "data transfer corrupted at 0x"
<< std::hex << offset << std::dec
<< " "
<< x << '.' << y << '.' << z << '.';
}
await_reply<struct any_frame>(F_FRAME_BULK_WRITE_TEST_REPLY);
}
void testBulkRead()
{
struct any_frame f = { .f = {.type = F_FRAME_BULK_READ_TEST_CMD, .size = sizeof(f)} };
usb_cmd_send(&f, f.f.size);
/* These must match the device. */
const int XSIZE = 64;
const int YSIZE = 256;
const int ZSIZE = 64;
Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
for (int x=0; x<XSIZE; x++)
for (int y=0; y<YSIZE; y++)
for (int z=0; z<ZSIZE; z++)
{
int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
bulk_buffer[offset] = uint8_t(x+y+z);
}
double start_time = getCurrentTime();
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, bulk_buffer);
double elapsed_time = getCurrentTime() - start_time;
std::cout << "Transferred "
<< bulk_buffer.size()
<< " bytes from PC -> FluxEngine in "
<< int(elapsed_time * 1000.0)
<< " ms ("
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
<< " kB/s)"
<< std::endl;
await_reply<struct any_frame>(F_FRAME_BULK_READ_TEST_REPLY);
}
Bytes read(int side, bool synced, nanoseconds_t readTime)
{
struct read_frame f = {
.f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
.synced = (uint8_t) synced
};
uint16_t milliseconds = readTime / 1e6;
((uint8_t*)&f.milliseconds)[0] = milliseconds;
((uint8_t*)&f.milliseconds)[1] = milliseconds >> 8;
usb_cmd_send(&f, f.f.size);
auto fluxmap = std::unique_ptr<Fluxmap>(new Fluxmap);
Bytes buffer(1024*1024);
int len = large_bulk_transfer(FLUXENGINE_DATA_IN_EP, buffer);
buffer.resize(len);
await_reply<struct any_frame>(F_FRAME_READ_REPLY);
return buffer;
}
void write(int side, const Bytes& bytes)
{
unsigned safelen = bytes.size() & ~(FRAME_SIZE-1);
Bytes safeBytes = bytes.slice(0, safelen);
struct write_frame f = {
.f = { .type = F_FRAME_WRITE_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
};
((uint8_t*)&f.bytes_to_write)[0] = safelen;
((uint8_t*)&f.bytes_to_write)[1] = safelen >> 8;
((uint8_t*)&f.bytes_to_write)[2] = safelen >> 16;
((uint8_t*)&f.bytes_to_write)[3] = safelen >> 24;
usb_cmd_send(&f, f.f.size);
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, safeBytes);
await_reply<struct any_frame>(F_FRAME_WRITE_REPLY);
}
void erase(int side)
{
struct erase_frame f = {
.f = { .type = F_FRAME_ERASE_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_ERASE_REPLY);
}
void setDrive(int drive, bool high_density, int index_mode)
{
struct set_drive_frame f = {
{ .type = F_FRAME_SET_DRIVE_CMD, .size = sizeof(f) },
.drive = (uint8_t) drive,
.high_density = high_density,
.index_mode = (uint8_t) index_mode
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_SET_DRIVE_REPLY);
}
void measureVoltages(struct voltages_frame* voltages)
{
struct any_frame f = {
{ .type = F_FRAME_MEASURE_VOLTAGES_CMD, .size = sizeof(f) },
};
usb_cmd_send(&f, f.f.size);
auto convert_voltages_from_usb = [&](const struct voltages& vin, struct voltages& vout)
{
vout.logic0_mv = read_short_from_usb(vin.logic0_mv);
vout.logic1_mv = read_short_from_usb(vin.logic1_mv);
};
struct voltages_frame* r = await_reply<struct voltages_frame>(F_FRAME_MEASURE_VOLTAGES_REPLY);
convert_voltages_from_usb(r->input_both_off, voltages->input_both_off);
convert_voltages_from_usb(r->input_drive_0_selected, voltages->input_drive_0_selected);
convert_voltages_from_usb(r->input_drive_1_selected, voltages->input_drive_1_selected);
convert_voltages_from_usb(r->input_drive_0_running, voltages->input_drive_0_running);
convert_voltages_from_usb(r->input_drive_1_running, voltages->input_drive_1_running);
convert_voltages_from_usb(r->output_both_off, voltages->output_both_off);
convert_voltages_from_usb(r->output_drive_0_selected, voltages->output_drive_0_selected);
convert_voltages_from_usb(r->output_drive_1_selected, voltages->output_drive_1_selected);
convert_voltages_from_usb(r->output_drive_0_running, voltages->output_drive_0_running);
convert_voltages_from_usb(r->output_drive_1_running, voltages->output_drive_1_running);
}
};
USB* createFluxengineUsb(libusb_device_handle* device)
{
return new FluxEngineUsb(device);
}

139
lib/usb/usb.cc Normal file
View File

@@ -0,0 +1,139 @@
#include "globals.h"
#include "flags.h"
#include "usb.h"
#include "protocol.h"
#include "fluxmap.h"
#include "bytes.h"
#include <libusb.h>
#include "fmt/format.h"
FlagGroup usbFlags;
static StringFlag device(
{ "--device" },
"serial number of hardware device to use",
"");
static USB* usb = NULL;
enum
{
DEV_FLUXENGINE,
};
struct CandidateDevice
{
libusb_device* device;
libusb_device_descriptor desc;
int type;
std::string serial;
};
USB::~USB()
{}
std::string USB::usberror(int i)
{
return libusb_strerror((libusb_error) i);
}
static const char* device_type(int i)
{
switch (i)
{
case DEV_FLUXENGINE: return "FluxEngine";
default: assert(false);
}
}
static std::map<std::string, std::unique_ptr<CandidateDevice>> get_candidates(libusb_device** devices, int numdevices)
{
std::map<std::string, std::unique_ptr<CandidateDevice>> candidates;
for (int i=0; i<numdevices; i++)
{
std::unique_ptr<CandidateDevice> candidate(new CandidateDevice());
candidate->device = devices[i];
(void) libusb_get_device_descriptor(devices[i], &candidate->desc);
if ((candidate->desc.idVendor == FLUXENGINE_VID) && (candidate->desc.idProduct == FLUXENGINE_PID))
{
candidate->type = DEV_FLUXENGINE;
candidate->serial = "";
libusb_device_handle* handle;
if (libusb_open(candidate->device, &handle) == 0)
{
unsigned char buffer[64];
libusb_get_string_descriptor_ascii(handle,
candidate->desc.iSerialNumber, buffer, sizeof(buffer));
candidate->serial = (const char*) buffer;
libusb_close(handle);
}
candidates[candidate->serial] = std::move(candidate);
}
}
return candidates;
}
static void open_device(CandidateDevice& candidate)
{
libusb_device_handle* handle;
int i = libusb_open(candidate.device, &handle);
if (i < 0)
Error() << "cannot open USB device: " << libusb_strerror((libusb_error) i);
std::cout << "Using " << device_type(candidate.type) << " with serial number " << candidate.serial << '\n';
usb = createFluxengineUsb(handle);
}
static CandidateDevice& select_candidate(const std::map<std::string, std::unique_ptr<CandidateDevice>>& devices)
{
if (devices.size() == 0)
Error() << "no USB devices found (is one plugged in? Do you have permission to access USB devices?)";
if (device.get() == "")
{
if (devices.size() == 1)
return *devices.begin()->second;
std::cout << "More than one USB device detected. Use --device to specify which one to use:\n";
for (auto& i : devices)
std::cout << " " << device_type(i.second->type) << ": " << i.first << '\n';
Error() << "specify USB device";
}
else
{
const auto& i = devices.find(device);
if (i != devices.end())
return *i->second;
Error() << "device with serial number '" << device.get() << "' not found";
}
}
USB& getUsb()
{
if (!usb)
{
int i = libusb_init(NULL);
if (i < 0)
Error() << "could not start libusb: " << libusb_strerror((libusb_error) i);
libusb_device** devices;
int numdevices = libusb_get_device_list(NULL, &devices);
if (numdevices < 0)
Error() << "could not enumerate USB bus: " << libusb_strerror((libusb_error) numdevices);
auto candidates = get_candidates(devices, numdevices);
auto candidate = select_candidate(candidates);
open_device(candidate);
libusb_free_device_list(devices, true);
}
return *usb;
}

60
lib/usb/usb.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef USB_H
#define USB_H
#include "bytes.h"
#include "flags.h"
class Fluxmap;
class libusb_device_handle;
class USB
{
public:
virtual ~USB();
virtual int getVersion() = 0;
virtual void recalibrate() = 0;
virtual void seek(int track) = 0;
virtual nanoseconds_t getRotationalPeriod() = 0;
virtual void testBulkWrite() = 0;
virtual void testBulkRead() = 0;
virtual Bytes read(int side, bool synced, nanoseconds_t readTime) = 0;
virtual void write(int side, const Bytes& bytes) = 0;
virtual void erase(int side) = 0;
virtual void setDrive(int drive, bool high_density, int index_mode) = 0;
virtual void measureVoltages(struct voltages_frame* voltages) = 0;
protected:
std::string usberror(int i);
libusb_device_handle* _device;
};
extern FlagGroup usbFlags;
extern USB& getUsb();
extern USB* createFluxengineUsb(libusb_device_handle* device);
static inline int usbGetVersion() { return getUsb().getVersion(); }
static inline void usbRecalibrate() { getUsb().recalibrate(); }
static inline void usbSeek(int track) { getUsb().seek(track); }
static inline void usbTestBulkWrite() { getUsb().testBulkWrite(); }
static inline void usbTestBulkRead() { getUsb().testBulkRead(); }
static inline void usbErase(int side) { getUsb().erase(side); }
static inline nanoseconds_t usbGetRotationalPeriod()
{ return getUsb().getRotationalPeriod(); }
static inline Bytes usbRead(int side, bool synced, nanoseconds_t readTime)
{ return getUsb().read(side, synced, readTime); }
static inline void usbWrite(int side, const Bytes& bytes)
{ getUsb().write(side, bytes); }
static inline void usbSetDrive(int drive, bool high_density, int index_mode)
{ getUsb().setDrive(drive, high_density, index_mode); }
static inline void usbMeasureVoltages(struct voltages_frame* voltages)
{ getUsb().measureVoltages(voltages); }
#endif

View File

@@ -47,8 +47,8 @@ void visualiseSectorsToFile(const SectorSet& sectors, const std::string& filenam
auto drawArc = [&](const std::unique_ptr<Sector>& sector, nanoseconds_t start, nanoseconds_t end, const std::string& colour)
{
start %= period*1000000;
end %= period*1000000;
start = fmod(start, period*1000000.0);
end = fmod(end, period*1000000.0);
if (end < start)
end += period*1000000;

View File

@@ -4,7 +4,7 @@
#include "writer.h"
#include "sql.h"
#include "protocol.h"
#include "usb.h"
#include "usb/usb.h"
#include "dataspec.h"
#include "encoders/encoders.h"
#include "fluxsource/fluxsource.h"

View File

@@ -154,6 +154,7 @@ buildlibrary libbackend.a \
lib/imagereader/imagereader.cc \
lib/imagereader/imgimagereader.cc \
lib/imagewriter/d64imagewriter.cc \
lib/imagewriter/diskcopyimagewriter.cc \
lib/imagewriter/imagewriter.cc \
lib/imagewriter/imgimagewriter.cc \
lib/imagewriter/ldbsimagewriter.cc \
@@ -190,6 +191,8 @@ buildlibrary libbackend.a \
lib/fluxsource/kryoflux.cc \
lib/fluxsource/sqlitefluxsource.cc \
lib/fluxsource/streamfluxsource.cc \
lib/usb/usb.cc \
lib/usb/fluxengineusb.cc \
lib/globals.cc \
lib/hexdump.cc \
lib/ldbs.cc \
@@ -197,7 +200,6 @@ buildlibrary libbackend.a \
lib/sector.cc \
lib/sectorset.cc \
lib/sql.cc \
lib/usb.cc \
lib/visualiser.cc \
lib/writer.cc \

View File

@@ -3,7 +3,7 @@
enum
{
FLUXENGINE_VERSION = 13,
FLUXENGINE_VERSION = 14,
FLUXENGINE_VID = 0x1209,
FLUXENGINE_PID = 0x6e00,

View File

@@ -18,6 +18,11 @@ static SettableFlag fortyTrackMode(
"set 48 tpi mode; only every other physical track is emitted"
);
static SettableFlag indexedMode(
{ "--indexed", "-i" },
"align data to track boundaries"
);
static SettableFlag singleSided(
{ "--single-sided", "-s" },
"only emit side 0"
@@ -45,14 +50,18 @@ static void write_le32(uint8_t dest[4], uint32_t v)
dest[3] = v >> 24;
}
static void appendChecksum(uint32_t& checksum, const Bytes& bytes)
{
ByteReader br(bytes);
while (!br.eof())
checksum += br.read_8();
}
static int strackno(int track, int side)
{
if (fortyTrackMode)
track /= 2;
if (singleSided)
return track;
else
return (track << 1) | side;
return (track << 1) | side;
}
int mainConvertFluxToScp(int argc, const char* argv[])
@@ -90,7 +99,8 @@ int mainConvertFluxToScp(int argc, const char* argv[])
fileheader.revolutions = 5;
fileheader.start_track = 0;
fileheader.end_track = maxStrack;
fileheader.flags = SCP_FLAG_INDEXED | (fortyTrackMode ? 0 : SCP_FLAG_96TPI);
fileheader.flags = (indexedMode ? SCP_FLAG_INDEXED : 0)
| (fortyTrackMode ? 0 : SCP_FLAG_96TPI);
fileheader.cell_width = 0;
fileheader.heads = singleSided ? 1 : 0;
@@ -104,9 +114,15 @@ int mainConvertFluxToScp(int argc, const char* argv[])
for (int side = 0; side <= maxside; side++)
{
int strack = strackno(track, side);
std::cout << fmt::format("FE track {}.{}, SCP track {}: ", track, side, strack) << std::flush;
std::cout << fmt::format("{}.{}: ", track, side) << std::flush;
auto fluxmap = sqlReadFlux(inputDb, track, side);
if (fluxmap->bytes() == 0)
{
std::cout << "missing\n";
continue;
}
ScpTrack trackheader = {0};
trackheader.track_id[0] = 'T';
trackheader.track_id[1] = 'R';
@@ -117,6 +133,9 @@ int mainConvertFluxToScp(int argc, const char* argv[])
Bytes fluxdata;
ByteWriter fluxdataWriter(fluxdata);
if (indexedMode)
fmr.findEvent(F_BIT_INDEX);
int revolution = 0;
unsigned revTicks = 0;
unsigned totalTicks = 0;
@@ -159,7 +178,7 @@ int mainConvertFluxToScp(int argc, const char* argv[])
trackdataWriter += Bytes((uint8_t*)&trackheader, sizeof(trackheader));
trackdataWriter += fluxdata;
std::cout << fmt::format("{} ms in {} bytes\n",
std::cout << fmt::format("{:.3f} ms in {} bytes\n",
totalTicks * MS_PER_TICK,
fluxdata.size());
}
@@ -167,6 +186,13 @@ int mainConvertFluxToScp(int argc, const char* argv[])
sqlClose(inputDb);
uint32_t checksum = 0;
appendChecksum(checksum,
Bytes((const uint8_t*) &fileheader, sizeof(fileheader))
.slice(0x10));
appendChecksum(checksum, trackdata);
write_le32(fileheader.checksum, checksum);
std::cout << "Writing output file...\n";
std::ofstream of(filenames[1], std::ios::out | std::ios::binary);
if (!of.is_open())

View File

@@ -1,10 +1,12 @@
#include "globals.h"
#include "flags.h"
#include "usb.h"
#include "usb/usb.h"
#include "dataspec.h"
#include "protocol.h"
static FlagGroup flags;
static FlagGroup flags = {
&usbFlags,
};
static DataSpecFlag source(
{ "--source", "-s" },

View File

@@ -16,7 +16,7 @@ static int endSide;
static void syntax()
{
std::cout << "Syntax: fluxengine convert cwftoflux <cwffile> <fluxfile>\n";
std::cout << "Syntax: fluxengine convert scptoflux <scpfile> <fluxfile>\n";
exit(0);
}
@@ -28,15 +28,11 @@ static void check_for_error()
static int trackno(int strack)
{
if (startSide == endSide)
return strack;
return strack >> 1;
}
static int headno(int strack)
{
if (startSide == endSide)
return startSide;
return strack & 1;
}
@@ -65,6 +61,8 @@ static void read_header()
static void read_track(int strack)
{
uint32_t offset = Bytes(header.track[strack], 4).reader().read_le32();
if (offset == 0)
return;
ScpTrack trackheader;
inputFile.seekg(offset, std::ios::beg);
@@ -112,7 +110,7 @@ static void read_track(int strack)
inputBytes += datalength*2;
}
std::cout << fmt::format(" {} ms in {} input bytes and {} output bytes\n",
std::cout << fmt::format(" {:.3f} ms in {} input bytes and {} output bytes\n",
fluxmap.duration() / 1e6, inputBytes, fluxmap.bytes());
sqlWriteFlux(outputDb, trackno(strack), headno(strack), fluxmap);
}

View File

@@ -1,9 +1,11 @@
#include "globals.h"
#include "flags.h"
#include "usb.h"
#include "usb/usb.h"
#include "protocol.h"
static FlagGroup flags;
static FlagGroup flags = {
&usbFlags,
};
static IntFlag drive(
{ "--drive", "-d" },

View File

@@ -1,8 +1,10 @@
#include "globals.h"
#include "flags.h"
#include "usb.h"
#include "usb/usb.h"
static FlagGroup flags;
static FlagGroup flags = {
&usbFlags,
};
int mainTestBandwidth(int argc, const char* argv[])
{

View File

@@ -1,10 +1,12 @@
#include "globals.h"
#include "flags.h"
#include "usb.h"
#include "usb/usb.h"
#include "protocol.h"
#include <fmt/format.h>
static FlagGroup flags;
static FlagGroup flags = {
&usbFlags,
};
static std::string display_voltages(struct voltages& v)
{

View File

@@ -9,13 +9,27 @@
static FlagGroup flags { &writerFlags, &brotherEncoderFlags };
static int brotherFormat = 240;
static ActionFlag preset120(
{ "--brother-preset-120" },
"Write the Brother 120kB format instead of the 240kB one.",
[] {
setWriterDefaultInput(":c=39:h=1:s=12:b=256");
brotherFormat = 120;
});
static IntFlag bias(
{ "--brother-track-bias" },
"Shift the entire format this many tracks on the disk.",
0);
int mainWriteBrother(int argc, const char* argv[])
{
setWriterDefaultInput(":c=78:h=1:s=12:b=256");
setWriterDefaultDest(":d=0:t=0-77:s=0");
flags.parseFlags(argc, argv);
BrotherEncoder encoder;
BrotherEncoder encoder(brotherFormat, bias);
writeDiskCommand(encoder);
return 0;