Compare commits

...

29 Commits

Author SHA1 Message Date
David Given
035dd1fad1 Merge pull request #209 from davidgiven/analyse
Add an analysis tool for checking drive response.
2021-01-10 02:01:33 +01:00
David Given
d2df79a665 Remember to get rid of the junk comments! 2021-01-10 01:09:35 +01:00
David Given
103e0a13bb Typo fix. 2021-01-10 01:06:13 +01:00
David Given
d1b5eec84a Add the analysis tool and Python script for drawing the results. 2021-01-10 00:51:43 +01:00
David Given
6b1e6b31ed Add initial version of the response analysis tool. 2021-01-09 13:42:37 +01:00
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
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
19 changed files with 1432 additions and 278 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

@@ -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

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

@@ -37,7 +37,7 @@ public:
HardwareFluxSink(unsigned drive):
_drive(drive)
{
if (hardSectorCount.get())
if (hardSectorCount != 0)
{
usbSetDrive(_drive, high_density, indexMode);
std::cerr << "Measuring rotational speed... " << std::flush;
@@ -45,6 +45,8 @@ public:
_hardSectorThreshold = oneRevolution * 3 / (4 * hardSectorCount);
std::cerr << fmt::format("{}ms\n", oneRevolution / 1e6);
}
else
_hardSectorThreshold = 0;
}
~HardwareFluxSink()

View File

@@ -45,7 +45,10 @@ public:
usbSetDrive(_drive, high_density, indexMode);
std::cerr << "Measuring rotational speed... " << std::flush;
_oneRevolution = usbGetRotationalPeriod(hardSectorCount);
_hardSectorThreshold = _oneRevolution * 3 / (4 * hardSectorCount);
if (hardSectorCount != 0)
_hardSectorThreshold = _oneRevolution * 3 / (4 * hardSectorCount);
else
_hardSectorThreshold = 0;
std::cerr << fmt::format("{}ms\n", _oneRevolution / 1e6);
}

View File

