Compare commits

...

29 Commits

Author SHA1 Message Date
David Given
f6f6db913e Merge pull request #208 from davidgiven/greaseweazel
Add client support for the GreaseWeazle.
2021-01-09 01:12:10 +01:00
David Given
ec0a6416fd Update documentation. 2021-01-09 00:52:16 +01:00
David Given
1787402be9 ...and again. 2021-01-09 00:45:27 +01:00
David Given
5f6d99f138 Attempt that fix again. 2021-01-09 00:37:37 +01:00
David Given
d1e2b0d1f8 Fix an issue with old C++ compilers and designated initialisers. 2021-01-09 00:29:46 +01:00
David Given
c2c51bbe33 Fix after merge. 2021-01-09 00:02:14 +01:00
David Given
bb806e3853 Merge from master. 2021-01-08 23:23:50 +01:00
David Given
a11d0e75c8 Merge pull request #200 from ejona86/hard-sectors
Add firmware support for hard sectors
2021-01-08 23:20:40 +01:00
David Given
5406ff0ea3 Typo fix. 2021-01-08 23:14:51 +01:00
David Given
c88317b44a Document how to make things work on Windows. 2021-01-08 23:13:27 +01:00
David Given
6898062d66 Document Windows horror. 2021-01-08 21:37:44 +01:00
David Given
6e1f264e6a Make --sync-with-index and --revolutions work properly on the GreaseWeazle. 2021-01-08 21:36:50 +01:00
David Given
082be14232 Add GreaseWeazle support for --high-density. 2021-01-08 21:20:33 +01:00
David Given
231aa44d03 The clock compensation factor for Mac doesn't seem to be needed? 2021-01-08 01:14:39 +01:00
David Given
cdb12f35d4 Update documentation to mention the GreaseWeazle. 2021-01-07 23:03:34 +01:00
David Given
e831ee8b44 Add erase support. 2021-01-07 22:46:11 +01:00
David Given
40e9a6082f Remove that 'packed' thing. 2021-01-07 22:17:19 +01:00
David Given
53cec292d0 Refactor the GreaseWeazle converter to allow it to be tested. 2021-01-07 22:06:45 +01:00
David Given
3f85309ee5 Commit non-functioning GreaseWeazle write support. 2021-01-07 20:42:06 +01:00
David Given
70944f8521 Reading flux now correctly handles index markers. 2021-01-07 20:02:51 +01:00
David Given
2ab00c42ff Handle index pulses on read. 2021-01-07 01:05:23 +01:00
David Given
a572742caa I read my first disk using the GreaseWeazel! 2021-01-07 00:58:19 +01:00
David Given
400e5f8580 The bandwidth tester works. 2021-01-06 23:24:28 +01:00
David Given
74f0fd89b6 We can successfully seek on the GreaseWeazle. 2021-01-06 22:52:52 +01:00
David Given
09f9bea7a2 Add boilerplate for the GreaseWeazle driver. 2021-01-06 20:39:13 +01:00
David Given
8bffb38117 Fix bad merge. 2021-01-05 02:16:55 +01:00
David Given
eb5d545c35 Remember to unicornify the Mac formats. 2021-01-05 02:09:47 +01:00
Eric Anderson
d3a5bb08d3 Set drive before checking rotation speed for writing 2020-12-24 23:11:34 -08:00
Eric Anderson
f1506d0dbd Add firmware support for hard sectors 2020-12-24 17:39:52 -08:00
26 changed files with 1387 additions and 302 deletions

View File

@@ -1,246 +1,246 @@
:400000000080002011000000CD0F0000CD0F0000064A08B5136843F020031360044B1A6803F53F5302331A6000F0EEFFE8460040FA46004010B5054C237833B9044B13B181
:400040000448AFF300800123237010BD6881FF1F0000000058380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF00000000B1
:400080006C81FF1F58380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8881FF1F2C
:4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8881FF1F3F000080024B012200205A7293
:4001000002F028B98881FF1F10B5C4B2204601F03DF90128FAD110BD08B572B60F4B0F49DA680132DA601A690132C82A08BF00221A615A6918690132A72A08BF00224A610E
:400140005B69002B0CBF02230023002814BF184643F0010002F066FE62B608BD8881FF1F38B50446C5B2284602F096F8062002F0B3FA44F00200C0B202F08EF8062002F0FD
:40018000ABFA284602F088F8BDE83840062002F08DBA10B5642402F079F828B9FFF7E0FF013CF8D1204610BD012010BD70B5C4B2054620460E4601F0E9F8012805D02046DB
:4001C00001F002FA2846FFF79FFF204601F0E6F8314605460246204601F0A2F9204601F0D5F80028FAD1284670BD000038B5044D0024285D013402F01FFA402CF9D138BDDB
:40020000A081FF1F08B502F039FC002002F042FC02F054FC02F05EFC80B208BD10B50446012002F051F8642002F040FAFFF7EAFF2080002002F048F8642002F037FAFFF73D
:40024000E1FF608010BD08B502F044FD002002F04DFD02F05FFD02F069FD80B208BD10B50446FFF796FF322002F020FAFFF7EBFF20800120FFF774FF322002F017FAFFF7BB
:40028000E2FF608010BD0FB400B593B014AB53F8042B402102A8019302F0C6FE02A802F060F802F06AF813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF7E3
:4002C00023FF62782146BDE81040042001F0B6B86C38000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8583043B1002095
:4003000001F0BEFF002002F051FD002384F8583010BD00BF8881FF1F38B5104D837895F85B2004469A4204D0FFF7E4FF002385F85E302368C5F859302279094B1A71A37821
:40034000002B14BF0220012002F030FDE07802F027FD2079BDE8384002F05EBD8881FF1FE181FF1F38B50D4C94F8585065B904F15900FFF7D1FF012001F082FF4FF47A70D9
:4003800002F094F984F85E50E3682366012384F85830BDE8384002F0C7B900BF8881FF1FF8B5214C0546FFF7DDFF94F85E3003B15DB91E48FFF767FFFFF7EBFE0120002323
:4003C00084F85E00636602F087F92A46616E1848FFF759FF144E0027636E9D4216D001F055FF00B17766636E9D4205DD0120FFF7B7FE736E013305E005DA0020FFF7B0FE0E
:40040000736E013B736602F08FF9E5E7322002F04DF92A2DCCBF0020012002F009FDBDE8F8400448FFF72FBF8881FF1F79380000803800009D3800002DE9F04F99B062B6B3
:4004400002F0DCF99949042002F000FA984801F029FF984802F0CCFC974801F05DFF02F0ADFB02F07FFA002002F0A0FC01F078FF0221002000F040FF904C012001F0B8F8D9
:40048000002384F85B30FFF76DFFFFF782FE84F86800FFF72FFF012384F85B30FFF762FFFFF777FE84F86900FFF724FF844B94F86800844994F869202546002A14BF0A46EA
:4004C0001A46002808BF19467F48FFF7DCFE0321084602F009F9264602F026F994F8583043B12A6EEB689B1A41F28832934201D9FFF700FF00F038FF18B97448FFF7C3FE30
:4005000004E000F037FF0028F7D10BE000F02CFF10B902F009F9F9E76D48FFF7B4FE032001F052F8032000F031FF0128D4D16948FFF7F2FE68490320FFF738FE94F86A10B5
:400540006648FFF7A0FE94F86A30023B142B00F2BA83DFE813F01500B8031E00B8032400B8034600B8036C00B803CF00B803B101B803F102B8031003B8031703B803310372
:4005800003238DF820308DF821300E238DF822300EE394F86C00FFF703FF514B05E3FFF7E1FE00236372E068627A02F0FF0132B9EB681B1AB3F57A7FF6DD0B4608E03BB10E
:4005C00000227272F168627A12B9EB685B1AFAE707228DF8202004228DF82120ADF82230E6E20220FFF790FD4FF000080DF1200A02F09AF84FF480790027C9EB0803DA1906
:4006000007F80A200137402FF9D10220FFF77CFD3A465146022000F011FFB9F10109EBD108F10108B8F1400FE2D12E4B38E04FF0010A4FF000080DF1200B02F075F84FF050
:40064000000959460120FFF7B1FD08EB090300270493049B1BF807203B44DBB2934209D08DE80C0041463B464A461F48FFF70BFE4FF0000A0137402FEBD109F10109B9F5E0
:40068000807FDED108F10108B8F1400FD5D151461648FFF7F8FDBAF1000F00F01581144B1B8807A8ADF81C3083E200BF19010000F900000091000000C50000008881FF1F76
:4006C000AF380000AB380000B2380000CA380000DD380000E181FF1FF281FF1FE73800005C3800005E380000F63800001239000060380000606EFFF753FE94F86C0001F0C6
:40070000E3FD94F86C0001F0C7FD02F0F9FBB64BDFF8F0821A78002702F0FB021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F0E7FB0220FFF7E4FC91
:40074000012141F6FF734FF48042084602F034FB84F8AA0001F056FF08F807000137402FF8D1DFF8A4A200270AF199091FFA89F80137402F14BF3A4600221AF8010F224438
:400780000623127E402101F071FF424646F24E419AF8000001F07CFF08F14008402F1FFA88F8E5D196F86D3033B100237372637A002BFCD00023737200234FF0FF32236069
:4007C00062602372236894F8AA002344197E01F0D1FE94F8AA0001F08FFE012194F8AA0001F062FE2368002BFCD0002398467360D6F80CA0012701F097FFE368B4F86E20C2
:40080000CAEB030393420DD367B1042195F8AA0001F0BCFE94F8AA0001F0C8FE0028F9D107463072237AFBB96A682B689A4202D1002FE0D118E00220FFF766FC6968402271
:4008400009EB8111022000F0F9FD6A68674B01321340002BBEBF03F1FF3363F03F03013308F101086360C6E70220277AFFF74CFC00221146022000F0E1FD0220FFF744FC15
:40088000FFB2FFF7B3FC002001F006FD37B15848FFF7F9FC0220FFF71DFD06E0554B08A81B88ADF82030FFF703FD627A4146237A5148FFF7E8FC09E25048FFF7E4FCD4F8CD
:4008C0006E7017F03F0701D00320FDE1686EFFF767FD95F86C0001F0F7FC95F86C0001F0DBFC012001F0F6FC02F00AFB444BDFF814811A7842F004021A701A7842F001021C
:400900001A701A7802F0FE021A701A7802F0FE021A7002F0F9FA01214FF4804341F6FF72084601F0DDFC85F8AA0001F06BFE08F807000137402FF8D1DFF8CC90002709F1F8
:4009400099031FFA83F804930137402F14BF3A46002219F8010F22440523127E402101F085FE414646F24B5299F8000001F090FE08F14008402F1FFA88F8E5D100274FF0AF
:40098000FF33376098467360BB463B46D6F86E9037725FEA99190CBF4FF0010A4FF0000A2168124A01310A40002ABCBF02F1FF3262F03F026068B8BF013282426CD02BB162
:4009C000227A002A77D16A7A002A74D12068049A059302EB8010BAF1000F17D040223F2102F0EEFA1DE000BF9E6400403F0000801C39000062380000363900004939000081
:400A00009B650040A081FF1F9F81FF1F014601370120FFF7CBFBC7EB0903D3F1000A4AEB030A2168AD4A01310A40002ABEBF02F1FF3262F03F02013222606268059B013223
:400A40002AD12A683F2A27D14FF00008C5F8048001F074FC85F808806B6895F8AA002B44197E01F087FD95F8AA0001F045FD012195F8AA0001F018FD85F80980637A002B3E
:400A8000FCD04FF00008012086F8098001F062FC404601F01FFC00E023B1237A5BB96B7A4BB90123626842453FF47AAF0BF1010BD5F8048074E701F047FC012001F00AFCFB
:400AC000002001F047FC042194F8AA0001F05EFD94F8AA0001F06AFD0028F9D196F8AA0001F0F8FC737A327A0293012303920193CDF800A05B463A4649467948FFF7C3FB59
:400B0000BAF1000F0BD0FFF771FB002001F0C4FB237A63B17348FFF7B6FB0220D4E0B945F1D071490120FFF741FB0137F7E76F48FFF7A9FB6E4B38E094F86C0001F0C4FB59
:400B4000606EFFF72DFC6B48FFF79DFB00236372637A002BFCD0012001F0FCFB00237372637A002BFCD0002001F0F4FB6248FFF78AFB624B19E0002084F85E00FFF710FCCF
:400B80005F4B12E094F8683023B195F869200AB985F86C2094F869201AB113B9012385F86C305848FFF7B8FB574B1B88ADF8203008A8FFF77DFB89E0FFF79CFB02F07CF8D9
:400BC000002002F01FF82A2701F04AFF002001F0EDFE3A46002108A802F0F2F917238DF820308DF8217001F09FFD002001F048FB002002F0DBF8C82001F058FD0DF12200DE
:400C0000FFF70CFB0DF13600FFF729FB01F08CFD012002F0CBF8322001F048FD0DF12600FFF7FCFA0DF13A00FFF719FB012001F027FB4FF4967001F039FD01F075FD0DF13A
:400C40002E00FFF7EBFA0DF14200FFF708FB002001F016FB4FF4967001F028FD01F064FD022002F0A3F8322001F020FD0DEB0700FFF7D4FA0DF13E00FFF7F1FA012001F05E
:400C8000FFFA4FF4967001F011FD01F04DFD0DF13200FFF7C3FA0DF14600FFF7E0FA002001F0EEFA4FF4967001F000FD01F03CFD002002F07BF8002384F85E3001F07EFF47
:400CC00001F050FE74E70120FFF704FB032000F07BFC0E48FFF7D7FAFFF7FEBB3F00008053390000833900003892FF1F8D3900006438000095390000A33900006638000055
:400D000068380000F281FF1F6A380000B03900002DE9F04172B6884B61221A70A3F5F06301221A801924854A9C7092E803008033062283F8002283E80300522203F5807326
:400D40001A707F4B7F4A1B787F4EDBB2137040F618027E4B00251A8041F2512223F8022C33784FF4F07003F0010343EA450502F0BFF8013C05F003052ED0032DF0D1744BD2
:400D80004FF480721A8007221A70724A002548211570917002221D705D7103F8032C0422DA716D4A6D4C13786D4E43F00103137012F8013C062743F0030302F8013C2378D8
:400DC000012243F0800323705B4B1A70654A137843F02003137000E0FEE707FB056300219A881868013502F0EBF8072DF5D15E485E4E002550F8041F05F1105303F14802D4
:400E000021F0FF074933C9B20B4452005B0002329A4206D012F802EC12F801CC0EF807C0F5E7B0420D44E5D1514A002313609360136193614F4B504F1A68504BDFF888818F
:400E40001A604F4B1A684F4B1A604F4A137843F002031370137C43F0020313742378A2F5863243F040032370413A137843F010031370464A464B07CA03C31A80454A28339D
:400E8000106843F8250C127903F8212C424A07CA03C31A80414AE83B07CA03C31A80404A083307CA03C31A803E4A3F4BA2F5616203CBC2F8100EC2F8141E1378042043F009
:400EC00008031370394B02F5AA521B783D78DBB298F80060EDB203F007010C321B091170F6B2537045F003033B7046F0030388F800302F4B48221A702E4A402313702E492B
:400F0000937013729372082382F81F3220220A7048710A72294A0A20137001F0DDFB284B88F8006044223D70264D1A7094E80F0007C52B80BDE8F081004800405C090048A4
:400F40000F010049A146004025420040224200400440004006400040A2430040A0430040B5390000E8460040FCFFFF478C0000480076004064090048F84600402076004024
:400F80006809004828760040035001401C090048C051004028090048300900483C090048480900483251004054090048CF0100491D51004001590040235B0040585B0040AE
:400FC00076580040B0430040F946004008B501F0CBFF03680C2B00D1FEE7FEE7084908B50B68084A1844821A802A01DC086005E001F0BAFF0C2303604FF0FF33184608BD0D
:40100000CC80FF1F8893FF1F80B51148114B0025C0B1A3F1100192C922460439161BB74204D051F8046F42F8046BF7E7114653F8046C8C1AA64202D041F8045BF9E7013884
:401040001033E5E701F096FFFFF7F6F9FEE700BF01000000843B0000124A134B10B51A60124A134C1368134843F4007313600023032B98BF54F823204FEA830188BF0E4A58
:401080000133302B4250F3D10C4B1A780C4B1A700C4B084A1A60FFF73BFEBDE8104001F0EDB900BF0004FA050CED00E014ED00E0000000000080FF1FCD0F0000BC7600403B
:4010C000C080FF1F08ED00E0F8B501F019FF4B4A01271378022643F001031370137C484C43F001031374474B02F5E3521F700B3203F8946C1378054603F07F031370002089
:4011000001F0EAFA2378404A03F0F90323701378384603F0DF03137023783B43237001F0DBFA282001F0D8FA384B30461A7802F07F021A701A7802F0BF021A7023783343ED
:40114000237001F0C9FA2378314A43F0040323700023137053702F4AFF2199540133092BFBD1284601F0D0FE0721172001F0FCFA2949172001F0EAFA0721182001F0F4FAF2
:401180002649182001F0E2FA0721152001F0ECFA2349152001F0DAFA0721052001F0E4FA2049052001F0D2FA0721062001F0DCFA1D49062001F0CAFA0721084601F0D4FA2F
:4011C0001A49072001F0C2FA0721082001F0CCFA1749082001F0BAFA0021162001F0C4FA1449162001F0B2FA07210C2001F0BCFABDE8F84010490C2001F0A8BAA543004076
:40120000944300409D60004012600040F851004084600040AD92FF1F971A0000D1180000951A0000C9190000F5190000251A00005D1A00009D1A0000111B0000214B224AF4
:4012400010B5187000231370204A40201370204A0F2413701F4A13701F4A13701F4A13701F4A13701F4B4FF400021A604FF080721A604FF400121A6020221A6018608020E7
:4012800018604FF480701860174804704FF480001860164B1A70933B19B91A7802F0FE0202E01A7842F001021A70114B03221A70802203F8202C012001F01AFE0D4B04221F
:4012C0001A7010BDC892FF1FCE92FF1FCC92FF1FCD92FF1FC992FF1FB892FF1FCB92FF1F4093FF1F00E100E09E6000409C600040286000401260004070B5074C0546237848
:401300000E461BB9FFF7E0FE0123237031462846BDE87040FFF792BF7892FF1F0A4A002313700A4A13700A4A13700A4A13700A4A13700A4A13700A4A13700A4B03221A7049
:40134000802203F8202C7047CE92FF1FCC92FF1FCD92FF1FC992FF1FB892FF1FCB92FF1F4093FF1F28600040014B1878704700BFCD92FF1F044B1A7802F0FF001AB1187845
:401380000022C0B21A707047CC92FF1F024A0C2303FB002040787047D492FF1F431E072B0CD8074A064B00010344805C5B7800F00F0043EA0020023880B2704700207047FA
:4013C000FC5F00401A4A38B50C2303FB00231B79090C13F0800F00F1FF35044619BF8AB24FF480438BB24FF48042032D18D8DFE805F002070C110021084601F01BF80DE049
:401400000021084600F0FAFF08E00021084600F0D9FF03E00021084600F0B8FF054B1855EDB2072D03D801F0EDF8034B185538BDD492FF1FA492FF1FAD92FF1F431E072B83
:401440002DE9F0470446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB2C3F500739D424FF00C0303FB007388BFD588DB7884BFC5F50075ADB2254A43EA1523060119
:40148000B354B244EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FB047200215170B9F1000F28D03DB31B4F385D01F011F811232946FE2218F8040001F0D6F8E3
:4014C00006F5C04278321FFA89F118F8040001F0DFF8124D18F80410385D01F04BF80121385D00F0E1FF735D43F002037355735D03F0FD037355BDE8F08703FB047463793A
:40150000DBB28AF80230BDE8F08700BFD492FF1FFC5F0040AD92FF1FA492FF1F706000402DE9F047044615468846002940D0431E072B3FD8FFF732FFA84203D22046FFF7CA
:401540002DFF05461D4E335DFF2B03D141462046FFF738FFDFF868A027011AF8040000F0B9FF1223FE222946305D01F07FF807F5C0411FFA88F27831305D01F089F8DFF854
:401580004490315D1AF8040000F0F4FF01211AF8040000F089FF17F8093043F0020307F8093017F8093003F0FD0307F8093002E00D4600E000252846BDE8F087AD92FF1FD4
:4015C000A492FF1F70600040431E072B0AD8064A0C2303FB002300225A705A79034BD2B200011A54704700BFD492FF1FFE5F0040431E072B9FBF024B000108221A5470474D
:40160000FE5F004030B51A4A1A491B4D0878138803449BB21380194A00231488D8B2A4B27CB1082B0CD050680078C0B2E85450680133013050601088013880B21080ECE768
:4016400018460B780E4C082B0E4A00D040B10E4D2B7883F080032B700F232370022301E0022323701370094B1870087030BD00BF4493FF1F4093FF1F00600040BC92FF1F5C
:40168000B992FF1FCE92FF1FCA92FF1F4193FF1F074B02221A70074B80221A70064B0F221A70064A00231370054A012013707047CE92FF1FCA92FF1FB992FF1F4093FF1F5B
:4016C0004193FF1F30B5164B16491B780A8803F00F03023BDBB21A4492B20A80124C134A0020118889B279B173B15568215C013BC9B229705168DBB20131516011880130B9
:40170000013989B21180ECE7094A1370094A137883F080031370084B0B221A7030BD00BF296000404493FF1F00600040BC92FF1F4193FF1FCA92FF1FB992FF1F064A0623D5
:401740001370064A01201370054B80221A70054B00221A70704700BFCE92FF1FB992FF1FCA92FF1F4193FF1F054B9A683AB19A68044910709A680988518000229A6070476E
:40178000BC92FF1F4493FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A137008BD0220FFF7E1FF0D4B1B7803F06003202B05D0402B06D043B900F012FC04E001F0ED
:4017C000A7FB01E000F046FD10B9034B03221A7008BD00BF28600040B992FF1F0060004008B5084A084B0120197813880B449BB21380064B00221A70FFF7B6FF044B03221E
:401800001A7008BD4493FF1F4093FF1FCE92FF1FB992FF1F08B50C4B1B78DBB2042B07D0062B09D0022B0DD1BDE80840FFF7D8BFBDE80840FFF746BF0320FFF795FF034BE0
:4018400003221A7008BD00BFCE92FF1FB992FF1F08B5054B002201201A70FFF785FF034B03221A7008BD00BFCE92FF1FB992FF1F08B50A4B1A7832B11A78094942F0800273
:401880000A7000221A70074B002201201A70FFF76BFF054B03221A7008BD00BFB892FF1F08600040CE92FF1FB992FF1F074B1B78DBB2042B05D0062B05D0022B05D1FFF794
:4018C000A1BEFFF7C5BFFFF7D3BF7047CE92FF1F38B51D4C2378DBB2DD0634D518060AD503F00F03012B2ED1FFF74EFF174B1B78190609D538BD5A0602D5FFF7D7FF03E0AB
:401900009D0620D5FFF786FF23781B061BD4104B1A78104B1B7813430F4A13701278934211D10A4A0849154613782078DBB2000605D41378DBB20B700B7803F00F03287851
:401940008342F1D138BD38BD28600040B992FF1FCA92FF1F4193FF1F29600040054A00231380054A916819B191680B7092685380704700BF4493FF1FBC92FF1F0E4808B54E
:4019800003889BB213B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FF094AD1799379028843EA012392B2934238BF0380FFF728FE012008BDBC92FF1FCE92FF1F3B
:4019C000CA92FF1F00600040084B01221A700F3B9B7C074B1A7B02F00302012A1EBFDA7B82F08002DA7301225A7370470B600040D492FF1F094B02221A700F3B93F82230F1
:401A0000074B1A7E02F00302012A1EBFDA7E82F08002DA7601225A76704700BF0B600040D492FF1F0B4B04221A700F3B93F83230094B93F8242002F00302012A1EBF93F89F
:401A4000272082F0800283F82720012283F82520704700BF0B600040D492FF1F0B4B08221A700F3B93F84230094B93F8302002F00302012A1EBF93F8332082F0800283F815
:401A80003320012283F83120704700BF0B600040D492FF1F7047FFF741BC0000F0B5184B184E19780C27C9B201234FF0000C31B3CA0720D5144A4FEA031E7244947850788D
:401AC0002040C5070DD507FB03652C79240608D5147804F0FE0414706D790C4CEDB204F80E50840706D507FB036425792D0658BF84F801C090700133DBB24908D7E7F0BD14
:401B00009F600040D492FF1F70600040FE5F004000F0ACBC70B50446184B88B003AA03F11006154618685968083303C5B3422A46F7D11B782B70FCB12223237001AD0323C9
:401B40002846637000F08AFE002220461146AB5C08AC04EB131414F8144C03F00F03847008AC234413F8143C0132082AC1700371417100F10400EAD108B070BDDF390000B8
:401B80002DE9F0431C4D01222E460C201F274FF0800E4FF0080C194B00FB02581401234418705F70164998F805902144B9F1000F07D098F8044024064CBF887081F802C009
:401BC00001E081F802E000FB0261CC880132E4B29C71CC88092AC4F30724DC71CC88E4B21C71C988C1F307215971D4D1054BFF221A70BDE8F08300BFD492FF1F70600040F2
:401C0000FC5F00400A600040064B074A1B7802EBC30253681A7C824286BF03EBC003586900207047C892FF1F403A00002DE9F84F424B1A78002A7ED01878414D0138C0B226
:401C4000FFF7E2FFA8463F4AC3681478007ADFF800C1E4B203EBC0000C2600274FF0010E834268D01A78A24263D11CF80420597891425ED19A7893F8039002F07F0206FB0C
:401C800002FA05EB0A01CF7093F802B009F0030981F804B093F803B005F80AB0B3F804A0A1F808A093F902A0BAF1000F0BDAB9F1010F0CBF4FF007094FF00D0981F80590F6
:401CC00081F801E009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEA02191A4906FB0282494481F802E0B2F808A0CAF3072A81F800A0B2F808A05FFA8AFA81F88C
:401D000001A0B2F806A011495FFA8AFA494481F806A0B2F80690C9F3072981F80790B2F806905FFA89F981F80490D288C2F307224A71083394E7BDE8F88F00BFCD92FF1F5A
:401D4000D492FF1FC992FF1FFC5F004070600040BA92FF1F08B5064B18780138C0B2FFF753FF20B143681B7900EBC300406908BDCD92FF1F00212DE9F84F0B464E4E0C2779
:401D800007FB01F401313219092933554FF000059370494CD3701381937253705371EFD118B1464B1D70464B1D70464B1A78002A7FD0187801250138C0B2FFF725FFA846EC
:401DC0004368DFF8F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010202F0FE021A70027AD20007FB0541C36803EB02094B4531D093F802A00AF07F06AE4229D1E5
:401E00000E89B3F804B0B6B25E4538BFA1F808B01E7893F801B01EF80660B3451AD181F804A0DE780E7093F902A0DE78BAF1000F06F0030607DA012E0CBF07260D264E7100
:401E400081F8018006E0012E0CBF052609264E7181F801C00833CBE70135092DC3D1C1680A328B1C0A440C200833934209D013F8081C13F80A5C01F07F0100FB01418D728C
:401E8000F2E7FFF767FF114B0121186000230C2000FB0142D3801289013113449BB203F00102134409299BB2F2D1BDE8F84FFFF767BEBDE8F88F00BFD492FF1FBA92FF1F37
:401EC0004293FF1FCD92FF1FCB92FF1FD092FF1F114B1B7903F07F035A1E072A19D80F490C2202FB031291781B0141F0010191700021D170517841F002015170127912F0DA
:401F0000800F074A1A4414BF8D2389239370FFF715BC0020704700BF00600040D492FF1FFC5F004030B4194B1A7902F07F02531E072B27D8164B0C2404FB02339978154DED
:401F400001F0FE0199700021D97029461201505D114400F07F0050555A7802F0FD025A701A795B78120605D5012B01D18C7006E00D2303E0012B0CBF082309238B7030BC59
:401F8000FFF7DCBB002030BC704700BF00600040D492FF1FFC5F004010B50D4B0D4C21791878C9B20138C0B2FFF72EFE43681B798B4201D2012909D8074A0848535CDBB2A4
:401FC0004354A3780120DBB2535410BD002010BDCD92FF1F00600040BA92FF1F4293FF1F38B58A4A8A4C13780021DBB221801806517840F18D800A2900F20581DFE811F09D
:402000005D00030103010301030103010B0003017E0003018200D3787C49012B09D17D4B1A787D4B03EBC2035B685B686360122310E0CB78022B12D18878FFF7E5FD002870
:4020400000F0E180436863606368DA7863689B7843EA02232380BDE83840FFF78FBCCB78032B26D16D4B00228878D5B2854209D3664A91786A4AEE2908BF1346634A91788D
:4020800081B106E0187801320028F1D018780344EAE764499278097C914203D16248FFF739FD614B1A78002A00F0AD801A78228018E0BDE8384000F02BBF13F0030313D067
:4020C000022B40F0A0802380504B0C211B7903F07F02564B01FB02339A78554BD2B21A7000225A706360B6E702222280514A11784F4AC9B2117053706260ACE701232380B4
:402100004D4BEFE70123238013794C4A1344E9E701390A2977D8DFE801F037764F76067676760A7620009378454ADBB25AE0937803F0FF0153B9404B1A7891425FD0197020
:40214000404B01201870FFF715FE58E0481EC0B2FFF75AFD0028EED155E0FFF71DFF002851D02A4A384913791279DBB2D2B20A70364A3249D25CCB5C9A4240D0314B0122DC
:402180001A70FFF753FD3AE003F00303012B2BD009D3022B37D11D4B9B78002B33D1BDE83840FFF7BFBE194B9B78012B2BD1214A137803F0FD0315E003F00303012B13D045
:4021C00008D3022B1FD1114B9B78E3B9BDE83840FFF77EBE0D4B9B78012B14D1154A137843F0020313700AE0084B1A795AB998781B791749DBB2CA5C22EA0002CA54BDE801
:402200003840FFF79BBA002038BD00BF00600040BC92FF1FC892FF1F403A0000A43A00002C3A0000173B00006093FF1FD492FF1F7992FF1FCB92FF1FCD92FF1FBA92FF1F53
:40224000B892FF1FCC92FF1FC992FF1F4293FF1FCF92FF1F074B1A78120609D55B78012B06D1054B054A5A6012781A80FFF786BB0020704700600040BC92FF1F043A00000A
:40228000014B1870704700BF7A640040014B1878704700BF6B650040014B1870704700BF79640040064A0123136002F688321268E0211064034A1170A2F540721360704724
:4022C00080E100E000E400E0014B1870704700BF7A650040014B1870704700BF7865004073B515461E460B4C05230022019200920A4601461846237000F064F932462946A0
:40230000207800F01FF90221207800F009F9207802B070BDD080FF1F064A0423136002F688321268E0219064034A1170A2F202321360704780E100E002E400E0014B042227
:402340001A60704700E100E0014B04221A60704780E100E0014B1870704700BF7E640040704738B505460078012428B100F068FD285D0134E4B2F8E738BD08B50D2000F063
:402380005FFDBDE808400A2000F05ABDF7B516461F460B4C00230325019300930A4601462846257000F00EF93A463146207800F0C9F80221207800F0B3F8207803B0F0BD4E
:4023C000E080FF1FF7B516461F460B4C00230225019300930A4601462846257000F0F2F83A463146207800F0ADF82946207800F097F8207803B0F0BDE180FF1FF7B516468D
:402400001F460B4C00230125019300930A4601462846257000F0D6F83A463146207800F091F80221207800F07BF8207803B0F0BDE280FF1F73B515461E460B4C0023019385
:4024400000930A4601461846237000F0BBF832462946207800F076F80221207800F060F8207802B070BD00BFE380FF1F024B1878C0F38010704700BF8F450040074A7F236A
:40248000802113705170064A013BDBB202F80839002BF9D1034A1370704700BFE480FF1FF87B00400078004017280FD8084B0001C25C11B142F0200201E002F0DF02C25443
:4024C000C25C42F00102C25400207047012070471070004017280BD8064B0001C25C02F0FE02C254C25C02F0DF02C25400207047012070471070004017280DD8074900017F
:402500000B4603441A7942F004021A71435C43F00103435400207047012070471070004017280BD8064A0001835C490003F0F10301F00E011943815400207047012070476F
:402540001070004041F6FF73994208BF4FF400519A4208BF4FF4005217289FBFC00000F1804000F5EC4081809ABFC280002001207047000017289FBF034B00011954002048
:4025800088BF0120704700BF1970004017289FBF054B00011A5C01F007019DBF1143195400200120704700BF1470004017289FBF034B0001185C00F0070088BFFF2070470B
:4025C00014700040172810B51AD8C00001F07F0100F1804441EAC21204F5EC44D2B222709DF8082003F00F0343EA0213DBB263709DF80C30002003F00F03A370E07010BD73
:40260000012010BD10B500F07BFC0A4A5378182B0AD91478013B5370E30003F1804303F5F0431B78137000E0FF2400F06DFC204610BD00BFE480FF1F030610B5044611D408
:4026400000F05EFC084AE300117803F1804303F5F04319705378147001335370BDE8104000F052BC10BD00BFE480FF1F30B504060CD411F4704509D1C40004F1804404F5C7
:40268000F0442180A270E370284630BD012030BD03065FBFC00000F1804000F5F04081805ABFC280002001207047000038B50446084DB4F5004F05D9286800F019FCA4F5D6
:4026C0000044F6E7034B58686043BDE8384000F00FBC00BFEC80FF1F024B1B7A584300F007BC00BFEC80FF1F0E4B00F003001A78490102F0FC02104318701A7801F0600197
:4027000042F080021A701A7802F07F021A701A7802F09F020A431A701A7842F010021A70704700BF83430040014B01221A70704784430040044B00F00F021B6853F82200FD
:4027400043F82210704700BF08ED00E0054A00F01F00126800F1100352F8230042F82310704700BF08ED00E000F01F0000F16040490100F56440C9B2017070470F4B10B55B
:402780000F4900240F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040313600A4B4FF47A72DB68B3FBF2F3084A1360084B4FF400421C60C3F8E82010BD7C92FF1FCF
:4027C0002928000010E000E0EC80FF1F14E000E018E000E0024A136843F002031360704710E000E008B5FFF7F5FF034A136843F00103136008BD00BF10E000E010B5054C06
:40280000A3691BB9FFF7BAFF0123A361BDE81040FFF7E8BF7C92FF1F024B1868C0F30040704700BF10E000E038B5FFF7F5FF012808D1054D002455F8243003B19847013465
:40284000052CF8D138BD00BF8092FF1F024B03EB80035868596070477C92FF1F134B144A1B78DBB20360127843EA0223114A0360127843EA0243104A0360127843EA0263B2
:4028800003600E4B0E4A1B78DBB24360127843EA02230C4A4360127843EA02430A4A4360127843EA02634360704700BF0301004904010049EC4600400201004901010049A0
:4028C00000010049050100490601004910B500F017FB204A044613780A2043F002031370137C43F00203137412F80A3C43F0010302F80A3C937943F00103937102F5AB52B4
:40290000137843F003031370134B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222183B1A70094A137843F008031370CB
:40294000FFF7CAFE064B10222046BDE810401A6000F0DABAAB4300400E5900402F5B004080E200E008B500F0CBFA0F4A137803F0FE031370A2F5AA521D3A137803F0FD03A7
:402980001370137C03F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE039371BDE8084000F0B1BA00BF08590040044A137803F03F0343EA8010C0B21070704700BF28
:4029C00008590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F664
:402A0000C41393FBF1F305490B60054B1A8070470A590040F03900004A93FF1F4C93FF1F5093FF1F08B5102000F0A6F907210420FFF79AFE07490420FFF788FE064A0C206C
:402A4000137843F006031370FFF7BCFF034B00221A8008BD1D2B0000095900404893FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF72ABF9892FF1F044B1A789F
:402A800002F0FB021A701A7842F001021A7070470859004010B5084B1C7814F0010403D10028F9D0002404E02046FFF715FE024B1B78204610BD00BF09590040034A044BF3
:402AC0001B881088181A00B2704700BF5093FF1FA25B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F3D7
:402B00000028D8BF5B42134493FBF1F000B270474A93FF1F4C93FF1F4893FF1F7047000010B500F0EDF9214A044613780A2043F001031370137C43F00103137412F80A3C06
:402B400043F0020302F80A3C937943F00203937102F5AA521832137843F003031370144B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8DF
:402B8000062CA3F597530222123B1A70094A137843F008031370FFF79FFD074B08222046BDE810401A6000F0AFB900BFAB43004006590040275B004080E200E008B500F04F
:402BC0009FF90F4A137803F0FE031370A2F5AA52153A137803F0FE031370137C03F0FE03137412F80A3C03F0FD0302F80A3C937903F0FD039371BDE8084000F085B900BF94
:402C000000590040044A137803F03F0343EA8010C0B21070704700BF00590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF60
:402C400033F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A80704702590040FA3900005693FF1F5C93FF1F5493FF1F08B51020DA
:402C800000F084F807210320FFF76EFD07490320FFF75CFD064A0C20137843F006031370FFF7BCFF034B00221A8008BD752D0000015900405893FF1F10B5054C23781BB92A
:402CC000FFF7DCFF01232370BDE81040FFF728BF9992FF1F044B1A7802F0FB021A701A7842F001021A7070470059004010B5084B1C7814F0010403D10028F9D0002404E0B4
:402D00002046FFF7E9FC024B1B78204610BD00BF01590040034A044B1B881088181A00B2704700BF5493FF1FA05B00400E4A13881BB223B111880A2309B2594301E00B4B8D
:402D400019680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475693FF1F5C93FF1F5893FF1F70470000034A00F0F8001378F0
:402D800003431370704700BF02410040034A00F0F800137803431370704700BF06410040014B1870704700BF78640040014B1870704700BF7965004073B515461E460B4CBF
:402DC00004230022019200920A46014618462370FFF7F8FB324629462078FFF7B3FB02212078FFF79DFB207802B070BDFC80FF1F074A0223136002F688321268E0215064AD
:402E0000044A11706FF440710A441360704700BF80E100E001E400E0014B1870704700BF7C640040014B1870704700BF7B640040014B1870704700BF7F6400400000000032
:402E4000FEB5494652465B460EB40746244909688A46244A12682448022100F071F8030020480068C018204900F06AF8143883460121C9430C460125002600F041F8814613
:402E800051460B7823400B705846013000F030F83800F04028400B78234003430B70584600F026F80136072EF2D9002001300138013001200B78234003430B705846043084
:402EC00000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF30080D480FF1FF880FF1F00C20100000000000230800803D000BF01380046FCD17047FA
:402F0000EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D113790C2103F07F02044B01FB02339B7A00E013790020704700600040D492FF1FB2
:402F4000002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB11217047704700BF014B1868704700BF6081FF1F0E4B70B51E460E4C0025E41A7D
:402F8000A410A54204D056F8253098470135F8E700F0DEFD084B094C1E46E41AA4100025A54204D056F8253098470135F8E770BD5C3B00005C3B00005C3B0000643B00007A
:402FC00003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B604FF0FF301DE04FF40273ADF80C300CBF234604F1FF33029305934FF6FF730091F6
:403000000491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B00221A701BB030BD000007B5009313460A46014603480068FFF7CBFF03B05DF804FB0F
:403040006081FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD02568096902236F1A656905EB450595FBF3F57B1C43449D4238BF1D4653050FD52946F8
:4030800000F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A4600F098FB064670B92169504600F0E8FA0C23CAF80030A3894FF0FF3043F04003F2
:4030C000A381BDE8F08726613E44266046466561ED1BA560464528BF464649463246206800F0B3FAA36800209B1BA36023681E442660BDE8F08700002DE9F04F9DB00393CD
:403100008B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F80030CDE040236B610023099320238DF82930DFF89CB130238DF82A3037463C461B
:4031400014F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF771FF013000F0A780099B4B4409933B78002B00F0A08000234FF0FF320493079332
:40318000059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46801A06FA00F018430490EFE7D90644BF20228DF853201A0744BF2B228DF853208C
:4031C00022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F00200079204900BE027463B780134303B092B03D800FB02320121F5E701B107923B786F
:403200002E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E0002319460593781C0A2407463A780130303A092A03D804FB01210123F5E703B109
:40324000059103223978224800F0E6F940B14023CBEB000003FA00F0049B013718430490397806221B487E1C8DF8281000F0D4F988B1194B33B9039B073323F007030833E3
:40328000039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A9404600F093F8B0F1FF3F824603D0099B5344099342E7AB895B0601D4099801E019
:4032C0004FF0FF301DB0BDE8F08F00BF2B3B0000313B0000353B000000000000453000002DE9F04791461F460A698B6806469342B8BF1346C9F8003091F843200C46DDF815
:40330000208012B10133C9F800302368990642BFD9F800300233C9F80030256815F0060510D104F1190A07E00123524639463046C04701301AD00135E368D9F800209B1A3C
:403340009D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F845102244023382F8431003E04FF0FF30BDE8F08704F1430239463046C0470130D7
:40338000F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500259342C4BF9B1AED184FF000091A344D4509D00123224639463046C0470130D5D08C
:4033C00009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9B01F1430E00F0AE8011D8632A22D009D8002A00F0BB80582A40F0CA8081F84520BC
:40340000834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B8800A6842F020020A603EE0752A24D0782A3AD0ADE01A6801F14205111D19601368C2
:4034400084F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F10401196002D0B2F9003000E01368002B3CDA2D225B4284F8432037E021681A6811F0800F53
:4034800002D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A14BF0A2208221BE078225A4984F845202268186812F0800F00F104051D6003D11E
:4034C000550601D5038800E00368D00744BF42F0200222601BB9226822F0200222601022002084F8430001E049490A226568002DA56008DB206820F0040020602BB9002D42
:403500007DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D1082A0BD12368DA0708D5236962689A42DEBF302305F8013C05F1FF35C5EB0E0360
:4035400023612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F104001860136801D0198000E0196000232361754616E01A68111D1960156800216268C6
:40358000284600F049F808B1401B6060636804E004F1420584F8422001232361002384F84330CDF800803B4603AA21463046FFF797FE013002D14FF0FF3026E023692A46C6
:4035C00039463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046C0470130E7D00135E368039A9B1A9D42F2DBE068039B9842B8BF184605E00B7890
:4036000004F1420584F842308AE705B0BDE8F083DF3900003C3B000010B5C9B202449042034605D01C7801308C42F8D1184610BD002010BD10B5431E0A44914204D011F885
:40364000014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21AD34204D013F8014D01F8014DF8E710BD994204D011F8014B02F8014FF8E710BD08
:4036800038B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B96360146030E0A3420DD92268A018834201BF18685B681218226063600C6023E087
:4036C000A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D110685268014419605A600DE002D90C232B6009E021686018824201BF1068526861
:403700000918216062605C602846BDE8384000F098B838BDA092FF1F70B5CD1C25F0030508350C2D38BF0C25002D064601DBA94202D90C23336046E000F082F8234B1C6831
:403740001A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B681360636018BF0C4615E00C464968E9E7174C23681BB9304600F052F8206029464B
:40378000304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B00231D20F00700C31A0ED05A42E25070BD211A304600F034F80130EBD10C2333609D
:4037C000304600F03EF8002070BD00BFA092FF1F9C92FF1FF8B5074615460E4621B91146BDE8F840FFF798BF1AB9FFF749FF2846F8BD00F027F885420ED929463846FFF7AD
:403800008BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD38B5064C0023054608462360FDF7D6FB431C02D1236803B12B6038BD8493FF1F15
:403840007047704751F8040C0028BEBF091851F8043CC0180438704700000000050209020B020D020F021102130215027265706C792030782530327800686F6D696E670074
:40388000626567696E6E696E67207365656B2066726F6D20256420746F2025640066696E6973686564207365656B00796573006E6F00647269766520303A202573206472D4
:4038C00069766520313A2025730057616974696E6720666F72205553422E2E2E0055534220726561647900636F6D6D616E6420307825303278006661696C2025642B2564B5
:403900002B2564203D3D2025642C206E6F74202564007061737365643D256400756E64657272756E206166746572202564207061636B65747300636F756E743D2564206933
:403940003D256420643D256400636D645F777269746500703D25642063723D25642063773D256420663D256420773D256420696E6465783D256420756E64657272756E3D51
:40398000256400756E64657272756E2100737563636573730073746172742065726173696E670073746F702065726173696E670069646C65000051004010004051004030AB
:4039C0000000000140001000140140000800400140000A004C0140000200500140200030313233343536373839414243444546000001000000040000001000010000000492
:403A00000000001028000000000104000100000000000000000157494E5553420000303030303100000000000000000012034D00530046005400310030003000010000009D
:403A400001000000483A000001000000173B0000000000000000000001000000603A000001000000E93A000004000000823A0000000000000000000000000000803A000037
:403A8000FF00000001024000FF00000082024000FF00000003034000FF00000084034000FF00020304030904160346006C007500780045006E00670069006E0065002A03A3
:403AC00043006F0077006C00610072006B00200054006500630068006E006F006C006F0067006900650073000009022E0001010080320904000004FF0000010705010240A2
:403B00000000070582024000000705030340000A0705840340000A12010002FF0001080912006E0100020180014300232D302B2000686C4C00656667454647003031323359
:403B400034353637383961626364656600000000F8B500BFF8BC08BC9E4670475900000059100000F8B500BFF8BC08BC9E46704735000000883B0000C880FF1FA0000000EB
:403B80002012000000000000000000008893FF1FFF000000675000400C00000007000000FFFFFFFF7F8000003F0000000000007D00FA0000400000000090D003FF0000003E
:403BC0000000000000000000000000000000000000000000000000000000000000000000293B00000000000000000000000000000000000000000000000000000000000061
:403C000000000000000000000000000000000000000000000000000000000000000000000081FF1F00000000000000000000000000000000000000000000000000000000E5
:403C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044
:403C80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004
:4000000000800020110000003110000031100000064A08B5136843F020031360044B1A6803F53F5302331A6001F020F8E8460040FA46004010B5054C237833B9044B13B18B
:400040000448AFF300800123237010BD6881FF1F00000000B8380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF0000000051
:400080006C81FF1FB8380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8881FF1FCC
:4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8881FF1F3F0000800E4BDA681AB901215F
:4001000019745A7410E05A7C1A741A7C0AB1002207E059699A69D8688A1A82428CBF002201225A745A699A611B7C13B1002002F043B970478881FF1F10B5C4B2204601F054
:4001400057F90128FAD110BD08B572B60F4B0F495A6901325A61DA690132C82A08BF0022DA611A6AD8690132A72A08BF00220A621B6A002B0CBF02230023002814BF184660
:4001800043F0010002F080FE62B608BD8881FF1F38B50446C5B2284602F0B0F8062002F0CDFA44F00200C0B202F0A8F8062002F0C5FA284602F0A2F8BDE83840062002F0C2
:4001C000A7BA10B5642402F093F828B9FFF7E0FF013CF8D1204610BD012010BD70B5C4B2054620460E4601F003F9012805D0204601F01CFA2846FFF79FFF204601F000F9AD
:40020000314605460246204601F0BCF9204601F0EFF80028FAD1284670BD000038B5044D0024285D013402F039FA402CF9D138BDAC81FF1F08B502F053FC002002F05CFCBD
:4002400002F06EFC02F078FC80B208BD10B50446012002F06BF8642002F05AFAFFF7EAFF2080002002F062F8642002F051FAFFF7E1FF608010BD08B502F05EFD002002F02B
:4002800067FD02F079FD02F083FD80B208BD10B50446FFF796FF322002F03AFAFFF7EBFF20800120FFF774FF322002F031FAFFF7E2FF608010BD0FB400B593B014AB53F835
:4002C000042B402102A8019302F0DEFE02A802F07AF802F084F813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF723FF62782146BDE81040042001F0D0B8B5
:40030000CC38000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8643043B1002001F0D8FF002002F06BFD002384F864304E
:4003400010BD00BF8881FF1F38B5104D837895F8672004469A4204D0FFF7E4FF002385F86A302368C5F865302279094B1A71A378002B14BF0220012002F04AFDE07802F02E
:4003800041FD2079BDE8384002F078BD8881FF1FED81FF1F38B50D4C94F8645065B904F16500FFF7D1FF012001F09CFF4FF47A7002F0AEF984F86A506369E366012384F84D
:4003C0006430BDE8384002F0E1B900BF8881FF1FF8B5214C0546FFF7DDFF94F86A3003B15DB91E48FFF767FFFFF7EBFE0120002384F86A00236702F0A1F92A46216F184872
:40040000FFF759FF144E0027236F9D4216D001F06FFF00B13767236F9D4205DD0120FFF7B7FE336F013305E005DA0020FFF7B0FE336F013B336702F0A9F9E5E7322002F047
:4004400067F92A2DCCBF0020012002F023FDBDE8F8400448FFF72FBF8881FF1FD9380000E0380000FD3800002DE9F04F99B062B602F0F6F99D49042002F01AFA9C4801F0D9
:4004800043FF9C4802F0E6FC9B4801F077FF02F0C7FB02F099FA002002F0BAFC01F092FF0221002000F05AFF944C012001F0D2F8002384F86730FFF76DFFFFF782FE84F8A3
:4004C0007400FFF72FFF012384F86730FFF762FFFFF777FE84F87500FFF724FF884B94F87400884994F875202546002A14BF0A461A46002808BF19468348FFF7DCFE032177
:40050000084602F023F9264602F040F994F8643043B1EA6E6B699B1A41F28832934201D9FFF700FF00F052FF18B97848FFF7C3FE04E000F051FF0028F7D10BE000F046FF85
:4005400010B902F023F9F9E77148FFF7B4FE032001F06CF8032000F04BFF0128D4D16D48FFF7F2FE6C490320FFF738FE94F876106A48FFF7A0FE94F87630023B142B00F229
:40058000D483DFE813F01500D2031E00D2032400D2034F00D2037500D203D700D203BF01D2030603D2032A03D2033103D2034B0303238DF820308DF821300F238DF8223084
:4005C00028E394F87800FFF703FF554B1FE3FFF7E1FE002323746069227C02F0FF0132B96B691B1AB3F57A7FF6DD0B4611E083B10022174696F878107069277494F810E007
:40060000BEF1000F02D16B691B1AF7E701329142F3DA07228DF8202004228DF82120ADF82230F7E20220FFF787FD4FF000080DF1200A02F0ABF84FF480790027C9EB080303
:40064000DA1907F80A200137402FF9D10220FFF773FD3A465146022000F022FFB9F10109EBD108F10108B8F1400FE2D12D4B38E04FF0010A4FF000080DF1200B02F086F844
:400680004FF0000959460120FFF7A8FD08EB090300270493049B1BF807203B44DBB29A4209D08DE80C0041463B464A461E48FFF702FE4FF0000A0137402FEBD109F101091B
:4006C000B9F5807FDED108F10108B8F1400FD5D151461648FFF7EFFDBAF1000F00F01A81134B1B8807A8ADF81C3094E249010000F900000091000000C50000008881FF1F0B
:400700000F3900000B390000123900002A3900003D390000ED81FF1FFE81FF1F47390000BC380000BE3800005639000072390000C0380000206FFFF74BFE94F8780001F081
:40074000F5FD94F8780001F0D9FD02F009FCB94BDFF8FC821A78002702F0FB021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F0F7FB0220FFF7DCFCF9
:40078000012141F6FF734FF48042084602F046FB84F8B60001F068FF08F807000137402FF8D1DFF8B0A200270AF195091FFA89F80137402F14BF3A4600221AF8010F2244C0
:4007C000062392F82420402101F082FF424646F24E419AF8000001F08DFF08F14008402F1FFA88F8E4D196F8793053B196F87C30F36000233374237C002BFCD000233374E1
:40080000F36000234FF0FF32236062602372236894F8B600234493F8241001F0DDFE94F8B60001F09BFE012194F8B60001F06EFE2368002BFCD0002398467360D6F814A0D4
:40084000012701F0A3FF6369B4F87A20CAEB030393420DD367B1042195F8B60001F0C8FE94F8B60001F0D4FE0028F9D107463072237AFBB96A682B689A4202D1002FE0D1D9
:4008800018E00220FFF758FC6968402209EB8111022000F005FE6A68674B01321340002BBEBF03F1FF3363F03F03013308F101086360C6E70220277AFFF73EFC0022114687
:4008C000022000F0EDFD0220FFF736FCFFB2FFF7A5FC002001F012FD37B15848FFF7EBFC0220FFF70FFD06E0554B08A81B88ADF82030FFF7F5FC227C4146237A5148FFF7BB
:40090000DAFC15E25048FFF7D6FCD4F87A7017F03F0701D0032009E2286FFFF759FD95F8780001F003FD95F8780001F0E7FC012001F002FD02F014FB444BDFF814811A7857
:4009400042F004021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F003FB01214FF4804341F6FF72084601F0E9FC85F8B60001F077FE08F8070001378C
:40098000402FF8D1DFF8CC90002709F195031FFA83F804930137402F14BF3A46002219F8010F2244052392F82420402101F090FE414646F24B5299F8000001F09BFE08F1FF
:4009C0004008402F1FFA88F8E4D100274FF0FF33376098467360BB463B46D6F87A9037725FEA99190CBF4FF0010A4FF0000A2168114A01310A40002ABCBF02F1FF3262F03B
:400A00003F026068B8BF013282426FD02BB1227A002A7AD12A7C002A77D12068049A059302EB8010BAF1000F16D040223F2102F0F7FA1CE09E6400403F0000807C390000C9
:400A4000C238000096390000A93900009B650040AC81FF1FAB81FF1F014601370120FFF7BDFBC7EB0903D3F1000A4AEB030A2168B34A01310A40002ABEBF02F1FF3262F0BB
:400A80003F02013222606268059B01322ED12A683F2A2BD14FF00008C5F8048001F080FC85F808806B6895F8B6002B4493F8241001F092FD95F8B60001F050FD012195F8BF
:400AC000B60001F023FD95F87E30EB6085F81080237C002BFCD04FF00008012086F8108001F06AFC404601F027FC00E023B1237A5BB92B7C4BB90123626842453FF477AFC1
:400B00000BF1010BD5F8048071E701F04FFC012001F012FC002001F04FFC042194F8B60001F066FD94F8B60001F072FD80460028F8D196F8B60001F0FFFC337C327A029318
:400B4000012303920193CDF800A05B463A4649467C48FFF7B0FBC6F80C80BAF1000F0BD0FFF75CFB002001F0C9FB237A63B17648FFF7A1FB0220D9E0B945F1D07349012035
:400B8000FFF72CFB0137F7E77148FFF794FB714B3DE094F8780001F0C9FB206FFFF718FC6D48FFF788FB94F87930E36000232374237C002BFCD0012001F0FEFB00233374CE
:400BC000237C002BFCD0002001F0F6FB00236348F360FFF770FB624B19E0002084F86A00FFF7F6FB5F4B12E094F8743023B195F875200AB985F8782094F875201AB113B9F6
:400C0000012385F878305848FFF79EFB574B1B88ADF8203008A8FFF763FB89E0FFF782FB02F07CF8002002F01FF82A2701F04AFF002001F0EDFE3A46002108A802F0F0F9E0
:400C400017238DF820308DF8217001F09FFD002001F048FB002002F0DBF8C82001F058FD0DF12200FFF7F2FA0DF13600FFF70FFB01F08CFD012002F0CBF8322001F048FD4D
:400C80000DF12600FFF7E2FA0DF13A00FFF7FFFA012001F027FB4FF4967001F039FD01F075FD0DF12E00FFF7D1FA0DF14200FFF7EEFA002001F016FB4FF4967001F028FD84
:400CC00001F064FD022002F0A3F8322001F020FD0DEB0700FFF7BAFA0DF13E00FFF7D7FA012001F0FFFA4FF4967001F011FD01F04DFD0DF13200FFF7A9FA0DF14600FFF756
:400D0000C6FA002001F0EEFA4FF4967001F000FD01F03CFD002002F07BF8002384F86A3001F07EFF01F050FE74E70120FFF7EAFA032000F07BFC0E48FFF7BDFAFFF7E4BBB6
:400D40003F000080B3390000E33900004092FF1FED390000C4380000F5390000033A0000C6380000C8380000FE81FF1FCA380000103A00002DE9F04172B6884B61221A70F9
:400D8000A3F5F06301221A801924854A9C7092E803008033062283F8002283E80300522203F580731A707F4B7F4A1B787F4EDBB2137040F618027E4B00251A8041F25122A6
:400DC00023F8022C33784FF4F07003F0010343EA450502F0BDF8013C05F003052ED0032DF0D1744B4FF480721A8007221A70724A002548211570917002221D705D7103F893
:400E0000032C0422DA716D4A6D4C13786D4E43F00103137012F8013C062743F0030302F8013C2378012243F0800323705B4B1A70654A137843F02003137000E0FEE707FBAD
:400E4000056300219A881868013502F0E9F8072DF5D15E485E4E002550F8041F05F1105303F1480221F0FF074933C9B20B4452005B0002329A4206D012F802EC12F801CC0C
:400E80000EF807C0F5E7B0420D44E5D1514A002313609360136193614F4B504F1A68504BDFF888811A604F4B1A684F4B1A604F4A137843F002031370137C43F002031374F7
:400EC0002378A2F5863243F040032370413A137843F010031370464A464B07CA03C31A80454A2833106843F8250C127903F8212C424A07CA03C31A80414AE83B07CA03C33B
:400F00001A80404A083307CA03C31A803E4A3F4BA2F5616203CBC2F8100EC2F8141E1378042043F008031370394B02F5AA521B783D78DBB298F80060EDB203F007010C329F
:400F40001B091170F6B2537045F003033B7046F0030388F800302F4B48221A702E4A402313702E49937013729372082382F81F3220220A7048710A72294A0A20137001F0C7
:400F8000DDFB284B88F8006044223D70264D1A7094E80F0007C52B80BDE8F081004800405C0900480F010049A146004025420040224200400440004006400040A2430040AF
:400FC000A0430040153A0000E8460040FCFFFF478C0000480076004064090048F8460040207600406809004828760040035001401C090048C051004028090048300900485A
:401000003C090048480900483251004054090048CF0100491D51004001590040235B0040585B004076580040B0430040F946004008B501F0C9FF03680C2B00D1FEE7FEE79D
:40104000084908B50B68084A1844821A802A01DC086005E001F0B8FF0C2303604FF0FF33184608BDCC80FF1F9093FF1F80B51148114B0025C0B1A3F1100192C92246043933
:40108000161BB74204D051F8046F42F8046BF7E7114653F8046C8C1AA64202D041F8045BF9E701381033E5E701F094FFFFF7DCF9FEE700BF01000000E43B0000124A134BF0
:4010C00010B51A60124A134C1368134843F4007313600023032B98BF54F823204FEA830188BF0E4A0133302B4250F3D10C4B1A780C4B1A700C4B084A1A60FFF73BFEBDE8CA
:40110000104001F0EDB900BF0004FA050CED00E014ED00E0000000000080FF1F31100000BC760040C080FF1F08ED00E0F8B501F017FF4B4A01271378022643F001031370EA
:40114000137C484C43F001031374474B02F5E3521F700B3203F8946C1378054603F07F031370002001F0EAFA2378404A03F0F90323701378384603F0DF03137023783B4325
:40118000237001F0DBFA282001F0D8FA384B30461A7802F07F021A701A7802F0BF021A7023783343237001F0C9FA2378314A43F0040323700023137053702F4AFF2199544A
:4011C0000133092BFBD1284601F0CEFE0721172001F0FCFA2949172001F0EAFA0721182001F0F4FA2649182001F0E2FA0721152001F0ECFA2349152001F0DAFA0721052032
:4012000001F0E4FA2049052001F0D2FA0721062001F0DCFA1D49062001F0CAFA0721084601F0D4FA1A49072001F0C2FA0721082001F0CCFA1749082001F0BAFA0021162047
:4012400001F0C4FA1449162001F0B2FA07210C2001F0BCFABDE8F84010490C2001F0A8BAA5430040944300409D60004012600040F851004084600040B592FF1FFB1A00008A
:4012800035190000F91A00002D1A0000591A0000891A0000C11A0000011B0000751B0000214B224A10B5187000231370204A40201370204A0F2413701F4A13701F4A1370D9
:4012C0001F4A13701F4A13701F4B4FF400021A604FF080721A604FF400121A6020221A601860802018604FF480701860174804704FF480001860164B1A70933B19B91A7851
:4013000002F0FE0202E01A7842F001021A70114B03221A70802203F8202C012001F018FE0D4B04221A7010BDD092FF1FD692FF1FD492FF1FD592FF1FD192FF1FC092FF1F97
:40134000D392FF1F4893FF1F00E100E09E6000409C600040286000401260004070B5074C054623780E461BB9FFF7E0FE0123237031462846BDE87040FFF792BF8092FF1F7A
:401380000A4A002313700A4A13700A4A13700A4A13700A4A13700A4A13700A4A13700A4B03221A70802203F8202C7047D692FF1FD492FF1FD592FF1FD192FF1FC092FF1F05
:4013C000D392FF1F4893FF1F28600040014B1878704700BFD592FF1F044B1A7802F0FF001AB118780022C0B21A707047D492FF1F024A0C2303FB002040787047DC92FF1FB8
:40140000431E072B0CD8074A064B00010344805C5B7800F00F0043EA0020023880B2704700207047FC5F00401A4A38B50C2303FB00231B79090C13F0800F00F1FF35044670
:4014400019BF8AB24FF480438BB24FF48042032D18D8DFE805F002070C110021084601F01BF80DE00021084600F0FAFF08E00021084600F0D9FF03E00021084600F0B8FF3E
:40148000054B1855EDB2072D03D801F0EDF8034B185538BDDC92FF1FAC92FF1FB592FF1F431E072B2DE9F0470446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB274
:4014C000C3F500739D424FF00C0303FB007388BFD588DB7884BFC5F50075ADB2254A43EA15230601B354B244EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FBBF
:40150000047200215170B9F1000F28D03DB31B4F385D01F011F811232946FE2218F8040001F0D6F806F5C04278321FFA89F118F8040001F0DFF8124D18F80410385D01F004
:401540004BF80121385D00F0E1FF735D43F002037355735D03F0FD037355BDE8F08703FB04746379DBB28AF80230BDE8F08700BFDC92FF1FFC5F0040B592FF1FAC92FF1FD4
:40158000706000402DE9F047044615468846002940D0431E072B3FD8FFF732FFA84203D22046FFF72DFF05461D4E335DFF2B03D141462046FFF738FFDFF868A027011AF893
:4015C000040000F0B9FF1223FE222946305D01F07FF807F5C0411FFA88F27831305D01F089F8DFF84490315D1AF8040000F0F4FF01211AF8040000F089FF17F8093043F0F1
:40160000020307F8093017F8093003F0FD0307F8093002E00D4600E000252846BDE8F087B592FF1FAC92FF1F70600040431E072B0AD8064A0C2303FB002300225A705A7991
:40164000034BD2B200011A54704700BFDC92FF1FFE5F0040431E072B9FBF024B000108221A547047FE5F004030B51A4A1A491B4D0878138803449BB21380194A00231488E7
:40168000D8B2A4B27CB1082B0CD050680078C0B2E85450680133013050601088013880B21080ECE718460B780E4C082B0E4A00D040B10E4D2B7883F080032B700F23237022
:4016C000022301E0022323701370094B1870087030BD00BF4C93FF1F4893FF1F00600040C492FF1FC192FF1FD692FF1FD292FF1F4993FF1F074B02221A70074B80221A70AE
:40170000064B0F221A70064A00231370054A012013707047D692FF1FD292FF1FC192FF1F4893FF1F4993FF1F30B5164B16491B780A8803F00F03023BDBB21A4492B20A80CC
:40174000124C134A0020118889B279B173B15568215C013BC9B229705168DBB20131516011880130013989B21180ECE7094A1370094A137883F080031370084B0B221A70DF
:4017800030BD00BF296000404C93FF1F00600040C492FF1F4993FF1FD292FF1FC192FF1F064A06231370064A01201370054B80221A70054B00221A70704700BFD692FF1F52
:4017C000C192FF1FD292FF1F4993FF1F054B9A683AB19A68044910709A680988518000229A607047C492FF1F4C93FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A1E
:40180000137008BD0220FFF7E1FF0D4B1B7803F06003202B05D0402B06D043B900F012FC04E001F0A5FB01E000F046FD10B9034B03221A7008BD00BF28600040C192FF1FC0
:401840000060004008B5084A084B0120197813880B449BB21380064B00221A70FFF7B6FF044B03221A7008BD4C93FF1F4893FF1FD692FF1FC192FF1F08B50C4B1B78DBB25E
:40188000042B07D0062B09D0022B0DD1BDE80840FFF7D8BFBDE80840FFF746BF0320FFF795FF034B03221A7008BD00BFD692FF1FC192FF1F08B5054B002201201A70FFF7B6
:4018C00085FF034B03221A7008BD00BFD692FF1FC192FF1F08B50A4B1A7832B11A78094942F080020A7000221A70074B002201201A70FFF76BFF054B03221A7008BD00BFA5
:40190000C092FF1F08600040D692FF1FC192FF1F074B1B78DBB2042B05D0062B05D0022B05D1FFF7A1BEFFF7C5BFFFF7D3BF7047D692FF1F38B51D4C2378DBB2DD0634D51B
:4019400018060AD503F00F03012B2ED1FFF74EFF174B1B78190609D538BD5A0602D5FFF7D7FF03E09D0620D5FFF786FF23781B061BD4104B1A78104B1B7813430F4A13705F
:401980001278934211D10A4A0849154613782078DBB2000605D41378DBB20B700B7803F00F0328788342F1D138BD38BD28600040C192FF1FD292FF1F4993FF1F29600040AD
:4019C000054A00231380054A916819B191680B7092685380704700BF4C93FF1FC492FF1F0E4808B503889BB213B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FFAC
:401A0000094AD1799379028843EA012392B2934238BF0380FFF728FE012008BDC492FF1FD692FF1FD292FF1F00600040084B01221A700F3B9B7C074B1A7B02F00302012A07
:401A40001EBFDA7B82F08002DA7301225A7370470B600040DC92FF1F094B02221A700F3B93F82230074B1A7E02F00302012A1EBFDA7E82F08002DA7601225A76704700BFFE
:401A80000B600040DC92FF1F0B4B04221A700F3B93F83230094B93F8242002F00302012A1EBF93F8272082F0800283F82720012283F82520704700BF0B600040DC92FF1F78
:401AC0000B4B08221A700F3B93F84230094B93F8302002F00302012A1EBF93F8332082F0800283F83320012283F83120704700BF0B600040DC92FF1F7047FFF741BC000081
:401B0000F0B5184B184E19780C27C9B201234FF0000C31B3CA0720D5144A4FEA031E7244947850782040C5070DD507FB03652C79240608D5147804F0FE0414706D790C4C35
:401B4000EDB204F80E50840706D507FB036425792D0658BF84F801C090700133DBB24908D7E7F0BD9F600040DC92FF1F70600040FE5F004000F0ACBC70B50446184B88B021
:401B800003AA03F11006154618685968083303C5B3422A46F7D11B782B70FCB12223237001AD03232846637000F08AFE002220461146AB5C08AC04EB131414F8144C03F053
:401BC0000F03847008AC234413F8143C0132082AC1700371417100F10400EAD108B070BD3F3A00002DE9F0431C4D01222E460C201F274FF0800E4FF0080C194B00FB02580B
:401C00001401234418705F70164998F805902144B9F1000F07D098F8044024064CBF887081F802C001E081F802E000FB0261CC880132E4B29C71CC88092AC4F30724DC71A2
:401C4000CC88E4B21C71C988C1F307215971D4D1054BFF221A70BDE8F08300BFDC92FF1F70600040FC5F00400A600040064B074A1B7802EBC30253681A7C824286BF03EB6F
:401C8000C003586900207047D092FF1FA03A00002DE9F84F424B1A78002A7ED01878414D0138C0B2FFF7E2FFA8463F4AC3681478007ADFF800C1E4B203EBC0000C260027FE
:401CC0004FF0010E834268D01A78A24263D11CF80420597891425ED19A7893F8039002F07F0206FB02FA05EB0A01CF7093F802B009F0030981F804B093F803B005F80AB0A6
:401D0000B3F804A0A1F808A093F902A0BAF1000F0BDAB9F1010F0CBF4FF007094FF00D0981F8059081F801E009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEAA3
:401D400002191A4906FB0282494481F802E0B2F808A0CAF3072A81F800A0B2F808A05FFA8AFA81F801A0B2F806A011495FFA8AFA494481F806A0B2F80690C9F3072981F8FE
:401D80000790B2F806905FFA89F981F80490D288C2F307224A71083394E7BDE8F88F00BFD592FF1FDC92FF1FD192FF1FFC5F004070600040C292FF1F08B5064B187801384A
:401DC000C0B2FFF753FF20B143681B7900EBC300406908BDD592FF1F00212DE9F84F0B464E4E0C2707FB01F401313219092933554FF000059370494CD37013819372537031
:401E00005371EFD118B1464B1D70464B1D70464B1A78002A7FD0187801250138C0B2FFF725FFA8464368DFF8F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010273
:401E400002F0FE021A70027AD20007FB0541C36803EB02094B4531D093F802A00AF07F06AE4229D10E89B3F804B0B6B25E4538BFA1F808B01E7893F801B01EF80660B34576
:401E80001AD181F804A0DE780E7093F902A0DE78BAF1000F06F0030607DA012E0CBF07260D264E7181F8018006E0012E0CBF052609264E7181F801C00833CBE70135092D89
:401EC000C3D1C1680A328B1C0A440C200833934209D013F8081C13F80A5C01F07F0100FB01418D72F2E7FFF767FF114B0121186000230C2000FB0142D38012890131134463
:401F00009BB203F00102134409299BB2F2D1BDE8F84FFFF767BEBDE8F88F00BFDC92FF1FC292FF1F4A93FF1FD592FF1FD392FF1FD892FF1F114B1B7903F07F035A1E072A3C
:401F400019D80F490C2202FB031291781B0141F0010191700021D170517841F002015170127912F0800F074A1A4414BF8D2389239370FFF715BC0020704700BF0060004065
:401F8000DC92FF1FFC5F004030B4194B1A7902F07F02531E072B27D8164B0C2404FB02339978154D01F0FE0199700021D97029461201505D114400F07F0050555A7802F013
:401FC000FD025A701A795B78120605D5012B01D18C7006E00D2303E0012B0CBF082309238B7030BCFFF7DCBB002030BC704700BF00600040DC92FF1FFC5F004010B50D4BA5
:402000000D4C21791878C9B20138C0B2FFF72EFE43681B798B4201D2012909D8074A0848535CDBB24354A3780120DBB2535410BD002010BDD592FF1F00600040C292FF1FB6
:402040004A93FF1F38B58A4A8A4C13780021DBB221801806517840F18D800A2900F20581DFE811F05D00030103010301030103010B0003017E0003018200D3787C49012B9C
:4020800009D17D4B1A787D4B03EBC2035B685B686360122310E0CB78022B12D18878FFF7E5FD002800F0E180436863606368DA7863689B7843EA02232380BDE83840FFF7EE
:4020C0008FBCCB78032B26D16D4B00228878D5B2854209D3664A91786A4AEE2908BF1346634A917881B106E0187801320028F1D018780344EAE764499278097C914203D180
:402100006248FFF739FD614B1A78002A00F0AD801A78228018E0BDE8384000F029BF13F0030313D0022B40F0A0802380504B0C211B7903F07F02564B01FB02339A78554BC3
:40214000D2B21A7000225A706360B6E702222280514A11784F4AC9B2117053706260ACE7012323804D4BEFE70123238013794C4A1344E9E701390A2977D8DFE801F0377608
:402180004F76067676760A7620009378454ADBB25AE0937803F0FF0153B9404B1A7891425FD01970404B01201870FFF715FE58E0481EC0B2FFF75AFD0028EED155E0FFF793
:4021C0001DFF002851D02A4A384913791279DBB2D2B20A70364A3249D25CCB5C9A4240D0314B01221A70FFF753FD3AE003F00303012B2BD009D3022B37D11D4B9B78002B47
:4022000033D1BDE83840FFF7BFBE194B9B78012B2BD1214A137803F0FD0315E003F00303012B13D008D3022B1FD1114B9B78E3B9BDE83840FFF77EBE0D4B9B78012B14D1B7
:40224000154A137843F0020313700AE0084B1A795AB998781B791749DBB2CA5C22EA0002CA54BDE83840FFF79BBA002038BD00BF00600040C492FF1FD092FF1FA03A000049
:40228000043B00008C3A0000773B00006893FF1FDC92FF1F8192FF1FD392FF1FD592FF1FC292FF1FC092FF1FD492FF1FD192FF1F4A93FF1FD792FF1F074B1A78120609D546
:4022C0005B78012B06D1054B054A5A6012781A80FFF786BB0020704700600040C492FF1F643A0000014B1870704700BF7A640040014B1878704700BF6B650040014B18702D
:40230000704700BF79640040064A0123136002F688321268E0211064034A1170A2F540721360704780E100E000E400E0014B1870704700BF7A650040014B1870704700BF89
:402340007865004073B515461E460B4C05230022019200920A4601461846237000F064F932462946207800F01FF90221207800F009F9207802B070BDD080FF1F064A04232E
:40238000136002F688321268E0219064034A1170A2F202321360704780E100E002E400E0014B04221A60704700E100E0014B04221A60704780E100E0014B1870704700BF30
:4023C0007E640040704738B505460078012428B100F066FD285D0134E4B2F8E738BD08B50D2000F05DFDBDE808400A2000F058BDF7B516461F460B4C002303250193009359
:402400000A4601462846257000F00EF93A463146207800F0C9F80221207800F0B3F8207803B0F0BDE080FF1FF7B516461F460B4C00230225019300930A4601462846257030
:4024400000F0F2F83A463146207800F0ADF82946207800F097F8207803B0F0BDE180FF1FF7B516461F460B4C00230125019300930A4601462846257000F0D6F83A463146DE
:40248000207800F091F80221207800F07BF8207803B0F0BDE280FF1F73B515461E460B4C0023019300930A4601461846237000F0BBF832462946207800F076F8022120782A
:4024C00000F060F8207802B070BD00BFE380FF1F024B1878C0F38010704700BF8F450040074A7F23802113705170064A013BDBB202F80839002BF9D1034A1370704700BFCC
:40250000E480FF1FF87B00400078004017280FD8084B0001C25C11B142F0200201E002F0DF02C254C25C42F00102C25400207047012070471070004017280BD8064B0001EA
:40254000C25C02F0FE02C254C25C02F0DF02C25400207047012070471070004017280DD8074900010B4603441A7942F004021A71435C43F001034354002070470120704740
:402580001070004017280BD8064A0001835C490003F0F10301F00E011943815400207047012070471070004041F6FF73994208BF4FF400519A4208BF4FF4005217289FBF85
:4025C000C00000F1804000F5EC4081809ABFC280002001207047000017289FBF034B00011954002088BF0120704700BF1970004017289FBF054B00011A5C01F007019DBFDC
:402600001143195400200120704700BF1470004017289FBF034B0001185C00F0070088BFFF20704714700040172810B51AD8C00001F07F0100F1804441EAC21204F5EC4422
:40264000D2B222709DF8082003F00F0343EA0213DBB263709DF80C30002003F00F03A370E07010BD012010BD10B500F079FC0A4A5378182B0AD91478013B5370E30003F1FB
:40268000804303F5F0431B78137000E0FF2400F06BFC204610BD00BFE480FF1F030610B5044611D400F05CFC084AE300117803F1804303F5F0431970537814700133537094
:4026C000BDE8104000F050BC10BD00BFE480FF1F30B504060CD411F4704509D1C40004F1804404F5F0442180A270E370284630BD012030BD03065FBFC00000F1804000F5D2
:40270000F04081805ABFC280002001207047000038B50446084DB4F5004F05D9286800F017FCA4F50044F6E7034B58686043BDE8384000F00DBC00BFEC80FF1F024B1B7AB4
:40274000584300F005BC00BFEC80FF1F0E4B00F003001A78490102F0FC02104318701A7801F0600142F080021A701A7802F07F021A701A7802F09F020A431A701A7842F060
:4027800010021A70704700BF83430040014B01221A70704784430040044B00F00F021B6853F8220043F82210704700BF08ED00E0054A00F01F00126800F1100352F8230009
:4027C00042F82310704700BF08ED00E000F01F0000F16040490100F56440C9B2017070470F4B10B50F4900240F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040334
:4028000013600A4B4FF47A72DB68B3FBF2F3084A1360084B4FF400421C60C3F8E82010BD8492FF1F8D28000010E000E0EC80FF1F14E000E018E000E0024A136843F0020334
:402840001360704710E000E008B5FFF7F5FF034A136843F00103136008BD00BF10E000E010B5054CA3691BB9FFF7BAFF0123A361BDE81040FFF7E8BF8492FF1F024B186891
:40288000C0F30040704700BF10E000E038B5FFF7F5FF012808D1054D002455F8243003B198470134052CF8D138BD00BF8892FF1F024B03EB80035868596070478492FF1F1F
:4028C000134B144A1B78DBB20360127843EA0223114A0360127843EA0243104A0360127843EA026303600E4B0E4A1B78DBB24360127843EA02230C4A4360127843EA024382
:402900000A4A4360127843EA02634360704700BF0301004904010049EC460040020100490101004900010049050100490601004910B500F015FB204A044613780A2043F07D
:4029400002031370137C43F00203137412F80A3C43F0010302F80A3C937943F00103937102F5AB52137843F003031370134B18221A7013F8012C42F0400203F8012C13F837
:40298000012C02F0FC0203F8012CCE2203F8062CA3F597530222183B1A70094A137843F008031370FFF7CAFE064B10222046BDE810401A6000F0D8BAAB4300400E59004026
:4029C0002F5B004080E200E008B500F0C9FA0F4A137803F0FE031370A2F5AA521D3A137803F0FD031370137C03F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE0388
:402A00009371BDE8084000F0AFBA00BF08590040044A137803F03F0343EA8010C0B21070704700BF08590040082804D00A280CBF8223C22300E0422308380E4AC0B2042812
:402A4000137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470A590040503A00005293FF1FB7
:402A80005493FF1F5893FF1F08B5102000F0A6F907210420FFF79AFE07490420FFF788FE064A0C20137843F006031370FFF7BCFF034B00221A8008BD812B0000095900402E
:402AC0005093FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF72ABFA092FF1F044B1A7802F0FB021A701A7842F001021A7070470859004010B5084B1C7814F0F1
:402B0000010403D10028F9D0002404E02046FFF715FE024B1B78204610BD00BF09590040034A044B1B881088181A00B2704700BF5893FF1FA25B00400E4A13881BB223B1D5
:402B400011880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475293FF1F5493FF1F5093FF1F29
:402B80007047000010B500F0EBF9214A044613780A2043F001031370137C43F00103137412F80A3C43F0020302F80A3C937943F00203937102F5AA521832137843F0030343
:402BC0001370144B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222123B1A70094A137843F008031370FFF79FFD074BEE
:402C000008222046BDE810401A6000F0ADB900BFAB43004006590040275B004080E200E008B500F09DF90F4A137803F0FE031370A2F5AA52153A137803F0FE031370137C9E
:402C400003F0FE03137412F80A3C03F0FD0302F80A3C937903F0FD039371BDE8084000F083B900BF00590040044A137803F03F0343EA8010C0B21070704700BF0059004016
:402C8000082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBDD
:402CC000F1F305490B60054B1A807047025900405A3A00005E93FF1F6493FF1F5C93FF1F08B5102000F084F807210320FFF76EFD07490320FFF75CFD064A0C20137843F035
:402D000006031370FFF7BCFF034B00221A8008BDD92D0000015900406093FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF728BFA192FF1F044B1A7802F0FB02D6
:402D40001A701A7842F001021A7070470059004010B5084B1C7814F0010403D10028F9D0002404E02046FFF7E9FC024B1B78204610BD00BF01590040034A044B1B88108822
:402D8000181A00B2704700BF5C93FF1FA05B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF86
:402DC0005B42134493FBF1F000B270475E93FF1F6493FF1F6093FF1F70470000034A00F0F800137803431370704700BF02410040034A00F0F800137803431370704700BF9A
:402E000006410040014B1870704700BF78640040014B1870704700BF7965004073B515461E460B4C04230022019200920A46014618462370FFF7F8FB324629462078FFF7AB
:402E4000B3FB02212078FFF79DFB207802B070BDFC80FF1F074A0223136002F688321268E0215064044A11706FF440710A441360704700BF80E100E001E400E0014B18708C
:402E8000704700BF7C640040014B1870704700BF7B640040014B1870704700BF7F640040FEB5494652465B460EB40746244909688A46244A12682448022100F071F803009B
:402EC00020480068C018204900F06AF8143883460121C9430C460125002600F041F8814651460B7823400B705846013000F030F83800F04028400B78234003430B705846AA
:402F000000F026F80136072EF2D9002001300138013001200B78234003430B705846043000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF3008039
:402F4000D480FF1FF880FF1F00C20100000000000230800803D000BF01380046FCD17047EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D181
:402F800013790C2103F07F02044B01FB02339B7A00E013790020704700600040DC92FF1F002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB0B
:402FC00011217047704700BF014B1868704700BF6081FF1F0E4B70B51E460E4C0025E41AA410A54204D056F8253098470135F8E700F0DEFD084B094C1E46E41AA41000251E
:40300000A54204D056F8253098470135F8E770BDBC3B0000BC3B0000BC3B0000C43B000003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B6026
:403040004FF0FF301DE04FF40273ADF80C300CBF234604F1FF33029305934FF6FF7300910491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B0022F6
:403080001A701BB030BD000007B5009313460A46014603480068FFF7CBFF03B05DF804FB6081FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD025685F
:4030C000096902236F1A656905EB450595FBF3F57B1C43449D4238BF1D4653050FD5294600F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A462B
:4031000000F098FB064670B92169504600F0E8FA0C23CAF80030A3894FF0FF3043F04003A381BDE8F08726613E44266046466561ED1BA560464528BF4646494632462068B9
:4031400000F0B3FAA36800209B1BA36023681E442660BDE8F08700002DE9F04F9DB003938B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F8D4
:403180000030CDE040236B610023099320238DF82930DFF89CB130238DF82A3037463C4614F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF7EA
:4031C00071FF013000F0A780099B4B4409933B78002B00F0A08000234FF0FF3204930793059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46E7
:40320000801A06FA00F018430490EFE7D90644BF20228DF853201A0744BF2B228DF8532022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F002006C
:40324000079204900BE027463B780134303B092B03D800FB02320121F5E701B107923B782E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E010
:40328000002319460593781C0A2407463A780130303A092A03D804FB01210123F5E703B1059103223978224800F0E6F940B14023CBEB000003FA00F0049B01371843049053
:4032C000397806221B487E1C8DF8281000F0D4F988B1194B33B9039B073323F007030833039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A937
:40330000404600F093F8B0F1FF3F824603D0099B5344099342E7AB895B0601D4099801E04FF0FF301DB0BDE8F08F00BF8B3B0000913B0000953B000000000000A5300000D1
:403340002DE9F04791461F460A698B6806469342B8BF1346C9F8003091F843200C46DDF8208012B10133C9F800302368990642BFD9F800300233C9F80030256815F00605F2
:4033800010D104F1190A07E00123524639463046C04701301AD00135E368D9F800209B1A9D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F84E
:4033C00045102244023382F8431003E04FF0FF30BDE8F08704F1430239463046C0470130F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500252C
:403400009342C4BF9B1AED184FF000091A344D4509D00123224639463046C0470130D5D009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9BE3
:4034400001F1430E00F0AE8011D8632A22D009D8002A00F0BB80582A40F0CA8081F84520834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B880A0
:403480000A6842F020020A603EE0752A24D0782A3AD0ADE01A6801F14205111D1960136884F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F104011960E2
:4034C00002D0B2F9003000E01368002B3CDA2D225B4284F8432037E021681A6811F0800F02D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A40
:4035000014BF0A2208221BE078225A4984F845202268186812F0800F00F104051D6003D1550601D5038800E00368D00744BF42F0200222601BB9226822F020022260102266
:40354000002084F8430001E049490A226568002DA56008DB206820F0040020602BB9002D7DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D131
:40358000082A0BD12368DA0708D5236962689A42DEBF302305F8013C05F1FF35C5EB0E0323612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F1040087
:4035C0001860136801D0198000E0196000232361754616E01A68111D1960156800216268284600F049F808B1401B6060636804E004F1420584F8422001232361002384F8A9
:403600004330CDF800803B4603AA21463046FFF797FE013002D14FF0FF3026E023692A4639463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046F8
:40364000C0470130E7D00135E368039A9B1A9D42F2DBE068039B9842B8BF184605E00B7804F1420584F842308AE705B0BDE8F0833F3A00009C3B000010B5C9B202449042CF
:40368000034605D01C7801308C42F8D1184610BD002010BD10B5431E0A44914204D011F8014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21A41
:4036C000D34204D013F8014D01F8014DF8E710BD994204D011F8014B02F8014FF8E710BD38B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B912
:403700006360146030E0A3420DD92268A018834201BF18685B681218226063600C6023E0A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D1F2
:4037400010685268014419605A600DE002D90C232B6009E021686018824201BF106852680918216062605C602846BDE8384000F098B838BDA892FF1F70B5CD1C25F003051A
:4037800008350C2D38BF0C25002D064601DBA94202D90C23336046E000F082F8234B1C681A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B6896
:4037C0001360636018BF0C4615E00C464968E9E7174C23681BB9304600F052F820602946304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B005E
:40380000231D20F00700C31A0ED05A42E25070BD211A304600F034F80130EBD10C233360304600F03EF8002070BD00BFA892FF1FA492FF1FF8B5074615460E4621B91146D1
:40384000BDE8F840FFF798BF1AB9FFF749FF2846F8BD00F027F885420ED929463846FFF78BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD40
:4038800038B5064C0023054608462360FDF7D8FB431C02D1236803B12B6038BD8C93FF1F7047704751F8040C0028BEBF091851F8043CC018043870470000000005020902A2
:4038C0000B020D020F021102130215027265706C792030782530327800686F6D696E6700626567696E6E696E67207365656B2066726F6D20256420746F2025640066696E08
:403900006973686564207365656B00796573006E6F00647269766520303A20257320647269766520313A2025730057616974696E6720666F72205553422E2E2E00555342F3
:4039400020726561647900636F6D6D616E6420307825303278006661696C2025642B25642B2564203D3D2025642C206E6F74202564007061737365643D256400756E646544
:403980007272756E206166746572202564207061636B65747300636F756E743D256420693D256420643D256400636D645F777269746500703D25642063723D256420637729
:4039C0003D256420663D256420773D256420696E6465783D256420756E64657272756E3D256400756E64657272756E21007375636365737300737461727420657261736912
:403A00006E670073746F702065726173696E670069646C650000510040100040510040300000000140001000140140000800400140000A004C0140000200500140200030F9
:403A400031323334353637383941424344454600000100000004000000100001000000040000001028000000000104000100000000000000000157494E5553420000303043
:403A800030303100000000000000000012034D005300460054003100300030000100000001000000A83A000001000000773B0000000000000000000001000000C03A000003
:403AC00001000000493B000004000000E23A0000000000000000000000000000E03A0000FF00000001024000FF00000082024000FF00000003034000FF00000084034000F7
:403B0000FF00020304030904160346006C007500780045006E00670069006E0065002A0343006F0077006C00610072006B00200054006500630068006E006F006C006F0003
:403B400067006900650073000009022E0001010080320904000004FF00000107050102400000070582024000000705030340000A0705840340000A12010002FF0001080921
:403B800012006E0100020180014300232D302B2000686C4C00656667454647003031323334353637383961626364656600000000F8B500BFF8BC08BC9E4670475900000094
:403BC000BD100000F8B500BFF8BC08BC9E46704735000000E83B0000C880FF1FA00000002812000000000000000000009093FF1FFF000000675000400C0000000700000097
:403C0000FFFFFFFF7F8000003F0000000000007D00FA0000400000000090D003FF000000000000000000000000000000000000000000000000000000000000000000000031
:403C4000893B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
:403C80000081FF1F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000065
:403CC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C4
:403D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083
:403D40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043
@@ -4615,12 +4615,12 @@
:0200000490105A
:04000000BC90ACAF55
:0200000490303A
:0200000096F078
:02000000CEC56B
:0200000490402A
:4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0
:400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
:400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
:4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
:0200000490501A
:0C00000000012E16106900002E2FA759D9
:0C00000000012E16106900002E2FDF2ECC
:00000001FF