@@ -233,13 +233,12 @@ public:
Bytes read(int side, bool synced, nanoseconds_t readTime,
nanoseconds_t hardSectorThreshold)
{
hardSectorThreshold += 5e5; /* Round to nearest ms. */
struct read_frame f = {
.f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
.synced = (uint8_t) synced,
.hardsec_threshold_ms = (uint8_t) (hardSectorThreshold / 1e6),
};
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;
@@ -259,13 +258,12 @@ public:
{
unsigned safelen = bytes.size() & ~(FRAME_SIZE-1);
Bytes safeBytes = bytes.slice(0, safelen);
hardSectorThreshold += 5e5; /* Round to nearest ms. */
struct write_frame f = {
.f = { .type = F_FRAME_WRITE_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
.hardsec_threshold_ms = (uint8_t) (hardSectorThreshold / 1e6),
};
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;
@@ -279,12 +277,11 @@ public:
void erase(int side, nanoseconds_t hardSectorThreshold)
{
hardSectorThreshold += 5e5; /* Round to nearest ms. */
struct erase_frame f = {
.f = { .type = F_FRAME_ERASE_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
.hardsec_threshold_ms = (uint8_t) (hardSectorThreshold / 1e6),
};
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

@@ -36,6 +36,7 @@ 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(); }

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 \
@@ -210,6 +212,7 @@ buildlibrary libbackend.a \
lib/writer.cc \
buildlibrary libfrontend.a \
src/fe-analysedriveresponse.cc \
src/fe-cwftoflux.cc \
src/fe-erase.cc \
src/fe-fluxtoau.cc \
@@ -271,6 +274,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 +282,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

@@ -0,0 +1,35 @@
import numpy
import matplotlib.pyplot as plt
import matplotlib.animation as animation
TICK_FREQUENCY = 12e6
TICKS_PER_US = TICK_FREQUENCY / 1e6
print(TICKS_PER_US)
# Load data.
data = numpy.loadtxt(open("driveresponse.csv", "rb"), delimiter=",", skiprows=1)
labels = data[:, 0]
frequencies = data[:, 1:]
# Scale the frequencies.
def scaled(row):
m = row.mean()
if m != 0:
return row / m
else:
return row
scaledfreq = numpy.array([scaled(row) for row in frequencies])
# Create new Figure with black background
fig = plt.figure(figsize=(8, 8), facecolor='#aaa')
plt.imshow(scaledfreq, extent=[0, 512/TICKS_PER_US, labels[0], labels[-1]], cmap='jet',
vmin=0, vmax=1, origin='lower', aspect='auto')
plt.colorbar()
plt.ylabel("Interval period (us)")
plt.xlabel("Response (us)")
plt.show()
plt.show()

View File

@@ -0,0 +1,135 @@
#include "globals.h"
#include "flags.h"
#include "usb/usb.h"
#include "dataspec.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "writer.h"
#include "protocol.h"
#include "fmt/format.h"
#include <fstream>
static FlagGroup flags = {
&usbFlags,
};
static DataSpecFlag dest(
{ "--dest", "-d" },
"destination to analyse",
":d=0:t=0:s=0");
static DoubleFlag minInterval(
{ "--min-interval-us" },
"Minimum pulse interval",
2.0);
static DoubleFlag maxInterval(
{ "--max-interval-us" },
"Maximum pulse interval",
10.0);
static DoubleFlag intervalStep(
{ "--interval-step-us" },
"Interval step, approximately",
0.2);
static StringFlag writeCsv(
{ "--write-csv" },
"Write detailed CSV data",
"driveresponse.csv");
int mainAnalyseDriveResponse(int argc, const char* argv[])
{
flags.parseFlags(argc, argv);
FluxSpec spec(dest);
if (spec.locations.size() != 1)
Error() << "the destination dataspec must contain exactly one track (two sides count as two tracks)";
usbSetDrive(spec.drive, false, F_INDEX_REAL);
usbSeek(spec.locations[0].track);
std::cout << "Measuring rotational speed...\n";
nanoseconds_t period = usbGetRotationalPeriod(0);
if (period == 0)
Error() << "Unable to measure rotational speed (try fluxengine rpm).";
std::ofstream csv;
if (writeCsv.get() != "")
csv.open(writeCsv);
for (double interval = minInterval; interval<maxInterval; interval += intervalStep)
{
unsigned ticks = (unsigned) (interval * TICKS_PER_US);
std::cout << fmt::format("Interval {:.2f}: ", ticks * US_PER_TICK);
std::cout << std::flush;
std::vector<int> frequencies(512);
if (interval >= 2.0)
{
/* Write the test pattern. */
Fluxmap outFluxmap;
while (outFluxmap.duration() < period)
{
outFluxmap.appendInterval(ticks);
outFluxmap.appendPulse();
}
usbWrite(spec.locations[0].side, outFluxmap.rawBytes(), 0);
/* Read the test pattern in again. */
Fluxmap inFluxmap;
inFluxmap.appendBytes(usbRead(spec.locations[0].side, true, period, 0));
/* Compute histogram. */
FluxmapReader fmr(inFluxmap);
fmr.seek((double)period*0.1); /* skip first 10% and last 10% as contains junk */
fmr.findEvent(F_BIT_PULSE);
while (fmr.tell().ns() < ((double)period*0.9))
{
unsigned ticks = fmr.findEvent(F_BIT_PULSE);
if (ticks < frequencies.size())
frequencies[ticks]++;
}
}
/* Compute standard deviation. */
int sum = 0;
int prod = 0;
for (int i=0; i<frequencies.size(); i++)
{
sum += frequencies[i];
prod += i * frequencies[i];
}
if (sum == 0)
std::cout << "failed\n";
else
{
double mean = prod / sum;
double sqsum = 0;
for (int i=0; i<frequencies.size(); i++)
{
double dx = (double)i - mean;
sqsum += (double)frequencies[i] * dx * dx;
}
double stdv = sqrt(sqsum / sum);
std::cout << fmt::format("{:.4f} {:.4f}\n", stdv, mean/TICKS_PER_US);
}
if (writeCsv.get() != "")
{
csv << interval;
for (int i : frequencies)
csv << "," << i;
csv << '\n';
}
}
return 0;
}

View File

@@ -2,6 +2,7 @@
typedef int command_cb(int agrc, const char* argv[]);
extern command_cb mainAnalyseDriveResponse;
extern command_cb mainErase;
extern command_cb mainConvertCwfToFlux;
extern command_cb mainConvertFluxToAu;
@@ -50,6 +51,7 @@ struct Command
static command_cb mainRead;
static command_cb mainWrite;
static command_cb mainConvert;
static command_cb mainAnalyse;
static command_cb mainTest;
static std::vector<Command> commands =
@@ -57,6 +59,7 @@ static std::vector<Command> commands =
{ "erase", mainErase, "Permanently but rapidly erases some or all of a disk." },
{ "convert", mainConvert, "Converts various types of data file.", },
{ "inspect", mainInspect, "Low-level analysis and inspection of a disk." },
{ "analyse", mainAnalyse, "Disk and drive analysis tools." },
{ "read", mainRead, "Reads a disk, producing a sector image.", },
{ "rpm", mainRpm, "Measures the disk rotational speed.", },
{ "seek", mainSeek, "Moves the disk head.", },
@@ -107,6 +110,11 @@ static std::vector<Command> convertables =
{ "image", mainConvertImage, "Converts one disk image to another.", },
};
static std::vector<Command> analysables =
{
{ "driveresponse", mainAnalyseDriveResponse, "Measures the drive's ability to read and write pulses.", }
};
static std::vector<Command> testables =
{
{ "bandwidth", mainTestBandwidth, "Measures your USB bandwidth.", },
@@ -153,6 +161,9 @@ static int mainWrite(int argc, const char* argv[])
static int mainConvert(int argc, const char* argv[])
{ return mainExtended(convertables, "convert", argc, argv); }
static int mainAnalyse(int argc, const char* argv[])
{ return mainExtended(analysables, "analyse", argc, argv); }
static int mainTest(int argc, const char* argv[])
{ return mainExtended(testables, "test", argc, argv); }

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;
}