View File

@@ -21,6 +21,10 @@ static bool drive1_present;
static volatile uint32_t clock = 0; /* ms */
static volatile bool index_irq = false;
/* Duration in ms. 0 causes every pulse to be an index pulse. Durations since
* last pulse greater than this value imply sector pulse. Otherwise is an index
* pulse. */
static volatile uint32_t hardsec_index_threshold = 0;
static bool motor_on = false;
static uint32_t motor_on_time = 0;
@@ -69,12 +73,34 @@ static void system_timer_cb(void)
CY_ISR(index_irq_cb)
{
index_irq = true;
/* Hard sectored media has sector pulses at the beginning of every sector
* and the index pulse is an extra pulse in the middle of the last sector.
* When the extra pulse is seen, the next sector pulse is also the start of
* the track. */
static bool hardsec_index_irq_primed = false;
static uint32_t hardsec_last_pulse_time = 0;
if (!hardsec_index_threshold)
{
index_irq = true;
hardsec_index_irq_primed = false;
}
else
{
index_irq = hardsec_index_irq_primed;
if (index_irq)
hardsec_index_irq_primed = false;
else
hardsec_index_irq_primed =
clock - hardsec_last_pulse_time <= hardsec_index_threshold;
hardsec_last_pulse_time = clock;
}
/* Stop writing the instant the index pulse comes along; it may take a few
* moments for the main code to notice the pulse, and we don't want to overwrite
* the beginning of the track. */
ERASE_REG_Write(0);
if (index_irq)
ERASE_REG_Write(0);
}
CY_ISR(capture_dma_finished_irq_cb)
@@ -269,7 +295,7 @@ static void cmd_recalibrate(void)
send_reply(&r);
}
static void cmd_measure_speed(struct any_frame* f)
static void cmd_measure_speed(struct measurespeed_frame* f)
{
start_motor();
@@ -288,10 +314,14 @@ static void cmd_measure_speed(struct any_frame* f)
if (elapsed != 0)
{
index_irq = false;
int target_pulse_count = f->hard_sector_count + 1;
start_clock = clock;
while (!index_irq)
elapsed = clock - start_clock;
for (int x=0; x<target_pulse_count; x++)
{
index_irq = false;
while (!index_irq)
elapsed = clock - start_clock;
}
}
DECLARE_REPLY_FRAME(struct speed_frame, F_FRAME_MEASURE_SPEED_REPLY);
@@ -404,10 +434,12 @@ static void cmd_read(struct read_frame* f)
if (f->synced)
{
hardsec_index_threshold = f->hardsec_threshold_ms;
index_irq = false;
while (!index_irq)
;
index_irq = false;
hardsec_index_threshold = 0;
}
dma_writing_to_td = 0;
@@ -596,6 +628,7 @@ static void cmd_write(struct write_frame* f)
/* Wait for the index marker. While this happens, the DMA engine
* will prime the FIFO. */
hardsec_index_threshold = f->hardsec_threshold_ms;
index_irq = false;
while (!index_irq)
;
@@ -630,6 +663,7 @@ abort:
}
print("p=%d cr=%d cw=%d f=%d w=%d index=%d underrun=%d", packets, count_read, count_written, finished, writing, index_irq, dma_underrun);
hardsec_index_threshold = 0;
if (!finished)
{
/* There's still some data to read, so just read and blackhole it ---
@@ -663,6 +697,7 @@ static void cmd_erase(struct erase_frame* f)
/* Disk is now spinning. */
print("start erasing");
hardsec_index_threshold = f->hardsec_threshold_ms;
index_irq = false;
while (!index_irq)
;
@@ -671,6 +706,7 @@ static void cmd_erase(struct erase_frame* f)
while (!index_irq)
;
ERASE_REG_Write(0);
hardsec_index_threshold = 0;
print("stop erasing");
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_ERASE_REPLY);
@@ -796,7 +832,7 @@ static void handle_command(void)
break;
case F_FRAME_MEASURE_SPEED_CMD:
cmd_measure_speed(f);
cmd_measure_speed((struct measurespeed_frame*) f);
break;
case F_FRAME_BULK_WRITE_TEST_CMD:

View File

@@ -24,6 +24,14 @@ Don't believe me? Watch the demo reel!
<iframe width="373" height="210" src="https://www.youtube.com/embed/m_s1iw8eW7o" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
**New!** The FluxEngine client software now works with
[GreaseWeazle](https://github.com/keirf/Greaseweazle/wiki) hardware. So, if you
can't find a PSoC5 development kit, or don't want to use the Cypress Windows
tools for programming it, you can use one of these instead. Very nearly all
FluxEngine features are available with the GreaseWeazle and it works out-of-the
box. See the [dedicated GreaseWeazle documentation page](doc/greaseweazle.md)
for more information.
**Important note.** On 2020-04-02 I changed the bytecode format (and firmware).
Flux files will need to be upgraded with `fluxengine upgradefluxfile`. The new
format should be more reliable and use way, way less bandwidth. Sorry for the
@@ -52,6 +60,11 @@ following friendly articles:
- [Using a FluxEngine](doc/using.md) ∾ what to do with your new hardware ∾
flux files and image files ∾ knowing what you're doing
- [Using GreaseWeazle hardware with the FluxEngine client
software](doc/greaseweazle.md) ∾ what works ∾ what doesn't work ∾ where to
go for help
- [Troubleshooting dubious disks](doc/problems.md) ∾ it's not an exact science ∾
the sector map ∾ clock detection and the histogram
@@ -82,7 +95,7 @@ people who've had it work).
| [Brother 120kB](doc/disk-brother.md) | 🦄 | | |
| [Brother 240kB](doc/disk-brother.md) | 🦄 | 🦄 | |
| [Brother FB-100](doc/disk-fb100.md) | 🦖 | | Tandy Model 100, Husky Hunter, knitting machines |
| [Macintosh 800kB](doc/disk-macintosh.md) | 🦖 | | and probably the 400kB too |
| [Macintosh 800kB](doc/disk-macintosh.md) | 🦄 | 🦄 | and probably the 400kB too |
| [TRS-80](doc/disk-trs80.md) | 🦖 | 🦖* | a minor variation of the IBM scheme |
{: .datatable }

View File

@@ -19,7 +19,7 @@ static DoubleFlag postIndexGapUs(
static DoubleFlag clockCompensation(
{ "--clock-compensation-factor" },
"Scale the output clock by this much.",
.9724);
1.0);
static bool lastBit;

71
doc/greaseweazle.md Normal file
View File

@@ -0,0 +1,71 @@
Using the FluxEngine client software with GreaseWeazle hardware
===============================================================
The FluxEngine isn't the only project which does this; another one is the
[GreaseWeazle](https://github.com/keirf/Greaseweazle/wiki), a Blue Pill based
completely open source solution. This requires more work to set up (or you can
buy a prebuilt GreaseWeazle board), but provides completely open source
hardware which doesn't require the use of the Cypress Windows-based tools that
the FluxEngine does. Luckily, the FluxEngine software supports it
out-of-the-box --- just plug it in and nearly everything should work.
I am aware that having _software_ called FluxEngine and _hardware_ called
FluxEngine makes things complicated when you're not using the FluxEngine client
software with a FluxEngine board, but I'm afraid it's too late to change that
now. Sorry.
If you're using Windows
-----------------------
In order to access the GreaseWeazle from Windows, you need to install a WinUSB
driver for it. You can do this with the [Zadig](https://zadig.akeo.ie/)
program. Download it, plug in the GreaseWeazle, and run it; select Options,
List All Devices, and then open the big dropdown box and select the
GreaseWeazle. You should see something like this.
<div style="text-align: center">
<img src="zadig.png" style="width:80%" alt="Zadig screenshot"></a>
</div>
Ensure that the Driver boxes say `usbser` and `WinUSB`. Then press 'Replace
Driver'. Once done, the GreaseWeazle will be visible to the FluxEngine client.
**Important note!** Unfortunately, now, the original GreaseWeazle client won't
work --- you can't use both drivers at once. I'm working on this. To switch
back to the original driver, for using the GreaseWeazle client software
instead, open up Zadig again, go through the same process, but make sure the left Driver box says `WinUSB` and the right one says `USB Serial (CDC)`. Now, when you press 'Replace Driver' the original driver will be restored.
What works
----------
Supported features with the GreaseWeazle include:
- simple reading and writing of disks, seeking etc
- erasing disks
- determining disk rotation speed
What doesn't work
-----------------
(I'm still working on this. If you have an urgent need for anything, please
[file an issue](https://github.com/davidgiven/fluxengine/issues/new) and I'll
see what I can do.)
- voltage measurement
- hard sectored disks (you can still read these, but you can't use
`--hard-sector-count`).
Who to contact
--------------
I want to make it clear that the FluxEngine code is _not_ supported by the
GreaseWeazle team. If you have any problems, please [contact
me](https://github.com/davidgiven/fluxengine/issues/new) and not them.
In addition, the GreaseWeazle release cycle is not synchronised to the
FluxEngine release cycle, so it's possible you'll have a version of the
GreaseWeazle firmware which is not supported by FluxEngine. Hopefully, it'll
detect this and complain. Again, [file an
issue](https://github.com/davidgiven/fluxengine/issues/new) and I'll look into
it.

View File

@@ -82,16 +82,10 @@ FluxEngine hardware on a $2 Blue Pill.
I am _not_ planning on replacing the PSoC5 with a Blue Pill, because someone
already has: [the GreaseWeazle](https://github.com/keirf/Greaseweazle/wiki) is
a completely open source firmware package which will read and write Supercard
Pro files via a standard Blue Pill. The GreaseWeazle's USB protocol is
different from the FluxEngine's so they're not directly interchangeable. You
can, however, read a Supercard Pro file with a GreaseWeazle and then use the
FluxEngine client to decode it. It should work the other way around, too, but
FluxEngine's SCP export [is curently
broken](https://github.com/davidgiven/fluxengine/issues/134).
I _am_ considering adding direct support for the GreaseWeazle to the FluxEngine
client, which will let you just plug one in and make it go as a direct
replacement to the FluxEngine hardware.
Pro files via a standard Blue Pill or via a prebuilt board. It's supported by
the FluxEngine client software, and you should, mostly, be able to use
GreaseWeazle hardware interchangeably with FluxEngine hardware. See the
[dedicated page](greaseweazle.md) for more information.
### Some useful links

BIN
doc/zadig.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -24,6 +24,7 @@ public:
};
extern void setHardwareFluxSinkDensity(bool high_density);
extern void setHardwareFluxSinkHardSectorCount(int sectorCount);
#endif

View File

@@ -3,6 +3,7 @@
#include "fluxmap.h"
#include "usb/usb.h"
#include "fluxsink/fluxsink.h"
#include "fmt/format.h"
FlagGroup hardwareFluxSinkFlags = {
&usbFlags,
@@ -15,17 +16,37 @@ static IntFlag indexMode(
"index pulse source (0=drive, 1=300 RPM fake source, 2=360 RPM fake source",
0);
static IntFlag hardSectorCount(
{ "--write-hard-sector-count" },
"number of hard sectors on the disk (0=soft sectors)",
0);
void setHardwareFluxSinkDensity(bool high_density)
{
::high_density = high_density;
}
void setHardwareFluxSinkHardSectorCount(int sectorCount)
{
::hardSectorCount.setDefaultValue(sectorCount);
}
class HardwareFluxSink : public FluxSink
{
public:
HardwareFluxSink(unsigned drive):
_drive(drive)
{
if (hardSectorCount != 0)
{
usbSetDrive(_drive, high_density, indexMode);
std::cerr << "Measuring rotational speed... " << std::flush;
nanoseconds_t oneRevolution = usbGetRotationalPeriod(hardSectorCount);
_hardSectorThreshold = oneRevolution * 3 / (4 * hardSectorCount);
std::cerr << fmt::format("{}ms\n", oneRevolution / 1e6);
}
else
_hardSectorThreshold = 0;
}
~HardwareFluxSink()
@@ -38,11 +59,12 @@ public:
usbSetDrive(_drive, high_density, indexMode);
usbSeek(track);
return usbWrite(side, fluxmap.rawBytes());
return usbWrite(side, fluxmap.rawBytes(), _hardSectorThreshold);
}
private:
unsigned _drive;
nanoseconds_t _hardSectorThreshold;
};
std::unique_ptr<FluxSink> FluxSink::createHardwareFluxSink(unsigned drive)

View File

@@ -30,6 +30,7 @@ public:
extern void setHardwareFluxSourceRevolutions(double revolutions);
extern void setHardwareFluxSourceDensity(bool high_density);
extern void setHardwareFluxSourceSynced(bool synced);
extern void setHardwareFluxSourceHardSectorCount(int sectorCount);
#endif

View File

@@ -24,6 +24,11 @@ static IntFlag indexMode(
"index pulse source (0=drive, 1=300 RPM fake source, 2=360 RPM fake source",
0);
static IntFlag hardSectorCount(
{ "--hard-sector-count" },
"number of hard sectors on the disk (0=soft sectors)",
0);
static bool high_density = false;
void setHardwareFluxSourceDensity(bool high_density)
@@ -39,7 +44,11 @@ public:
{
usbSetDrive(_drive, high_density, indexMode);
std::cerr << "Measuring rotational speed... " << std::flush;
_oneRevolution = usbGetRotationalPeriod();
_oneRevolution = usbGetRotationalPeriod(hardSectorCount);
if (hardSectorCount != 0)
_hardSectorThreshold = _oneRevolution * 3 / (4 * hardSectorCount);
else
_hardSectorThreshold = 0;
std::cerr << fmt::format("{}ms\n", _oneRevolution / 1e6);
}
@@ -52,7 +61,8 @@ public:
{
usbSetDrive(_drive, high_density, indexMode);
usbSeek(track);
Bytes data = usbRead(side, synced, revolutions * _oneRevolution);
Bytes data = usbRead(
side, synced, revolutions * _oneRevolution, _hardSectorThreshold);
auto fluxmap = std::make_unique<Fluxmap>();
fluxmap->appendBytes(data);
return fluxmap;
@@ -72,6 +82,7 @@ private:
unsigned _drive;
unsigned _revolutions;
nanoseconds_t _oneRevolution;
nanoseconds_t _hardSectorThreshold;
};
void setHardwareFluxSourceRevolutions(double revolutions)
@@ -84,6 +95,11 @@ void setHardwareFluxSourceSynced(bool synced)
::synced.setDefaultValue(synced);
}
void setHardwareFluxSourceHardSectorCount(int sectorCount)
{
::hardSectorCount.setDefaultValue(sectorCount);
}
std::unique_ptr<FluxSource> FluxSource::createHardwareFluxSource(unsigned drive)
{
return std::unique_ptr<FluxSource>(new HardwareFluxSource(drive));

View File

@@ -91,6 +91,11 @@ void setReaderRevolutions(int revolutions)
setHardwareFluxSourceRevolutions(revolutions);
}
void setReaderHardSectorCount(int sectorCount)
{
setHardwareFluxSourceHardSectorCount(sectorCount);
}
static void writeSectorsToFile(const SectorSet& sectors, const ImageSpec& spec)
{
std::unique_ptr<ImageWriter> writer(ImageWriter::create(sectors, spec));

View File

@@ -13,6 +13,7 @@ extern FlagGroup readerFlags;
extern void setReaderDefaultSource(const std::string& source);
extern void setReaderDefaultOutput(const std::string& output);
extern void setReaderRevolutions(int revolutions);
extern void setReaderHardSectorCount(int sectorCount);
extern std::vector<std::unique_ptr<Track>> readTracks();

View File

@@ -144,9 +144,12 @@ public:
await_reply<struct any_frame>(F_FRAME_RECALIBRATE_REPLY);
}
nanoseconds_t getRotationalPeriod(void)
nanoseconds_t getRotationalPeriod(int hardSectorCount)
{
struct any_frame f = { .f = {.type = F_FRAME_MEASURE_SPEED_CMD, .size = sizeof(f)} };
struct measurespeed_frame f = {
.f = {.type = F_FRAME_MEASURE_SPEED_CMD, .size = sizeof(f)},
.hard_sector_count = (uint8_t) hardSectorCount,
};
usb_cmd_send(&f, f.f.size);
auto r = await_reply<struct speed_frame>(F_FRAME_MEASURE_SPEED_REPLY);
@@ -227,13 +230,15 @@ public:
await_reply<struct any_frame>(F_FRAME_BULK_READ_TEST_REPLY);
}
Bytes read(int side, bool synced, nanoseconds_t readTime)
Bytes read(int side, bool synced, nanoseconds_t readTime,
nanoseconds_t hardSectorThreshold)
{
struct read_frame f = {
.f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
.synced = (uint8_t) synced
.synced = (uint8_t) synced,
};
f.hardsec_threshold_ms = (hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
uint16_t milliseconds = readTime / 1e6;
((uint8_t*)&f.milliseconds)[0] = milliseconds;
((uint8_t*)&f.milliseconds)[1] = milliseconds >> 8;
@@ -249,7 +254,7 @@ public:
return buffer;
}
void write(int side, const Bytes& bytes)
void write(int side, const Bytes& bytes, nanoseconds_t hardSectorThreshold)
{
unsigned safelen = bytes.size() & ~(FRAME_SIZE-1);
Bytes safeBytes = bytes.slice(0, safelen);
@@ -258,6 +263,7 @@ public:
.f = { .type = F_FRAME_WRITE_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
};
f.hardsec_threshold_ms = (hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
((uint8_t*)&f.bytes_to_write)[0] = safelen;
((uint8_t*)&f.bytes_to_write)[1] = safelen >> 8;
((uint8_t*)&f.bytes_to_write)[2] = safelen >> 16;
@@ -269,12 +275,13 @@ public:
await_reply<struct any_frame>(F_FRAME_WRITE_REPLY);
}
void erase(int side)
void erase(int side, nanoseconds_t hardSectorThreshold)
{
struct erase_frame f = {
.f = { .type = F_FRAME_ERASE_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
};
f.hardsec_threshold_ms = (hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_ERASE_REPLY);

156
lib/usb/greaseweazle.cc Normal file
View File

@@ -0,0 +1,156 @@
#include "globals.h"
#include "usb.h"
#include "protocol.h"
#include "bytes.h"
#include "fmt/format.h"
#include "greaseweazle.h"
Bytes fluxEngineToGreaseWeazle(const Bytes& fldata, nanoseconds_t clock)
{
Bytes gwdata;
ByteWriter bw(gwdata);
ByteReader br(fldata);
uint32_t ticks_fl = 0;
uint32_t ticks_gw = 0;
auto write_28 = [&](uint32_t val) {
bw.write_8(1 | (val<<1) & 0xff);
bw.write_8(1 | (val>>6) & 0xff);
bw.write_8(1 | (val>>13) & 0xff);
bw.write_8(1 | (val>>20) & 0xff);
};
while (!br.eof())
{
uint8_t b = br.read_8();
ticks_fl += b & 0x3f;
if (b & F_BIT_PULSE)
{
uint32_t newticks_gw = ticks_fl * NS_PER_TICK / clock;
uint32_t delta = newticks_gw - ticks_gw;
if (delta < 250)
bw.write_8(delta);
else
{
int high = (delta-250) / 255;
if (high < 5)
{
bw.write_8(250 + high);
bw.write_8(1 + (delta-250) % 255);
}
else
{
bw.write_8(255);
bw.write_8(FLUXOP_SPACE);
write_28(delta - 249);
bw.write_8(249);
}
}
ticks_gw = newticks_gw;
}
}
bw.write_8(0); /* end of stream */
return gwdata;
}
Bytes greaseWeazleToFluxEngine(const Bytes& gwdata, nanoseconds_t clock)
{
Bytes fldata;
ByteReader br(gwdata);
ByteWriter bw(fldata);
auto read_28 = [&]() {
return ((br.read_8() & 0xfe) >> 1)
| ((br.read_8() & 0xfe) << 6)
| ((br.read_8() & 0xfe) << 13)
| ((br.read_8() & 0xfe) << 20);
};
uint32_t ticks_gw = 0;
uint32_t lastevent_fl = 0;
uint32_t index_gw = ~0;
while (!br.eof())
{
uint8_t b = br.read_8();
if (!b)
break;
uint8_t event = 0;
if (b == 255)
{
switch (br.read_8())
{
case FLUXOP_INDEX:
index_gw = ticks_gw + read_28();
break;
case FLUXOP_SPACE:
ticks_gw += read_28();
break;
default:
Error() << "bad opcode in GreaseWeazle stream";
}
}
else
{
if (b < 250)
ticks_gw += b;
else
{
int delta = 250 + (b-250)*255 + br.read_8() - 1;
ticks_gw += delta;
}
event = F_BIT_PULSE;
}
if (event)
{
uint32_t index_fl = (index_gw * clock) / NS_PER_TICK;
uint32_t ticks_fl = (ticks_gw * clock) / NS_PER_TICK;
if (index_gw != ~0)
{
if (index_fl < ticks_fl)
{
uint32_t delta_fl = index_fl - lastevent_fl;
while (delta_fl > 0x3f)
{
bw.write_8(0x3f);
delta_fl -= 0x3f;
}
bw.write_8(delta_fl | F_BIT_INDEX);
lastevent_fl = index_fl;
index_gw = ~0;
}
else if (index_fl == ticks_fl)
event |= F_BIT_INDEX;
}
uint32_t delta_fl = ticks_fl - lastevent_fl;
while (delta_fl > 0x3f)
{
bw.write_8(0x3f);
delta_fl -= 0x3f;
}
bw.write_8(delta_fl | event);
lastevent_fl = ticks_fl;
}
}
return fldata;
}
/* Left-truncates at the first index mark, so the resulting data as aligned at
* the index. */
Bytes stripPartialRotation(const Bytes& fldata)
{
for (unsigned i=0; i<fldata.size(); i++)
{
uint8_t b = fldata[i];
if (b & F_BIT_INDEX)
return fldata.slice(i);
}
return fldata;
}

203
lib/usb/greaseweazle.h Normal file
View File

@@ -0,0 +1,203 @@
#ifndef GREASEWEAZLE_H
#define GREASEWEAZLE_H
#define GREASEWEAZLE_VID 0x1209
#define GREASEWEAZLE_PID 0x4d69
#define EP_OUT 0x02
#define EP_IN 0x83
#define GREASEWEAZLE_VERSION 22
extern Bytes fluxEngineToGreaseWeazle(const Bytes& fldata, nanoseconds_t clock);
extern Bytes greaseWeazleToFluxEngine(const Bytes& gwdata, nanoseconds_t clock);
extern Bytes stripPartialRotation(const Bytes& fldata);
/* Copied from https://github.com/keirf/Greaseweazle/blob/master/inc/cdc_acm_protocol.h.
*
* WANING: these headers were originally defined with 'packed', which is a gccism so it's
* been dummied out. Don't use them expecting wire protocol structures. */
#define packed /* */
/*
* GREASEWEAZLE COMMAND SET
*/
/* CMD_GET_INFO, length=3, idx. Returns 32 bytes after ACK. */
#define CMD_GET_INFO 0
/* [BOOTLOADER] CMD_UPDATE, length=6, <update_len>.
* Host follows with <update_len> bytes.
* Bootloader finally returns a status byte, 0 on success. */
/* [MAIN FIRMWARE] CMD_UPDATE, length=10, <update_len>, 0xdeafbee3.
* Host follows with <update_len> bytes.
* Main firmware finally returns a status byte, 0 on success. */
#define CMD_UPDATE 1
/* CMD_SEEK, length=3, cyl#. Seek to cyl# on selected drive. */
#define CMD_SEEK 2
/* CMD_HEAD, length=3, head# (0=bottom) */
#define CMD_HEAD 3
/* CMD_SET_PARAMS, length=3+nr, idx, <nr bytes> */
#define CMD_SET_PARAMS 4
/* CMD_GET_PARAMS, length=4, idx, nr_bytes. Returns nr_bytes after ACK. */
#define CMD_GET_PARAMS 5
/* CMD_MOTOR, length=4, drive#, on/off. Turn on/off a drive motor. */
#define CMD_MOTOR 6
/* CMD_READ_FLUX, length=2-8. Argument is gw_read_flux.
* Returns flux readings until EOStream. */
#define CMD_READ_FLUX 7
/* CMD_WRITE_FLUX, length=2-4. Argument is gw_write_flux.
* Host follows with flux readings until EOStream. */
#define CMD_WRITE_FLUX 8
/* CMD_GET_FLUX_STATUS, length=2. Last read/write status returned in ACK. */
#define CMD_GET_FLUX_STATUS 9
/* CMD_SWITCH_FW_MODE, length=3, <mode> */
#define CMD_SWITCH_FW_MODE 11
/* CMD_SELECT, length=3, drive#. Select drive# as current unit. */
#define CMD_SELECT 12
/* CMD_DESELECT, length=2. Deselect current unit (if any). */
#define CMD_DESELECT 13
/* CMD_SET_BUS_TYPE, length=3, bus_type. Set the bus type. */
#define CMD_SET_BUS_TYPE 14
/* CMD_SET_PIN, length=4, pin#, level. */
#define CMD_SET_PIN 15
/* CMD_RESET, length=2. Reset all state to initial (power on) values. */
#define CMD_RESET 16
/* CMD_ERASE_FLUX, length=6. Argument is gw_erase_flux. */
#define CMD_ERASE_FLUX 17
/* CMD_SOURCE_BYTES, length=6. Argument is gw_sink_source_bytes. */
#define CMD_SOURCE_BYTES 18
/* CMD_SINK_BYTES, length=6. Argument is gw_sink_source_bytes. */
#define CMD_SINK_BYTES 19
#define CMD_MAX 19
/*
* CMD_SET_BUS CODES
*/
#define BUS_NONE 0
#define BUS_IBMPC 1
#define BUS_SHUGART 2
/*
* ACK RETURN CODES
*/
#define ACK_OKAY 0
#define ACK_BAD_COMMAND 1
#define ACK_NO_INDEX 2
#define ACK_NO_TRK0 3
#define ACK_FLUX_OVERFLOW 4
#define ACK_FLUX_UNDERFLOW 5
#define ACK_WRPROT 6
#define ACK_NO_UNIT 7
#define ACK_NO_BUS 8
#define ACK_BAD_UNIT 9
#define ACK_BAD_PIN 10
#define ACK_BAD_CYLINDER 11
/*
* CONTROL-CHANNEL COMMAND SET:
* We abuse SET_LINE_CODING requests over endpoint 0, stashing a command
* in the baud-rate field.
*/
#define BAUD_NORMAL 9600
#define BAUD_CLEAR_COMMS 10000
/*
* Flux stream opcodes. Preceded by 0xFF byte.
*
* Argument types:
* N28: 28-bit non-negative integer N, encoded as 4 bytes b0,b1,b2,b3:
* b0 = (uint8_t)(1 | (N << 1))
* b1 = (uint8_t)(1 | (N >> 6))
* b2 = (uint8_t)(1 | (N >> 13))
* b3 = (uint8_t)(1 | (N >> 20))
*/
/* FLUXOP_INDEX [CMD_READ_FLUX]
* Args:
* +4 [N28]: ticks to index, relative to sample cursor.
* Signals an index pulse in the read stream. Sample cursor is unaffected. */
#define FLUXOP_INDEX 1
/* FLUXOP_SPACE [CMD_READ_FLUX, CMD_WRITE_FLUX]
* Args:
* +4 [N28]: ticks to increment the sample cursor.
* Increments the sample cursor with no intervening flux transitions. */
#define FLUXOP_SPACE 2
/* FLUXOP_ASTABLE [CMD_WRITE_FLUX]
* Args:
* +4 [N28]: astable period.
* Generate regular flux transitions at specified astable period.
* Duration is specified by immediately preceding FLUXOP_SPACE opcode(s). */
#define FLUXOP_ASTABLE 3
/*
* COMMAND PACKETS
*/
/* CMD_GET_INFO, index 0 */
#define GETINFO_FIRMWARE 0
struct packed gw_info {
uint8_t fw_major;
uint8_t fw_minor;
uint8_t is_main_firmware; /* == 0 -> update bootloader */
uint8_t max_cmd;
uint32_t sample_freq;
uint8_t hw_model, hw_submodel;
uint8_t usb_speed;
};
extern struct gw_info gw_info;
/* CMD_GET_INFO, index 1 */
#define GETINFO_BW_STATS 1
struct packed gw_bw_stats {
struct packed {
uint32_t bytes;
uint32_t usecs;
} min_bw, max_bw;
};
/* CMD_READ_FLUX */
struct packed gw_read_flux {
/* Maximum ticks to read for (or 0, for no limit). */
uint32_t ticks;
/* Maximum index pulses to read (or 0, for no limit). */
uint16_t max_index;
};
/* CMD_WRITE_FLUX */
struct packed gw_write_flux {
/* If non-zero, start the write at the index pulse. */
uint8_t cue_at_index;
/* If non-zero, terminate the write at the next index pulse. */
uint8_t terminate_at_index;
};
/* CMD_ERASE_FLUX */
struct packed gw_erase_flux {
uint32_t ticks;
};
/* CMD_SINK_SOURCE_BYTES */
struct packed gw_sink_source_bytes {
uint32_t nr_bytes;
};
/* CMD_{GET,SET}_PARAMS, index 0 */
#define PARAMS_DELAYS 0
struct packed gw_delay {
uint16_t select_delay; /* usec */
uint16_t step_delay; /* usec */
uint16_t seek_settle; /* msec */
uint16_t motor_delay; /* msec */
uint16_t auto_off; /* msec */
};
/* CMD_SWITCH_FW_MODE */
#define FW_MODE_BOOTLOADER 0
#define FW_MODE_NORMAL 1
#endif

390
lib/usb/greaseweazleusb.cc Normal file
View File

@@ -0,0 +1,390 @@
#include "globals.h"
#include "usb.h"
#include "protocol.h"
#include "fluxmap.h"
#include "bytes.h"
#include <libusb.h>
#include "fmt/format.h"
#include "greaseweazle.h"
#define TIMEOUT 5000
static const char* gw_error(int e)
{
switch (e)
{
case ACK_OKAY: return "OK";
case ACK_BAD_COMMAND: return "Bad command";
case ACK_NO_INDEX: return "No index";
case ACK_NO_TRK0: return "No track 0";
case ACK_FLUX_OVERFLOW: return "Overflow";
case ACK_FLUX_UNDERFLOW: return "Underflow";
case ACK_WRPROT: return "Write protected";
case ACK_NO_UNIT: return "No unit";
case ACK_NO_BUS: return "No bus";
case ACK_BAD_UNIT: return "Invalid unit";
case ACK_BAD_PIN: return "Invalid pin";
case ACK_BAD_CYLINDER: return "Invalid cylinder";
default: return "Unknown error";
}
}
class GreaseWeazleUsb : public USB
{
private:
uint8_t _readbuffer[4096];
int _readbuffer_ptr = 0;
int _readbuffer_fill = 0;
void read_bytes(uint8_t* buffer, int len)
{
while (len > 0)
{
if (_readbuffer_ptr < _readbuffer_fill)
{
int buffered = std::min(len, _readbuffer_fill - _readbuffer_ptr);
memcpy(buffer, _readbuffer + _readbuffer_ptr, buffered);
_readbuffer_ptr += buffered;
buffer += buffered;
len -= buffered;
}
if (len == 0)
break;
int actual;
int rc = libusb_bulk_transfer(_device, EP_IN,
_readbuffer, sizeof(_readbuffer),
&actual, TIMEOUT);
if (rc < 0)
Error() << "failed to receive command reply: " << usberror(rc);
_readbuffer_fill = actual;
_readbuffer_ptr = 0;
}
}
void read_bytes(Bytes& bytes)
{
read_bytes(bytes.begin(), bytes.size());
}
Bytes read_bytes(unsigned len)
{
Bytes b(len);
read_bytes(b);
return b;
}
uint8_t read_byte()
{
uint8_t b;
read_bytes(&b, 1);
return b;
}
uint32_t read_28()
{
return ((read_byte() & 0xfe) >> 1)
| ((read_byte() & 0xfe) << 6)
| ((read_byte() & 0xfe) << 13)
| ((read_byte() & 0xfe) << 20);
}
void write_bytes(const uint8_t* buffer, int len)
{
while (len > 0)
{
int actual;
int rc = libusb_bulk_transfer(_device, EP_OUT, (uint8_t*)buffer, len, &actual, 0);
if (rc < 0)
Error() << "failed to send command: " << usberror(rc);
buffer += actual;
len -= actual;
}
}
void write_bytes(const Bytes& bytes)
{
write_bytes(bytes.cbegin(), bytes.size());
}
void do_command(const Bytes& command)
{
write_bytes(command);
uint8_t buffer[2];
read_bytes(buffer, sizeof(buffer));
if (buffer[0] != command[0])
Error() << fmt::format("command returned garbage (0x{:x} != 0x{:x} with status 0x{:x})",
buffer[0], command[0], buffer[1]);
if (buffer[1])
Error() << fmt::format("GreaseWeazle error: {}", gw_error(buffer[1]));
}
public:
GreaseWeazleUsb(libusb_device_handle* device)
{
_device = device;
/* Configure the device. */
int i;
int cfg = -1;
libusb_get_configuration(_device, &cfg);
if (cfg != 1)
{
i = libusb_set_configuration(_device, 1);
if (i < 0)
Error() << "the GreaseWeazle would not accept configuration: " << usberror(i);
}
/* Detach the existing kernel serial port driver, if there is one, and claim it ourselves. */
for (int i = 0; i < 2; i++)
{
if (libusb_kernel_driver_active(_device, i))
libusb_detach_kernel_driver(_device, i);
int rc = libusb_claim_interface(_device, i);
if (rc < 0)
Error() << "unable to claim interface: " << libusb_error_name(rc);
}
int version = getVersion();
if (version != GREASEWEAZLE_VERSION)
Error() << "your GreaseWeazle firmware is at version " << version
<< " but the client is for version " << GREASEWEAZLE_VERSION
<< "; please upgrade";
/* Configure the hardware. */
do_command({ CMD_SET_BUS_TYPE, 3, BUS_IBMPC });
}
int getVersion()
{
do_command({ CMD_GET_INFO, 3, GETINFO_FIRMWARE });
Bytes response = read_bytes(32);
ByteReader br(response);
br.seek(4);
nanoseconds_t freq = br.read_le32();
_clock = 1000000000 / freq;
br.seek(0);
return br.read_be16();
}
void recalibrate()
{
seek(0);
}
void seek(int track)
{
do_command({ CMD_SEEK, 3, (uint8_t)track });
}
nanoseconds_t getRotationalPeriod(int hardSectorCount)
{
if (hardSectorCount != 0)
Error() << "hard sectors are currently unsupported on the GreaseWeazel";
/* The GreaseWeazle doesn't have a command to fetch the period directly,
* so we have to do a flux read. */
do_command({ CMD_READ_FLUX, 2 });
uint32_t ticks_gw = 0;
uint32_t firstindex = ~0;
uint32_t secondindex = ~0;
for (;;)
{
uint8_t b = read_byte();
if (!b)
break;
if (b == 255)
{
switch (read_byte())
{
case FLUXOP_INDEX:
{
uint32_t index = read_28() + ticks_gw;
if (firstindex == ~0)
firstindex = index;
else if (secondindex == ~0)
secondindex = index;
break;
}
case FLUXOP_SPACE:
read_bytes(4);
break;
default:
Error() << "bad opcode in GreaseWeazle stream";
}
}
else
{
if (b < 250)
ticks_gw += b;
else
{
int delta = 250 + (b-250)*255 + read_byte() - 1;
ticks_gw += delta;
}
}
}
if (secondindex == ~0)
Error() << "unable to determine disk rotational period (is a disk in the drive?)";
do_command({ CMD_GET_FLUX_STATUS, 2 });
_revolutions = (nanoseconds_t)(secondindex - firstindex) * _clock;
return _revolutions;
}
void testBulkWrite()
{
const int LEN = 10*1024*1024;
Bytes cmd(6);
ByteWriter bw(cmd);
bw.write_8(CMD_SINK_BYTES);
bw.write_8(cmd.size());
bw.write_le32(LEN);
do_command(cmd);
Bytes junk(LEN);
double start_time = getCurrentTime();
write_bytes(LEN);
read_bytes(1);
double elapsed_time = getCurrentTime() - start_time;
std::cout << "Transferred "
<< LEN
<< " bytes from PC -> GreaseWeazle in "
<< int(elapsed_time * 1000.0)
<< " ms ("
<< int((LEN / 1024.0) / elapsed_time)
<< " kB/s)"
<< std::endl;
}
void testBulkRead()
{
const int LEN = 10*1024*1024;
Bytes cmd(6);
ByteWriter bw(cmd);
bw.write_8(CMD_SOURCE_BYTES);
bw.write_8(cmd.size());
bw.write_le32(LEN);
do_command(cmd);
double start_time = getCurrentTime();
read_bytes(LEN);
double elapsed_time = getCurrentTime() - start_time;
std::cout << "Transferred "
<< LEN
<< " bytes from GreaseWeazle -> PC in "
<< int(elapsed_time * 1000.0)
<< " ms ("
<< int((LEN / 1024.0) / elapsed_time)
<< " kB/s)"
<< std::endl;
}
Bytes read(int side, bool synced, nanoseconds_t readTime, nanoseconds_t hardSectorThreshold)
{
if (hardSectorThreshold != 0)
Error() << "hard sectors are currently unsupported on the GreaseWeazel";
int revolutions = (readTime+_revolutions-1) / _revolutions;
do_command({ CMD_HEAD, 3, (uint8_t)side });
{
Bytes cmd(4);
cmd.writer()
.write_8(CMD_READ_FLUX)
.write_8(cmd.size())
.write_le32(revolutions + (synced ? 1 : 0));
do_command(cmd);
}
Bytes buffer;
ByteWriter bw(buffer);
for (;;)
{
uint8_t b = read_byte();
if (!b)
break;
bw.write_8(b);
}
do_command({ CMD_GET_FLUX_STATUS, 2 });
Bytes fldata = greaseWeazleToFluxEngine(buffer, _clock);
if (synced)
fldata = stripPartialRotation(fldata);
return fldata;
}
void write(int side, const Bytes& fldata, nanoseconds_t hardSectorThreshold)
{
if (hardSectorThreshold != 0)
Error() << "hard sectors are currently unsupported on the GreaseWeazel";
do_command({ CMD_HEAD, 3, (uint8_t)side });
do_command({ CMD_WRITE_FLUX, 3, 1 });
write_bytes(fluxEngineToGreaseWeazle(fldata, _clock));
read_byte(); /* synchronise */
do_command({ CMD_GET_FLUX_STATUS, 2 });
}
void erase(int side, nanoseconds_t hardSectorThreshold)
{
if (hardSectorThreshold != 0)
Error() << "hard sectors are currently unsupported on the GreaseWeazel";
do_command({ CMD_HEAD, 3, (uint8_t)side });
Bytes cmd(6);
ByteWriter bw(cmd);
bw.write_8(CMD_ERASE_FLUX);
bw.write_8(cmd.size());
bw.write_le32(200e6 / _clock);
do_command(cmd);
read_byte(); /* synchronise */
do_command({ CMD_GET_FLUX_STATUS, 2 });
}
void setDrive(int drive, bool high_density, int index_mode)
{
do_command({ CMD_SELECT, 3, (uint8_t)drive });
do_command({ CMD_MOTOR, 4, (uint8_t)drive, 1 });
do_command({ CMD_SET_PIN, 4, 2, (uint8_t)(high_density ? 0 : 1) });
}
void measureVoltages(struct voltages_frame* voltages)
{ Error() << "unsupported operation on the GreaseWeazle"; }
private:
nanoseconds_t _clock;
nanoseconds_t _revolutions;
};
USB* createGreaseWeazleUsb(libusb_device_handle* device)
{
return new GreaseWeazleUsb(device);
}
// vim: sw=4 ts=4 et

View File

@@ -6,6 +6,7 @@
#include "bytes.h"
#include <libusb.h>
#include "fmt/format.h"
#include "greaseweazle.h"
FlagGroup usbFlags;
@@ -19,6 +20,7 @@ static USB* usb = NULL;
enum
{
DEV_FLUXENGINE,
DEV_GREASEWEAZLE,
};
struct CandidateDevice
@@ -42,11 +44,28 @@ static const char* device_type(int i)
switch (i)
{
case DEV_FLUXENGINE: return "FluxEngine";
case DEV_GREASEWEAZLE: return "GreaseWeazle";
default: assert(false);
}
return NULL;
}
static const std::string get_serial_number(libusb_device* device, libusb_device_descriptor* desc)
{
std::string serial;
libusb_device_handle* handle;
if (libusb_open(device, &handle) == 0)
{
unsigned char buffer[64];
libusb_get_string_descriptor_ascii(handle, desc->iSerialNumber, buffer, sizeof(buffer));
serial = (const char*) buffer;
libusb_close(handle);
}
return serial;
}
static std::map<std::string, std::unique_ptr<CandidateDevice>> get_candidates(libusb_device** devices, int numdevices)
{
std::map<std::string, std::unique_ptr<CandidateDevice>> candidates;
@@ -56,22 +75,24 @@ static std::map<std::string, std::unique_ptr<CandidateDevice>> get_candidates(li
candidate->device = devices[i];
(void) libusb_get_device_descriptor(devices[i], &candidate->desc);
if ((candidate->desc.idVendor == FLUXENGINE_VID) && (candidate->desc.idProduct == FLUXENGINE_PID))
uint32_t id = (candidate->desc.idVendor << 16) | candidate->desc.idProduct;
switch (id)
{
candidate->type = DEV_FLUXENGINE;
candidate->serial = "";
libusb_device_handle* handle;
if (libusb_open(candidate->device, &handle) == 0)
case (FLUXENGINE_VID<<16) | FLUXENGINE_PID:
{
unsigned char buffer[64];
libusb_get_string_descriptor_ascii(handle,
candidate->desc.iSerialNumber, buffer, sizeof(buffer));
candidate->serial = (const char*) buffer;
libusb_close(handle);
candidate->type = DEV_FLUXENGINE;
candidate->serial = get_serial_number(candidate->device, &candidate->desc);
candidates[candidate->serial] = std::move(candidate);
break;
}
candidates[candidate->serial] = std::move(candidate);
case (GREASEWEAZLE_VID<<16) | GREASEWEAZLE_PID:
{
candidate->type = DEV_GREASEWEAZLE;
candidate->serial = get_serial_number(candidate->device, &candidate->desc);
candidates[candidate->serial] = std::move(candidate);
break;
}
}
}
@@ -86,7 +107,16 @@ static void open_device(CandidateDevice& candidate)
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);
switch (candidate.type)
{
case DEV_FLUXENGINE:
usb = createFluxengineUsb(handle);
break;
case DEV_GREASEWEAZLE:
usb = createGreaseWeazleUsb(handle);
break;
}
}
static CandidateDevice& select_candidate(const std::map<std::string, std::unique_ptr<CandidateDevice>>& devices)

View File

@@ -15,12 +15,14 @@ public:
virtual int getVersion() = 0;
virtual void recalibrate() = 0;
virtual void seek(int track) = 0;
virtual nanoseconds_t getRotationalPeriod() = 0;
virtual nanoseconds_t getRotationalPeriod(int hardSectorCount) = 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 Bytes read(int side, bool synced, nanoseconds_t readTime,
nanoseconds_t hardSectorThreshold) = 0;
virtual void write(int side, const Bytes& bytes,
nanoseconds_t hardSectorThreshold) = 0;
virtual void erase(int side, nanoseconds_t hardSectorThreshold) = 0;
virtual void setDrive(int drive, bool high_density, int index_mode) = 0;
virtual void measureVoltages(struct voltages_frame* voltages) = 0;
@@ -34,22 +36,27 @@ extern FlagGroup usbFlags;
extern USB& getUsb();
extern USB* createFluxengineUsb(libusb_device_handle* device);
extern USB* createGreaseWeazleUsb(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 void usbErase(int side, nanoseconds_t hardSectorThreshold)
{ getUsb().erase(side, hardSectorThreshold); }
static inline Bytes usbRead(int side, bool synced, nanoseconds_t readTime)
{ return getUsb().read(side, synced, readTime); }
static inline nanoseconds_t usbGetRotationalPeriod(int hardSectorCount)
{ return getUsb().getRotationalPeriod(hardSectorCount); }
static inline void usbWrite(int side, const Bytes& bytes)
{ getUsb().write(side, bytes); }
static inline Bytes usbRead(int side, bool synced, nanoseconds_t readTime,
nanoseconds_t hardSectorThreshold)
{ return getUsb().read(side, synced, readTime, hardSectorThreshold); }
static inline void usbWrite(int side, const Bytes& bytes,
nanoseconds_t hardSectorThreshold)
{ getUsb().write(side, bytes, hardSectorThreshold); }
static inline void usbSetDrive(int drive, bool high_density, int index_mode)
{ getUsb().setDrive(drive, high_density, index_mode); }

View File

@@ -43,6 +43,11 @@ void setWriterDefaultInput(const std::string& input)
::input.set(input);
}
void setWriterHardSectorCount(int sectorCount)
{
setHardwareFluxSinkHardSectorCount(sectorCount);
}
static SectorSet readSectorsFromFile(const ImageSpec& spec)
{
return ImageReader::create(spec)->readImage();

View File

@@ -11,6 +11,7 @@ class Geometry;
extern void setWriterDefaultDest(const std::string& dest);
extern void setWriterDefaultInput(const std::string& input);
extern void setWriterHardSectorCount(int sectorCount);
extern void writeTracks(const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer);

View File

@@ -199,6 +199,8 @@ buildlibrary libbackend.a \
lib/fluxsource/streamfluxsource.cc \
lib/usb/usb.cc \
lib/usb/fluxengineusb.cc \
lib/usb/greaseweazle.cc \
lib/usb/greaseweazleusb.cc \
lib/globals.cc \
lib/hexdump.cc \
lib/ldbs.cc \
@@ -271,6 +273,7 @@ buildsimpleprogram brother240tool \
libemu.a \
libfmt.a \
runtest amiga-test tests/amiga.cc
runtest bitaccumulator-test tests/bitaccumulator.cc
runtest bytes-test tests/bytes.cc
runtest compression-test tests/compression.cc
@@ -278,6 +281,6 @@ runtest dataspec-test tests/dataspec.cc
runtest flags-test tests/flags.cc
runtest fluxpattern-test tests/fluxpattern.cc
runtest fmmfm-test tests/fmmfm.cc
runtest greaseweazle-test tests/greaseweazle.cc
runtest kryoflux-test tests/kryoflux.cc
runtest ldbs-test tests/ldbs.cc
runtest amiga-test tests/amiga.cc

View File

@@ -3,7 +3,7 @@
enum
{
FLUXENGINE_VERSION = 14,
FLUXENGINE_VERSION = 15,
FLUXENGINE_VID = 0x1209,
FLUXENGINE_PID = 0x6e00,
@@ -48,7 +48,7 @@ enum
F_FRAME_GET_VERSION_REPLY, /* version_frame */
F_FRAME_SEEK_CMD, /* seek_frame */
F_FRAME_SEEK_REPLY, /* any_frame */
F_FRAME_MEASURE_SPEED_CMD, /* any_frame */
F_FRAME_MEASURE_SPEED_CMD, /* measurespeed_frame */
F_FRAME_MEASURE_SPEED_REPLY, /* speed_frame */
F_FRAME_BULK_WRITE_TEST_CMD, /* any_frame */
F_FRAME_BULK_WRITE_TEST_REPLY, /* any_frame */
@@ -125,6 +125,12 @@ struct seek_frame
uint8_t track;
};
struct measurespeed_frame
{
struct frame_header f;
uint8_t hard_sector_count;
};
struct speed_frame
{
struct frame_header f;
@@ -137,6 +143,7 @@ struct read_frame
uint8_t side;
uint8_t synced;
uint16_t milliseconds;
uint8_t hardsec_threshold_ms;
};
struct write_frame
@@ -144,12 +151,14 @@ struct write_frame
struct frame_header f;
uint8_t side;
uint32_t bytes_to_write;
uint8_t hardsec_threshold_ms;
};
struct erase_frame
{
struct frame_header f;
uint8_t side;
uint8_t hardsec_threshold_ms;
};
struct set_drive_frame

View File

@@ -16,7 +16,7 @@ int mainReadMicropolis(int argc, const char* argv[])
{
setReaderDefaultSource(":t=0-76");
setReaderDefaultOutput("micropolis.img");
setReaderRevolutions(21); /* 17 index holes * 1.25 */
setReaderHardSectorCount(16);
flags.parseFlags(argc, argv);
MicropolisDecoder decoder;

View File

@@ -13,13 +13,18 @@ static DataSpecFlag source(
"source for data",
":d=0:t=0:s=0");
static IntFlag hardSectorCount(
{ "--hard-sector-count" },
"number of hard sectors on the disk (0=soft sectors)",
0);
int mainRpm(int argc, const char* argv[])
{
flags.parseFlags(argc, argv);
FluxSpec spec(source);
usbSetDrive(spec.drive, false, F_INDEX_REAL);
nanoseconds_t period = usbGetRotationalPeriod();
nanoseconds_t period = usbGetRotationalPeriod(hardSectorCount);
if (period != 0)
std::cout << "Rotational period is " << period/1000000 << " ms (" << 60e9/period << " rpm)" << std::endl;
else

109
tests/greaseweazle.cc Normal file
View File

@@ -0,0 +1,109 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "globals.h"
#include "fluxmap.h"
#include "lib/usb/greaseweazle.h"
static Bytes operator + (const Bytes& left, const Bytes& right)
{
Bytes output;
ByteWriter bw(output);
bw += left;
bw += right;
return output;
}
static Bytes operator * (const Bytes& left, size_t count)
{
Bytes output;
ByteWriter bw(output);
while (count--)
bw += left;
return output;
}
#define E28(val) \
(1 | (val<<1) & 0xff), \
(1 | (val>>6) & 0xff), \
(1 | (val>>13) & 0xff), \
(1 | (val>>20) & 0xff)
static void test_convert(const Bytes& gwbytes, const Bytes& flbytes)
{
Bytes gwtoflbytes = greaseWeazleToFluxEngine(gwbytes, 2*NS_PER_TICK);
Bytes fltogwbytes = fluxEngineToGreaseWeazle(flbytes, 2*NS_PER_TICK);
if (gwtoflbytes != flbytes)
{
std::cout << "GreaseWeazle to FluxEngine conversion failed.\n";
std::cout << "GreaseWeazle bytes:" << std::endl;
hexdump(std::cout, gwbytes);
std::cout << std::endl << "Produced this:" << std::endl;
hexdump(std::cout, gwtoflbytes);
std::cout << std::endl << "Expected this:" << std::endl;
hexdump(std::cout, flbytes);
abort();
}
if (fltogwbytes != gwbytes)
{
std::cout << "FluxEngine to GreaseWeazle conversion failed.\n";
std::cout << "FluxEngine bytes:" << std::endl;
hexdump(std::cout, flbytes);
std::cout << std::endl << "Produced this:" << std::endl;
hexdump(std::cout, fltogwbytes);
std::cout << std::endl << "Expected this:" << std::endl;
hexdump(std::cout, gwbytes);
abort();
}
}
static void test_conversions()
{
/* Simple one-byte intervals. */
test_convert(
Bytes{ 1, 1, 1, 1, 0 },
Bytes{ 0x82, 0x82, 0x82, 0x82 }
);
/* Larger one-byte intervals. */
test_convert(
Bytes{ 32, 0 },
Bytes{ 0x3f, 0x81 }
);
test_convert(
Bytes{ 64, 0 },
Bytes{ 0x3f, 0x3f, 0x82 }
);
test_convert(
Bytes{ 128, 0 },
Bytes{ 0x3f, 0x3f, 0x3f, 0x3f, 0x84 }
);
/* Two-byte intervals. */
test_convert(
Bytes{ 250, 1, 0 },
Bytes{ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xbb }
);
/* Very long intervals. */
test_convert(
Bytes{ 255, FLUXOP_SPACE, E28(2048 - 249), 249, 0 },
Bytes{ 0x3f }*0x41 + Bytes{ 0x81 }
);
}
int main(int argc, const char* argv[])
{
test_conversions();
return 0;
}