Compare commits

...

1558 Commits

Author SHA1 Message Date
David Given
d4c0853e1f Reset the Greaseweazle data stream when connecting. 2023-05-25 22:23:28 +02:00
David Given
363a4e959c Finally fix that format error when measuring disk speed. 2023-05-25 22:23:17 +02:00
David Given
9336a04681 Merge pull request #682 from davidgiven/docs
More documentation tweaking.
2023-05-25 22:10:10 +02:00
David Given
214ff38749 Tweak documentation layout. 2023-05-25 22:08:28 +02:00
David Given
a8f3c01d8b Add basic documentation for the extension formats. 2023-05-25 22:06:23 +02:00
David Given
4da6585ef9 Merge pull request #681 from davidgiven/bb679
Allow writing to Greaseweazle disks again by not setting hardSectorThresholdNs to inf.
2023-05-25 21:58:59 +02:00
David Given
df40100feb Merge pull request #680 from davidgiven/docs
Overhaul docs.
2023-05-25 21:40:32 +02:00
David Given
f2d92e93fb Format. 2023-05-25 21:27:49 +02:00
David Given
b4d8d569d2 Allow writing to Greaseweazle disks again by not setting hardSectorThresholdNs
to inf...
2023-05-25 21:26:44 +02:00
David Given
854b3e9c59 Better autogenerated documentation. 2023-05-25 21:14:41 +02:00
David Given
28ca2b72f1 Polishing. 2023-05-25 21:14:32 +02:00
David Given
7781c8179f Typo fix. 2023-05-25 20:20:02 +02:00
David Given
69ece3ffa0 Polish documentation. 2023-05-25 20:07:33 +02:00
David Given
53adcd92ed Spell (and capitalise) Greaseweazle correctly. 2023-05-25 19:50:05 +02:00
David Given
2bef6ca646 Merge pull request #678 from davidgiven/requirements
Overhaul config system and lots of other stuff
2023-05-16 01:29:58 +02:00
dg
bab350d771 Update Ubuntu build version. 2023-05-15 23:09:52 +00:00
dg
048dac223f Enable workflow cancelling when a new one is pushed. 2023-05-15 22:59:59 +00:00
dg
b7634da310 Work around Apple dev kit stupidity (definiting BYTE_SIZE in a standard
header...)
2023-05-15 22:51:16 +00:00
dg
882c92c64a Merge. 2023-05-15 22:49:52 +00:00
dg
4592dbe28b Add drive types for the Micropolis drives. 2023-05-15 22:49:15 +00:00
dg
edc0f21ae7 Remove all the requires TPI constraints --- I'm not sure this is a good idea. 2023-05-15 22:48:33 +00:00
dg
8715b7f6c1 Don't crash if no format is selected. 2023-05-15 22:14:06 +00:00
dg
99511910dd If an incoming FL2 file has no TPI, use the default rather than 0 (the default
will probably be zero, but anyway).
2023-05-15 22:00:03 +00:00
dg
a03478b011 Don't store the actual DriveProto in FL2 files, because it makes the proto tags
significant.
2023-05-15 21:59:24 +00:00
dg
5c428e1f07 Don't require the user to specify the drive TPI if they don't want to. 2023-05-15 21:51:05 +00:00
dg
ee57615735 Deal with invalid options in the GUI. 2023-05-15 20:55:33 +00:00
dg
67300e5769 Add the ability to validate the configuration, at least in the CLI; this may
require some refactoring for the GUI to apply cleanly.
2023-05-14 23:18:48 +00:00
dg
873e05051c Massive rework of the config system to be clearer, more robust, and more
flexible. (But it doesn't check options any more.)
2023-05-14 22:04:51 +00:00
dg
4daaec46a7 Greying out of the option buttons now works; but the whole way configs are
handled is pretty unsatisfactory and needs work.
2023-05-13 23:29:34 +00:00
dg
dd8cc7bfd4 Attempt to move the configuration setup logic into Config, so it's centralised. 2023-05-13 12:42:31 +00:00
dg
5568ac382f Eliminate Environment --- we don't use it and Config contains this
functionality.
2023-05-13 00:04:42 +00:00
dg
dcdb3e4455 Encoders and decoders are routed through Config. 2023-05-12 23:58:44 +00:00
dg
17b29b1626 Flux sinks and image writers are routed through Config. 2023-05-12 23:47:09 +00:00
dg
dcfcc6271c Sort out a whole bunch of other things, including cleaning up the way the
verification source is handled.
2023-05-12 23:28:25 +00:00
dg
1d77ba6429 ImageReaders can now contribute config. 2023-05-12 22:20:13 +00:00
dg
ff5f019ac1 Fetching the image reader is now done through Config. 2023-05-12 21:52:53 +00:00
dg
e61eeb8c6f Fetching the flux source is now done through Config. 2023-05-12 21:25:54 +00:00
dg
68d22e7f54 Fix build error. 2023-05-11 23:31:38 +00:00
dg
790f0a42e3 Move setting the image writer into Config. 2023-05-11 23:06:24 +00:00
dg
08e9e508cc Move setting the image reader into Config. 2023-05-11 23:02:05 +00:00
dg
ad1a8d608f Migrate setting the flux sink to Config. 2023-05-11 22:54:32 +00:00
dg
d74ed71023 Move setting the flux source into Config. 2023-05-11 22:47:00 +00:00
dg
0c7f9e0888 Enforce option requirements --- but the config stuff is still kinda broken and
will need rethinking, especially if flux files can carry configs with them.
2023-05-11 21:58:10 +00:00
dg
ba5f6528a8 Move option handling into Config. 2023-05-11 20:37:54 +00:00
dg
65cf552ec2 Some cleanup. 2023-05-11 20:03:25 +00:00
dg
715c0a0c42 Move config file loading into config.cc. 2023-05-11 19:58:16 +00:00
dg
9e383575d1 Any drive settings in the global config will override loaded settings from an
fl2 file.
2023-05-11 19:21:59 +00:00
dg
d84c366480 You can now fetch config fields by path. 2023-05-11 19:03:36 +00:00
dg
42e6c11081 Migrate to a new global config object. 2023-05-10 23:13:33 +00:00
dg
9ba3f90f1e Change the global config variable to a globalConfig() function. 2023-05-10 22:07:17 +00:00
dg
24ff51274b Fix formatting. 2023-05-10 21:14:30 +00:00
dg
4c4c752827 Add missing file. 2023-05-10 21:11:10 +00:00
dg
5022b67e4a Drive information is stored in FL2 files. 2023-05-10 20:47:55 +00:00
dg
6b990a9f51 Overhaul the TPI stuff; now both the drive and the layout have a TPI setting,
which must be set.
2023-05-10 19:58:44 +00:00
dg
e69ce3b8df Merge. 2023-05-10 18:31:42 +00:00
dg
cf537b6222 Add the proto part of option requirements. 2023-05-10 18:29:46 +00:00
David Given
9d1160faff Merge pull request #677 from davidgiven/errors
Clean up error handling.
2023-05-10 01:13:49 +02:00
noreply@github.com
ed4067f194 Merge pull request #677 from davidgiven/errors
Clean up error handling.
2023-05-09 23:13:49 +00:00
dg
d4b55cd8f5 Switch from Logger() to log(). 2023-05-09 22:47:36 +00:00
dg
baaeb0bca7 Fix mangled formatting caused by clang-format. 2023-05-09 21:39:35 +00:00
dg
466c3c34e5 Replace the Error() object with an error() function which takes fmt
formatspecs, making for much cleaner code. Reformatted everything.

This actually happened in multiple steps but then I corrupted my local
repository and I had to recover from the working tree.
2023-05-09 20:59:44 +00:00
dg
099d7969ca Add the drive types dropdown, plus config fragments. Change the TPI settings to
floats (because 40-track 3.5" uses a TPI of 67.5...).
2023-05-08 23:04:52 +00:00
dg
5adfa95a85 Add a preliminary format for the 8050. 2023-05-08 23:03:37 +00:00
David Given
bfa0846ad0 Merge pull request #676 from davidgiven/doc
Correct index table rendering.
2023-05-08 20:38:53 +02:00
dg
7099264334 Correct index table rendering. 2023-05-08 18:37:16 +00:00
David Given
69b44e7968 Merge pull request #674 from davidgiven/doc
Overhaul documentation.
2023-05-08 01:13:57 +01:00
dg
fe39977ff7 Remember to add links to each profile's documentation. 2023-05-07 23:51:55 +00:00
dg
b9fc8de5b5 OSX compatibility. 2023-05-07 23:33:36 +00:00
dg
f7b8022d3a Switch to the traditional unicorn/dinosaur support categorisation. 2023-05-07 23:06:56 +00:00
dg
a62346c515 Add short names to each profile. 2023-05-07 21:49:14 +00:00
dg
e372d757ad Some tidying. 2023-05-07 21:32:36 +00:00
dg
ab1b10f935 Typo fix. 2023-05-07 21:30:09 +00:00
dg
8e918706dc First draft at autogenerating the table in the README. 2023-05-07 21:28:42 +00:00
dg
76450d00bf Tidy. 2023-05-07 19:53:57 +00:00
dg
ee53542e18 Eliminate config includes, as nothing uses them any more and it just makes
things like documentation generation hard.
2023-05-07 19:35:55 +00:00
dg
db004bc787 Preparse ConfigProto objects. 2023-05-07 19:28:29 +00:00
dg
71a7f3554e Remember to actually add the documentation files... 2023-05-07 18:40:24 +00:00
dg
5c3f422a53 First pass at automatic document generation. 2023-05-07 18:36:30 +00:00
dg
2fe0cec04a Copy documentation into the config definitions. 2023-05-07 16:48:17 +00:00
David Given
de59e781b5 Merge pull request #673 from davidgiven/options
Do more options overhauling.
2023-05-07 13:21:28 +01:00
dg
8c77af651b Run corpus tests on other platforms. 2023-05-07 11:56:32 +00:00
dg
638f6928cf Fix checkouts, maybe? 2023-05-07 11:53:56 +00:00
dg
ccc8e597a7 Don't use vformat, as apparently it's problematic. 2023-05-07 11:49:08 +00:00
dg
585f19d884 More fix. 2023-05-07 11:46:30 +00:00
dg
bb2b7d7df6 Typo fix. 2023-05-07 11:45:07 +00:00
dg
e75d218438 Attempt to run the corpus tests on github for Linux. 2023-05-07 11:44:14 +00:00
dg
7f81b554fd Try to decode the test corpus and make sure there were no decode regressions. 2023-05-07 11:37:50 +00:00
dg
2490f19a1a Add a preliminary option linter. Fix the format errors which showed up. 2023-05-07 00:29:21 +00:00
David Given
30f382bf22 Merge pull request #670 from davidgiven/dmf
Support DMF.
2023-05-07 00:15:13 +01:00
dg
ad03c187cf Merge from master. 2023-05-06 22:45:46 +00:00
David Given
06560b5a5a Merge pull request #672 from davidgiven/usb
Upgrade libusbp.
2023-05-06 23:43:37 +01:00
dg
7c40093698 Try to work around weird test failure on Windows. 2023-05-06 22:30:50 +00:00
dg
d37c75d703 Made test failures log to stdout. 2023-05-06 22:15:01 +00:00
dg
82bfb9a303 Upgrade libusbp. 2023-05-06 21:19:07 +00:00
dg
01682101a6 Update documentation. 2023-05-06 19:59:45 +00:00
dg
3c46f787b1 Always do an update when the state changes, because otherwise certain events
get lost.
2023-05-06 19:21:31 +00:00
dg
591d200283 Adjust DMF gaps. 2023-05-06 19:20:32 +00:00
dg
195534c21e Configure the 1680kB DMF format file system. 2023-05-06 18:11:24 +00:00
dg
0f9d851a29 Adjust the DMF format timings to match that of the Microsoft disk image. 2023-05-06 17:26:56 +00:00
dg
18a03baf99 Display object lengths in the flux viewer. 2023-05-06 15:34:44 +00:00
dg
5e06db4a52 Add preliminary DMF support. 2023-05-06 11:02:09 +00:00
David Given
bf78508ef7 Merge pull request #669 from davidgiven/hplif
Do some LIF enhancement.
2023-05-06 11:38:17 +01:00
dg
137c0340fb Fix month, which was off-by-one. Add custom attributes for the other LIF dirent
properties.
2023-05-06 10:20:10 +00:00
dg
e6d9de2d80 Decode timestamps into a custom property. 2023-05-06 10:16:12 +00:00
dg
d9b319eaed Add textual file types (where known) for LIF files. 2023-05-06 10:00:12 +00:00
dg
f2e713bde3 Stop trying to build for OSX 10.15, because it looks like the github runners
have been turned off.
2023-05-05 23:19:44 +00:00
David Given
94e2e494c9 Merge pull request #667 from davidgiven/options
Overhaul the options system.
2023-05-06 00:18:41 +01:00
dg
5af408e1d1 Add missing file. 2023-05-05 23:07:57 +00:00
dg
77bdc727ab Properly handle default options in the CLI. 2023-05-05 22:57:49 +00:00
dg
eb26426424 Consolidate the Victor formats into each other. 2023-05-05 22:29:26 +00:00
dg
f624bb6e5b Consolidate the Mac formats into each other. 2023-05-05 22:24:28 +00:00
dg
4a8fb9288c Remove obsolete file. 2023-05-05 22:16:11 +00:00
dg
f8f5873973 Consolidate (and typo fix) the ampro format. 2023-05-05 22:15:37 +00:00
dg
5f4903f2d1 Rename the commodore1541 options to be a bit more standard. 2023-05-05 22:07:13 +00:00
dg
b02a894663 Consolidate the Brother formats. 2023-05-05 22:03:49 +00:00
dg
510b530551 Consolidate all the IBM formats together. 2023-05-05 21:37:49 +00:00
dg
c36662205b Typo fix. 2023-05-05 21:18:27 +00:00
dg
a2ffe06792 Consolidate the MX formats into each other. 2023-05-05 21:16:26 +00:00
dg
0f56108bf5 Consolidate the Apple II formats together. 2023-05-05 21:11:06 +00:00
dg
199cefdb71 Fix radiobuttons for multiple option groups. 2023-05-05 21:06:57 +00:00
dg
1bdeaa326c Consolidate some Hewlett-Packard LIF disks together. 2023-05-05 20:46:49 +00:00
dg
cce8cfe88d Consolidate the Tiki 100 formats. 2023-05-05 20:36:39 +00:00
dg
bcfc0217dc Consolidate the Northstar formats into each other. 2023-05-05 20:29:45 +00:00
dg
7cfa080220 Merge from master. 2023-05-05 20:23:17 +00:00
dg
45ebc0f40f Consolidate the Micropolis formats into one. 2023-05-05 20:22:55 +00:00
dg
38d575eda7 Remember to set a default format. 2023-05-05 20:18:53 +00:00
dg
9cb284583b Consolidate all the Atari ST formats together. 2023-05-05 20:15:47 +00:00
dg
137b921e8d Consolidate all the Acorn formats together. 2023-05-05 20:07:44 +00:00
dg
8c876f555d Move from option exclusivity groups to option groups, which are better. 2023-05-05 19:55:56 +00:00
David Given
0988dd524b Merge 2dc649ef09 into 51fa3c5293 2023-05-04 21:10:25 +00:00
dg
2dc649ef09 Add read-only support for LIF filesystems. 2023-05-04 21:04:55 +00:00
dg
baf02cb849 Add support for the HPLIF 616kB format (contributed by Eric Rechlin). 2023-05-04 19:12:51 +00:00
David Given
51fa3c5293 Merge pull request #664 from bdwheele/ibmpc-8-sector-formats
Adding IBM PC 8-sector formats
2023-05-02 12:27:15 +01:00
Brian Wheeler
134dd6c37d Adding IBM PC 8-sector formats 2023-05-01 08:24:24 -04:00
David Given
d766e1f9a9 Merge pull request #663 from ejona86/micropolis-200ms
Micropolis: disk rotate period is 200 ms
2023-04-24 13:12:18 +02:00
Eric Anderson
d298f5b16e Micropolis: disk rotate period is 200 ms
The disks are expected to contain 100,000 bitcells, so clock_period_us
and rotational_period_ms need to align.
2023-04-23 13:54:50 -07:00
dg
ed634fbbf6 Fix build failure. 2023-04-07 16:20:32 +00:00
dg
4c776d584b Add read support for A2R v2 files. 2023-04-07 15:00:20 +00:00
David Given
c2c04862a2 Merge pull request #662 from davidgiven/scp
Adjust the SCP write logic so an unspecified TPI is treated as 96.
2023-04-07 11:25:00 +02:00
dg
ccd9539015 Adjust the SCP write logic so an unspecified TPI is treated as 96 (the usual). 2023-04-07 09:02:46 +00:00
David Given
624c597735 Merge pull request #661 from davidgiven/scp
Fix reading 48tpi SCP files.
2023-04-06 23:51:30 +02:00
dg
9300aa79c3 Read 48tpi SCP files correctly. 2023-04-06 21:49:06 +00:00
David Given
9e522c7da2 Merge ef60bfff6b into df6e47fa50 2023-04-06 18:20:31 +00:00
dg
ef60bfff6b Looks like the Roland D-20 format is the same as Brother240??? 2023-04-06 17:07:00 +00:00
dg
635c6c7bfe Add an explorer option to show raw bits. 2023-04-06 16:07:18 +00:00
David Given
df6e47fa50 Merge pull request #659 from davidgiven/n88
Add a histogram viewer to the imager. Because it's there.
2023-04-06 11:20:36 +02:00
dg
654cdcd3d1 Add a histogram viewer to the imager. Because it's there. 2023-04-06 08:59:05 +00:00
dg
a633b73e12 Add boilerplate for Roland D20 decoder. 2023-04-05 22:36:54 +00:00
David Given
ba93dae240 Merge pull request #657 from davidgiven/d20
Improve the explorer.
2023-04-05 23:11:49 +02:00
dg
8e0ca85d1e Add the histogram viewer and clock guess button. 2023-04-05 20:43:49 +00:00
dg
56a4926bd3 Factor out the clock guess code so it can be used elsewhere. 2023-04-05 19:17:37 +00:00
dg
6a2aae4ef2 Create new branch named "d20" 2023-04-05 17:47:31 +00:00
dg
ec68ce3bfa Try to fix dev release. 2023-04-04 22:43:06 +00:00
dg
a777a5be30 Typo fix. 2023-04-04 21:48:45 +00:00
David Given
b553a8b1fb Merge pull request #654 from davidgiven/search
Overhaul the GUI, to make it... gooier.
2023-04-04 22:37:37 +01:00
dg
b119e1f72d Tidying. 2023-04-04 21:02:03 +00:00
dg
7345d3e6c1 Fix merge conflict. 2023-04-04 20:23:05 +00:00
dg
e9b7a7bb52 Fix the icon background colour on Windows. 2023-04-04 20:20:32 +00:00
dg
2022732dd9 Some final tidying. 2023-04-04 20:12:21 +00:00
dg
63544647b6 Add a custom IconButton class. Rework the source icon list. Again. 2023-04-04 19:42:24 +00:00
dg
6b62585ad5 Be more intelligent about resizing the main window. 2023-04-03 22:45:25 +00:00
dg
14027210f7 Even more GUI tweaking. 2023-04-03 21:53:36 +00:00
dg
3df17b23b8 Turns out you can't unselect exclusive options in the GUI, so add an 'off' for
the Apple filesystem selection.
2023-04-03 21:53:26 +00:00
dg
cbf3f56562 The xxd binary is in the vim package. For some reason. 2023-04-02 22:59:20 +00:00
dg
1f74d9189f Make the new GUI actually work, to a certain extent. 2023-04-02 22:54:09 +00:00
dg
137658d1d6 Flesh out the source list a bit. 2023-04-02 21:34:02 +00:00
dg
5b627bd2b1 wxImageList tweak. 2023-04-02 19:54:08 +00:00
dg
38ff08885a Experiment with wxImageList. 2023-04-02 19:42:55 +00:00
dg
a89993aabb Fix the UI. 2023-04-02 19:19:16 +00:00
dg
d6353403e2 Set the icon again. 2023-04-02 17:14:59 +00:00
dg
bc62ee04c0 Some random tweaks to improve state machine look and feel. 2023-04-02 17:11:46 +00:00
dg
d3ff836b63 Put the headers in the right order to keep Windows happy. 2023-04-02 16:54:37 +00:00
dg
a7aac5578e Remove the explorer search button for now. 2023-04-02 16:41:56 +00:00
dg
add5a141d3 Actually make the new GUI model work. Mostly? 2023-04-02 12:38:12 +00:00
dg
330410ec61 Rework the GUI so that each panel is a different class. It doesn't work yet,
but the bulk of the restructuring is done.
2023-04-02 12:37:27 +00:00
dg
d0f49dcfa6 Add (but don't implement) the explorer search box. 2023-04-01 18:27:01 +00:00
David Given
124f6ab7cb Merge 471f63592e into e4204196cd 2023-04-01 13:05:59 +00:00
dg
471f63592e Typo fix. 2023-04-01 12:56:17 +00:00
dg
50e210c72f It seems the build artifact needs to be renamed for 10.15. 2023-04-01 12:40:05 +00:00
dg
d3396aa535 Use two threads for building --- seems we can do this on github. 2023-04-01 12:32:47 +00:00
dg
5ed8b838bc Another typo fix. 2023-04-01 12:15:04 +00:00
dg
d1757eacc2 Typo fix. 2023-04-01 12:14:37 +00:00
dg
0692e5f5d5 Try building for OSX 10.15 and see what happens. 2023-04-01 12:13:34 +00:00
David Given
e4204196cd Merge pull request #650 from davidgiven/flags
Allow options to be set in the GUI.
2023-03-31 23:37:17 +01:00
dg
241d4342e4 Make exclusivity groups work in the GUI. 2023-03-31 22:11:40 +00:00
dg
c04cbc631c Option name tidy. 2023-03-31 22:11:19 +00:00
dg
29b273ad7b Correctly set the path of files. 2023-03-31 22:10:47 +00:00
dg
9720dab2f6 Optimise the option radiobuttons a bit. 2023-03-31 22:10:13 +00:00
dg
bddc64a324 Merge from master. 2023-03-31 22:09:11 +00:00
David Given
eb324f14de Merge pull request #648 from davidgiven/basis
Add support for the Basis-108 Apple II clone.
2023-03-31 22:34:32 +01:00
David Given
b78a057c81 Merge branch 'master' into basis 2023-03-31 22:10:47 +01:00
dg
5751725213 Allow options to be selected in the GUI. 2023-03-31 21:09:40 +00:00
dg
f194392f99 Fix the broken AppleDOS double-sided disks. Allow access to side 1 on AppleDOS
volumes.
2023-03-31 18:24:03 +00:00
dg
fea62178af Apply what might be the right translation to the CP/M boot tracks. 2023-03-31 18:06:21 +00:00
David Given
33ef4ce8de Merge pull request #649 from davidgiven/pme
Rename the PME format to psos800.
2023-03-31 18:56:34 +01:00
dg
3728120f95 Add support for CP/M disks and filesystems. 2023-03-31 17:56:18 +00:00
dg
2944b9b3f6 Rename the PME format to psos800. 2023-03-31 17:23:33 +00:00
David Given
3430574364 Merge pull request #646 from davidgiven/pme
Add a format for the PME-68-12 SBC.
2023-03-31 11:59:00 +01:00
dg
fc5a5212c0 Merge. 2023-03-30 22:21:30 +00:00
dg
20f724ed13 Update README. 2023-03-30 22:21:00 +00:00
dg
94c1d21938 Rename the pme profile to pme68_800. 2023-03-30 22:20:29 +00:00
David Given
a1a9666b6f Fix the AppleDOS sector translation. 2023-03-30 12:26:13 +02:00
dg
0551ddc276 Add write support for Apple II 640kB disks. 2023-03-28 20:36:43 +00:00
dg
049ffd3b04 Add a profile for the Basis Apple II format. 2023-03-28 19:40:58 +00:00
dg
c28f757c5c Add a very prototype AppleDOS VFS plugin. 2023-03-28 19:29:02 +00:00
dg
91dbb86e64 Add missing files. Rename the Apple II formats. 2023-03-28 16:29:59 +00:00
dg
27a04ee22b Add initial support for the Basis-108. 2023-03-27 23:07:59 +00:00
dg@cowlark.com
5cefce9922 Fix the thread termination errors every time the directory browser is used. 2023-03-27 21:06:59 +00:00
dg
8fb4c90bed Remove the retry limit when reading from virtual flux sources, to allow flux
files with very large numbers of reads to be processed.
2023-03-27 20:14:49 +00:00
dg
81753669cc Add the 'fluxengine merge' command. 2023-03-27 20:12:46 +00:00
dg
0a0a72bcf3 Add configurable head jiggle on error, just to see if the head needs settling. 2023-03-27 18:40:35 +00:00
dg
c4a6e3e063 Fix the Windows development build artifact. 2023-03-26 23:15:20 +00:00
dg
1138e6b77f Try a different way to fetch the filedes length. 2023-03-26 21:22:11 +00:00
dg
030f9218d6 Hopefully fix the layout this time? 2023-03-26 21:17:07 +00:00
dg
2fff32e8f2 Don't return bad data which makes the GUI crash. 2023-03-26 18:52:29 +00:00
dg
5b2aa9926f Robustness and warning fixes. 2023-03-26 18:50:14 +00:00
dg
921e178e83 Tone down the bad-sector-size warning a bit. 2023-03-26 18:23:25 +00:00
dg
25ffd900c8 Realise that the PME format is HCS. Add a really basic and probably broken
PHILE filesystem reader.
2023-03-26 18:21:51 +00:00
dg
7ea4e116cc Add a warning if the configured sector size doesn't match the one on disk. 2023-03-26 16:25:40 +00:00
dg
a9daec36f5 Add prototype PME-68-12 format. 2023-03-24 21:07:48 +00:00
David Given
cebc7c6cd2 Merge 3f85c9f006 into 909f0d628b 2023-01-06 21:30:18 +00:00
dg
3f85c9f006 Adjust timings to be more correct. 2023-01-06 21:28:51 +00:00
dg
ed5efd7b87 Reenable optimisation. Again. 2023-01-06 21:28:35 +00:00
dg
4984a53bfd First hypothetically working version of the agat encoder. 2023-01-05 18:36:01 +00:00
dg
b0c77653a2 Add the boilerplate for the Agat encoder. 2023-01-05 12:04:36 +00:00
David Given
909f0d628b Merge pull request #637 from davidgiven/cpm
Fix an issue with extent handling in the CP/M file system.
2022-12-18 23:21:45 +01:00
dg
e27e3ada92 Fix an issue with extent handling in the CP/M file system; actually add a CP/M
test.
2022-12-18 22:00:52 +00:00
dg
339ea3b5a4 Move the * and + Bytes methods onto Bytes itself. 2022-12-18 22:00:16 +00:00
dg
9bd8b8915e Update format file. 2022-12-18 21:59:14 +00:00
dg
35008656a9 Remove stray logging. 2022-12-17 17:54:33 +00:00
David Given
825089458f Merge pull request #636 from davidgiven/tiki
Add support for the Tiki 100 formats.
2022-12-17 12:20:18 +01:00
dg
4a086d94b7 Add best-guess CP/M filesystem definitions for the Tiki 90kB and 800kB formats. 2022-12-17 11:01:46 +00:00
dg
0aeddf7e98 Add support for the Tiki 100 formats. 2022-12-17 10:59:30 +00:00
David Given
4922d1deb4 Merge pull request #634 from davidgiven/mac2
Fix sector skew, again.
2022-12-05 21:57:45 +01:00
dg
86d0893261 Adjust mac encoder clock to be more like the real thing. 2022-12-05 20:27:52 +00:00
dg
e4c67f18bd Fix the sector skew stuff, again. Modify the mac400 format to emit sectors in
the right order.
2022-12-05 20:22:01 +00:00
David Given
d07c5a94e1 Merge pull request #632 from davidgiven/layout
Rework the layout stuff to be more correct.
2022-12-04 21:32:17 +01:00
dg
a91dee27e7 Rework the layout stuff to be more correct. Physical skew no longer affects the
order in the resulting images.
2022-12-04 19:19:37 +00:00
David Given
e3219087c9 Merge pull request #630 from davidgiven/brother
Fix some nasty Brother bugs.
2022-12-02 22:20:32 +01:00
dg
cc9ec84aec Physical skew turns out to be horribly broken, so turn it off for the Brother
formats (the only ones which use it) until we can sort it out.
2022-12-02 20:17:42 +00:00
dg
a33cc5710c Be more rigorous about checking for invalid brother120fs filesystems --- even
though the filesystem is so simple that positively identifying it is quite
hard.
2022-12-02 19:54:58 +00:00
David Given
c2b148288a Merge pull request #628 from davidgiven/osx
Fix a bunch of OSX things.
2022-12-01 22:24:47 +01:00
David Given
a483567564 Fix the explorer to work on OSX. Lots of other vaguely OSX-related changes. 2022-12-01 21:37:59 +01:00
David Given
bd99bc6d94 Don't trust isprint() to return ascii characters, because Unicode. 2022-12-01 21:28:49 +01:00
David Given
8f79071aad Turn optimisation back on! 2022-12-01 21:28:31 +01:00
David Given
ef9071049b Merge pull request #627 from davidgiven/osx
Produce more correct OSX app bundles.
2022-12-01 20:53:58 +01:00
David Given
60e1ab8cca Dependency fix? 2022-12-01 20:21:33 +01:00
David Given
d3dbfd3154 Use dylibbundler to create possibly-working OSX app bundles. 2022-12-01 19:49:50 +01:00
David Given
ee2dffb498 Try and generate correct OSX app bundles. 2022-12-01 19:45:51 +01:00
David Given
6d9510cc65 Merge pull request #626 from elosha/macosxfixes
Library fallback path fixed & MacPorts compatible
2022-12-01 17:17:36 +01:00
Eliza Winterborn
49f0f5d000 Library fallback path fixed & MacPorts compatible
Use correct variable. Also look for libs in MacPorts' default lib path /opt/local/lib, not just HomeBrew's
2022-12-01 17:03:36 +01:00
dg
d8528d889a Add another missing file. 2022-11-29 20:55:45 +00:00
dg
ec439a5f2a Add file which has been missing since forever... 2022-11-29 20:55:07 +00:00
David Given
d88110488e Merge pull request #624 from davidgiven/flx
Add support for reading FLUXCOPY .flx files.
2022-11-29 21:53:13 +01:00
dg
2f4b15293a Adjust the FLX tick rate to something more resembling normality. 2022-11-29 20:26:58 +00:00
dg
3d71f32587 Update documentation. 2022-11-29 20:18:21 +00:00
dg
5136dda598 Made the FLX reader work. 2022-11-29 20:17:24 +00:00
dg
d26940304b Boilerplate for a FLX file reader. 2022-11-29 19:33:47 +00:00
David Given
f9f11b4966 Merge pull request #623 from davidgiven/smaky
Improve the flux viewer.
2022-11-28 21:42:23 +01:00
dg
1297b568ba Display the raw flux view in bytes as well as bits. 2022-11-28 20:22:34 +00:00
dg
fd10840cc0 Display raw bits in the flux viewer. 2022-11-28 20:15:57 +00:00
David Given
7e86be979d Merge pull request #622 from davidgiven/smaky
Add basic filesystem support for the Smaky 6.
2022-11-28 00:00:48 +01:00
dg
731d1efcc4 Add missing file. 2022-11-27 22:38:44 +00:00
dg
dfda8be30c Support tabs in the file viewer text view. 2022-11-27 22:24:30 +00:00
dg
7dd1b6d8e9 Add Smaky 6 filesystem documentation. 2022-11-27 22:23:55 +00:00
dg
ec1bcdb9e5 Add a simple Smaky 6 filesystem backend. 2022-11-27 22:22:59 +00:00
dg
c0f46d2bd4 Create new branch named "smaky" 2022-11-27 22:21:41 +00:00
David Given
615dca3130 Merge pull request #621 from davidgiven/osx
Add a proper OSX GUI application.
2022-11-27 17:27:04 +01:00
David Given
9cf9597c54 Try and make an OSX release. 2022-11-27 17:26:43 +01:00
David Given
e24ee648e7 Add license texts for the included libraries. 2022-11-27 17:06:28 +01:00
David Given
e3518dc389 Upload the OSX package. 2022-11-27 16:59:45 +01:00
David Given
693ba20606 Add rules to build the OSX package. 2022-11-27 16:58:50 +01:00
dg
b947c6c186 Add the skeleton of an OSX application. 2022-11-27 15:00:31 +00:00
David Given
7f8ecb8514 Fix broken conditional due to typo. 2022-11-27 15:18:07 +01:00
David Given
4df6afa9c1 Merge pull request #620 from davidgiven/b593
Correct generation of SCP files.
2022-11-27 13:28:02 +01:00
dg
d0620f8efe Fix readTracks to use locations. 2022-11-27 11:55:25 +00:00
dg
2b1a6dbb03 Make the scp flux sink use the layout to determine which tracks and sectors are
being written rather than the range flags directly.
2022-11-27 11:52:16 +00:00
David Given
c6ef667c3f Merge pull request #619 from davidgiven/b602
Fix weird error when trying to create fatfs filesystems.
2022-11-27 12:33:33 +01:00
dg
8c1d1bec93 Create new branch named "b593" 2022-11-27 11:20:30 +00:00
dg
8be174e65a Fix incorrect types when calling fatfs which was leading to really weird
behaviour when optimisation was enabled.
2022-11-27 11:11:26 +00:00
dg
6d37fafb02 Add a helper function for checking if a file exists. 2022-11-27 11:10:58 +00:00
dg
46ce882daa Allow implicit creation of disk images when writing. 2022-11-27 11:09:46 +00:00
David Given
6d14cbdb9b Merge pull request #618 from davidgiven/b609
Fix --doc to not hang.
2022-11-27 11:57:38 +01:00
dg
4bf6b433ae Create new branch named "b602" 2022-11-27 10:26:11 +00:00
dg
87a1b2e6f8 Don't hang when calling findAllProtoFields on recursive protos. 2022-11-27 10:24:40 +00:00
dg
c6ee48ec85 Create new branch named "b609" 2022-11-27 10:04:42 +00:00
David Given
b58a6b1649 Merge pull request #617 from davidgiven/smaky
Add support for the Smaky 6.
2022-11-27 01:00:21 +01:00
dg
bd9736df93 Typo fix. 2022-11-26 23:59:50 +00:00
dg
3b9c966e3d Add Smaky documentation. 2022-11-26 23:34:20 +00:00
dg
96c9a89171 Typo fixes. 2022-11-26 23:28:23 +00:00
dg
c374ffd15e Increase the smaky disk signature to 32 bits. 2022-11-26 22:56:45 +00:00
dg
c53109e1a1 Add the disk explorer. 2022-11-26 22:56:24 +00:00
dg
4598b3a7a6 Fix line endings. 2022-11-26 11:19:05 +00:00
dg
cf975b74bf Rename smaky to smaky6. 2022-11-26 11:18:38 +00:00
dg
5d65dcf3c8 Even better Smaky reads, and maybe with sector 0 in the right place. 2022-11-25 21:28:59 +00:00
dg
f299ec1f8d More Smaky improvements. 2022-11-25 20:23:24 +00:00
dg
6677034774 Improved record view. 2022-11-25 20:22:52 +00:00
dg
3c7c4639a9 Massively improved the quality of Smaky reads. 2022-11-25 19:33:29 +00:00
dg
7e9a1268a5 Add an extremely prototype version of the Smaky decoder. 2022-11-23 21:44:40 +00:00
dg
a60b8e68ca Add a Bytes method for reversing each bit. 2022-11-23 21:44:10 +00:00
dg
b2161aa67e Create new branch named "smaky" 2022-11-22 19:09:54 +00:00
David Given
d1fffb1d08 Merge pull request #616 from davidgiven/gui
Fix a crash when using the GUI.
2022-11-21 20:37:52 +01:00
dg
52d66d9555 Only iterate the environment if the set has been created. 2022-11-21 19:10:37 +00:00
David Given
66f2d359e2 Merge pull request #615 from davidgiven/vfs
Don't define a global structure called Entry
2022-11-21 20:07:51 +01:00
dg
8327f33ee6 Create new branch named "gui" 2022-11-21 18:37:47 +00:00
dg
d4a94551d9 Don't define a global structure called Entry because it conflicts with
something in adflib.
2022-11-21 18:31:59 +00:00
dg
d2a545d83e Merge from master. 2022-11-21 17:39:39 +00:00
David Given
aee4eac271 Merge pull request #613 from davidgiven/proto
Do some long needed proto cleanup
2022-11-21 00:13:32 +01:00
David Given
cbeddb11bc Fix one last use of has_ to identify a flux writer type. 2022-11-20 23:37:04 +01:00
David Given
4c51cf8316 Update documentation. 2022-11-20 23:36:47 +01:00
David Given
345cd6bd92 Convert the VFS protos to use enums rather than oneofs. 2022-11-20 10:37:45 +01:00
David Given
48540245b5 Convert the imagereader/writer to use proto enums rather than oneofs. 2022-11-20 10:25:19 +01:00
David Given
088bd9434d Switch from using a oneof to an explicit enum for the flux source/sink
configurations, as this allows default options for multiple source/sink types.
2022-11-19 22:52:52 +01:00
David Given
8ba8c58377 Add a config for the ICL Model 30. 2022-11-19 20:10:39 +01:00
David Given
4ae43d0503 Merge pull request #607 from tdaede/fix_layout_cache
Remove the layout cache and rename layout -> trackInfo.
2022-10-13 23:01:44 +02:00
David Given
00fdb90adf Merge pull request #556 from tdaede/fix_d88_data_length
D88: Use the data length field to seek past any extra data.
2022-10-03 18:25:42 +02:00
Thomas Daede
f8257e697a D88: Use the data length field to seek past any extra data.
This will effectively ignore the field length if it is shorter
than the sector size (or 0).
2022-10-02 16:45:51 -07:00
David Given
0ddfd3bead Merge pull request #605 from tdaede/fix_physical_sector_ordering
D88/NFD: Add physical layout before adding sector.
2022-10-02 20:46:38 +02:00
David Given
4797d1ea10 Merge pull request #604 from tdaede/density_fix_2
Force density setting for D88/NFD.
2022-10-02 20:45:28 +02:00
Thomas Daede
d20ce3dde7 Remove the layout cache and rename layout -> trackInfo.
The layout cache is busted and seems not worth the effort.

Fixes #603.
2022-10-02 09:43:48 -07:00
Thomas Daede
ef5f7ec9c3 D88/NFD: Add physical layout before adding sector.
Partially fixes #603, it is still busted if the number of
tracks is specified larger than what is in the image, which
used to work.
2022-10-01 19:43:17 -07:00
Thomas Daede
69c60c4026 Force density setting for D88/NFD.
This fixes #560 (partially).

I do not understand how the protobuf parsing stuff works enough
to make it detect optional fields.
2022-10-01 18:48:12 -07:00
David Given
2f4f2083ba Merge pull request #600 from wybren1971/IMDwriter
bug fix in data rates
2022-09-21 16:00:02 +02:00
wybren1971
77ada0471b bug fix in data rates 2022-09-21 15:20:58 +02:00
David Given
00cab46a0d Merge pull request #596 from davidgiven/c64
Minor C64 changes.
2022-09-16 19:46:11 +02:00
David Given
7f7460f3f3 Update C64 documentation. 2022-09-16 19:16:30 +02:00
David Given
c225eef9ad Add a format for the CMD FD2000. 2022-09-16 19:13:14 +02:00
David Given
460f920502 Use options for the 1541 35-track/40-track switch. 2022-09-16 18:52:39 +02:00
David Given
c7677e5514 Merge pull request #592 from davidgiven/cleanup
Do lots of cleanup.
2022-09-16 12:58:56 +02:00
David Given
12fb39baa9 More variable renaming. 2022-09-16 00:31:19 +02:00
David Given
201fd22861 Roll requiredSectors into readerwriter.cc. 2022-09-16 00:26:02 +02:00
David Given
d0fb85e712 Rename all the remaining layout and location variables to trackInfo (where
appropriate).
2022-09-16 00:20:54 +02:00
David Given
81cbd00cc8 Rename Track to TrackInfo, which better describes what it is. 2022-09-16 00:09:39 +02:00
David Given
4a565b5ea0 Split the Layout class. 2022-09-16 00:07:22 +02:00
David Given
82f61eee12 Eliminate Location in favour of Layout. 2022-09-15 23:52:07 +02:00
David Given
88fc7ff9c3 Begin cleaning up the Layout stuff. 2022-09-15 21:45:12 +02:00
David Given
8eb17bf104 Merge pull request #591 from davidgiven/c64
Rework the entire layout stuff.
2022-09-15 21:07:16 +02:00
David Given
9a8fc80220 Massive overhaul of how the physical/logical layout mapping is done, to make it
more consistent and bidirectional everywhere and just generally better.
Hopefully this fixes that pesky 1581 problem.
2022-09-15 20:23:33 +02:00
David Given
e5e652fad2 Merge pull request #590 from davidgiven/options
Add a feature where configs can add simple command line options.
2022-09-14 00:53:14 +02:00
David Given
924d077315 Don't allow includes inside options. 2022-09-14 00:20:16 +02:00
David Given
44a7505295 Clean up the Atari formats. 2022-09-14 00:18:44 +02:00
David Given
b1a6fa4084 Make the AppleDOS and ProDOS settings options. 2022-09-14 00:16:42 +02:00
David Given
0665fc0a6f Add an option for, er, options defined in config files. 2022-09-14 00:04:29 +02:00
David Given
4bf5fd49d6 Merge pull request #589 from davidgiven/osx
Error out if you're not using GNU Make 4.
2022-09-13 22:50:35 +02:00
David Given
0a6bc99ecd Error out if you're not using GNU Make 4. 2022-09-13 22:48:03 +02:00
David Given
45cc617fc5 Merge pull request #588 from davidgiven/c64
One more attempt to fix the 1581 format...
2022-09-13 21:41:24 +02:00
David Given
6dc5eabdf0 Oops. I got the sides swapped on the 1581 format. 2022-09-13 21:35:26 +02:00
David Given
48d3bc55bf Merge pull request #586 from davidgiven/cleanup
Lots of cleanup stuff I've been putting off
2022-09-13 00:58:31 +02:00
David Given
6b7e81d7fb Move requiredSectors() to the base class. 2022-09-13 00:32:04 +02:00
David Given
dce6248193 Victor9k no longer uses collectSectors(). 2022-09-13 00:22:46 +02:00
David Given
0e349ede4c TIDS990 no longer uses collectSectors. 2022-09-13 00:19:45 +02:00
David Given
60117471a7 No more collectSectors in Brother. 2022-09-13 00:06:55 +02:00
David Given
3c23e7b047 Merge. 2022-09-12 23:58:03 +02:00
David Given
b48e1ba9e0 Remove the trivial collectSectors() methods which have been replaced by the
base class.
2022-09-12 23:55:52 +02:00
David Given
1267191e8e Abstract is for Java. 2022-09-12 23:52:24 +02:00
David Given
65a43b64ae Merge pull request #585 from davidgiven/doc
Update documentation.
2022-09-12 23:37:16 +02:00
David Given
09e446a26e Adjust screenshot sizes to be less overwhelming. 2022-09-12 23:34:51 +02:00
David Given
2ecb3059e5 Typo fix. 2022-09-12 23:33:10 +02:00
David Given
237cb42695 Update documentation. 2022-09-12 23:32:20 +02:00
David Given
49b6bbff37 Add sector skew support to SectorListProto; add a partial definition for the
Victor 9000.
2022-09-12 23:10:24 +02:00
David Given
5e05083008 Merge pull request #584 from davidgiven/c64
Another attempt at fixing the C64 1581 bug.
2022-09-12 22:20:39 +02:00
David Given
339e9cca10 Hopefully actually fix the C64 side issue, this time, by rethinking how the IBM
encoder/decoder handles side swapping. We want to be able to swap everything,
or just the side bytes.
2022-09-12 22:04:58 +02:00
David Given
d441ad8875 Remember to mark the Brother filesystem as R/W. 2022-09-12 17:48:08 +02:00
David Given
72fc6bf913 Merge pull request #583 from davidgiven/windows
Add a Windows installer.
2022-09-12 17:42:18 +02:00
David Given
fcf278c61c Yaml syntax fix. 2022-09-12 16:59:58 +02:00
David Given
890404ded5 Fix the installer to work. 2022-09-12 16:44:07 +02:00
David Given
6fd9b47c45 Attempt to add a Windows installer. 2022-09-12 16:19:05 +02:00
David Given
003651ec68 Merge pull request #582 from davidgiven/c64
Fix the 1581 encoder/decoder configuration.
2022-09-12 16:00:19 +02:00
David Given
c63a761ca4 Actually fix the c64 1581 side issue. Turns out the data's not flipped between
the two sides, only the bit in the sector header...
2022-09-12 15:34:21 +02:00
David Given
32dcd1551b Apparently the 1581 doesn't want sides swapped after all? 2022-09-12 15:09:32 +02:00
David Given
90fbdd5fab Merge pull request #581 from davidgiven/brother2
Add put and delete support for the Brother120 filesystem.
2022-09-12 01:18:59 +02:00
David Given
1fea200582 Implement delete. Fix a pile of bugs. 2022-09-12 00:56:34 +02:00
David Given
55b8a62f64 Add Brother120 support for putting files. 2022-09-12 00:33:13 +02:00
David Given
a8906bf58f Merge pull request #576 from davidgiven/mapper
Fix the mapper, by removing most of it.
2022-09-11 22:35:59 +02:00
David Given
0d6b9263d4 Merge from master. 2022-09-11 21:05:31 +02:00
David Given
f091a54ca6 Merge pull request #580 from davidgiven/gui
Remember to clear the config before loading a new one.
2022-09-11 21:04:50 +02:00
David Given
5262929c16 Remember to clear the config before loading a new one. 2022-09-11 21:03:47 +02:00
David Given
c1ca8a3ae6 Merge pull request #578 from davidgiven/gui
Make includes work in the GUI.
2022-09-11 20:37:42 +02:00
David Given
a7e36472d5 Make includes work in the GUI. 2022-09-11 20:08:47 +02:00
David Given
538a22e2f7 Another attempt at making filesystem sector ordering work again. 2022-09-11 19:53:18 +02:00
David Given
0c40a3e79c File system mapping now sort of works in the filesystem, but there are
problems. These are potentially due to an incorrect Prodos mapping but I'm not
sure.
2022-09-11 19:01:25 +02:00
David Given
3cb098f9ba Rip out the old approach to soft sector remapping as it wasn't working. 2022-09-11 17:05:40 +02:00
David Given
ea1ab029f3 Add a very untested ProDOS filesystem module. 2022-09-11 13:19:15 +02:00
David Given
92a76a6d39 Reset the environment in the right place. 2022-09-11 13:08:46 +02:00
David Given
c451950dbf Fix the 40-track option to actually work! 2022-09-10 23:56:59 +02:00
David Given
644adc43ed Add UI for setting 40-track mode (because it's important). 2022-09-10 23:51:18 +02:00
David Given
63a7340c21 Realise that I was actually applying sector mappings _twice_, resulting in them
all cancelling out...
2022-09-10 23:29:00 +02:00
David Given
6824f00867 Add the remaining ADFS formats. 2022-09-10 22:51:37 +02:00
David Given
3cb48b40aa Add missing file. 2022-09-10 22:48:03 +02:00
David Given
dda713a6be Remove the rest of the mapper. 2022-09-10 22:47:35 +02:00
David Given
415aa82a6f The top-level tracks and heads fields are now inferred automatically from the
layout if not set, so we don't have to set them everywhere.
2022-09-10 22:29:27 +02:00
David Given
4ae664fd93 Sanitise the layout structure somewhat. Fix ADFS and Apple II formats. 2022-09-10 22:08:23 +02:00
David Given
5d1e304642 Apple II disks actually have 35 sectors. 2022-09-10 22:00:13 +02:00
David Given
6b228d7a0a Add functionality for clearing the cache between runs. 2022-09-10 21:27:14 +02:00
David Given
085ad5f2a4 Actually start using the new physical sector stuff. 2022-09-10 20:43:24 +02:00
David Given
e40e6bd07f Remove the remapper part of the mapper. 2022-09-10 20:13:38 +02:00
David Given
a6db36e7b3 Convert from LayoutData to Layout. 2022-09-10 19:53:05 +02:00
David Given
1a4caccd07 Rename Sector.logicalSector to Sector.physicalSector. 2022-09-10 18:59:06 +02:00
David Given
7f1017ebd9 Remove some obsolete mapper code. 2022-09-10 18:47:55 +02:00
David Given
f792cd2677 Merge pull request #575 from davidgiven/vfs
Fix some late GUI bugs and add an icon.
2022-09-10 13:51:49 +02:00
David Given
1361cab603 Once again put the optimisation level back to O3. 2022-09-10 13:42:16 +02:00
David Given
05b784d203 Make the icon marginally less awful. 2022-09-10 13:29:56 +02:00
David Given
542c3e38f5 Add a terrible icon. 2022-09-10 13:07:09 +02:00
David Given
bcdead1eca Don't use the DnD workaround on non-gtk platforms. 2022-09-10 11:28:50 +02:00
David Given
a79a927a13 Merge pull request #574 from davidgiven/vfs
Dramatic GUI overhaul and addition of the new file browser.
2022-09-10 01:18:43 +02:00
David Given
5bcc9071f6 Remember to make dialogues resizable. 2022-09-10 00:44:37 +02:00
David Given
88869ff6d4 Fix a _whole_ pile of bugs in the HFS plugin, and get it to produce meaningful
error messages.
2022-09-10 00:22:02 +02:00
David Given
1655505b95 Add in some missing file commands. 2022-09-09 23:33:16 +02:00
David Given
9abfa3726e Add documentation. 2022-09-09 23:11:05 +02:00
David Given
89a4abf9cf FatFS _can_ delete files. 2022-09-09 22:39:15 +02:00
David Given
854afc6de3 Don't show _ profiles in the extensions list. 2022-09-09 22:36:17 +02:00
David Given
03048797c5 Atari formats do _not_ use normal fatfs, so disable it. 2022-09-09 22:33:15 +02:00
David Given
2478ccd8ef Apply a hack to make sure that systems with mixed-case filenames on FAT disks
(which is illegal, but happens) work sensibly.
2022-09-09 22:29:04 +02:00
David Given
827cfd818e The browser write progress bar works. 2022-09-09 20:03:09 +02:00
David Given
b0e0cd6a1f Fix the mapper issue; writes work again. 2022-09-09 19:23:45 +02:00
David Given
003e919bd5 Formatting now works rather better, but this has revealed a problem with the
mapper --- which needs to switch to using Layout anyway.
2022-09-09 00:34:44 +02:00
David Given
56dadfc228 Remember to reformat... 2022-09-08 20:37:03 +02:00
David Given
b70603e92b Fix a bunch of minor dialog focus glitches. 2022-09-08 19:57:55 +02:00
David Given
6294bc4505 Drag-and-drop for moving one file at a time works... badly. 2022-09-08 00:08:31 +02:00
David Given
326bc931ad You can write back disk images from the browser now. 2022-09-07 20:49:16 +02:00
David Given
734913f638 Read only media is now supported. Fix some threading race conditions where I
was calling the filesystem from the UI thread.
2022-09-07 20:08:28 +02:00
David Given
157a525ec1 Bugfixes and tidying. 2022-09-07 00:37:26 +02:00
David Given
352abe07d8 Creating directories works, more or less. 2022-09-07 00:32:01 +02:00
David Given
0111ed37ba UI tidying. 2022-09-07 00:18:24 +02:00
David Given
079c4c955c Moving files works (via the clumsy dialog). 2022-09-07 00:14:47 +02:00
David Given
922715480e Allow moving a file without changing the actual name. 2022-09-07 00:14:19 +02:00
David Given
99ca175e9a Renaming files sort of works. 2022-09-07 00:02:23 +02:00
David Given
c1fefb7e13 Merge. 2022-09-06 22:31:24 +02:00
David Given
31495d484d Rewrite the browser model to be a custom and much, much more robust job. 2022-09-06 22:29:07 +02:00
David Given
1c6ae0bd88 Remove a spurious ItemChanged which was making OSX unhappy. 2022-09-06 00:55:58 +02:00
David Given
177aadbb45 Finally make that blasted 'more' button work. 2022-09-06 00:48:51 +02:00
David Given
a6868acfa0 Add a disk space gauge. 2022-09-06 00:09:00 +02:00
David Given
907d46a28b Various pieces of better error checking. 2022-09-06 00:08:50 +02:00
David Given
87dda265f4 Formatting now works a lot better (i.e. works). 2022-09-05 23:54:30 +02:00
David Given
7106882212 Files can be deleted (probably). 2022-09-05 23:11:39 +02:00
David Given
59e5f3d27e Replace the browser job queue with a much cleaner one using lambda abuse. 2022-09-05 22:52:19 +02:00
David Given
b2eba66bff You can format disks now. 2022-09-05 22:13:41 +02:00
David Given
b8b5509d49 Remember to add capabilities to the Brother filesystem. 2022-09-05 22:08:33 +02:00
David Given
e667e302dc You can now put files, with a conflict dialogue. But you can't flush them to
disk yet.
2022-09-05 21:26:07 +02:00
David Given
ba68a13712 Properly throw FileNotFoundException if a file is not found (not BadPath). 2022-09-05 21:25:39 +02:00
David Given
c562c6f23a Don't crash on adflib errors. 2022-09-05 19:04:49 +02:00
David Given
fad79e51ac You can save files off disk now. 2022-09-05 18:43:32 +02:00
David Given
9438d68dae Redesign the toolbars to fit more stuff on (and look better). 2022-09-05 01:22:16 +02:00
David Given
39b761ea16 Add an (unimplemented) rename operation. 2022-09-04 22:47:25 +02:00
David Given
64689cf59d Add some disk flushing machinery. 2022-09-04 22:28:38 +02:00
David Given
17f80e4e53 Add a proper file viewer. 2022-09-04 22:15:49 +02:00
David Given
4117e233b3 Simple hex file viewing. 2022-09-04 20:47:44 +02:00
David Given
6c0bb3781a Allow probing of the filesystem capabilities. 2022-09-04 20:42:51 +02:00
David Given
eb55fe1fe3 Browser getfileinfo works. Handle restarts and errors much more gracefully
(i.e. don't crash as often).
2022-09-04 20:26:22 +02:00
David Given
dc396d5cf7 Typo fix. 2022-09-04 20:25:40 +02:00
David Given
80ec4407bc Attributes are now returned as part of the dirent. 2022-09-04 19:30:12 +02:00
David Given
1e45374d41 Filesystem job queue works. 2022-09-04 18:58:03 +02:00
David Given
92ae233f39 First draft of the filesystem browser works. 2022-09-04 17:59:56 +02:00
David Given
d3f8eb09d0 Merge pull request #573 from davidgiven/vfs
Overhaul the GUI.
2022-09-03 22:21:34 +02:00
David Given
058ef8495b Tidy. 2022-09-03 21:50:57 +02:00
David Given
e2ac8eb43c Update file picker settings? 2022-09-03 21:40:54 +02:00
David Given
56121e39e2 Adjust file picker flags. 2022-09-03 20:53:24 +02:00
David Given
57bc2c459b Don't pop up a jillion menus when right clicking. 2022-09-03 20:10:11 +02:00
David Given
311beacfff Try to make OSX happy. 2022-09-03 19:48:32 +02:00
David Given
67e9c65313 Warning fix. 2022-09-03 19:32:59 +02:00
David Given
9a0257caaa Fix a bunch of issues that was confusing Windows. 2022-09-03 19:32:44 +02:00
David Given
a121fb8c6e Disable the browser for merge. 2022-09-03 17:54:23 +02:00
David Given
a044f9a4a4 Custom configuration settings now work. 2022-09-03 17:52:11 +02:00
David Given
0b7a2d7b34 Turn the various viewer windows into dialogues. 2022-09-03 17:01:44 +02:00
David Given
07ba160119 More eventing cleanup. 2022-09-03 16:50:08 +02:00
David Given
d887c86cd4 Overhaul event handling. Get rid of the top-level notebook in favour of menu
items.
2022-09-03 16:42:10 +02:00
David Given
372af03d9e You can now quit the application while an operation is in progress without
rashing.
2022-09-03 15:33:09 +02:00
David Given
aed6a6f142 Emergency stop works. 2022-09-03 13:42:52 +02:00
David Given
f5fa89bafe Some tweaks to progress information. 2022-09-03 13:34:00 +02:00
David Given
6a1d181a34 Add progress information. 2022-09-03 13:24:51 +02:00
David Given
dbcd8c27c5 Implement the bulk of the custom status bar. 2022-09-03 12:59:45 +02:00
David Given
2764be5d32 Add a basic about dialogue. 2022-09-03 00:24:59 +02:00
David Given
d69c534614 Fiddle with some state stuff. 2022-09-03 00:00:18 +02:00
David Given
f436d6e48c Fix state machine when errors are reported from the worker thread. 2022-09-02 23:54:45 +02:00
David Given
e49fcd12fa Better selection cursor rendering. 2022-09-02 23:42:25 +02:00
David Given
f2699c4f1e The reader and writer now work again. 2022-09-02 23:11:21 +02:00
David Given
2319243444 Get state loading and saving working with the new UI. 2022-09-02 20:17:20 +02:00
David Given
8e44e6625a Refactor MainWindow so it doesn't need a header file. 2022-09-02 00:36:04 +02:00
David Given
4cd87ab08a More UI overhaul. 2022-09-02 00:11:34 +02:00
David Given
98a125eb06 First draft of the new UI. 2022-09-01 00:31:45 +02:00
David Given
7c690695b9 Merge pull request #572 from davidgiven/vfs
Add filesystem metadata.
2022-08-31 23:37:52 +02:00
David Given
066a12edaa CBMFS now supports filesystem metadata. 2022-08-31 22:46:04 +02:00
David Given
4c0971800d File metadata for CPMFS works. 2022-08-31 21:58:11 +02:00
David Given
c375b73db9 Dirents need to be shared. 2022-08-31 21:41:11 +02:00
David Given
a392b84d9b Filesystem metadata works on Brother 120. 2022-08-31 21:33:51 +02:00
David Given
34f034c6c4 Return volume names correctly. 2022-08-31 20:56:38 +02:00
David Given
f5640b970b Acorn DFS volume metadata. 2022-08-31 20:46:41 +02:00
David Given
b7028f20e6 Volume metadata for HFS. 2022-08-31 20:28:49 +02:00
David Given
0cb059e59f Volume metadata for FFS. 2022-08-31 20:23:06 +02:00
David Given
8d90a974c6 Start work on volume metadata. 2022-08-31 20:17:50 +02:00
David Given
d44b83e60d Some API tidying. 2022-08-31 20:01:58 +02:00
David Given
ac7493fdc0 Merge pull request #571 from davidgiven/vfs
Allow formatting disks.
2022-08-31 13:08:02 +02:00
David Given
c6c2553b42 Merge from master. 2022-08-31 00:59:06 +02:00
David Given
7d5460f8d1 Formatting FATFS filesystems works, more or less. 2022-08-31 00:58:28 +02:00
David Given
f9fa8a3616 Merge pull request #570 from davidgiven/vfs
Add VFS write support.
2022-08-31 00:32:38 +02:00
David Given
6c117df0a3 Allow quick formatting. 2022-08-31 00:32:31 +02:00
David Given
b8bf08eace Formatting disks on FFS works. 2022-08-31 00:27:01 +02:00
David Given
027af76d5d Formatting HFS disks works. 2022-08-30 23:47:03 +02:00
David Given
b7eee599f4 Mac HFS writing works. 2022-08-30 23:30:23 +02:00
David Given
42a350156a Amiga FFS writes work. 2022-08-30 23:13:59 +02:00
David Given
f382b70cdf Fix verification bug with multiple sector sizes. 2022-08-30 23:13:30 +02:00
David Given
f753929e87 Writing files works in fatfs. 2022-08-30 22:51:31 +02:00
David Given
b3c5ab0b4e Merge pull request #569 from davidgiven/vfs
Add read-only CBMFS support.
2022-08-30 20:55:40 +02:00
David Given
9d8adcc511 Update documentation. 2022-08-30 20:27:13 +02:00
David Given
1afa9ce697 Allow reading files from cbmfs disks (not .REL, though). 2022-08-30 20:21:30 +02:00
David Given
b91edac0ba Refactor the C64 formats into 35 and 40 track variants. Start work on the C64
filesystem.
2022-08-30 00:49:24 +02:00
David Given
5d6f031973 Merge pull request #568 from davidgiven/vfs
Add Macintosh HFS VFS support.
2022-08-29 23:16:40 +02:00
David Given
72c56ee337 Don't use fmt::format to format chrono times, as apparently that's too new for
Ubuntu.
2022-08-29 22:56:41 +02:00
David Given
72498a200a Fetching HFS files now gives you a AppleSingle file with both data and resource
forks.
2022-08-29 22:23:36 +02:00
David Given
1e9e95754c Add a helper routine for ISO-8601 formatting. 2022-08-29 22:22:49 +02:00
David Given
eb99cd895c HFS now works, inasmuch as you can fetch data forks. 2022-08-29 20:24:29 +02:00
dtrg
5625cf5254 More hfs boilerplate. 2022-08-29 13:41:53 +00:00
dtrg
9c6dc0a2f8 Make hfsutils build. 2022-08-29 13:31:08 +00:00
David Given
3458874a68 Raw import of hfsutils. 2022-08-29 01:22:24 +02:00
David Given
4e93b43d8d Merge pull request #567 from davidgiven/vfs
Add basic read-only Amiga FFS support, via adflib.
2022-08-29 01:04:38 +02:00
David Given
b895545ec3 Merge from master. 2022-08-29 00:33:27 +02:00
David Given
92316c4d83 Add read-only Amiga FFS support, via adflib. 2022-08-29 00:33:01 +02:00
David Given
af71409cab Merge pull request #566 from davidgiven/vfs
Add read-only support for CP/M file systems.
2022-08-28 19:50:05 +02:00
David Given
df5a60d946 Make adflib build. 2022-08-28 18:28:13 +02:00
David Given
0181ab1c03 Raw import of adflib. 2022-08-28 18:18:51 +02:00
David Given
1a5ccca67a Add pretty dubious support for logical/physical extents; we can, at least, read
files from my only PF-10 test disk.
2022-08-28 18:01:59 +02:00
David Given
99e1a90729 Implement basic CP/M filesystem support. 2022-08-28 16:37:21 +02:00
David Given
54e11e96e9 Merge pull request #565 from davidgiven/mapper
Remove the sector info from the IBM trackdata in favour of the new layout{} structure.
2022-08-28 00:40:27 +02:00
David Given
b339a776fc Add the new logical sector mappings. 2022-08-28 00:24:13 +02:00
David Given
db1a84b490 Copy the sector mapping stuff into Layout. 2022-08-28 00:15:46 +02:00
David Given
3256b4f627 Eliminate the IBM trackdata sector layout stuff in favour of the layout{}
clause.
2022-08-27 23:23:28 +02:00
David Given
c16ab349b1 Refactor a pile of stuff to allow the physical and logical sector mappings to
be put in the layout{} config section, where they belong.
2022-08-27 19:58:48 +02:00
David Given
a981cb72d0 Merge pull request #564 from davidgiven/vfs
Add filesystem support, allowing direct access to files on disk.
2022-08-27 18:31:07 +02:00
David Given
5251a5f195 Add a couple of missing entrypoints. 2022-08-27 18:10:10 +02:00
David Given
917d5d2dd2 Enable FatFS on a bunch of formats; better error reporting (i.e., some). 2022-08-27 18:07:04 +02:00
David Given
983f6caf46 FatFS works (read-only, lightly tested). 2022-08-27 17:43:46 +02:00
David Given
c9a58e9d57 Allow pulling files off disk. 2022-08-27 16:06:33 +02:00
David Given
c5fd24496f Zero-pad files which don't line up with sector boundaries. 2022-08-27 16:04:46 +02:00
David Given
0d502933ae Refactor a bunch of file stuff to be shorter and cleaner. 2022-08-27 13:21:03 +02:00
David Given
cc4bb3a5ec Undo accidental change. 2022-08-27 13:03:49 +02:00
David Given
b42f82ecb1 Real disk access works, at least for ls. 2022-08-27 12:57:57 +02:00
David Given
b6beaae7da Try and make Acorn DFS writable. 2022-08-27 12:57:41 +02:00
David Given
e698900497 Merge from master. 2022-08-27 01:09:48 +02:00
David Given
bbad2fc1ba Merge pull request #563 from davidgiven/includes
Allow including config files from other config files.
2022-08-27 01:09:04 +02:00
David Given
e9f5087eb6 Don't show the _micropolis include config in the profiles list. 2022-08-27 00:55:51 +02:00
David Given
c1caa22524 Support simple includes in config files, useful for organisation. 2022-08-27 00:52:06 +02:00
David Given
5303903f89 Merge pull request #562 from davidgiven/layout
Refactor disk layout information.
2022-08-27 00:51:44 +02:00
David Given
27d520e42a Merge from master. 2022-08-27 00:16:49 +02:00
David Given
8607f4af57 Add a mode field to the dirent structure. 2022-08-27 00:06:16 +02:00
David Given
f0ec8bd5b9 More SectorInterface refactoring. 2022-08-26 00:43:01 +02:00
David Given
9007e264cf Configure and make fatfs build. 2022-08-26 00:33:38 +02:00
David Given
529bd6fa33 Raw import of fatfs. 2022-08-26 00:21:48 +02:00
David Given
eb8827bdf2 Merge from master. 2022-08-26 00:10:28 +02:00
David Given
be78d91a07 Merge pull request #561 from davidgiven/truncate
Fix CI builds on OSX.
2022-08-26 00:10:08 +02:00
David Given
20b7008994 Move sectorinterface into vfs. 2022-08-26 00:05:58 +02:00
David Given
84d7b1d4ba Or maybe it is. 2022-08-25 23:56:49 +02:00
David Given
0154ed46e8 Looks like truncate is already there? 2022-08-25 23:40:24 +02:00
David Given
5a11fa4704 Try to use coreutils truncate rather than Homebrew truncate, which has gone. 2022-08-25 23:38:17 +02:00
David Given
8b9e153ac4 Use the new layout information for VFS access. 2022-08-25 23:23:07 +02:00
David Given
badc7366c2 Fixup after merge. 2022-08-25 23:21:33 +02:00
David Given
24ef479913 Merge layout stuff. 2022-08-25 22:53:25 +02:00
David Given
1dd94a7d82 Refactor disk layout information out of ImgReaderWriterProto into a new
top-level config paragraph.
2022-08-25 22:51:26 +02:00
David Given
40d5d92dbc More API overhauling. 2022-08-25 21:40:21 +02:00
David Given
0cc3433d6d API cleanup; you can now view file metadata. 2022-08-25 19:37:41 +02:00
David Given
0e157af8d9 DFS and Brother120 can now list files (more or less). 2022-08-24 23:31:17 +02:00
David Given
0e719c2b86 More VFS boilerplate. 2022-08-24 20:21:55 +02:00
David Given
82fd336792 Prototype VFS API. 2022-08-24 00:32:36 +02:00
David Given
e733dc90d8 Merge pull request #550 from tdaede/d88_fix_sector_id
D88: Fix sector ID conditions when writing.
2022-08-10 11:35:04 +02:00
David Given
9985d80432 Merge pull request #549 from tdaede/d88_dd_sector_gaps
Add n88basic sector gaps to d88 reader for PC-88 DD disks.
2022-08-10 11:34:16 +02:00
Thomas Daede
7177a781dc D88: Fix sector ID conditions when writing.
This was failing to write the last sector in the image.
2022-08-09 22:46:43 -07:00
Thomas Daede
498558c2b1 Add n88basic sector gaps to d88 reader for PC-88 DD disks.
The values used for 1.2MB HD disks are too narrow for the
PC-88's disk controller.
2022-08-09 21:47:34 -07:00
David Given
c8e6795a90 Merge pull request #546 from wybren1971/IMDwriter
Im dwriter
2022-08-07 20:45:36 +02:00
David Given
1edc51c067 Merge pull request #547 from davidgiven/driveselect
Add a flux visualiser to the GUI.
2022-08-07 18:36:48 +02:00
David Given
6f7054c4b2 Set useful titles to all the windows. Also fix a nasty spurious drag caused by creating a
window with the left mouse button held down.
2022-08-07 18:12:57 +02:00
David Given
03d2a3a685 Windows are properly stacked and destructed in the right order. 2022-08-07 17:26:17 +02:00
David Given
1d4dccf454 Dataless sectors don't confuse the visualiser any more. 2022-08-07 17:17:24 +02:00
David Given
cb93890d13 Hex viewer windows are sized properly on Windows. 2022-08-07 17:13:35 +02:00
David Given
f39b2801b8 Make sure that drags are processed only when the left button is pressed. 2022-08-07 17:04:43 +02:00
David Given
073a8f5fbd Force a redraw when the control is resized. 2022-08-07 17:01:56 +02:00
David Given
669c19882a Update firmware image. 2022-08-07 14:22:43 +02:00
David Given
43ca6437ab Add a maximum zoom to avoid overflow embarrassments. 2022-08-07 14:12:46 +02:00
David Given
e61a1ea582 Remove the flux viewer status bar as it's not used. 2022-08-07 14:09:48 +02:00
David Given
a477655270 Make the hex viewer work. 2022-08-07 14:07:51 +02:00
David Given
88e1acfdff Change the colour of records. 2022-08-07 00:40:00 +02:00
David Given
39e1dcb900 Add sector labels. 2022-08-07 00:37:57 +02:00
David Given
2489b93de8 Fix a bunch of issues that for some reason were only showing up on Windows. 2022-08-06 23:30:23 +02:00
David Given
8eb25a911d Allow dragging the flux viewer. 2022-08-06 21:37:46 +02:00
David Given
2d5e91b853 Flux viewer control performance improvements. 2022-08-06 21:29:05 +02:00
David Given
4f8c68c40d The flux visualiser now uses a density map for better displaying when zoomed
out.
2022-08-06 18:41:08 +02:00
David Given
9a5c66a311 Add the boilerplate for the hex viewer. 2022-08-06 12:58:15 +02:00
David Given
69bb6a74b8 Split sectors and records. Draw the horizontal scale. 2022-08-06 12:48:52 +02:00
David Given
7aa5fc91cf The flux source/sink value is now persistent. 2022-08-06 01:03:51 +02:00
David Given
de0b53de62 Initial version of the flux visualiser. 2022-08-06 00:57:54 +02:00
David Given
a1df18e3ca Rename class to make sense. 2022-08-05 23:32:20 +02:00
David Given
6a4c85245d The format setting is now persistent. 2022-08-05 23:15:39 +02:00
David Given
52a121c9ed Open the flux visualiser window when a track is clicked on (which contains
flux).
2022-08-05 23:02:15 +02:00
David Given
2acda695ba Add a cursor for selecting tracks in the visualiser. 2022-08-05 21:40:31 +02:00
David Given
89953d9e84 Allow specifying which track in a group to write (rather than the lowest). 2022-08-04 23:10:02 +02:00
David Given
7054a0eded Scan the drives on every USB reset, not just on power on. 2022-08-04 00:08:07 +02:00
David Given
6105e16b28 Don't invoke the vbs patch script (as it's gone). 2022-08-04 00:07:41 +02:00
wybren1971
eb9d0dbbd4 Added real time and not the compiletime of fluxengine 2022-07-29 03:09:29 +02:00
David Given
567b074792 Merge pull request #543 from tdaede/fix_d88_regression
Fix D88 writer regression caused by 4ba30fcf.
2022-07-07 13:42:02 +02:00
Thomas Daede
416c70f5e5 Fix D88 writer regression caused by 4ba30fcf. 2022-07-07 01:47:52 -07:00
wybren1971
30681dfa2c Corrected spelling error 2022-07-02 16:04:54 +02:00
David Given
c5b78ffa99 Merge pull request #541 from wybren1971/IMDwriter
add Imd writer option
2022-06-27 14:19:32 +02:00
wybren1971
cb5e859c34 Add imd to core concepts 2022-06-26 20:04:24 +02:00
wybren1971
4470fb4312 add breaks to the switch statements 2022-06-26 18:26:03 +02:00
David Given
da11fad696 Merge pull request #542 from VorpalBlade/features/install
Add install rule and fix some build system issues
2022-06-21 20:55:45 +02:00
Arvid Norlander
72ae4b2b53 Add missing ignore entry for upgrade-flux-file. Also sort the ignore file. 2022-06-21 20:51:37 +02:00
Arvid Norlander
b8e72039b9 Allow overriding the compiler to build with clang for example 2022-06-21 20:50:22 +02:00
Arvid Norlander
878ccd5128 Add install rule.
The support configuration variables are based on common conventions.
2022-06-21 20:50:22 +02:00
wybren1971
45e0946b6b remove debuglines 2022-06-20 15:47:20 +02:00
wybren1971
b57af55b8a Updating the documentation. cleanup code 2022-06-20 13:10:14 +02:00
wybren1971
677ee5da91 update to IMD writer to accept PROTO input for comment and encoding 2022-06-19 23:45:42 +02:00
wybren1971
a6526ccc13 adding comment in IMD file 2022-06-19 17:28:04 +02:00
wybren1971
8323593720 Set ibm as default format 2022-06-19 16:51:39 +02:00
wybren1971
dd00a69018 enable writing for hplif profile 2022-06-19 16:50:41 +02:00
wybren1971
4d7ddf3d9e add writing IMD images functionality 2022-06-19 16:49:31 +02:00
wybren1971
fbc39a41f8 Add writing IMD images functionality 2022-06-19 16:45:03 +02:00
David Given
3e934a8527 Merge pull request #539 from davidgiven/ubuntu20
Make the build work on Ubuntu 20 again.
2022-06-18 00:45:04 +02:00
David Given
bc474724c6 Don't need the enum conversion warning suppression any more, I think. 2022-06-18 00:19:47 +02:00
David Given
553eccf029 Try an alternative way of finding wx-config. 2022-06-18 00:03:43 +02:00
David Given
63e344d6cf Try and work around the weirdness on Ubuntu 20? 2022-06-17 20:47:50 +02:00
David Given
0dc992561e Don't override the -std=c++17 line on CI. 2022-06-17 19:56:23 +02:00
David Given
77006d5736 Try and make work on Ubuntu 20 again. 2022-06-17 19:30:29 +02:00
David Given
359f5583a6 Merge pull request #537 from davidgiven/bounds
Fix some bounds checking issues.
2022-06-16 20:02:32 +02:00
David Given
58dbb1c7ef Add a 'binaries' pseudotarget so that just the binaries can be built without
the tests.
2022-06-16 19:41:57 +02:00
David Given
47ac8238f7 Enable glibc bounds checking on the CI build. 2022-06-16 19:40:52 +02:00
David Given
fa760b702c Fix C automatic dependencies to actually work. 2022-06-16 19:39:47 +02:00
David Given
2a6ebe2c04 Avoid overzealous bounds check assertions. 2022-06-16 19:37:19 +02:00
David Given
3e97faa704 Fix bounds checking assertion failure caused by overzealous glibc. 2022-06-16 19:30:43 +02:00
David Given
a9e82676c7 Merge in the new AppleSauce flux writer. 2022-06-15 23:40:14 +02:00
David Given
d755fd9c08 Maybe this will work this time. 2022-06-15 23:40:00 +02:00
David Given
c4f1d7bccb Update documentation. 2022-06-15 23:39:11 +02:00
David Given
b55ebe95d9 Reformat. 2022-06-15 23:37:50 +02:00
David Given
cb5c0d5ebe Make the a2r flux sink build. Tracks are now always in the native numbering. 2022-06-15 23:33:00 +02:00
David Given
404f583dc2 Merge from master. 2022-06-15 23:27:33 +02:00
David Given
19cd5638ba Another latest-tag change. 2022-06-15 22:48:06 +02:00
David Given
b917552ca5 Typo fix. 2022-06-15 22:31:32 +02:00
David Given
71deb14c85 Switch back to the old version of latest-tag. 2022-06-15 22:19:28 +02:00
David Given
e9c1041d9b Merge pull request #535 from davidgiven/make
Use the correct extensions for the output binaries.
2022-06-15 22:03:42 +02:00
David Given
ffece005c5 Use the correct extensions for the output binaries. 2022-06-15 21:46:50 +02:00
David Given
ff5d22adbe Another attempt at fixing the CI script. 2022-06-14 23:17:09 +02:00
David Given
aaffe1b208 Merge. 2022-06-14 22:34:00 +02:00
David Given
b86d2466d2 Looks like the autorelease script got broken? 2022-06-14 22:32:44 +02:00
David Given
c2230d0462 Merge pull request #533 from davidgiven/upstreams
Update missing UPSTREAMs and the README.
2022-06-14 22:16:02 +02:00
David Given
dc4e4aa9c7 Update missing UPSTREAMs and the README. 2022-06-14 22:14:53 +02:00
David Given
56e5b4e529 Merge pull request #532 from davidgiven/make
Port the build system to... make!
2022-06-14 22:08:09 +02:00
David Given
3577812dd1 Update documentation. 2022-06-14 21:54:01 +02:00
David Given
256c42833a Rememeber to run the proto test. 2022-06-14 21:36:55 +02:00
David Given
372a9075d7 Remove obsolete files. 2022-06-14 21:22:43 +02:00
David Given
9a5d0db3d4 Oops! | doesn't do the same thing in make as it does in ninja, resulting on
broken rebuilds. These should now work.
2022-06-14 21:07:45 +02:00
David Given
82fe952d7f Merge from master. 2022-06-14 20:50:57 +02:00
David Given
da8382236f Final CI change, I hope. 2022-06-14 20:50:42 +02:00
David Given
4d2d03b8fc Update docs and CI setup now we don't need ninja or lua. 2022-06-14 20:37:13 +02:00
David Given
b025e6bb88 Remove dep/fmt completely. 2022-06-14 20:12:55 +02:00
David Given
521bbd4ea5 Make build on Windows (again). 2022-06-14 19:59:49 +02:00
David Given
95d5d42608 Run the encode/decode tests. 2022-06-14 19:33:20 +02:00
David Given
90884d8877 Actually, let's do single-threaded CI builds. 2022-06-14 01:18:34 +02:00
David Given
62258be400 Add missing file. 2022-06-14 01:07:20 +02:00
David Given
8cbdce1d5b Run simple tests. 2022-06-14 01:05:18 +02:00
David Given
9099d59c08 Cleanup. 2022-06-14 00:37:31 +02:00
David Given
f6bcc37168 Remove ackbuilder. Sob. 2022-06-14 00:24:31 +02:00
David Given
0db1ddc788 Make work on Windows. 2022-06-14 00:15:23 +02:00
David Given
93821c8991 Adjust CI parallelism. 2022-06-13 23:52:24 +02:00
David Given
195c4ca3e5 Add missing file. 2022-06-13 23:49:55 +02:00
David Given
f6023ebbd0 Build the GUI. 2022-06-13 23:49:22 +02:00
David Given
41dde0e516 Build on OSX. 2022-06-13 23:38:33 +02:00
David Given
07b1719b17 Let's try an actual vanilla Makefile. 2022-06-13 23:21:06 +02:00
David Given
d31399702b Merge pull request #529 from wybren1971/IMDwriter
Imd reader
2022-06-13 15:46:54 +02:00
David Given
601e220b18 Update documentation. 2022-06-12 22:15:28 +02:00
David Given
246e580bb5 Update release script. 2022-06-12 22:09:10 +02:00
David Given
e141267cfb Fix github artifact upload. 2022-06-12 22:08:08 +02:00
David Given
eb21f33f99 Windows needs pkg-config. 2022-06-12 21:59:13 +02:00
David Given
f91ddaeec7 Change OSX lua version. 2022-06-12 21:44:52 +02:00
David Given
afc432078c Try to make work on OSX. 2022-06-12 21:41:47 +02:00
David Given
4ba30fcfec Don't require C++20 as it's still too new. 2022-06-12 21:35:13 +02:00
David Given
d7fd99cad6 Don't try to create empty ar archives (OSX doesn't like it). Properly declare
headers as outputs of clibrary.
2022-06-12 20:59:57 +02:00
David Given
eaab2a3ec4 Don't use install to install files. 2022-06-12 20:06:12 +02:00
David Given
1f938457db OSX doesn't want luaposix. 2022-06-12 19:43:42 +02:00
David Given
855a9d5224 Require Ubuntu 22.04. 2022-06-12 18:50:56 +02:00
David Given
d1a9531175 Don't need dep/fmt any more --- it's available on all platforms we care about. 2022-06-12 12:42:31 +02:00
David Given
58aacd3f28 Finally make build on Windows again. 2022-06-12 12:29:53 +02:00
David Given
4aee2732bc Use the system libstb where possible. 2022-06-12 01:14:04 +02:00
David Given
24a3468928 Run the simple tests. 2022-06-12 01:10:24 +02:00
David Given
dff7502a92 Build the tools. 2022-06-11 23:57:05 +02:00
David Given
c34f4bff08 Libraries carry dependencies recursively. 2022-06-11 23:53:26 +02:00
David Given
5753cd4877 Remove globbing to avoid the luaposix dependency. Start work on Windows stuff. 2022-06-11 20:52:19 +02:00
David Given
0949cf254f Add another missing file. 2022-06-11 17:19:34 +02:00
David Given
935a68d871 Add missing file. 2022-06-11 15:05:33 +02:00
David Given
7592556e4d Build the GUI. 2022-06-11 12:22:29 +02:00
David Given
e3f06cbdd4 We need luaposix. 2022-06-11 12:22:22 +02:00
David Given
5397482ca3 Add missing files. 2022-06-11 11:54:48 +02:00
David Given
eafb5d9f7f First attempt at make working on github CI. 2022-06-11 11:41:33 +02:00
David Given
5efbb38270 Initial port to ackbuilder. The fluxengine binary builds on Linux. 2022-06-11 01:00:31 +02:00
wybren1971
c4603e1230 Clean up code IMD imagereader 2022-06-10 09:02:20 +02:00
wybren1971
c6fd31564d update in skewsettings 2022-06-09 23:26:28 +02:00
wybren1971
c4d72d3c11 Write IMD file to floppy 2022-06-09 07:57:11 +02:00
David Given
d7ce10001b Merge pull request #510 from davidgiven/groups
Allow an option to force the reader to read all tracks in a group.
2022-06-03 22:15:32 +02:00
David Given
4220a3fedd Merge pull request #522 from tdaede/fix_fluxop_space_rpm
Add D88 image writer.
2022-06-03 22:14:58 +02:00
Thomas Daede
65100a18d2 Add D88 image writer.
This is currently a very minimal implementation and does not
support deleted sectors, CRC errors, etc.
2022-06-01 20:46:37 -07:00
David Given
d913b0910b Merge pull request #517 from tdaede/fix_fluxop_space_rpm
Greaseweazle: Fix RPM measurement in some circumstances.
2022-05-30 12:18:34 +02:00
Thomas Daede
968184fa7a Greaseweazle: Fix RPM measurement in some circumstances.
Accumulate number of ticks returned in FLUXOP_SPACE so that they
count towards the total track time.
2022-05-29 23:17:35 -07:00
David Given
7e2d300017 Allow an option to force the reader to read all tracks in a group. 2022-04-23 12:37:52 +02:00
Jeff Epler
6cf86a4797 fix some whitespace 2022-04-22 11:03:51 -05:00
Jeff Epler
feb5eac02e Never write via structures, always write via a ByteWriter 2022-04-22 11:03:46 -05:00
David Given
a107d4f17f Merge pull request #492 from jepler/applediskii
Add support for the interfacing to an Apple Disk ][
2022-04-19 21:02:18 +02:00
Jeff Epler
5e4c7719ff Add support for the interfacing to an Apple Disk ][
Some folks may want to use interfaces besides PC/Shugart ones.
Together with https://github.com/adafruit/Adafruit_Floppy/pull/15
and a hand wired board I'm able to read flux from an Apple Disk ][
2022-04-19 08:51:16 -05:00
Jeff Epler
00d30fe26b fix several missing override declarations 2022-04-12 11:20:46 -05:00
Jeff Epler
f2083ed5e9 fix macos-only build error (narrowing diagnostic) 2022-04-12 11:20:40 -05:00
Jeff Epler
6ac98d02a7 Merge remote-tracking branch 'origin/master' into applea2r 2022-04-12 10:53:17 -05:00
David Given
8baf4ffd2f Merge pull request #459 from davidgiven/bk0010formats
Add formats for the BK.
2022-04-06 18:26:40 +02:00
David Given
632357ff9d Add BK documentation. 2022-04-06 18:12:23 +02:00
David Given
074714180b Merge from master. 2022-04-05 01:07:46 +02:00
David Given
bd62b7a9bf Merge pull request #508 from davidgiven/diskspeed
Automatically compensate for the drive speed
2022-03-29 22:21:19 +01:00
David Given
49723d05cf Update documentation with lots more stuff about disk densities. 2022-03-29 23:08:07 +02:00
David Given
f75422a412 We don't need the _525 variants of the profiles any more. 2022-03-29 19:44:03 +02:00
Jeff Epler
5e8f35c94e Fix review items, try again on applesauce compatibility
After these changes, applesauce accepts the image but produces a
non-working result.  passport.py still likes the files just fine, if
they're of the limited sorts that it can handle.

(applesauce wants just one STRM chunk with all the flux from all the
tracks; the whole STRM ends with an extra FF, which is not counted in the
size of any of the TIMING blocks. passport.py / a2archery is considerably
more forgiving)
2022-03-29 10:54:15 -05:00
David Given
15eb88e922 Convert, hopefully, the remaining encoders to do automatic disk sizing. Ther e
may be bugs.
2022-03-29 01:23:35 +02:00
David Given
9a299b758a Correctly set the format byte. 2022-03-29 00:03:59 +02:00
David Given
21afc26b68 Fix sigsegv on non-standard sector sizes. 2022-03-28 22:41:27 +02:00
David Given
5c68b47a29 Add missing file. 2022-03-28 00:19:02 +02:00
David Given
adff739a5d Partial conversion to do automatic scaling of flux based on the disk rotation
speed. Although, something seems to have broken for 5.25" disks.
2022-03-27 23:50:32 +02:00
David Given
0da3d8b231 Merge pull request #497 from davidgiven/inspect
Increase the generated pulse width in the firmware.
2022-03-26 22:30:10 +00:00
David Given
3c17e74f6d Bump the protocol version to ensure people upgrade. 2022-03-26 21:54:29 +00:00
David Given
bf35983ebb Merge from master. 2022-03-26 21:39:32 +00:00
David Given
cc31b325ea Merge pull request #506 from davidgiven/logical
Rework to use logical track numbers throughout.
2022-03-26 21:24:43 +00:00
David Given
7c0eb464c1 Remove the obsolete news paragraph. 2022-03-26 20:31:41 +01:00
David Given
8884ca09fa Add documentation on drive configuration. 2022-03-26 20:30:53 +01:00
David Given
287f0d8909 Adjust the TPI for an Apple 2 drive. 2022-03-26 13:08:06 +01:00
David Given
26c20e2262 Rename the drive extensions to be more distinct. 2022-03-26 13:00:37 +01:00
David Given
b062582d15 Multiple reads from flux files now work again. 2022-03-26 00:35:35 +01:00
David Given
fb66e49a1f Correct number of cylinders (now that empty tracks aren't ignored any more). 2022-03-26 00:35:20 +01:00
David Given
79e37f2c18 Format. 2022-03-26 00:19:22 +01:00
David Given
9ab1dae553 Correctly support retrying on hardware. 2022-03-26 00:19:07 +01:00
David Given
c5ad0b4bec Fix track display in the visualiser when reading and writing. 2022-03-25 23:01:34 +01:00
David Given
606d1012d3 Don't use <=>. Sigh. 2022-03-25 22:10:27 +01:00
David Given
45f2d98f3c Typo fix? 2022-03-25 21:57:02 +01:00
David Given
5cf15a9b11 Try to work around the issue with <compare>. 2022-03-25 21:37:39 +01:00
David Given
178aa9d32f Remove the obsolete reader.* and rename writer.* to readerwriter.*. 2022-03-25 20:57:42 +01:00
David Given
29f181f9bf Change the readers to correctly set the physical track for each sector using
the mapper (where appropriate).
2022-03-25 20:50:57 +01:00
David Given
86c5cccb08 Rename cylinder to track throughout (mostly). 2022-03-25 20:16:12 +01:00
David Given
ea8af83d61 Add missing header. 2022-03-25 00:45:25 +01:00
David Given
d303067deb Massive refactor to work in logical cylinders everywhere. The logical/physical
mapping is now done at the last stage and should, ideally, be automatic. I'm
sure there are bugs everywhere.
2022-03-25 00:22:28 +01:00
Jeff Epler
6e817e2d7c Add a2r as a flux sink
a2r is preferred for apple2 disk archiving and should have a pathway
to emulation via passport.py (woz and dsk conversion)
2022-03-23 14:06:01 -05:00
Jeff Epler
16277f803c Allow directly building fluxengine, skipping other targets 2022-03-23 14:05:47 -05:00
Jeff Epler
ef55e10ff2 fe-write: Only enable the flux source if verifying
Otherwise, a command like
```
./fluxengine-debug write apple2 prodos -i prodos-log.img -d prodos.a2r --no-verify
```
fails saying "Error: no devices found (is one plugged in? Do you have the appropriate permissions?"

This only occurs when the output flux format can't be used as a source.
It probably affects types like .au as well.
2022-03-23 14:05:47 -05:00
David Given
aaccd648b3 Merge pull request #502 from davidgiven/windows
Don't build the Windows CLI as a GUI program.
2022-03-21 19:48:30 +00:00
David Given
9596cbd85a Don't use the wxwidgets settings for the console fluxengine binary, because it
ends up building a windows-mode binary and not a console-mode binary.
2022-03-21 19:25:38 +00:00
David Given
5b6320b61a Merge pull request #457 from davidgiven/agat
Add support for the Agat 340kB disk format.
2022-03-19 19:04:15 +00:00
David Given
4de4fdc0d1 Add documentation. 2022-03-19 19:13:04 +01:00
David Given
77a0c9f341 Typo fix. 2022-03-19 18:43:20 +01:00
David Given
fc7859dc27 Update comment about the generation of the desync sequence. 2022-03-19 18:41:03 +01:00
David Given
39d6b0525f Fix incredibly stupid bug. 2022-03-19 18:07:10 +01:00
David Given
51e091ded6 Better documentation of the missing clock bit. 2022-03-19 14:00:35 +01:00
David Given
52407848c1 Do more sanity checks for bad sectors, improving the reads by a lot. 2022-03-19 13:55:22 +01:00
David Given
a6e2511e6b Don't test SCP files --- it's taking too long. 2022-03-19 13:55:00 +01:00
David Given
422f3ba8c8 Sort the records and sectors dumps. 2022-03-19 13:54:48 +01:00
David Given
276282e847 Merge from master. 2022-03-19 00:51:59 +01:00
David Given
7782e27cc5 Merge pull request #500 from davidgiven/retry
Retries got broken in a recent change. Fix them.
2022-03-18 19:02:07 +00:00
David Given
4f0a178984 Fix retries to work. 2022-03-18 19:19:08 +01:00
David Given
28ddda4635 Merge pull request #483 from davidgiven/mapper
Add support for soft sector skew
2022-03-16 15:31:26 +00:00
David Given
36e20ec396 Merge from master. 2022-03-16 16:01:24 +01:00
David Given
22e65227fb Increase the generated pulse width to try and generate a stronger signal. This
does seem to make Amiga disks read more reliably.
2022-03-15 23:56:49 +00:00
David Given
9ea68b66f7 Allow viewing the raw bitstream as bytes. 2022-03-16 00:17:29 +01:00
David Given
7d93692468 Merge pull request #496 from davidgiven/fixes1
Fix regression with verification on writes.
2022-03-15 20:13:24 +00:00
David Given
93121275ae Verification on writes now works by default. 2022-03-15 20:49:37 +01:00
David Given
07b48b2e0d Merge pull request #493 from davidgiven/drive
Move drive configuration settings out of flux_source/flux_sink into a common config area.
2022-03-14 19:39:40 +00:00
David Given
294672d946 Update documentation. 2022-03-13 21:15:18 +01:00
David Given
ba3f806616 Move a lot of drive parameters into a toplevel drive{} config rather than
leaving them in the source/sink configs (which means they have to be set
twice).
2022-03-13 21:13:56 +01:00
David Given
9e4d99faca Merge pull request #489 from jepler/further-apple-fixes
Further apple fixes - have now booted a DOS 3.3 written with FE + GW
2022-03-08 16:01:15 +00:00
David Given
e89648200b Merge pull request #490 from jepler/mapper
Upgrade Apple II writing to 'unicorn', fix & explain remapping
2022-03-08 15:13:06 +00:00
Jeff Epler
3c305e8a37 Fix & Document Apple II sector mapping
* Update documentation (note apple flux writing is a unicorn now even though this isn't quite true until #489)
 * Fix DOS 3.3 mapping
 * Add ProDOS (all versions) mapping
2022-03-08 08:44:14 -06:00
Jeff Epler
6cfe69634c Merge remote-tracking branch 'origin/master' into further-apple-fixes 2022-03-08 07:45:20 -06:00
Jeff Epler
61be3714a5 apple2: encoder: Fix writing of "FF40"
I was writing "FF40" as "FF48": 1111 1111 0000 instead of 1111 1111 00.
This sequence of four zeros will not allow the real hardware to synchronize
to the bitstream.

With this change, I was able to boot a DOS 3.3 disk I rearranged into
"physical" order; some 'there's a disk error' raspberries occur (probably
indicating a data error within a sector) during the boot process, so
something is still obviously marginal, but this is a huge step forward.
2022-03-08 07:44:03 -06:00
Jeff Epler
0aed615ee5 Fix crash when reading disks written on real apple2
`readRaw8` throws an exception if you run out of data. This did not
turn up during my testing, because I was reading back a disk written by
fluxengine, so everything was aligned to the index pulse and no sector
ever straddled a revolution. On a "real" apple2-written disk, this was
not the case.

Incidentally, the "extra 0" problem exists on a real apple2-written
floppy as well.
2022-03-07 22:40:33 -06:00
David Given
6797037bdb Merge pull request #488 from davidgiven/multiread
Refactor to deprecate F_DESYNC.
2022-03-07 23:14:07 +00:00
David Given
39f8b25fd8 Tidy. 2022-03-07 00:28:19 +01:00
David Given
96214bf3fd Refactor for better multi-read support. Each read is now held separately, with
F_DESYNC being deprecated, and FluxSource returns an iterator which can be used
to retry reads.
2022-03-07 00:07:42 +01:00
David Given
400cd87802 Update documentation with new dependencies.
Fixes: #487
2022-03-06 12:07:55 +01:00
David Given
00c458db1e Merge pull request #486 from davidgiven/brother
Add support for creating bootable Brother 120kB disks.
2022-03-06 01:00:45 +00:00
David Given
1454e200db Create bootable Brother disks. 2022-03-06 01:32:19 +01:00
David Given
752875061c Format. 2022-03-06 00:47:11 +01:00
David Given
78186d8a45 Archive. 2022-03-06 00:46:53 +01:00
David Given
a4ef434f11 Merge pull request #485 from davidgiven/brother
Add support for writing 120kB Brother disk images.
2022-03-04 23:49:09 +00:00
David Given
9842c9945d Typo fix. 2022-03-05 00:33:10 +01:00
David Given
6dcd97dedf Create executable files (although possibly not correctly). 2022-03-05 00:15:11 +01:00
David Given
549a984eab Add support for writing 120kB Brother disk images. Also add some utils tests,
because they needed it.
2022-03-04 23:42:29 +01:00
David Given
aa805f81e0 Simplify. 2022-03-04 20:28:09 +01:00
David Given
93a67dadf6 Merge from master. 2022-03-04 20:19:42 +01:00
David Given
e9286f6ae9 Merge. 2022-03-04 20:19:14 +01:00
David Given
a31fcdb753 Merge pull request #484 from davidgiven/brother
Fix the Brother 120kB format.
2022-03-02 23:36:02 +00:00
David Given
0edca836f0 Let the image reader do the physical/logical track mapping. 2022-03-03 00:18:49 +01:00
David Given
8537f291b7 Update README. 2022-03-02 23:57:22 +01:00
David Given
46611ec720 Fix writing to 120kB Brother disks. 2022-03-02 23:56:02 +01:00
David Given
cbc3db8100 Display erased tracks a bit more gracefully. 2022-03-02 23:55:53 +01:00
David Given
7d5b07bf37 Merge from master. 2022-03-02 23:26:18 +01:00
David Given
5bd633d5bb Merge pull request #481 from jepler/apple2decoder
apple2: Comment decoder, add fix/workaround for stray 0 at start of sector data
2022-03-02 22:26:00 +00:00
David Given
17fdad1d6e Add a hopefully correct AppleDOS sector mapping table. 2022-03-02 23:21:41 +01:00
David Given
1ad26671b0 Merge from master. 2022-03-02 23:15:56 +01:00
David Given
2dc5064409 Add support for remapping sectors. 2022-03-02 23:00:20 +01:00
Jeff Epler
79eec41bcd Revamp how extra zeros are handled
It's not OK to call seek() here. Instead, add a function which can read
an apple 8-bit flux value in terms of readRaw8 and readRawBits. Apply
this function to all the data bytes, rather than just the first one.
2022-03-02 07:45:24 -06:00
Jeff Epler
386d22a45e Makefile: give a clear error at build time if wx is not available 2022-03-02 07:44:09 -06:00
Jeff Epler
d90fcbf7ad Handle extra 0-bits at the start of a sector 2022-03-02 07:43:45 -06:00
Jeff Epler
c4e4520058 apple2 decoder: explain the progression of sector status values 2022-03-02 07:43:43 -06:00
David Given
dd49f01499 Merge pull request #480 from jepler/apple2encoder
Add apple2 encoder
2022-03-02 10:24:05 +00:00
Jeff Epler
2c698cee71 Mark apple2 image writing as dinosaur, add note about writing, not ro anymore 2022-03-01 19:05:53 -06:00
Jeff Epler
87cb4b6d18 Add apple2 encoder
This is tested with encodedecodetest.sh but is not tested on HW yet.
It's likely that the sector order (interleave) doesn't match real systems.
2022-02-28 16:22:53 -06:00
David Given
2b7c747209 Merge pull request #471 from jepler/rx50-pdp11-format
Add support for PDP-11 RX-50 disks
2022-02-27 18:59:10 +00:00
Jeff Epler
707308b490 Fix alphetization of format list 2022-02-27 10:47:33 -06:00
Jeff Epler
ed1012bf07 Fix rx50 format description & test it
gap3 follows f10f52ded8/src/image/img.c (L135)
2022-02-27 10:47:22 -06:00
David Given
cc5b2bc27b Merge pull request #479 from davidgiven/wxw
Add a GUI.
2022-02-27 12:24:38 +00:00
David Given
a1a7cfa735 We don't need the rich text wxwidgets addon. 2022-02-27 13:12:29 +01:00
David Given
4624ff92df Remember to set the high density bit. 2022-02-27 13:11:11 +01:00
David Given
47dde98728 Include the GUI exe in release artifacts. 2022-02-27 13:05:39 +01:00
David Given
6f1031e95b Writing flux now works. 2022-02-27 12:54:57 +01:00
David Given
d5245e3784 Don't capture parameters in static blocks. That never ends well... 2022-02-27 12:51:51 +01:00
David Given
d97e72edb6 The image writers now log to the logger. 2022-02-27 00:27:44 +01:00
David Given
23b9e9ef5f The image readers now log to the logger. 2022-02-27 00:20:20 +01:00
David Given
02c7b86f85 You can write images now. 2022-02-27 00:07:59 +01:00
Paul Devine
f796b6b40d works in physical victor 9000 machine 2022-02-26 14:59:09 -08:00
David Given
528454c361 You can now load images into memory. 2022-02-26 23:32:24 +01:00
Jeff Epler
9ae3f7e61d Correct description of media type 2022-02-26 15:41:14 -06:00
David Given
dd33922810 Errors are now caught. 2022-02-26 19:52:07 +01:00
David Given
b52fdb3155 Additional settings pane now works. 2022-02-26 19:33:20 +01:00
Jeff Epler
edf9f9e714 Implement review suggestions 2022-02-26 12:06:08 -06:00
David Given
38eda6ed3c Add log and settings panels. The log panel is populated during a read (or
write).
2022-02-26 18:36:21 +01:00
David Given
32f0c5dc09 Update buttons as the application state changes. 2022-02-26 18:10:00 +01:00
David Given
b45b45b1b3 Enable double buffering for the visualiser in Windows. 2022-02-26 13:36:28 +00:00
David Given
f1c60b7177 Adjust header order so that things build on Windows. 2022-02-26 13:33:00 +00:00
David Given
2d8ff826f3 Merge pull request #475 from hharte/improve-hard-sector-decode
Improve hard sector decode
2022-02-26 12:37:48 +00:00
Howard M. Harte
d028db1ba3 northstar: Improve decode a little more.
seekToPattern() can skip the index hole if it doesn't find the
SYNC pattern.  If this happens too close to the end of the flux
stream, it can result in a conflicted sector.  In this case,
discard the sector.
2022-02-25 22:08:57 -08:00
Howard M. Harte
2d4e2b87bc micropolis: Improve decode a little more.
seekToPattern() can skip the index hole if it doesn't find the
SYNC pattern.  If this happens too close to the end of the flux
stream, it can result in a conflicted sector.  In this case,
discard the sector.
2022-02-25 21:59:18 -08:00
David Given
1d7a75c7b3 Okay, the visualiser looks pretty much done. 2022-02-26 00:23:17 +01:00
David Given
65584a953d Merge pull request #473 from hharte/fix-fluxmap-split
fluxmap: Don't push_back() if Fluxmap begins with F_DESYNC.
2022-02-25 11:15:31 +00:00
Howard M. Harte
4cbd19d2e5 fluxmap: Don't push_back() if Fluxmap begins with F_DESYNC.
Fluxmap::split() creates a 0-length Fluxmap if the Fluxmap
begins with F_DESYNC.  Fix this by not doing push_back()
if a F_DESYNC is encountered at the start of the Fluxmap.
2022-02-24 19:07:09 -08:00
David Given
a59b59fea4 First sector visualisation! Doesn't look bad. 2022-02-25 01:31:10 +01:00
David Given
5c063a9de3 Replumb the visualiser data flow... again. I think I might have it right this
time.
2022-02-25 00:36:12 +01:00
David Given
3666a7d7bd Merge. 2022-02-24 23:52:18 +01:00
David Given
8250112c7f Merge pull request #468 from JohnVeness/JohnVeness-fix-docs
Fix various doc typos
2022-02-24 22:16:48 +00:00
David Given
30957f4105 Merge pull request #472 from davidgiven/proto
Make the internal disk data structures all const.
2022-02-24 22:15:58 +00:00
David Given
eade2e279e Make the internal disk data structures all const, to allow us to pass them to
the GUI UI thread safely.
2022-02-24 22:46:10 +01:00
Jeff Epler
b4489b9402 Add support for PDP-11 RX-50 disks
According to my source [https://www.cbmstuff.com/forum/showthread.php?tid=634] the format of RX-50 is

 * single sided
 * 80 tracks
 * 10 sectors per track
 * 96 tpi (tracks per inch)
 * 300 rpm (revolutions per minute)
 * 250 KHz data rate

I have a disk labeled
```
BL-T540E-MC
CZUFDE0 MICRO PDP-11
USER TESTS
© 1983, 1894
DIGITAL EQUIPMENT CORP.
```
and stamped 14131.

The image read all sectors and the content looks plausible.  `strings` on it says things like
```
$ strings rx50.img  | grep -i pdp
; This file implements the USER FRIENDLY Diagnostic for the MICRO PDP11 & 11/73
```
however, I don't have an emulator or other system to use the disk image with so
I can't 100% vouch for the image being complete and correct.
2022-02-23 17:24:05 -06:00
David Given
a67b7c80c1 Rework the visualiser to look better, maybe? Also fiddle with the logger
somewhat. There's a pending problem where it's not safe to send mutable objects
through the logger to the visualiser, which will need work.
2022-02-23 23:50:29 +01:00
David Given
dfc0cdd0fa Merge from master. 2022-02-23 20:43:19 +01:00
John Veness
d6faf5b074 Change to consistent "-sided" 2022-02-23 11:05:57 +00:00
John Veness
44ffa6a3b0 Change to consistent "-sided" 2022-02-23 10:58:13 +00:00
John Veness
168b78d9d7 Change to consistent "-sided" 2022-02-23 10:57:17 +00:00
John Veness
bd8f313ccb Change to consistent "-sided" 2022-02-23 10:56:36 +00:00
David Given
4a0fc3d566 Merge pull request #469 from hharte/fix-warnings
Fix override warnings in decoders.
2022-02-23 10:53:15 +00:00
Howard M. Harte
8d04d17e39 Fix override warnings in decoders. 2022-02-22 22:45:39 -08:00
John Veness
9f44b1e783 Add space between sentences 2022-02-23 00:08:15 +00:00
John Veness
1b15271fe2 Fix "seperated" typo 2022-02-22 23:45:51 +00:00
John Veness
f451d3203c Made case consistent on "Read only" 2022-02-22 23:44:19 +00:00
John Veness
c713d38c19 Fix representing typo 2022-02-22 23:39:34 +00:00
John Veness
4d51f9d097 Fix GreaseWeazel internal link and bracket 2022-02-22 23:36:25 +00:00
John Veness
8750341862 Fix grammar in building.md 2022-02-22 23:31:56 +00:00
John Veness
3a4fe086ea Fix resale/rescale typo 2022-02-22 23:06:55 +00:00
John Veness
212e457c4c Fixed grammar in README 2022-02-22 23:06:06 +00:00
John Veness
790e2b534f Fixed end bracket in README 2022-02-22 22:54:16 +00:00
David Given
48414f0ce9 Merge pull request #467 from davidgiven/logging
Miscellaneous minor fixes
2022-02-22 22:53:19 +00:00
David Given
b5c3e75f10 Fix the new logger output. 2022-02-22 23:39:08 +01:00
David Given
548e07ce17 Fix off-by-one error in the MX decoder. 2022-02-22 22:51:14 +01:00
David Given
0aa0c6866c Do a very basic read/write visualisation. It looks like suck. 2022-02-22 22:35:13 +01:00
David Given
3d4cf7df26 Merge from master. 2022-02-22 18:12:33 +01:00
David Given
c4ba180a0c Merge pull request #462 from davidgiven/logging
Overhaul the logging system
2022-02-22 13:11:28 +00:00
David Given
bd392b91b7 Make the C++ old enough for Ubuntu. 2022-02-22 13:56:50 +01:00
David Given
042f7b0502 Log messages are now forwarded to the UI thread. 2022-02-22 00:50:58 +01:00
David Given
0fc5f0ee7d Merge pull request #465 from hharte/fix-northstar-decoder
northstar: Fix after decoder change.
2022-02-21 21:42:13 +00:00
Howard M. Harte
8cbef669b1 northstar: Fix after decoder change.
Recent changes to the decoders broke North Star.
2022-02-21 13:34:34 -08:00
David Given
f9004fb14c Merge. 2022-02-21 22:22:10 +01:00
David Given
40a42c65c1 The rotational speed message is now done via the logger. 2022-02-21 22:21:46 +01:00
David Given
e14030e369 Actually start using bits of the decoder framework. 2022-02-21 22:02:28 +01:00
David Given
c6cef191a7 Start work on setting the controls in the main window. 2022-02-21 00:57:39 +01:00
David Given
6ca9f83bfe Add the main threading stuff. 2022-02-21 00:14:27 +01:00
David Given
b3fcdc5f40 Come up with a UI design I'm happy with. Start work on the disk visualiser. 2022-02-20 22:41:47 +01:00
David Given
c6591cc11a Start using wxformbuilder for GUI design. 2022-02-20 00:07:47 +01:00
David Given
e31ba479b2 Merge from master. 2022-02-19 23:38:51 +01:00
David Given
659b668012 Merge pull request #463 from hharte/improve_micropolis_decoder
Improve micropolis decoder
2022-02-19 22:34:14 +00:00
Howard M. Harte
d69944dd8c Micropolis: Add support for MZOS checksum.
Vector MZOS uses a different sector checksum than the standard
Micropolis checksum used by MDOS, CP/M and OASIS.  Automatically
detect the checksum on the disk by default, but allow the user
to force the use of a specific checksum using the
--decoder.micropolis.checksum_type parameter.
2022-02-19 14:10:56 -08:00
Howard M. Harte
a25111e411 Micropolis: Improve decode.
* Discard a partial sector at the end of the track.
    * Do not seek to the index mark for the first sector.
    * Use a 64-bit pattern to match the SYNC.
    * If SYNC is found too early, search for a subsequent SYNC.
    * While decoding the sector record, enforce the SYNC pattern
      and track ID.
2022-02-19 14:10:44 -08:00
Howard M. Harte
6866d5d3fa Require 16 sectors for Micropolis disks. 2022-02-19 14:10:35 -08:00
David Given
21b3d1c521 Remove stuff which is too modern for Ubuntu. 2022-02-19 22:56:13 +01:00
David Given
f2bdd1cc49 More work on the logger overhaul: the reader should be done now. 2022-02-19 22:48:44 +01:00
David Given
9f09794ae6 Merge pull request #461 from hharte/fix_upgrade_windows
upgrade-flux-file: Fix for Windows
2022-02-19 21:29:54 +00:00
Howard M. Harte
6a1fb3d829 upgrade-flux-file: Fix for Windows
Close the database and remove the input file prior to renaming.
2022-02-19 13:14:25 -08:00
David Given
631b9768af Attempt to fix the BK formats. 2022-02-17 23:57:23 +01:00
Eugene Bolshakoff
a2d9db85cb adjust some numbers 2022-02-17 19:20:50 +01:00
David Given
4cb9d0b50a Merge pull request #458 from davidgiven/atarist
Fix the Atari ST profiles
2022-02-16 22:24:48 +01:00
David Given
73f37ef289 Update Atari ST documentation. 2022-02-16 22:06:26 +01:00
David Given
dbda19a209 Make sure that Atari ST disks ignore any spurious sector 66, because of
FastCopy.
2022-02-16 22:03:15 +01:00
David Given
0d7de7bbc0 Add support for ignoring certain sectors (needed for FastCopy disks on the
Atari ST).
2022-02-16 22:02:50 +01:00
David Given
649b78611c Work-in-progress logging overhaul. 2022-02-16 21:24:17 +01:00
Eugene Bolshakoff
22a21d76a4 trying to set clock_mhz the same as for ibm 2022-02-15 15:31:39 +01:00
David Given
5668a74aef Forgot to check in the build script. 2022-02-13 21:21:10 +01:00
David Given
482638601a Add missing files. 2022-02-13 21:07:01 +01:00
David Given
1bfe518f74 First draft (very bad) agat340 decoder. 2022-02-13 20:53:23 +01:00
David Given
eeb5ba40fb Merge pull request #455 from jepler/serial-behavior-improvements
Some quality of life improvements for Adafruit generic GW-compatibles
2022-02-13 12:43:18 +01:00
Jeff Epler
33bd912476 Some quality of life improvements for Adafruit generic GW-compatibles
The addition of the new tcsetattr call fixes a problem where interrupting
fluxengine during a flux read will leave data in the device and/or Linux's
serial buffers, so that the next invocation of fluxengine will fail similar
to
```
Error: command returned garbage (0x27 != 0x0 with status 0x31)
```
(the specific value differs because it's actually flux data)

Merely changing the existing tcsetattr call to specify TCSAFLUSH was not
enough; moving it after the 200ms pause seems to be enough.

Note that it doesn't seem feasible in our USB stack to make DTR reset
the device, since that would take down the USB stack and require a fresh
USB connection.

The addition of the special case for `rlen == 0` in read is for when the
GW-compatible board is reset or crashes and usb-disconnects during a
reading operation. Without this change, fluxengine spins forever at 100%
of a CPU, repeating a read().  After the change, this will cause
the host computer to print messages like:
```
  2.1: 200 ms in 68928 bytes
  3.0: Error: serial read returned no data (device removed?)
```
and exit.

I only tested these changes on Linux (Debian with kernel 5.10) and in
particular I don't know if/how it will work on a Mac.
2022-02-12 22:04:18 -06:00
David Given
33f1084f0a Merge from master. 2022-02-13 00:20:12 +01:00
David Given
e8a9b7cae3 Merge pull request #454 from davidgiven/mx
Fix a regression in the MX decoder.
2022-02-13 00:05:24 +01:00
David Given
f6b1d9c493 Fix a regression in the MX decoder. 2022-02-12 23:37:20 +01:00
David Given
624c34b378 Merge pull request #453 from davidgiven/mx
Fix regressions caused by the decoder change.
2022-02-12 22:56:57 +01:00
David Given
bc6753e5bf Correctly record sector positions. 2022-02-12 22:42:40 +01:00
David Given
c539debc84 Fix the Amiga decoder, which got broken with the decoder change. Also fix the
encoder which was always broken (but Amigas apparently didn't care).
2022-02-12 22:42:15 +01:00
David Given
830f4cec0f Merge pull request #452 from davidgiven/mx
Prevent bad reads on gapless formats.
2022-02-12 15:32:36 +01:00
David Given
03dd9e6e83 Add back support for capturing raw records. 2022-02-12 15:16:25 +01:00
David Given
e8d1c90182 Fix, hopefully, the rest of the decoders. 2022-02-12 15:02:42 +01:00
David Given
0933dc1afa Partially complete rework of all the decoders to avoid seeking inside the
fluxmap. This requires resetting the FluxDecoder, which loses any pending
state, resulting in bad reads for (some) formats which don't have gaps between
sectors --- the DVK MX is the main victim.
2022-02-12 00:55:09 +01:00
David Given
610b7fe95c Merge pull request #451 from jepler/scp-84track
Support 84-track SCP files
2022-02-11 16:25:35 +01:00
Jeff Epler
a6bf6f901f Error gracefully when requested to write a too-large scp image
This prevents a crash when more tracks were recorded than fit in an
scp image. When I first encountered a problem doing an 84-track operation,
this led to a segfault later on since memory was written past the end
of _fileheader, and (I think) stomped on a pointer.
2022-02-11 09:10:20 -06:00
Jeff Epler
ca2e37e852 Include 168 track data headers
According to version 2.2 of the SCP image specification
<https://www.cbmstuff.com/downloads/scp/scp_image_specs.txt>
the number of Track Data Entries for a floppy can be up to 168,
so that up to 84 tracks can be recorded (e.g., fluxengine -c 0-83).

> BYTES 0x10-0x2AF (for floppy image files) are a table of longwords with each entry being
> an offset to a Track Data Header (TDH) for each track that is stored in the image.  The table
> is always sequential.  There is an entry for every track, with up to 168 entries supported for
> floppy disks.  This means that floppy disk images of up to 84 tracks with sides 0/1 are possible.
2022-02-11 09:08:59 -06:00
David Given
d1ffaaa327 Merge pull request #449 from davidgiven/macdsk
Change the mac profiles to produce simple sector images rather the DiskCopy files.
2022-02-08 22:04:48 +01:00
David Given
8b7f551505 Change the mac profiles to produce simple sector images rather the DiskCopy
files (as that seems to be what most people typically use).
2022-02-08 21:21:22 +01:00
David Given
759b3f8fcd Merge pull request #447 from adafruit/master
fix 2 byte pack for revolution count
2022-02-08 20:04:57 +01:00
lady ada
9ec56f1dfa fix 2 byte pack for revolution count 2022-02-08 11:43:20 -05:00
David Given
84e997949f Merge. 2022-02-06 22:14:11 +01:00
David Given
f82bd45fec Pull out extension profiles into their own section of the documentation list. 2022-02-06 22:13:26 +01:00
David Given
3863724007 Merge pull request #443 from davidgiven/upgrade
Rework flux file upgrading and remove the Sqlite dependency.
2022-02-04 22:16:31 +01:00
David Given
7e0ed9fe93 Try to fix the build on Windows and OSX. 2022-02-04 22:00:44 +01:00
David Given
945e3f3c5f Typo fix. 2022-02-04 21:29:56 +01:00
David Given
6f100219f7 Include the upgrade-flux-file binaries in the releases. 2022-02-04 21:29:04 +01:00
David Given
89688394f8 Replace the upgradefluxfile builtin with a seperate upgrade-flux-file tool.
This allows us to remove all the SQL stuff from the main program, and restores
the ability to upgrade from version 2 SQL files.
2022-02-04 21:27:24 +01:00
David Given
091ef6d972 Merge pull request #439 from tdaede/d88_doublestep
D88: Add 2D (40 track double density) support.
2022-02-03 00:27:55 +01:00
Eric Anderson
e501c44985 Merge branch 'master' into micropolis-275 2022-01-29 10:09:35 -07:00
Eric Anderson
bc9b761903 Prefer IMG for Micropolis, and use a vgi profile to swap format 2022-01-29 10:05:54 -07:00
Thomas Daede
dfb461b05c D88: Add 2D (40 track double density) support.
This assumes a 360rpm drive, and double stepping.

If fluxengine supports the concept of 40 vs 80 track drives in the
future, it might make sense for this to assume a 300rpm drive
instead.
2022-01-28 10:50:36 -08:00
David Given
86f6a2f722 Merge pull request #437 from davidgiven/verify
Fix --no-verify.
2022-01-27 22:05:23 +01:00
David Given
77d6d0d5be Fix --no-verify. 2022-01-27 20:49:38 +01:00
David Given
4946909c6d Merge pull request #436 from davidgiven/dim
Fix some autoconfigure issues.
2022-01-27 00:07:23 +01:00
David Given
2439736cb4 When autoconfiguring, set up the decoder to allow disk verification. 2022-01-26 23:53:44 +01:00
David Given
8fb2ad1986 Automatically set the number of heads and cylinders where appropriate. 2022-01-26 23:53:16 +01:00
David Given
1d7bbcec66 Merge pull request #435 from davidgiven/brother
Don't use signed values for track/sector/side values
2022-01-26 23:29:51 +01:00
David Given
6ac9d34aac Don't rely on the geometry autodetector for Brother formats. 2022-01-26 23:13:40 +01:00
David Given
3369029e9a Don't use signed ints for the track/side/sector values. 2022-01-26 23:13:22 +01:00
David Given
11f411b745 Merge pull request #433 from davidgiven/rawread
Don't crash when trying to read flux from a fl2 file where there isn't any.
2022-01-26 23:09:48 +01:00
David Given
9bb6e15900 Don't crash when trying to read flux from a fl2 file where there isn't any
flux.
2022-01-26 22:09:50 +01:00
David Given
d2a6b37f5f Merge pull request #430 from tdaede/greaseweazle_time_read
Support time-based reads for Greaseweazle V24+.
2022-01-26 21:17:44 +01:00
David Given
0c1f6163f7 Merge pull request #432 from tdaede/no_verify
Add flag to skip verification during write.
2022-01-26 21:16:21 +01:00
Thomas Daede
6860317a58 Add flag to skip verification during write.
Fixes #425
2022-01-26 11:45:28 -08:00
Thomas Daede
77257b0989 Support time-based reads for Greaseweazle V24+.
This substantially speeds up read and verify operations, allowing
only 1.2 revs rather than 2 revs to be read.

Fixes #426
2022-01-26 08:02:30 -08:00
David Given
8f35da5163 Merge pull request #428 from tdaede/doc_pc98
Add documentation for PC-98, X68000, etc.
2022-01-26 17:00:40 +01:00
Thomas Daede
82d8c40b5f Add documentation for PC-98, X68000, etc. 2022-01-25 09:53:56 -08:00
David Given
dbfc0f98c3 Merge pull request #422 from tdaede/nfdr0
Add NFD r0 image reader.
2022-01-22 23:28:55 +01:00
David Given
ca15bf4a92 Merge pull request #417 from tdaede/fluxsink_ratio
Add option to rescale flux source pulses.
2022-01-22 23:27:53 +01:00
David Given
3e4d4cc002 Hotfix for Windows build. 2022-01-22 22:29:34 +01:00
David Given
4257544402 Merge. 2022-01-22 21:20:48 +01:00
David Given
83d44bd03e Update documentation. 2022-01-22 20:46:40 +01:00
David Given
745e0685a4 Allow specifying the GreaseWeazle serial port directly (for devices which don't
have the GreaseWeazle VID/PID).
2022-01-22 20:41:27 +01:00
David Given
d6db2128df Toggle DTR on Unixes as well as on Windows. 2022-01-22 20:39:20 +01:00
David Given
6e83ed5654 Update clang-format file. 2022-01-22 20:37:58 +01:00
Thomas Daede
a4f44933ec Add NFD r0 image reader.
This format is very similar to D88, but used mostly for PC-98
rather than PC-88.
2022-01-22 08:33:43 -08:00
David Given
fe080e1d90 Merge from master. 2022-01-21 19:36:18 +01:00
David Given
dd462873d2 Merge pull request #410 from pauldevine/victor9k
Victor9k Double Sided Support
2022-01-20 23:51:24 +01:00
Paul Devine
628fec2c92 Merge branch 'victor9k' of github.com:pauldevine/fluxengine into victor9k 2022-01-20 13:57:25 -08:00
Paul Devine
c4700f3cb8 updated documentation for double-sided, made note about documentaiton bug for track format 2022-01-20 13:56:35 -08:00
Paul Devine
fac606c475 adding Victor9k double sided src format 2022-01-20 13:56:35 -08:00
Paul Devine
5a0aadabc1 adding support for double-sided Victor9k disks 2022-01-20 13:56:35 -08:00
Paul Devine
283b9871cb correct media size in README 2022-01-20 13:56:35 -08:00
David Given
f9d88cc2a0 Merge pull request #420 from davidgiven/contains
Don't use string::contains as it's not sufficiently widespread yet.
2022-01-20 20:40:19 +01:00
David Given
c751810b84 Don't use string::contains. 2022-01-20 20:24:10 +01:00
David Given
2dbc9c39b1 Merge pull request #419 from davidgiven/clang
Add initial clang-format file.
2022-01-20 19:54:07 +01:00
Paul Devine
a9933c7764 updated documentation for double-sided, made note about documentaiton bug for track format 2022-01-19 16:21:40 -08:00
David Given
ffcac3a12d Add initial clang-format file. 2022-01-20 00:08:26 +01:00
David Given
a4130e5f78 Merge pull request #418 from tdaede/auto_fdi
FDI: Add support for automatically setting format.
2022-01-19 23:12:41 +01:00
Thomas Daede
de8f8b7d91 FDI: Add support for automatically setting format. 2022-01-17 17:02:26 -08:00
Thomas Daede
b8c58b12fd Add option to rescale flux source pulses.
This actually scales by the reciprotal of the flux sink, to
allow the same value to be set for both options.
2022-01-17 16:53:50 -08:00
David Given
4af355e1f9 Merge pull request #416 from jepler/patch-1
Fix a little markup error
2022-01-17 15:36:43 +01:00
Jeff Epler
0e7e9d5643 Line columns up again 2022-01-16 20:08:01 -06:00
Jeff Epler
fafadc3333 Fix a markup typo 2022-01-16 20:07:08 -06:00
Limor "Ladyada" Fried
752c92bb21 Merge pull request #1 from adafruit/floppzy
PR check!
2022-01-16 13:38:23 -05:00
lady ada
45d7b284e3 1) update CI to add zip on commit for quick testing
2) fix gw readflux command (should be 8 bytes long but was packed as 10)
3) add support for non-gw VID/PID (generic serial port that is gw compatible)
4) manually set/clear DTR on serial port devices - this seems to be essential for tinyusb/arduino CDC

not tested with a gw but ought not to have broken anything!
2022-01-16 12:43:47 -05:00
Paul Devine
085fca7e31 adding Victor9k double sided src format 2022-01-10 15:34:37 -08:00
Paul Devine
8744114790 adding support for double-sided Victor9k disks 2022-01-10 15:33:29 -08:00
Paul Devine
c658a764d0 correct media size in README 2022-01-10 15:06:29 -08:00
David Given
6a07ba850c Merge pull request #409 from davidgiven/hcs
Support HCS as well as CHS format in the image reader/writer.
2022-01-11 00:03:04 +01:00
David Given
c4e29e74b2 Support HCS as well as CHS format in the image reader/writer. 2022-01-10 23:49:25 +01:00
David Given
40bca8b38c Merge pull request #370 from tdaede/rescale 2022-01-10 23:02:18 +01:00
David Given
18af881fe5 Attach the underlying raw records to Sector structures; add a .raw exporter for
getting the MFM/FM/GCR stream.
2022-01-04 23:49:39 +01:00
David Given
dfd97d9fc5 Merge pull request #359 from tdaede/compdb
Add Make target to build compdb.
2022-01-03 00:15:18 +01:00
David Given
ebd2c329ff Merge pull request #397 from hharte/master
reader: Fix conflicting sector error message.
2022-01-02 17:38:11 +01:00
David Given
265b169d43 Merge from master. 2022-01-02 15:07:38 +01:00
David Given
d154b1464f Merge pull request #404 from davidgiven/shugart
Add support for selecting the Shugart or IBMPC GreaseWeazle bus types.
2022-01-01 23:05:26 +01:00
David Given
a32ea6e5f8 Add support for selecting the Shugart or IBMPC GreaseWeazle bus types. 2022-01-01 22:45:27 +01:00
David Given
d7b21bf07e Merge pull request #400 from davidgiven/update
Update firmware to version 16.
2021-12-31 21:41:05 +01:00
David Given
d120790da7 Update firmware to version 16. 2021-12-31 20:24:51 +00:00
Eric Anderson
bd854d29a4 Merge branch 'master' into micropolis-275 2021-12-29 12:39:07 -06:00
Eric Anderson
d06e3db90b Support .vgi for image writing
vgi was already added to imagereader, but was missing from imagewriter.
2021-12-29 12:23:04 -06:00
Howard M. Harte
5ada533b06 reader: Fix conflicting sector error message.
The conflicting sector error message incorrectly displays the track instead
of the sector in conflict.
2021-12-25 09:10:53 -08:00
David Given
6f5e648751 Merge pull request #394 from hharte/fixserial
Windows: Support serial ports > COM9
2021-12-17 23:06:41 +00:00
Howard M. Harte
9ee5b3eaf3 Windows: Support serial ports > COM9
Windows requires the use of the DOS UNC path name for COM ports
greater than COM9.  Prefix the port name with \\.\
2021-12-17 14:21:19 -08:00
David Given
0560da246b Add wxwidgets to the build dependencies. 2021-12-17 20:24:50 +00:00
David Given
8b6b3ee3b8 Fix wxwidgets building on Windows. We now have it working on all three
platforms.
2021-12-17 20:10:11 +00:00
David Given
45fe83951a Merge pull request #392 from davidgiven/warnings
Fix some minor build glitches and warnings.
2021-12-16 13:54:26 +00:00
David Given
57e5d8911d Fix missing build dependency causing sporadic build failures. 2021-12-16 14:27:45 +01:00
David Given
5768a766b8 Silence stray warnings. 2021-12-16 14:27:18 +01:00
David Given
051e9e38f3 Add a boilerplate wxwidgets example. 2021-12-15 22:55:57 +01:00
David Given
63a5954dfa Merge pull request #390 from davidgiven/victor9k
Victor 9000 fixes
2021-12-15 15:08:41 +00:00
David Given
897931f273 Merge pull request #386 from davidgiven/pulsefix
Various firmware fixes
2021-12-14 21:07:06 +00:00
David Given
03e80b2f1c Fix the format so it no longer discards sector 0. 2021-12-14 21:10:38 +01:00
David Given
53c8ec864c Clean up the GreaseWeazle bandwidth tester. Sometimes it doesn't read the right
amount of data from the device?
2021-12-14 18:01:53 +00:00
David Given
5d3002f118 Announce the device serial number on connection again. 2021-12-13 23:24:02 +01:00
David Given
5eeb52660c Update documentation for the USB autodetection. 2021-12-13 23:19:16 +01:00
David Given
3dfafaa278 GreaseWeazle autodetection now works (at least on Linux). 2021-12-13 23:09:33 +01:00
David Given
462bd9ae5e Rewrite the sampler pulse detection... again. 2021-12-12 23:13:23 +00:00
David Given
dab0fcc7c0 Hopefully fix Linux build. 2021-12-12 21:15:30 +01:00
David Given
b8a3e8085e Fix after merge. 2021-12-12 19:58:19 +00:00
David Given
c9d1d72ba3 Merge in the libusbp changes. 2021-12-12 19:50:33 +00:00
David Given
ccf5d513d2 Merge from master. 2021-12-12 19:49:32 +00:00
David Given
d157b7b05d Make libusbp work on Windows; rework the Microsoft-specific bits of the
firmware to match. This does at least allow us to get rid of the patcher.
2021-12-12 17:25:24 +00:00
dg
05981ac46c Make work on OSX. 2021-12-12 15:40:12 +00:00
David Given
08615a5954 Make libusbp build, at least on Linux; port fluxengineusb to use it. 2021-12-12 16:29:51 +01:00
David Given
b6dbe26c7e Raw import of libusbp. 2021-12-11 18:59:44 +01:00
David Given
4f0d61b379 Merge pull request #388 from davidgiven/supercardpro
Tell the various IBM decoders which sectors to expect, for more reliable retries.
2021-12-11 11:26:51 +00:00
David Given
9e5e494f88 Merge from master. 2021-12-11 12:18:06 +01:00
David Given
b15fd05e8d Add support in the IBM decoder for trackdata protos, and then configure the IBM
profiles to know about which sectors it should see in the result image to allow
retries if sectors aren't found.
2021-12-11 12:17:32 +01:00
David Given
faabc28d1b Merge pull request #387 from davidgiven/supercardpro
Refactor the serial port code.
2021-12-10 23:45:49 +00:00
David Given
4b815846ee ...and fix for OSX. 2021-12-10 23:35:58 +00:00
David Given
fe8be18c4c Fix the serial port code on Windows. 2021-12-10 23:29:20 +00:00
David Given
519c30321d Split the serial port code off into its own file so we can use it for the SCP
driver.
2021-12-11 00:06:14 +01:00
David Given
77be88a5bb Add a package that the tests need. 2021-12-10 21:25:00 +01:00
David Given
8d04931f9f Turns out the high density pin is asserted when _double_ density. Set it
correctly.
2021-12-10 20:07:02 +00:00
David Given
3d1ee7a43e Don't spin in an infinite loop if the decoder tries to find a data record and
the matcher fails to find anything, leaving the seek point unchanged.
2021-12-10 19:56:00 +00:00
David Given
2584a25527 Update components and binary. 2021-12-10 19:55:06 +00:00
David Given
0647a82d38 The tests will run on Windows now. 2021-12-10 19:54:31 +00:00
David Given
c56459c98c Add back the pulse converter, as a 12MHz sample clock only allows pulses which
are 83ns long, which I think is too big. We now sample the index signal at
64MHz which allows pulses down to 16ns.
2021-12-10 17:40:58 +00:00
David Given
7b0a4a057d Merge pull request #376 from tdaede/scpfix2
Fix SCP writer to always write complete revolutions.
2021-12-09 22:26:36 +00:00
David Given
3c3e520594 Merge pull request #378 from tdaede/ibm_n88basic
Add ibm_n88basic format.
2021-12-08 19:56:35 +00:00
Thomas Daede
ce1daf6a2b Add option to rescale flux sink pulses.
Note that this does not affect the decoder used for verify,
but at least for tame rescaling the decoders are still able to
lock.

This is a precursor to automatic scaling via the disk speed
measurement.
2021-12-07 14:45:32 -08:00
Thomas Daede
68314cd44d Add n88basic format.
This isn't really intended to be used directly (bare images are
normally .d88 etc) but is useful for testing the FM encoder.
2021-12-07 14:39:31 -08:00
David Given
17787b97d4 Merge pull request #371 from tdaede/d88hd
Set media density automatically for D88 format.
2021-12-07 21:54:58 +00:00
David Given
da84297b2c Merge pull request #380 from tdaede/last_fluxmap
SCP: Only write the last continuous fluxmap.
2021-12-07 21:47:54 +00:00
David Given
e3e3bb770d Merge pull request #379 from tdaede/no_disk_sector_check
Remove per-track sector check from D88.
2021-12-07 21:45:47 +00:00
David Given
9d4f180741 Merge pull request #381 from hharte/fixnorthstar
Fix North Star after decoder update
2021-12-07 21:44:26 +00:00
David Given
316836d9f6 Merge pull request #382 from darkstar/protofix
Fix a small typo in proto.cc
2021-12-07 17:24:24 +00:00
Michael D
aa3b0a117a Fix a small typo in proto.cc
Also add long-forms "yes" and "no" as valid options
2021-12-07 18:10:53 +01:00
Howard M. Harte
0f7df3281f NorthStar: Fix nsiimagewriter image size in informational message. 2021-12-06 17:57:08 -10:00
Howard M. Harte
4a91a35799 Fix the North Star decoder after the PLL upgrade. 2021-12-06 17:55:54 -10:00
dg
36c2263675 Fix the Micropolis decoder after the PLL upgrade. 2021-12-06 22:20:18 +00:00
David Given
28068d8d31 Merge pull request #375 from davidgiven/fl2
Make the new FL2 format the default.
2021-12-06 22:14:33 +00:00
Thomas Daede
0c3c08db36 SCP: Only write the last continuous fluxmap. 2021-12-06 11:47:42 -08:00
Thomas Daede
bb5d62fe69 Remove per-track sector check from D88.
It's no longer needed to be this strict.
2021-12-06 10:13:30 -08:00
David Given
376270dd53 Merge pull request #377 from tdaede/d88trackfill
Set FM gap fill byte in D88.
2021-12-06 16:21:13 +00:00
Thomas Daede
9056b23eaa Set FM gap fill byte in D88. 2021-12-06 08:10:02 -08:00
Thomas Daede
634be4ae51 Fix SCP writer to always write complete revolutions.
There were a couple of problems that needed to be fixed:

- Fluxmaps can be produced with zero index pulses by encoders.
This is somewhat ambiguous but it's assumed that this means there
is exactly one revolution.

- The SCP unindexed mode wasn't properly supported, as it should
create equal-length revolutions not aligned to indexes. However,
fluxmaps don't contain any information on rotation speed. Therefore,
drop the ability to create unindexed SCPs and slightly change the
meaning of the option to not include pre-index data.

- Real flux reads can potentially produce duplicate index pulses.
Avoid creating zero-length revs.

- Fix off-by-one error for rev count.
2021-12-06 07:41:16 -08:00
David Given
422620f4fe Merge pull request #374 from davidgiven/ibm
Make the gap fill byte configurable.
2021-12-05 22:27:45 +00:00
dg
194b9b1193 Modify the flux upgrader to convert from sqlite flux files to fl2 flux files. 2021-12-05 17:46:23 +00:00
dg
27fceb3f41 Make the new FL2 flux format the default. 2021-12-05 17:09:02 +00:00
dg
930e0bcd67 Let the tests run on OSX. 2021-12-05 17:04:35 +00:00
dg
d81e0a3ed4 Merge from master. 2021-12-05 16:44:15 +00:00
dg
ebb5c17be4 Make the IBM format gap fill byte configurable. 2021-12-05 16:43:45 +00:00
dg
1e99fe5b04 Merge from master. 2021-12-05 16:37:04 +00:00
David Given
5f5f22c82b Merge pull request #373 from davidgiven/fl2
Add a new much simpler file format.
2021-12-05 16:29:21 +00:00
David Given
709e300960 More typo fixing. 2021-12-05 17:17:03 +01:00
David Given
d2f677d84e Typo fix. 2021-12-05 17:00:28 +01:00
dg
18d90c44dd Rework the flux decoders to add a desync opcode for separating multiple reads
in a flux stream. Finally add a unit test for the flux decoder.
2021-12-05 14:42:57 +00:00
dg
298f77f52e Add the FL2 reader. 2021-12-05 11:33:19 +00:00
David Given
70b2f7efb4 Merge pull request #372 from davidgiven/osx
Add a warning if you use a /dev/tty file on OSX
2021-12-04 21:14:46 +00:00
dg
78a070bd99 Add a simple warning if you're using OSX with a GreaseWeazle and select a
/dev/tty file rather than a /dev/cu one (the /dev/tty files usually don't work).
2021-12-04 20:57:53 +00:00
dg
c88ff973b1 Merge from master. 2021-12-04 19:23:41 +00:00
Thomas Daede
66f82f764d Set media density automatically for D88 format. 2021-12-04 10:31:17 -08:00
dg
d36a18c17a Add the experimental FL2 encoder. 2021-12-04 12:39:36 +00:00
David Given
8efd95346a Merge pull request #369 from tdaede/d88plus
Use per-track encoding specified in D88 files.
2021-12-04 11:44:28 +00:00
Thomas Daede
04b91377c9 Use per-track encoding specified in D88 files.
This supports varying sector sizes, counts, and modulation on
a per-track basis. It also supports writing sectors in the
original order.
2021-12-04 03:36:06 -08:00
David Given
f5d04d989c Merge pull request #366 from davidgiven/victor9k
Add a Victor 9000 encoder.
2021-12-03 23:03:46 +00:00
David Given
06a497f346 As the victor9k profile is single-sided only, rename it. 2021-12-03 23:43:29 +01:00
David Given
b1cf706a8f Update Victor9K documentation. 2021-12-03 23:41:53 +01:00
David Given
91f04e36e9 Bash the Victor9K encoder into producing images that look almost plausible. 2021-12-03 23:39:06 +01:00
David Given
5d76eec0f6 Merge pull request #361 from tdaede/autodim
Automatically configure format for DIM when no format is specified.
2021-12-03 21:38:47 +00:00
Thomas Daede
7822325866 Automatically configure format for DIM when no format is specified. 2021-12-03 13:34:35 -08:00
David Given
521123f139 Merge from master. 2021-12-03 22:34:31 +01:00
David Given
b3f511b025 Merge pull request #364 from davidgiven/scp
Improve SCP handling.
2021-12-03 21:28:43 +00:00
David Given
ff63840beb Run the encodedecode tests with scp as well as flux. 2021-12-03 22:12:33 +01:00
David Given
1fbcf4aa5f More Victor9K encoder. 2021-12-03 22:04:25 +01:00
dtrg
8d6a811204 Correctly write the number of revolutions in SCP files. 2021-12-03 18:13:17 +00:00
dtrg
08fe2a9e27 Merge from master. 2021-12-03 13:59:03 +00:00
David Given
d8465f4374 Merge pull request #357 from tdaede/d88
Add minimal D88 image format reader.
2021-12-03 11:59:28 +00:00
Thomas Daede
8a3cd14723 Add minimal D88 image format reader.
This implements a subset of D88, only supporting the first floppy
in an image. It only supports images with equal sector sizes,
equal numbers of sectors per track, and no bad sectors.
2021-12-03 02:18:29 -08:00
Thomas Daede
08c1671f21 Add Make target to build compdb. 2021-12-03 02:13:01 -08:00
David Given
91e794add1 Merge pull request #358 from davidgiven/imageformats
Refactor to allow image readers to specify the encoder/decoder formats, if desired.
2021-12-02 22:55:23 +00:00
dg
9c98caf21d Remove the prototype IMD automatic encoder code, because it needs a lot more
work.
2021-12-02 22:44:33 +00:00
dg
f97c42017f Instead of preloading the image, just explicitly load it in fe-write and change
the writeDiskCommand interface to take an Image rather than an ImageReader.
2021-12-01 19:58:24 +00:00
dg
3033a2cb95 Add semi-functioning prototype code to set the encoder and decoder
automatically with IMD files. Also make the IMD reader work properly again.
2021-12-01 18:18:48 +00:00
dg
bcf6f48d46 In fe-read, load the image before constructing the encoder, decoder etc so as
to allow the image reader to modify the config if necessary.
2021-12-01 17:55:05 +00:00
David Given
4c97f15a65 Merge pull request #355 from tdaede/lowercase
Make filename endings case insensitive.
2021-12-01 17:23:08 +01:00
David Given
5dc6349818 Merge pull request #356 from tdaede/fix_sizes
Fix reported size of DIM and FDI images.
2021-12-01 17:22:08 +01:00
Thomas Daede
6ab5c4012a Make filename endings case insensitive. 2021-12-01 06:35:15 -08:00
Thomas Daede
1d50e45777 Fix reported size of DIM and FDI images.
The computation for size accidentally included header size.
2021-12-01 06:32:18 -08:00
David Given
3cbf420acd Merge pull request #354 from tdaede/fdi
Add FDI format reader.
2021-12-01 13:00:41 +01:00
Thomas Daede
ea407b2182 Add FDI format reader.
This format is common for NEC PC-98 disk images.
2021-12-01 03:44:18 -08:00
David Given
5228885760 Merge pull request #348 from tdaede/dim
Add DIM format reader.
2021-12-01 12:01:33 +01:00
Thomas Daede
676845aaf3 Add DIM format reader.
This format is common for Japanese PC floppy images.
2021-12-01 00:07:02 -08:00
David Given
61af9b3810 Merge pull request #351 from tdaede/xdf
Add alias for XDF image files.
2021-11-30 12:42:33 +01:00
Thomas Daede
406a433c3f Add alias for XDF image files.
This is a common extension for Sharp X68000 bare disk images.
2021-11-30 00:40:54 -08:00
David Given
2abd413c08 Merge pull request #350 from tdaede/gw_density
Fix inverted density select in Greaseweazle driver.
2021-11-29 13:25:03 +01:00
Thomas Daede
ad96c9bb9f Fix inverted density select in Greaseweazle driver.
Pin 2 should be set high when high density is selected.
2021-11-28 19:31:26 -08:00
David Given
b412d54998 Do the framework and hopefully a lot of the maths of the Victor 9K encoder. 2021-11-27 23:38:57 +01:00
David Given
6247d3c5e6 Don't print lots of digits after the decimal point in the amount of read data. 2021-11-27 23:38:25 +01:00
David Given
b7ee513dfd Set up the config for the Victor 9000 format. 2021-11-27 20:49:36 +01:00
David Given
3795abc19e Add the Victor9k boilerplate. 2021-11-27 19:44:53 +01:00
David Given
1d32a4d984 Merge pull request #346 from davidgiven/victor9k
Rewrite the flux decoder to use a PLL-driven algorithm.
2021-11-26 22:43:58 +01:00
David Given
a20a78cd64 Merge from trunk. 2021-11-26 21:37:25 +01:00
David Given
a2f51b36ec Check in missing file... 2021-11-26 21:36:22 +01:00
David Given
910ccb273a Added the PLL phase adjustment stuff to the decoder. Much better decodes. 2021-11-26 21:27:58 +01:00
David Given
2cbe39e553 Fix bugs to make the IBM decoder work again. 2021-11-26 21:17:58 +01:00
David Given
994c8fc02d Merge pull request #347 from tdaede/jpc
Add Japanese PC 1.2MB format.
2021-11-26 12:56:47 +01:00
Thomas Daede
57dcd6d520 Add Japanese PC 1.2MB format.
This format is used by the PC-98 and Sharp X68000 series.

3.5" disks are spun at 360rpm to match the clock rate of 5.25".
2021-11-26 03:50:44 -08:00
David Given
20ade1de7b Replace the flux decoder with a PLL-based one stolen from samdisk. It almost
works.
2021-11-25 00:00:51 +01:00
David Given
28aff78469 Make victor9k disk images triangular. 2021-11-21 21:41:14 +01:00
Eric Anderson
3b95e56418 Support Micropolis variations and prefer VGI extension 2021-11-07 21:54:00 -08:00
David Given
056055bf0b Merge pull request #341 from davidgiven/ibm
Minor fixes to do the the imgwriter and Acorn formats.
2021-11-04 20:17:50 +01:00
David Given
368d329459 Set enable_side_byte for Acorn formats, because the machines don't always write
it.
2021-11-03 14:16:26 +01:00
David Given
2fe1ffeaf1 Add an ignore_track_byte setting. 2021-11-03 14:15:51 +01:00
David Given
e252e8eb1d If no sector_count is specified in a sector_range, autodetect it. 2021-11-03 14:15:25 +01:00
David Given
e160bcc7d5 Merge pull request #333 from ejona86/micropolis-write
Add Micropolis encoder
2021-10-31 10:33:24 +01:00
David Given
41bd035690 Merge pull request #312 from davidgiven/teledisk
Add read support for Teledisk images, plus add a format for the HP 9121 disk drive
2021-10-30 23:58:20 +02:00
David Given
b0003fc7e5 Merge from master. 2021-10-30 22:19:37 +02:00
David Given
8e1be97589 Merge pull request #335 from ejona86/scp-revs-crash
scpfluxsource: Support more than 5 revolutions
2021-10-30 20:39:45 +02:00
Eric Anderson
181f2f38d8 s/ScpTrackStart/ScpTrackHeader/ and use in ScpTrack 2021-10-30 10:29:17 -07:00
David Given
661940114e Merge pull request #338 from davidgiven/atari
Switch the Atari ST formats to use 1-based sector IDs.
2021-10-30 13:30:57 +02:00
David Given
9874d4bec5 Update compiler flags to make newer compilers happy. 2021-10-30 13:22:00 +02:00
David Given
7d5fedf35f Switch the Atari ST formats to use 1-based sector IDs. 2021-10-28 19:40:16 +02:00
Eric Anderson
69ad36a9ae scpfluxsource: Support more than 5 revolutions
This prevents uninitialized memory reads for files with many
revolutions. See #322.
2021-10-08 20:40:23 -07:00
Eric Anderson
ce5fcaf172 Micropolis raw 275 byte sectors
These are used by formats like VGI since the ECC varied per machine and
the 10 extra bytes of user data may contain useful information.
2021-10-02 22:59:14 -07:00
Eric Anderson
a00137d742 Remove unused fe-writemicropolis.cc 2021-10-02 17:37:33 -07:00
Eric Anderson
e002842640 Add Micropolis writing documentation snippet 2021-10-02 17:26:46 -07:00
Eric Anderson
41e9c46cba Port Micropolis encoder to protobuf-based config 2021-10-02 16:24:38 -07:00
David Given
bc2d58bee3 Merge pull request #332 from ejona86/redir-typo
Redirect dd to stdout instead of file '1'
2021-10-02 20:28:00 +00:00
Eric Anderson
c54de27503 Merge branch 'master' into micropolis-write
Let's not act like this actually compiles.
2021-10-02 12:45:56 -07:00
Eric Anderson
d9bfd77fba Redirect dd to stdout instead of file '1'
Looks like a typo, that would have had mostly the intended effect.
2021-10-02 11:41:06 -07:00
David Given
336cc0077c Merge pull request #331 from davidgiven/proto
Assorted minor fixes.
2021-09-25 21:06:40 +00:00
David Given
0be673b210 Clarify documentation in the building doc. 2021-09-25 22:43:19 +02:00
David Given
028e2498fb Update references to --input.flux and --output.flux. 2021-09-25 22:35:02 +02:00
David Given
249f1dea9d Merge from master. 2021-09-25 22:24:14 +02:00
David Given
07c7b22669 Allow setting enum proto values from the command line. 2021-09-19 22:59:47 +02:00
David Given
6623058a18 Merge pull request #321 from wybren1971/patch-3
Update commodore1541.textpb
2021-08-19 13:22:10 +02:00
Wybren van Duinen
68e4d569d0 Update commodore1541.textpb
Changed to default .d64 for use with emulators
2021-08-17 15:00:56 +02:00
David Given
7186b25a69 Merge pull request #317 from davidgiven/tests
Fix a bunch of C++ incompatibilities.
2021-08-04 20:39:06 +02:00
David Given
42cacb18bd Lots of warning fixes. Can we switch to C++ 2A? 2021-08-04 20:29:04 +02:00
David Given
d09c03f4d2 Update fmt::format to version 7.1.3 (the last before module support is needed). 2021-08-04 20:14:53 +02:00
David Given
ad240b150e Merge pull request #316 from davidgiven/sectorids
Switch to using sector IDs rather than zero-index sector numbers.
2021-08-03 22:45:02 +02:00
David Given
1177ef6f8d Allow the LDBS data rate and recording mode to be specified, or (crudely)
guessed if not.
2021-08-03 22:31:33 +02:00
David Given
2ff50c0c56 Fix merge issues. 2021-08-01 20:46:48 +02:00
David Given
4f4ed1307f Merge from master. 2021-08-01 19:42:04 +02:00
David Given
6d5b7cb64e Merge pull request #315 from davidgiven/tests
Overhaul the tests.
2021-08-01 19:37:15 +02:00
David Given
00fc4e3890 Merge from master. 2021-08-01 19:26:39 +02:00
David Given
7344ee4402 Fix binary extension on Windows. 2021-08-01 19:12:16 +02:00
David Given
740eacc7ac real_*_oneof() is too new for GI's proto library. 2021-08-01 15:29:34 +02:00
David Given
8b1bcf21ee Don't run the encodedecode tests on non-Linux platforms. 2021-08-01 15:29:15 +02:00
David Given
b0a5174c0a Wire up the (available) tests. 2021-08-01 14:58:46 +02:00
David Given
b7cca1b95b Add up_to_track in the image reader/writer trackdata proto. Abstract out the
routine which fetches the trackdata for more consistent (and correct) results.
2021-08-01 13:57:35 +02:00
David Given
44c51f1246 Don't crash when given a missing sector. 2021-08-01 13:56:51 +02:00
David Given
516d43d7a8 Correctly parse extra config textpbs as textpbs, not binarypbs. 2021-08-01 13:56:31 +02:00
David Given
86d49d563e Write the correct values to the sector headers! 2021-08-01 13:30:26 +02:00
David Given
a2911a9585 Allow setting oneof message fields to the default value. 2021-07-31 14:24:49 +02:00
David Given
9a12b651f9 Remember to wire up the tids990 encoder. 2021-07-31 00:44:07 +02:00
David Given
9fa631acca Fix the Brother encoder. 2021-07-31 00:42:25 +02:00
David Given
8df7998a83 Don't use ninja -v. 2021-07-31 00:41:46 +02:00
David Given
c81c1926c0 Wire up the encode/decode tests. Hey, look, failures! 2021-07-31 00:38:13 +02:00
David Given
7ab1288424 Pretty sure the Atari formats don't need swap_sides at all. 2021-07-31 00:37:55 +02:00
David Given
3ee88adfa9 Add a simple tool for doing round-trip encode/decode tests. 2021-07-30 00:10:54 +02:00
David Given
29e8c99b4f Hopefully get the imagereader/imagewriter and IBM encoder/decoders using sector
IDs.
2021-07-29 00:02:46 +02:00
David Given
8b115f8156 Convert the IBM encoder/decoder to use lists of sector IDs rather than
sector_skew strings.
2021-07-28 21:51:12 +02:00
David Given
11c546f113 Merge pull request #313 from davidgiven/ibm
swap_sides isn't symmetrical for the encoder and the decoder
2021-07-28 20:47:46 +02:00
David Given
c087539eb7 swap_sides isn't symmetrical for the encoder and the decoder. On the encoder,
it affects which logical side is being written to which physical side. On the
decoder, it modifies the logical side bit in the sector header itself.
2021-07-28 20:33:07 +02:00
David Given
27f7cbb892 Allow .TD0 and well as .td0 extensions. 2021-07-24 00:09:56 +02:00
David Given
11ffb09b66 Attempt to fix build error on OSX. 2021-07-24 00:03:22 +02:00
David Given
114f9f522d Add a completely guessed-at HP 9121 format. Tidy up the Teledisk reader
somewhat.
2021-07-23 23:52:30 +02:00
David Given
91dc51d59a Just skip missing sectors rather than wiping the entire track. 2021-07-23 23:51:59 +02:00
David Given
11b3922b02 Remember to calculate the disk geometry! 2021-07-23 23:22:12 +02:00
David Given
05552cc3e5 Add a basic and largely untested TD0 reader. 2021-07-23 23:21:30 +02:00
David Given
3db7964509 Fix a horrifying Bytes.slice bug which was causing memory corruption. 2021-07-23 23:17:19 +02:00
David Given
2a06adcabb Merge pull request #310 from davidgiven/ibm
The 1581 format doesn't need swap_sides.
2021-07-22 00:20:42 +02:00
David Given
8cec3354ea The 1581 format doesn't need swap_sides. 2021-07-22 00:01:07 +02:00
David Given
f9df728f45 Merge pull request #309 from davidgiven/ibm
Fix swap_sides on the IBM encoder/decoder.
2021-07-21 21:12:36 +02:00
David Given
46eead34c4 Warning fix. 2021-07-21 21:00:57 +02:00
David Given
786b35fee2 Fix swap_sides on the ibm encoder/decoder to work; remember to specify it for
both the encoder and decoder.
2021-07-21 20:57:43 +02:00
David Given
5c6c609991 Merge pull request #308 from davidgiven/protobuf
Properly merge in the default settings.
2021-07-20 21:15:49 +02:00
David Given
0be8fedf26 MergeFromString() is apparently too new for the version of protobuf on GI's
Linux distro.
2021-07-20 20:42:33 +02:00
David Given
1f9aaf1ac0 Properly merge in the default settings. 2021-07-20 20:24:18 +02:00
David Given
3926de3fa1 Merge pull request #306 from davidgiven/verifier
Verify disks when writing them
2021-07-19 22:48:05 +02:00
David Given
9086f18413 Update documentation. 2021-07-19 22:39:14 +02:00
David Given
ad2576bc76 Hopefully, add support for 400kB Mac disks. 2021-07-19 22:39:07 +02:00
David Given
4523407d9d Fix the C64 encoder to work again. 2021-07-19 22:38:49 +02:00
David Given
53162ac934 Ensure that the 1200kB 5.25" IBM format uses high_density: true. 2021-07-19 22:12:50 +02:00
David Given
1dac51d4ba Don't try to verify when writing to files. 2021-07-19 21:52:16 +02:00
David Given
13deef3416 Plumb the verifier stuff through the rest of the encoders; fix it to actually
work and not crash, god I hate C++.
2021-07-19 21:46:48 +02:00
David Given
b887bebb26 First largely untested draft of a working verifier. 2021-07-19 00:13:06 +02:00
David Given
d51160babb Convert all the readables to formats. 2021-07-16 23:41:22 +02:00
David Given
44b9e7a398 Convert all the writables into formats. 2021-07-16 22:46:45 +02:00
David Given
065cd113c1 Rework to support combined format definitions, although with only the Amiga
format enabled.
2021-07-16 22:39:38 +02:00
David Given
a5f618b7e4 Merge pull request #304 from davidgiven/verifier
Massive refactor to simplify the logic.
2021-07-16 19:04:55 +01:00
David Given
a06e4e862d Remove junk files. 2021-07-16 19:56:07 +02:00
David Given
084c858446 Fix seg fault when writing non-existent sectors. 2021-07-16 19:55:55 +02:00
David Given
5d889d4d95 Remove Track, which is now obsolete. 2021-07-16 00:00:11 +02:00
David Given
f589e094b2 Remove RawRecord, which is now obsolete. 2021-07-15 23:55:29 +02:00
David Given
2eff798d74 Remove SectorSet, which is now obsolete. 2021-07-15 23:51:02 +02:00
David Given
239cecff9e Convert all the encoders and imagereaders to the new scheme. 2021-07-15 23:43:07 +02:00
David Given
fa36af454e The encoders and decoders all build now. 2021-07-15 23:13:59 +02:00
David Given
458d0f7a1b Another mahoosive refactor: move away from using protos for the disk output in
favour of pod objects, which allows STL contains. Lots of shared_ptrs
everywhere. Much simpler and more effective code. The reader works with one of
the decoders; still have to do the rest of them and the encoders.
2021-07-15 22:50:29 +02:00
David Given
f1f27ffd33 The protoified reader now builds and runs, although doesn't work properly. 2021-07-11 23:53:50 +02:00
David Given
4a2e09e8eb Merge from master. 2021-07-11 17:38:09 +02:00
David Given
4b3fada646 Archival checkin (WIP). 2021-07-11 17:36:42 +02:00
David Given
690befc98b Merge pull request #302 from davidgiven/cleanup
More refactoring and cleanup, this time of the encoders.
2021-07-11 16:34:56 +01:00
David Given
c6b3c0f9eb Remove stray flags from the Macintosh encoder. 2021-07-11 17:03:34 +02:00
David Given
b423a71b38 Refactor the encoders the same way that's been done for the decoders. 2021-07-11 16:55:18 +02:00
David Given
e226773066 Merge pull request #301 from davidgiven/cleanup
Refactor the decoders
2021-07-11 15:19:14 +01:00
David Given
98918d160a Convert the ZilogMcz decoder. 2021-07-11 16:12:34 +02:00
David Given
969298fb58 Convert the Victor9k decoder. 2021-07-11 16:10:26 +02:00
David Given
bc60f3b45a Convert the Northstar decoder. 2021-07-11 16:08:16 +02:00
David Given
f7a4785d22 Convert the Tids990 decoder. 2021-07-11 16:04:44 +02:00
David Given
1bf41cbfd7 Convert the MX decoder. 2021-07-11 16:02:41 +02:00
David Given
e9d80423ae Convert the Micropolis decoder. 2021-07-11 16:00:46 +02:00
David Given
928ffbd7af Convert the Macintosh decoder. 2021-07-11 15:58:40 +02:00
David Given
25ebad2448 Convert the IBM decoder. 2021-07-11 15:56:20 +02:00
David Given
dd951dc0ed Convert the Fb100 decoder (which I think got lost in the last big refactor). 2021-07-11 15:54:04 +02:00
David Given
aed9e44b6b Convert the Durango F85 decoder. 2021-07-11 15:50:35 +02:00
David Given
1fa2547aff Convert the C64 decoder. 2021-07-11 13:04:07 +02:00
David Given
164ceb845e Convert the Brother decoder. 2021-07-11 13:01:56 +02:00
David Given
ce463686dc Convert the Apple2 decoder. 2021-07-11 12:57:41 +02:00
David Given
92aa28cac2 Convert the AES Lanier decoder. 2021-07-11 12:55:05 +02:00
David Given
f8674230ed Add prototypes for all the decoders because it's easier to do them in one go... 2021-07-11 12:52:34 +02:00
David Given
326969e488 Clean up the Amiga decoder. 2021-07-11 12:34:40 +02:00
David Given
155e8c2a4b Merge pull request #296 from hharte/northstar_mixed_density
Northstar: Handle mixed-density disks
2021-07-09 22:47:55 +01:00
David Given
7f95f6b43f Merge pull request #299 from davidgiven/greaseweazle
Support GreaseWeazle versions above 24.
2021-07-08 10:08:59 +01:00
David Given
b2d7ba1a65 Treat all GreaseWeazle versions from 24 or above the same, as the USB protocol
has been frozen.
2021-07-08 10:50:31 +02:00
David Given
b550bbbd08 Don't link against libudev any more. 2021-07-08 10:49:57 +02:00
Howard M. Harte
cc79977ac0 Northstar: Handle mixed-density disks
North Star DOS provided an upgrade path for disks formatted as
single-density to hold double-density data without reformatting.  In
this case, the four directory blocks will be single-density but other
areas of the disk are double-density.  This cannot be accurately
represented using a .nsi file, so in these cases, we pad the sector to
512-bytes, filling with spaces.
2021-07-06 17:34:53 -07:00
David Given
4cbb75df60 Merge pull request #273 from davidgiven/amiga
Fix the Amiga encoder after the protobuf rewrite.
2021-07-06 23:05:45 +01:00
David Given
7d8926659e Merge pull request #292 from wybren1971/patch-2
Fix distorted picture
2021-07-06 14:34:18 +01:00
David Given
37ca3f74c7 Make the Amiga encoder a unicorn. 2021-07-05 23:16:37 +02:00
David Given
56cbf39d59 Finally make the Amiga encoder work properly. Do some FM/MFM/bits refactoring. 2021-07-05 23:16:03 +02:00
Wybren van Duinen
a783e7f500 fix typo 2021-07-05 17:41:37 +02:00
Wybren van Duinen
2f4ac42684 Fix distorted picture
modify available space (width and height) with subtraction of two times the border (up en down, and left and right)
2021-07-05 16:56:57 +02:00
David Given
ec689e100e Merge from trunk. 2021-07-04 23:04:02 +02:00
David Given
6f0909d992 Merge pull request #289 from davidgiven/c64
Fix writing C64 disks
2021-07-04 11:21:20 +01:00
David Given
0092dec49e Hopefully make writing C64 disks work again by fixing the logical:physical
track mapping.
2021-07-03 12:09:10 +02:00
David Given
3447689c19 Don't crash when trying to encode tracks with no data. 2021-07-03 11:38:45 +02:00
David Given
d4563fd842 Merge pull request #282 from davidgiven/osx
Fix some OSX build inelegancies.
2021-06-19 13:28:44 +02:00
David Given
d2ecec6009 Typo fixes. 2021-06-19 12:54:06 +02:00
David Given
26f26aec50 Try to fix ranlib on Linux and Windows. 2021-06-19 12:35:18 +02:00
David Given
34dfc2767f Fix some OSX build inelegancies. 2021-06-19 12:24:47 +02:00
David Given
bc3d3cabce Merge pull request #276 from davidgiven/greaseweazle
Fix GreaseWeazle support.
2021-06-04 23:53:45 +02:00
David Given
23d80b93ae Update GreaseWeazle documentation for the new version. 2021-06-04 23:42:23 +02:00
David Given
0d59d3d195 Typo fix on the Linux/OSX side of things. 2021-06-04 23:36:25 +02:00
David Given
192427cf80 Finally figure out why Windows wasn't working --- the Win32 ReadFile / ::read
call waits until _all_ bytes are read, rather than returning as soon as any
bytes are read. Fixed.
2021-06-04 22:03:44 +01:00
David Given
98e4094d70 Set up the GreaseWeazle termios properly. 2021-06-02 22:51:57 +02:00
David Given
e88b939866 Allow multiple fallback parameters using =. 2021-06-02 22:51:41 +02:00
David Given
aed5a02ee1 Remove flag references from the Amiga encoder. 2021-05-28 16:58:28 +02:00
David Given
9f1e1bb2b6 Merge pull request #271 from hharte/rpm-retry
Perform retries when calculating drive RPM.
2021-05-27 11:29:51 +02:00
David Given
15e9383d0b Merge pull request #269 from hharte/fix-doc
Add mingw-w64-i686-protobuf to list of packages
2021-05-27 11:21:43 +02:00
David Given
4ec056900c Merge pull request #270 from hharte/track-ruler
Add ruler for track numbers.
2021-05-27 11:21:24 +02:00
David Given
983feb59b0 Merge pull request #272 from hharte/fix-typo
Fix typo.
2021-05-27 11:21:00 +02:00
Howard M. Harte
8b2ce33f83 Fix typo. 2021-05-26 23:12:09 -07:00
Howard M. Harte
44b452b30b Perform retries when calculating drive RPM.
The drive RPM measurement fails about 3% of the time.  Retry up to
five times until it succeeds, and exit with an error if it doesn't.
2021-05-26 23:02:34 -07:00
Howard M. Harte
f60c9981b8 Add mingw-w64-i686-protobuf to list of packages
When building under Windows, the mingw-w64-i686-protobuf support
package is required.
2021-05-26 22:54:20 -07:00
Howard M. Harte
7c3df93580 Add ruler for track numbers. 2021-05-26 22:50:13 -07:00
David Given
601d7dfcf8 Merge pull request #268 from davidgiven/windows
Make the Windows build static again.
2021-05-26 20:37:25 +02:00
David Given
41cb53a550 We neeed update:true to get the version of the protobuf package with the static
library in it.
2021-05-26 20:23:59 +02:00
David Given
87c13ae20c Make the Windows binary static again. 2021-05-26 18:51:33 +01:00
David Given
c30482af66 Merge pull request #266 from davidgiven/hharte-northstar
Merge in hharte's Northstar changes
2021-05-25 20:25:27 +02:00
David Given
5f4f2f10d7 Update FluxEngine components and rebuild firmware. 2021-05-25 19:11:32 +01:00
David Given
ae630a0e18 Merge pull request #267 from hharte/hharte-northstar
Northstar: Fix configurations.
2021-05-25 18:55:29 +02:00
Howard M. Harte
c66931cb12 Northstar: Fix configurations.
Tested read/write with 350K, 175K, and 87K disks.
2021-05-24 20:40:17 -07:00
David Given
fb2dbe25cd Add missing file. 2021-05-24 23:33:40 +02:00
David Given
bf4831be9f Migrate the Northstar code to the new framework. 2021-05-24 23:20:59 +02:00
David Given
0f83082cf0 Merge branch 'northstar' of https://github.com/hharte/fluxengine into hharte-northstar 2021-05-24 21:39:51 +02:00
David Given
fbfde604e7 Merge pull request #264 from davidgiven/salfter-local
Overhaul profiles and merge in salfter's IBM formats
2021-05-24 12:24:21 +02:00
David Given
5e2dfbed73 Add the 9-sector IBM formats. 2021-05-24 12:10:21 +02:00
David Given
571602d3de Go back to only having a single Atari ST read profile. 2021-05-24 12:10:01 +02:00
David Given
85693f2577 Show the profile comment in the help. 2021-05-24 11:15:21 +02:00
David Given
60bc8ad888 Add comment fields to all the configs; fix some documentation. 2021-05-23 23:57:38 +02:00
David Given
9bf309eb5c Add physical mapping options. 2021-05-23 23:57:22 +02:00
David Given
eeddfa87b6 Merge branch 'local' of https://github.com/salfter/fluxengine into salfter-local 2021-05-23 21:18:58 +02:00
David Given
861fe2466c Merge pull request #263 from davidgiven/hpingel-greaseweazle_v024_compat
Update the GreaseWeazle and USB support.
2021-05-23 21:12:03 +02:00
David Given
3a2cce78ca Update incorrect reference to usb.device. 2021-05-23 20:49:34 +02:00
David Given
4e01f1de0d Update GreaseWeazle documentation. 2021-05-23 20:41:10 +02:00
David Given
4f0a5e854c Attempt to rework the USB handling to make the GreaseWeazle work on Windows and
Mac --- we abandon autodetection for anything other that FluxEngines as libusb
is painfully inconsistent with serial devices.
2021-05-23 18:53:25 +01:00
David Given
c669a9c808 Add libudev autodetect for GreaseWeazle devices. 2021-05-23 17:44:01 +02:00
David Given
e4d827256f Add test code for platform detection. 2021-05-23 12:42:49 +02:00
David Given
1c1ad2725e Port the Greaseweazle driver to use the serial port rather than raw libusb. 2021-05-23 12:39:41 +02:00
David Given
d18e54c5a3 Add Greaseweazle support for firmwares 22 and 24. 2021-05-23 12:01:29 +02:00
David Given
7a885e23d3 Merge branch 'greaseweazle_v024_compat' of https://github.com/hpingel/fluxengine into hpingel-greaseweazle_v024_compat 2021-05-23 10:50:35 +02:00
David Given
33dde3a465 Merge pull request #262 from davidgiven/wybren1971-Commodore-64-encoder
Merge in wybren1971's 1541 encoder and D64 reader.
2021-05-22 23:42:52 +02:00
David Given
8d4ac59f03 Try and fix that intermittent dependency problem with the test protos. 2021-05-22 23:22:24 +02:00
David Given
4fbfc492ce Mark the C64 format as a unicorn. 2021-05-22 20:59:54 +02:00
David Given
ac1fb71bc5 Note the limitation when writing 1541 disks. 2021-05-22 20:58:28 +02:00
David Given
01d6b37b83 Try to update the dev build with the current date. 2021-05-22 10:36:23 +02:00
David Given
2284b4dce1 Upgrade to checkout@v2. 2021-05-22 10:21:43 +02:00
David Given
b92f80e6be And try to fix it again. 2021-05-22 00:01:00 +02:00
David Given
bd5596fa30 Fix the autorelease script. 2021-05-21 23:57:39 +02:00
David Given
4614b63c30 Convert wybren1971's D64 reader and 1541 encoder to work with the new
architecture.
2021-05-21 23:34:28 +02:00
David Given
cf41b6cbb2 Merge branch 'Commodore-64-encoder' of https://github.com/wybren1971/fluxengine into wybren1971-Commodore-64-encoder 2021-05-21 23:02:04 +02:00
David Given
87f3e0e291 Merge pull request #253 from davidgiven/protobuf
Rework the configuration system to use protobufs.
2021-05-21 22:36:58 +02:00
David Given
1997abcde6 Warning fix pass. 2021-05-21 22:12:14 +02:00
David Given
8b761298ee Using a tool which wants a profile with no parameters now lists the available
profiles.
2021-05-21 21:39:00 +02:00
David Given
df0356b841 Update documentation. 2021-05-21 21:27:42 +02:00
David Given
dc158ebff4 Add a documentation page for the Eco1. 2021-05-21 21:15:51 +02:00
David Given
f8192b90f4 Add README banner about the command line change. 2021-05-21 20:42:50 +02:00
David Given
5237049e2c Fix fe-inspect to work. 2021-05-21 20:40:52 +02:00
David Given
9d70fb261e Remove some obsolete tools. 2021-05-21 20:40:42 +02:00
David Given
8370b8a743 Adjust documentation formatting. 2021-05-20 23:00:28 +02:00
David Given
25656d81a1 Update documentation for the new version. 2021-05-20 22:41:03 +02:00
David Given
be97791428 Set sane defaults for rpm and seek. Adjust the aeslanier cylinder range. 2021-05-20 22:40:51 +02:00
David Given
18b683d22e Add drive:N as a valid flux source and sink specifier; remove the --drive
option. Also, a bunch of option cleanup.
2021-05-20 22:14:22 +02:00
David Given
34e9742f71 Use .st for the default Atari ST image filenames. 2021-05-20 19:07:15 +02:00
David Given
087eb7777e Bizarrely, we need to install vim to get xxd. 2021-05-19 23:03:16 +01:00
David Given
c469c5e3a1 Set the right msystem? 2021-05-19 22:56:34 +01:00
David Given
485c29bb49 Install maybe the right packages? 2021-05-19 22:53:53 +01:00
David Given
aa0cf150bd Apparently I've been using the wrong compiler all along. 2021-05-19 22:50:29 +01:00
David Given
91c52e91cd Merge. 2021-05-19 23:02:08 +02:00
David Given
a1f4014738 Rework the ibm encoder to allow per-track configurations. Rename everything for
consistency.
2021-05-19 23:01:32 +02:00
David Given
8a136ac4f6 Allow imgimagewriter to write mixed-format disks; add an eco1 disk format for the VDS Eco1. 2021-05-19 22:12:23 +02:00
David Given
ea43b3dc6a Apply Windows compatability hack. 2021-05-18 22:46:57 +01:00
David Given
57fc787819 You can load config files now. 2021-05-18 22:44:41 +02:00
David Given
a0164b8de3 Rip out dataspecs everywhere. 2021-05-18 22:22:06 +02:00
David Given
9df35c1814 Fix a bunch of documentation strings. 2021-05-18 21:10:59 +02:00
David Given
9d2a5fee86 Range ends default to range start. 2021-05-18 21:09:31 +02:00
David Given
a6b2e932fa copy-flux-to works. 2021-05-18 20:13:03 +02:00
David Given
05aaa2634b Don't hard-code references to the global config in the
updateConfigForFilename() methods.
2021-05-18 19:57:23 +02:00
David Given
2c98f5c542 Convert cwftoflux to a proper flux source. 2021-05-18 19:37:43 +02:00
David Given
d246fca9df Convert scptoflux to a proper flux source. 2021-05-18 01:09:55 +02:00
David Given
c79feb405c Switch to resource path type thingies for specifying flux file types, where
appropriate. Much better.
2021-05-18 00:40:16 +02:00
David Given
b1145f8da3 Flux sink type is now detected from file extension. 2021-05-18 00:23:38 +02:00
David Given
3588d681a2 Convert fluxscp to a proper flux sink. 2021-05-17 23:28:30 +02:00
David Given
55d894ae1f Convert fluxtovcd to a proper flux sink. 2021-05-17 22:12:44 +02:00
David Given
0509caf432 Allow the .au writer to write multiple tracks. 2021-05-17 21:53:15 +02:00
David Given
5d1d807e78 Convert fluxtoau to a real flux sink. 2021-05-17 21:42:25 +02:00
David Given
22ba38b2e0 Add rawread. 2021-05-17 01:06:09 +02:00
Howard M. Harte
42f858267c Hard Sectors: Fix index_irq assertion.
The index_irq could trigger one sector too late, in the case where
index_irq was reset just before the sector pulse was received.

Check the duration of the time between pulses before propagating the
hardsec_index_irq_primed to index_irq.

While this change is required for reading and writing North Star
floppies (due to lack of positioning information in the sector header)
it is also helpful for reading Micropolis disks, as currently,
occasionally the first sector will be missed, and will be found on the
next rotation of the disk.  This change is required for reliably writing
Micropolis disks though, when that functionality becomes available.
2021-05-16 15:41:55 -07:00
Howard M. Harte
5b1a3173f8 Add support for North Star hard-sectored floppies.
North Star Floppy disks use 10-sector hard sectored disks with either FM
or MFM encoding.  They may be single- or double-sided.  Each of the 10
sectors contains 256 (FM) or 512 (MFM) bytes of data.  The disk has 35
cylinders, with tracks 0-34 on side 0, and tracks 35-69 on side 1.
Tracks on side 1 are numbered "backwards" in that track 35 corresponds
to cylinder 34, side 1, and track 69 corresponds to cylinder 0, side 1.

The North Star sector format does not include any head positioning
information.  As such, reads from North Star floppies need to be
synchronized with the index pulse, in order to properly identify the
sector being read.  While there is a command line option:
--sync-with-index, that does this, the North Star reader forces this
behavior by default.

MFM sectors have 32 bytes of 00's followed by two sync characters,
specified in the North Star MDS manual as 0xFBFB.

This is true for most disks; however, I found a few disks, including an
original North Star DOS/BASIC v2.2.1 DQ disk, that uses 0xFBnn, where
nn is an incrementing pattern.

While searching for the start of a sector header, seekToPattern()
ignores the sector pulses.  If a sector header cannot be decoded for any
reason, seekToPattern() will advance past one or more sector pulses.
For this reason, the _hardSectorId is calulated after the sector header
is found.

Due to the nature of the track ordering on side 1, an .nsi image reader
and writer are provided for double-sided disks.  The .nsi image format
supports both single- and double-sided disks; however, single-sided .nsi
images are equivalent to .img images.
2021-05-16 15:41:55 -07:00
David Given
97822bd9a8 analysedriveresponse works. 2021-05-16 23:01:03 +02:00
David Given
b926f5a692 Setting boolean config fields works. 2021-05-16 23:00:34 +02:00
David Given
191dfdf13f Make seek work. 2021-05-16 20:13:02 +02:00
David Given
a100f1fe1e Merge. 2021-05-16 18:23:30 +02:00
David Given
471ccf281c Allow setting cylinder and head ranges. 2021-05-16 18:11:40 +02:00
David Given
6c959be188 Remove reference to dead code. 2021-05-16 00:13:17 +01:00
David Given
010887f18f Try and make build on Windows again. 2021-05-16 00:36:32 +02:00
David Given
e4b5e4c502 Make Kryoflux streams again. 2021-05-16 00:25:41 +02:00
David Given
2de3b4f92e Make rawwrite work (which replaces erase and writeflux). 2021-05-15 23:30:58 +02:00
David Given
225a93330d Rename more config stuff. 2021-05-15 22:37:29 +02:00
David Given
41b36649a9 Rename a bunch of config fields for clarity. 2021-05-15 22:23:42 +02:00
David Given
55c26ab1c4 Make rpm work. 2021-05-15 21:48:42 +02:00
David Given
8b1e60a1fd Remove unused dataspecs. 2021-05-15 21:35:38 +02:00
David Given
ed0f38748b Add helper flags to fe-read and fe-write for setting common parameters. 2021-05-15 21:28:02 +02:00
David Given
7d75a720ca Remove dependent FlagGroups, to make sure that we can't use them by mistake. 2021-05-15 19:00:45 +02:00
David Given
5a38db166f Remove reader flags. 2021-05-15 18:56:30 +02:00
David Given
3c9fb79263 The -Wl,--no-as-needed option isn't needed any more and doesn't work on OSX. 2021-05-15 18:30:49 +02:00
David Given
98d5a2dad9 Add missing file. 2021-05-15 18:07:05 +02:00
David Given
4ab8b4984d Rename all InputProto and OutputProtos for clarity. 2021-05-15 18:05:53 +02:00
David Given
f741ad058e Convert the ZilogMcz decoder. 2021-05-15 17:40:20 +02:00
David Given
2512a4fc32 Convert the Victor9k decoder. 2021-05-15 17:29:59 +02:00
David Given
5554d5e608 Convert the TIDS990 encoder and decoder. 2021-05-15 17:24:20 +02:00
David Given
48d5ed2ff9 Convert the MX decoder. 2021-05-15 17:06:28 +02:00
David Given
2632668d0e Convert (hopefully correctly) the IBM writables. 2021-05-15 14:26:06 +02:00
David Given
f46e444aa2 Convert the Micropolis decoder. 2021-05-15 14:10:08 +02:00
David Given
1149ad86a2 Convert the Apple2 decoder. 2021-05-15 14:04:44 +02:00
David Given
e1398d98b0 Remove some stray files. 2021-05-15 13:50:13 +02:00
David Given
8133e2b7aa Convert the Atari ST configuration. 2021-05-15 13:49:31 +02:00
David Given
ebe678b18b Fix IBM sector base default; convert the ampro configuration. 2021-05-15 13:47:34 +02:00
David Given
509217606c Remove sqlite flux sink flags. 2021-05-15 13:42:30 +02:00
David Given
6fb694669c Remove the fluxmapreader flags. 2021-05-15 13:34:26 +02:00
David Given
5a63172a86 Remove the hardware source/sink flags. 2021-05-15 13:06:53 +02:00
David Given
93dcc7e242 Convert the image readers and Mac encoder and decoder. 2021-05-15 12:33:22 +02:00
David Given
243eea33e9 Convert the FB100 decoder. 2021-05-15 12:11:11 +02:00
David Given
38a8367f62 Convert the F85 decoder. 2021-05-15 12:05:31 +02:00
David Given
a02953cccc Fix the last fix. 2021-05-15 11:56:50 +02:00
David Given
f13f96967e Try to fix the dependency issue with protobufs. 2021-05-15 11:09:31 +02:00
David Given
f7c31281e0 Convert the C64 decoder to the new configuration scheme. Also convert all the
imagewriters.
2021-05-15 00:25:32 +02:00
David Given
ac34a43d9b Add the missing Brother 120kB format. 2021-05-15 00:08:49 +02:00
David Given
c8d0950979 Convert the Amiga encoder and decoder to the new system. 2021-05-15 00:06:39 +02:00
David Given
a4ff59eccb Remember to actually hook the aeslanier decoder up. 2021-05-14 23:42:01 +02:00
David Given
c3a50f9442 Add missing file... 2021-05-14 23:39:47 +02:00
David Given
57e81ee72e Convert the AES Lanier decoder to the new configuration system. 2021-05-14 23:39:07 +02:00
David Given
05df0a37b1 Add (pretty basic) config dumping and documentation help. 2021-05-14 23:29:16 +02:00
David Given
25f2c3a8c1 Move the USB flags into the config file. 2021-05-13 23:35:05 +02:00
David Given
c3aa12db78 Add support for filename flags to the flags parser. Allow setting config values
from the command line.
2021-05-13 23:16:52 +02:00
David Given
a3bd7cc644 Programmatically create the readables and writables mappings. 2021-05-13 19:39:45 +02:00
David Given
5a186b6960 Rename all protos to end with 'Proto' to avoid name conflicts. 2021-05-13 18:05:08 +02:00
David Given
639588fa68 Hardware flux sources are configurable. 2021-05-13 17:39:49 +02:00
David Given
f9510c54b2 Split the encoder/decoder configuration away from inputs and outputs. Make the
test pattern creator a flux source.
2021-05-13 17:23:06 +02:00
David Given
3a8ddf8025 The writer now works with the new config system. 2021-05-13 15:55:05 +02:00
a2801ea88c add write support for various 5.25" formats 2021-05-12 17:28:59 -07:00
David Given
c2aae7ee18 fe-read now supports multiple readable formats. 2021-05-13 00:02:54 +02:00
David Given
9d0804eff4 Convert the IBM decoder to use the new config system. 2021-05-12 23:42:38 +02:00
David Given
6ff84b3693 Bash the imagewriter stuff into working with the new config system. 2021-05-12 23:08:17 +02:00
wybren1971
b641e0282b Update disk-c64.md
wrong quotes
2021-05-12 15:32:37 +02:00
wybren1971
37a467cabc updated the Doc 2021-05-12 15:24:35 +02:00
wybren1971
e01a7110ac Added format bytes (disk ID) to header from BAM 2021-05-12 14:55:32 +02:00
wybren1971
5dad5de548 removed a testmessage 2021-05-12 10:14:12 +02:00
wybren1971
295325a20b Added some checks on sector status 2021-05-12 10:11:51 +02:00
David Given
df0a9bac96 More config machinery: the reader now reads (but can't put the resulting image
anywhere).
2021-05-12 00:26:42 +02:00
David Given
cf9cef6f87 Added the machinery for including literal protobufs in the code. 2021-05-11 22:13:10 +02:00
wybren1971
f92814b24b Added option to write d64 images back to disk 2021-05-11 19:54:50 +02:00
David Given
331b59cd1e Merge pull request #256 from davidgiven/writeflux
Fix writeflux to actually work.
2021-05-11 00:04:10 +01:00
David Given
ed6d211aff Fix writeflux to actually work. 2021-05-11 00:22:19 +02:00
David Given
a8f1469d36 Flesh out the proto config stuff some more. 2021-05-10 22:38:04 +02:00
David Given
74cb332706 The dotted path setter should now work properly. 2021-05-08 23:56:45 +02:00
David Given
89165369b1 Import Snowhouse. 2021-05-08 23:01:40 +02:00
David Given
a54e3d33a6 Add a first draft of the dotted config file setter. 2021-05-08 22:58:29 +02:00
David Given
0f17984f41 Merge pull request #227 from hharte/ibm-presets
Add presets for 5.25" IBM PC floppies.
2021-05-08 15:41:45 +01:00
wybren1971
6527ceb913 Added comment 2021-05-08 12:41:11 +02:00
wybren1971
64a57ba837 Change sectorskew IMD starts numbering with 1 2021-05-08 12:39:28 +02:00
wybren1971
74da9330f8 IMD file expect sectornum to start with 1 not 0 2021-05-08 12:23:58 +02:00
David Given
4fa1dd6860 There aren't any static protobuf libraries, so we're going to have to build
dynamic Windows executables now.
2021-05-08 10:56:15 +01:00
Howard M. Harte
e55effc9ca Add presets for 5.25" IBM PC floppies.
For reading:
    --ibm-preset-360 - Read a 5.25" 360kB disk in a 48tpi drive.
    --ibm-preset-360-96tpi - Read a 5.25" 360kB disk in a 96tpi
        drive.
    --ibm-preset-1200 - Read a 5.25" 1.2MB disk.

For writing:
    --ibm-preset-360 - Write a 5.25" 360kB disk in a 48tpi drive.
    --ibm-preset-1200 - Write a 5.25" 1.2MB disk in a 96tpi drive.

Test:
    Test reading and writing using a Teac FD-55GFR-571 96tpi drive.
    Test reading and writing using an Alps DFC222B05A 48tpi drive.

Fixes davidgiven/fluxengine#189
2021-05-07 23:20:58 -07:00
David Given
924b862f7c Adjust protobuf configuration. 2021-05-07 23:58:41 +02:00
David Given
5f077762d5 Hopefully set the right protoc compiler? 2021-05-07 23:45:43 +02:00
David Given
1b0ec50711 Add lots more protobuf machinery. 2021-05-07 23:38:31 +02:00
David Given
22f320f1c4 Merge from master. 2021-05-07 22:27:45 +02:00
David Given
9949476584 Merge pull request #252 from davidgiven/40track
Add support for possibly writing 40-track disks on an 80-track drive.
2021-05-07 22:21:02 +02:00
David Given
1b5b170557 Add support for possibly writing 40-track disks on an 80-track drive. 2021-05-07 22:01:20 +02:00
David Given
925a3a4bdb Merge pull request #251 from davidgiven/40track
Better 40-track support.
2021-05-07 21:16:19 +02:00
David Given
720fe9f95f Make sure that places which use usbSeek() honour --40-track. 2021-05-07 20:45:46 +02:00
David Given
7c4f8e1443 Added documentation on 40-track disks and drives. 2021-05-07 20:40:22 +02:00
David Given
79d24dff46 Add boilerplate for a protobuf definition. 2021-05-07 20:05:50 +02:00
wybren1971
928435a31d Added error catching to exit gracely on an error 2021-05-07 15:59:03 +02:00
wybren1971
26ac92eaa3 Standardized messages in classes 2021-05-07 13:25:25 +02:00
wybren1971
2974c08b08 Fix bug in printing sectorskew to screen 2021-05-07 13:11:43 +02:00
wybren1971
5a7b0b3050 Warning was commented out. Fixed 2021-05-07 13:03:41 +02:00
wybren1971
cc2d9bbdd1 Bugfix wrong sectorsize used 2021-05-07 12:54:54 +02:00
David Given
e912152784 Common out the high-density flag too. 2021-05-07 00:27:10 +02:00
David Given
d00681f623 Make the new 40-track flag common between source and sink. 2021-05-07 00:21:13 +02:00
wybren1971
52942c3d2a Fixed a bug with sectorskew for 1.44 diskettes 2021-05-06 17:40:57 +02:00
wybren1971
dedabdd8d8 Added possibility to write IMD images 2021-05-06 14:26:21 +02:00
wybren1971
3061499860 Small bugfixes and added a check on the sectorskew 2021-05-06 14:18:40 +02:00
wybren1971
20f18755ed added an example in the comment 2021-05-06 14:17:37 +02:00
wybren1971
333f2aba54 added a bool for FM or MFM coding 2021-05-06 14:17:05 +02:00
wybren1971
1cb673ed80 Added a bool for knowing FM or MFM use 2021-05-06 14:16:32 +02:00
wybren1971
57e0bc784a Fix bug in startsectorID 2021-05-01 09:12:38 +02:00
wybren1971
425afa93da Better Comment reading with string 2021-04-30 21:45:22 +02:00
wybren1971
259b2cebc7 Added optional cylindermap and sector head map 2021-04-30 20:33:39 +02:00
wybren1971
06589826c8 Small Tweak in gap3 value 2021-04-30 19:43:57 +02:00
wybren1971
2245cd982a Leave destination as is 2021-04-30 19:38:02 +02:00
wybren1971
065b50769f Update IMD writing based on geometry in image 2021-04-30 15:26:06 +02:00
wybren1971
d1e99852bc Merge pull request #2 from davidgiven/master
Update fork with master commits
2021-04-21 08:29:54 +02:00
David Given
bf8f6ae687 Add imagereader offset and step modes, and hardware forty-track modes. Not sure
this is the right way to do it.
2021-04-21 00:41:36 +02:00
David Given
4ad6805ea1 Change to house style. 2021-04-19 20:29:55 +02:00
David Given
4f4e3f0b89 Qualify LIF options, to allow multiple LIF formats. 2021-04-19 20:29:48 +02:00
David Given
b51f2c1ec8 Fix C++ standard issue. 2021-04-19 20:27:44 +02:00
David Given
1bec06fd75 Merge branch 'master' of https://github.com/wybren1971/fluxengine into wybren1971-master 2021-04-19 19:28:53 +02:00
David Given
451d2e2d1d Merge pull request #244 from davidgiven/visualiser
Update the visualiser to use AGG.
2021-04-19 16:56:47 +02:00
David Given
9cee12f9f4 Add index mark and sector alignment. 2021-04-19 15:31:24 +02:00
David Given
f5d6011a77 Merge from trunk. 2021-04-19 13:25:27 +02:00
David Given
64b2ff19ea Mark the new releases as not being prereleases. 2021-04-19 13:25:10 +02:00
David Given
9c17a64773 Don't fail the deletion if no assets exist. 2021-04-19 13:15:38 +02:00
David Given
69c877ff66 Adjust wording. 2021-04-19 12:58:21 +02:00
David Given
ac557ffedc Update git checkout action. 2021-04-19 12:24:04 +02:00
David Given
b1e41bc583 I think that the git tag command doesn't like newlines in the message. Fix. 2021-04-19 12:12:24 +02:00
David Given
a144395ec9 Syntax fix. 2021-04-19 11:49:07 +02:00
David Given
0cf9d05489 Try a different tag action. 2021-04-19 11:47:39 +02:00
David Given
0e6c0a41d0 Merge pull request #245 from davidgiven/github
Switch to github CI for Windows rather than Appveyor.
2021-04-19 11:30:46 +02:00
David Given
8a83652d08 Add the github autorelease script. 2021-04-19 11:24:06 +02:00
David Given
91ee72a8d6 Need to use the C++ compiler. 2021-04-19 01:29:06 +02:00
David Given
91b1c8c13c Getting there. Configure ar. 2021-04-19 01:17:20 +02:00
David Given
26effeabe6 More path tweaking. 2021-04-19 01:13:55 +02:00
David Given
611c9740ed Getting there. More path adjustment. 2021-04-19 01:08:26 +02:00
David Given
2a048c3228 Add diagnostics. 2021-04-19 01:04:19 +02:00
David Given
f4fd83d999 Adjust package list again. 2021-04-19 00:55:42 +02:00
David Given
cf68585808 Remove the appveyor script. 2021-04-19 00:52:12 +02:00
David Given
9f9e926cff Try explicitly building with the mingw compiler. 2021-04-19 00:51:49 +02:00
wybren1971
9dc0dd75fd Fix bug in ibm-preset-720 2021-04-18 22:51:10 +02:00
David Given
9f285710f8 Update msys version. 2021-04-18 22:08:35 +02:00
David Given
ee1c448327 Try to set up build correctly. 2021-04-18 22:00:14 +02:00
David Given
e85bf1713e Enable github CI for Windows. 2021-04-18 21:55:55 +02:00
David Given
7341c71277 Cleanup. 2021-04-18 21:42:10 +02:00
David Given
d579863311 Try and make build on Windows. 2021-04-18 21:41:32 +02:00
David Given
c79cfc19aa Update the visualiser documentation. 2021-04-18 21:05:22 +02:00
David Given
997a6be267 Update the visualiser to use the new bitmap library instead of emitting SVG. 2021-04-18 21:01:21 +02:00
David Given
762bb3006d Move the visualiser code into fe-analyselayout. 2021-04-18 13:09:49 +02:00
wybren1971
07f2bd8cab small fix in tracks info 2021-04-18 09:49:06 +02:00
David Given
daf83db9b3 The standalone visualiser functions. 2021-04-18 01:21:52 +02:00
wybren1971
55c6e19af4 add IMD file reader 2021-04-17 19:32:03 +02:00
wybren1971
9cadc94c5a add IMD file reader 2021-04-17 19:12:52 +02:00
wybren1971
cacdf9ef56 add IMD file reader 2021-04-17 19:06:56 +02:00
wybren1971
a3042fc6c0 typo fix 2021-04-17 18:53:11 +02:00
wybren1971
3efd492525 add IMD file reader 2021-04-17 18:52:41 +02:00
wybren1971
55a5cbc356 Merge pull request #1 from davidgiven/master
update fork
2021-04-17 17:49:52 +02:00
wybren1971
2887b024ab add HP-LIF preset 2021-04-17 17:01:49 +02:00
wybren1971
917303edb9 add IMD file reader 2021-04-17 16:55:42 +02:00
wybren1971
c712c15a30 add IMD file reader 2021-04-17 16:55:25 +02:00
wybren1971
0c541db8e0 add IMD file reader 2021-04-17 16:55:07 +02:00
wybren1971
603b1520d7 add IMD file reader 2021-04-17 16:49:41 +02:00
David Given
c7eb8ad5c8 Add CSV reader and skeleton layout analyser. 2021-04-17 00:08:06 +02:00
David Given
0b285aa7f4 Remove the visualiser integration from the reader. 2021-04-16 21:38:26 +02:00
David Given
e8665bd00c Build a cscope cross-reference (if cscope is installed). 2021-04-16 21:38:08 +02:00
David Given
fb4eaa4332 Merge pull request #241 from wybren1971/patch-1
Update fe-writeibm.cc
2021-04-13 01:14:49 +02:00
wybren1971
874a9eae76 Update fe-writeibm.cc 2021-04-12 21:39:40 +02:00
wybren1971
8230520956 Update fe-writeibm.cc 2021-04-12 20:40:23 +02:00
wybren1971
66da9675f1 Update fe-writeibm.cc
Added a HP-LIF preset to write HP-LIF floppy disks for old HP analyzers in need of LIF formatted disks with 76 tracks and sectors of 1024 bytes.
2021-04-12 20:37:19 +02:00
David Given
61ff48c005 Merge pull request #236 from erichelgeson/eric/typo
Fix minor typo
2021-02-20 22:02:46 +01:00
Eric Helgeson
5fc8a1e52a Fix minor typo 2021-02-20 14:42:02 -06:00
David Given
df1ac74069 Merge pull request #233 from davidgiven/ibm
Refactor the IBM readers and writers.
2021-02-16 23:47:50 +01:00
David Given
91f718bf38 Fold the Atari ST writer into write ibm. Update documentation. 2021-02-16 23:24:41 +01:00
David Given
46e987e393 Refactor the IBM readers so they just set flags and readibm.cc is doing all the
work.
2021-02-16 23:07:25 +01:00
David Given
a59b4f7be7 Document writing 1581 disks. 2021-02-16 21:04:40 +01:00
David Given
ca66e3c35c Merge pull request #218 from hpingel/ibm_enc_swapsides_preset_1581
Adding preset for Commodore 1581 (cbm1581) to IBM writer frontend
2021-02-16 21:01:19 +01:00
hpingel
31e2ad8cba Enforce usage of Greaseweazle firmware v0.24 after adapting CMD_READ_FLUX and CMD_WRITE_FLUX 2021-01-21 23:44:13 +01:00
hpingel
320f32895a adding preset cbm1581 to ibm writer frontend 2021-01-18 23:08:33 +01:00
David Given
d4db131d3c Merge pull request #217 from davidgiven/writes
Update sequencer to not lose one tick from every non-zero interval.
2021-01-18 11:22:02 +01:00
David Given
27c2c9045e Update sequencer to not lose one tick from every non-zero interval. 2021-01-18 00:27:00 +01:00
David Given
f97b7c7d62 Merge pull request #215 from davidgiven/ibm
Fix the timing when writing IBM 1440kB disks.
2021-01-17 19:39:30 +01:00
David Given
9eb33d31ac Merge from master. 2021-01-17 18:57:49 +01:00
David Given
6dd84d6fc2 Add swapSides parameter to the Atari ST writer. 2021-01-17 18:53:43 +01:00
David Given
daddd60581 Merge branch 'ibm_enc_swapsides' of https://github.com/hpingel/fluxengine into hpingel-ibm_enc_swapsides 2021-01-17 18:43:31 +01:00
David Given
e832723ee4 Merge pull request #203 from keithclark/atari-st-format
Write Atari ST formats
2021-01-17 18:34:40 +01:00
Keith Clark
35f4a63c0e Fix type in IBM disk documentation 2021-01-17 14:02:37 +00:00
Keith Clark
28478ea4ac Add Atari ST disk documentation 2021-01-17 14:02:37 +00:00
Keith Clark
0f93a68694 Add Atari ST disk reader front-end 2021-01-17 14:02:37 +00:00
Keith Clark
1a2d5d13b3 Add .st to list of supported extensions 2021-01-17 14:02:37 +00:00
Keith Clark
5f2894fc5b Add support for writing Atari ST floppy formats 2021-01-17 14:02:37 +00:00
hpingel
66cb39dce2 added parameter swapSides to IBM encoder 2021-01-17 12:54:14 +01:00
David Given
d44c871c54 Remove stray debug tracing. 2021-01-16 22:26:54 +01:00
David Given
dff0378ba8 Fix timings for 1440kB disks. 2021-01-16 22:26:45 +01:00
David Given
4f7b1b7140 Merge pull request #212 from davidgiven/analysis
Rework the disk response analyser to draw its own graphs
2021-01-15 10:52:44 +01:00
David Given
662514304b Merge from master. 2021-01-14 01:24:19 +01:00
David Given
0913e9e0c0 Allow saving the other image formats supported by libstb. 2021-01-13 20:34:21 +01:00
David Given
d403733627 Update graphs and documentation. 2021-01-13 20:05:13 +01:00
David Given
fae5b439d0 Fix buffer overrun bug. 2021-01-13 20:01:36 +01:00
David Given
bc66de6d85 Can't initialise variable-sized objects. 2021-01-13 10:43:16 +01:00
David Given
57e598156c Add support to analyse driveresponse for drawing its own graphs via AGG. 2021-01-13 01:46:48 +01:00
David Given
b570e44ee4 Merge pull request #211 from davidgiven/patch-1
Add missing zlib dependency.
2021-01-11 11:59:22 +01:00
David Given
b115d0b55b Add missing zlib dependency.
Fixes: #190
2021-01-11 11:57:59 +01:00
David Given
03fc1419de Archival checkin of partial work towards drawing my own graphs. 2021-01-11 00:56:09 +01:00
Eric Anderson
8b71c0d737 Add Micropolis encoder
Has not been tested on a Micropolis machine.
2021-01-10 15:18:00 -08:00
David Given
a833aa0a00 Import STB's image writer library and hook it up to AGG. 2021-01-10 16:38:49 +01:00
David Given
52332b04ac Import the AGG graphics library. 2021-01-10 14:45:32 +01:00
David Given
529488cab0 Typo fix. 2021-01-10 13:55:55 +01:00
David Given
b2429a7ca3 Typo fixes. 2021-01-10 12:34:55 +01:00
David Given
0bce12d3b4 Merge pull request #210 from davidgiven/analysis
Add the drive response documentation page.
2021-01-10 12:26:09 +01:00
David Given
75f557cb18 Add the drive response documentation page. 2021-01-10 12:15:37 +01:00
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
a11d0e75c8 Merge pull request #200 from ejona86/hard-sectors
Add firmware support for hard sectors
2021-01-08 23:20:40 +01:00
David Given
5406ff0ea3 Typo fix. 2021-01-08 23:14:51 +01:00
David Given
c88317b44a Document how to make things work on Windows. 2021-01-08 23:13:27 +01:00
David Given
6898062d66 Document Windows horror. 2021-01-08 21:37:44 +01:00
David Given
6e1f264e6a Make --sync-with-index and --revolutions work properly on the GreaseWeazle. 2021-01-08 21:36:50 +01:00
David Given
082be14232 Add GreaseWeazle support for --high-density. 2021-01-08 21:20:33 +01:00
David Given
231aa44d03 The clock compensation factor for Mac doesn't seem to be needed? 2021-01-08 01:14:39 +01:00
David Given
cdb12f35d4 Update documentation to mention the GreaseWeazle. 2021-01-07 23:03:34 +01:00
David Given
e831ee8b44 Add erase support. 2021-01-07 22:46:11 +01:00
David Given
40e9a6082f Remove that 'packed' thing. 2021-01-07 22:17:19 +01:00
David Given
53cec292d0 Refactor the GreaseWeazle converter to allow it to be tested. 2021-01-07 22:06:45 +01:00
David Given
3f85309ee5 Commit non-functioning GreaseWeazle write support. 2021-01-07 20:42:06 +01:00
David Given
70944f8521 Reading flux now correctly handles index markers. 2021-01-07 20:02:51 +01:00
David Given
2ab00c42ff Handle index pulses on read. 2021-01-07 01:05:23 +01:00
David Given
a572742caa I read my first disk using the GreaseWeazel! 2021-01-07 00:58:19 +01:00
David Given
400e5f8580 The bandwidth tester works. 2021-01-06 23:24:28 +01:00
David Given
74f0fd89b6 We can successfully seek on the GreaseWeazle. 2021-01-06 22:52:52 +01:00
David Given
09f9bea7a2 Add boilerplate for the GreaseWeazle driver. 2021-01-06 20:39:13 +01:00
Eric Anderson
d3a5bb08d3 Set drive before checking rotation speed for writing 2020-12-24 23:11:34 -08:00
Eric Anderson
f1506d0dbd Add firmware support for hard sectors 2020-12-24 17:39:52 -08:00
933 changed files with 177881 additions and 19835 deletions

View File

@@ -1,42 +0,0 @@
version: '{branch}.{build}'
clone_depth: 1
skip_tags: true
image: Visual Studio 2019
environment:
MSYSTEM: MINGW32
init:
- git config --global core.autocrlf input
install:
- set PATH=c:\msys64\mingw32\bin;c:\msys64\usr\bin;c:\msys64\bin;%PATH%
- echo %PATH%
- pacman -S --noconfirm --needed make ninja mingw-w64-i686-libusb mingw-w64-i686-sqlite3 mingw-w64-i686-zlib mingw-w64-i686-gcc zip
build_script:
- make
- zip -9 fluxengine.zip fluxengine.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
artifacts:
- path: fluxengine.zip
name: fluxengine.zip
deploy:
release: FluxEngine Windows client version $(APPVEYOR_BUILD_NUMBER)
description: >
This is an automatically built version of the FluxEngine Windows client
which is generated whenever a significant checkin has happened. It's had
no testing whatsoever.
To use, download it, put it somewhere, and then run it from a cmd window
or other command line shell.
provider: GitHub
auth_token:
secure: dfJjj7fWCoDUz+Ni11OcNPB/U3TNJFwNA2AsL++ChFjniUsZLlC6SDWHiL/t4FZo
artifact: fluxengine.zip
draft: false
prerelease: false
on:
branch: master

44
.clang-format Normal file
View File

@@ -0,0 +1,44 @@
---
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
AlignArrayOfStructures: Left
AlignEscapedNewlines: Left
AllowAllArgumentsOnNextLine: 'true'
AllowAllConstructorInitializersOnNextLine: 'false'
AllowAllParametersOfDeclarationOnNextLine: 'true'
AllowShortBlocksOnASingleLine: 'true'
AllowShortCaseLabelsOnASingleLine: 'false'
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: None
AllowShortLoopsOnASingleLine: 'false'
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: 'true'
AlwaysBreakTemplateDeclarations: 'Yes'
BinPackArguments: 'false'
BinPackParameters: 'false'
BreakBeforeBraces: Allman
BreakConstructorInitializers: 'AfterColon'
BreakInheritanceList: AfterColon
BreakStringLiterals: 'true'
ColumnLimit: '80'
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
FixNamespaceComments: 'false'
IncludeBlocks: Preserve
IndentCaseLabels: 'true'
IndentWidth: '4'
IndentWrappedFunctionNames: 'false'
KeepEmptyLinesAtTheStartOfBlocks: 'true'
NamespaceIndentation: All
PointerAlignment: Left
ReflowComments: 'true'
SortIncludes: 'false'
SortUsingDeclarations: 'true'
SpaceAfterTemplateKeyword: 'true'
SpaceBeforeAssignmentOperators: 'true'
SpaceBeforeCtorInitializerColon: 'false'
SpaceBeforeInheritanceColon: 'true'
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: 'false'
...

View File

@@ -2,40 +2,98 @@ name: C/C++ CI
on: [push]
concurrency:
group: environment-${{ github.head_ref }}
cancel-in-progress: true
jobs:
build-linux:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
fetch-depth: 1
repository: 'davidgiven/fluxengine'
path: 'fluxengine'
- uses: actions/checkout@v2
with:
repository: 'davidgiven/fluxengine-testdata'
path: 'fluxengine-testdata'
- name: apt
run: sudo apt update && sudo apt install libusb-1.0-0-dev libsqlite3-dev ninja-build
run: sudo apt update && sudo apt install libudev-dev libsqlite3-dev protobuf-compiler libwxgtk3.0-gtk3-dev libfmt-dev
- name: make
run: make
run: CXXFLAGS="-Wp,-D_GLIBCXX_ASSERTIONS" make -j2 -C fluxengine
build-macos:
build-macos-current:
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
fetch-depth: 1
repository: 'davidgiven/fluxengine'
path: 'fluxengine'
- uses: actions/checkout@v2
with:
repository: 'davidgiven/fluxengine-testdata'
path: 'fluxengine-testdata'
- name: brew
run: brew install sqlite pkg-config libusb ninja
run: brew install sqlite pkg-config libusb protobuf wxwidgets fmt make coreutils dylibbundler libjpeg
- name: make
run: make
run: gmake -j2 -C fluxengine
# build-windows:
# runs-on: windows-latest
# steps:
# - uses: numworks/setup-msys2@v1
# with:
# path-type: inherit
# - uses: actions/checkout@v1
# - name: pacman
# run: |
# msys2do pacman -S --noconfirm --needed make ninja mingw-w64-i686-libusb mingw-w64-i686-sqlite3 mingw-w64-i686-zlib mingw-w64-i686-gcc zip
# - name: build
# run: |
# msys2do make
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: ${{ github.event.repository.name }}.${{ github.sha }}
path: fluxengine.FluxEngine.pkg
build-windows:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- uses: msys2/setup-msys2@v2
with:
update: true
msystem: MINGW32
install: >-
diffutils
make
mingw-w64-i686-fmt
mingw-w64-i686-gcc
mingw-w64-i686-libusb
mingw-w64-i686-pkg-config
mingw-w64-i686-protobuf
mingw-w64-i686-sqlite3
mingw-w64-i686-wxWidgets
mingw-w64-i686-zlib
mingw-w64-i686-nsis
zip
vim
- uses: actions/checkout@v2
with:
repository: 'davidgiven/fluxengine'
path: 'fluxengine'
- uses: actions/checkout@v2
with:
repository: 'davidgiven/fluxengine-testdata'
path: 'fluxengine-testdata'
- name: build
run: make -j2 -C fluxengine
- name: nsis
run: |
cd fluxengine
strip fluxengine.exe -o fluxengine-stripped.exe
strip fluxengine-gui.exe -o fluxengine-gui-stripped.exe
makensis -v2 -nocd -dOUTFILE=fluxengine-installer.exe extras/windows-installer.nsi
- name: zip
run: |
cd fluxengine
zip -9 fluxengine-windows.zip fluxengine.exe fluxengine-gui.exe upgrade-flux-file.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex fluxengine-installer.exe
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: ${{ github.event.repository.name }}.${{ github.sha }}
path: fluxengine/fluxengine-windows.zip

123
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,123 @@
name: Autorelease
concurrency:
group: environment-${{ github.head_ref }}
cancel-in-progress: true
on:
push:
branches:
- "master"
jobs:
dev-release:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- uses: msys2/setup-msys2@v2
with:
update: true
msystem: MINGW32
install: >-
diffutils
make
mingw-w64-i686-fmt
mingw-w64-i686-gcc
mingw-w64-i686-libusb
mingw-w64-i686-pkg-config
mingw-w64-i686-protobuf
mingw-w64-i686-sqlite3
mingw-w64-i686-wxWidgets
mingw-w64-i686-zlib
mingw-w64-i686-nsis
zip
vim
- uses: actions/checkout@v3
- name: build
run: |
make -j2
- name: nsis
run: |
strip fluxengine.exe -o fluxengine-stripped.exe
strip fluxengine-gui.exe -o fluxengine-gui-stripped.exe
makensis -v2 -nocd -dOUTFILE=fluxengine-installer.exe extras/windows-installer.nsi
- name: zip
run: |
zip -9 fluxengine.zip fluxengine.exe fluxengine-gui.exe upgrade-flux-file.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
- name: date
run: |
echo "RELEASE_DATE=$(date --rfc-3339=date)" >> ${GITHUB_ENV}
- name: tag
uses: EndBug/latest-tag@latest
with:
tag-name: dev
force-branch: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: delete-old-assets
uses: mknejp/delete-release-assets@v1
with:
token: ${{ github.token }}
tag: dev
assets: |
fluxengine.zip
fluxengine-installer.exe
fail-if-no-assets: false
- name: release
uses: softprops/action-gh-release@v1
with:
name: Development build ${{ env.RELEASE_DATE }}
files: |
fluxengine.zip
fluxengine-installer.exe
tag_name: dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: brew
run: brew install sqlite pkg-config libusb protobuf wxwidgets fmt make coreutils dylibbundler libjpeg
- name: make
run: gmake
- name: tag
uses: EndBug/latest-tag@latest
with:
tag-name: dev
force-branch: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: delete-old-assets
uses: mknejp/delete-release-assets@v1
with:
token: ${{ github.token }}
tag: dev
assets: |
FluxEngine.pkg
fail-if-no-assets: false
- name: release
uses: softprops/action-gh-release@v1
with:
name: Development build ${{ env.RELEASE_DATE }}
files: |
FluxEngine.pkg
tag_name: dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

8
.gitignore vendored
View File

@@ -1,2 +1,10 @@
.obj
.project
/.ninja*
/brother120tool
/brother120tool-*
/brother240tool
/brother240tool-*
/fluxengine
/fluxengine-*
/upgrade-flux-file

View File

@@ -1,250 +1,250 @@
: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
:403CC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C4
:403D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083
:403D40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043
:403D80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003
:400000000080002011000000A1100000A1100000064A08B5136843F020031360044B1A6803F53F5302331A6001F058F8E8460040FA46004010B5054C237833B9044B13B173
:400040000448AFF300800123237010BD6081FF1F00000000F0380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF0000000021
:400080006481FF1FF0380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8081FF1FA4
:4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8081FF1F3F000080114BDA68196919B905
:4001000001221A75597514E09969521A19698A4294BF587D00201875187D094908B1002204E0086982428CBF002201224A75DA689A611B7D13B1002002F082B9704700BF34
:400140008081FF1F10B5C4B2204601F089F90128FAD110BD08B572B60F4B0F49DA680132DA60DA690132C82A08BF0022DA611A6AD8690132A72A08BF00220A621B6A002BEB
:400180000CBF02230023002814BF184643F0010002F0DAFB62B608BD8081FF1F38B50446C5B2284602F0C4F8062002F099FD44F00200C0B202F0BCF8062002F091FD284602
:4001C00002F0B6F8BDE83840062002F073BD10B5642402F0A7F828B9FFF7E0FF013CF8D1204610BD012010BD70B5C4B2054620460E4601F035F9012805D0204601F04EFAE3
:400200002846FFF79FFF204601F032F9314605460246204601F0EEF9204601F021F90028FAD1284670BD000038B5044D0024285D013402F005FD402CF9D138BDA481FF1F75
:4002400008B502F0FDF9002002F006FA02F018FA02F022FA80B208BD10B50446012002F07FF8642002F026FDFFF7EAFF2080002002F076F8642002F01DFDFFF7E1FF608047
:4002800010BD08B502F008FB002002F011FB02F023FB02F02DFB80B208BD10B50446FFF796FF322002F006FDFFF7EBFF20800120FFF774FF322002F0FDFCFFF7E2FF6080A1
:4002C00010BD0FB400B593B014AB53F8042B402102A8019302F0F4FE02A802F0BEF802F0C8F813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF723FF62787E
:400300002146BDE81040042001F002B90C3A000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8643043B1002001F0ECFF79
:40034000002002F083F8002384F8643010BD00BF8081FF1F38B5124D837895F8672004469A4204D0FFF7E4FF002385F86A302368C5F8653022790B4B1A71A378002B14BF38
:400380000220012002F062F8E078B0FA80F0400902F0D4FA2079BDE8384002F0DBBA00BF8081FF1FE581FF1F38B50D4C94F8645065B904F16500FFF7CDFF012001F0ACFF17
:4003C0004FF47A7002F076FC84F86A50E368E366012384F86430BDE8384002F0A9BC00BF8081FF1FF8B5214C0546FFF7DDFF94F86A3003B15DB91E48FFF763FFFFF7E7FE5C
:400400000120002384F86A00236702F069FC2A46216F1848FFF755FF144E0027236F9D4216D001F07FFF00B13767236F9D4205DD0120FFF7B3FE336F013305E005DA002094
:40044000FFF7ACFE336F013B336702F071FCE5E7322002F02FFC2A2DCCBF0020012002F07FFABDE8F8400448FFF72BBF8081FF1F193A0000203A00003D3A00002DE9F04F9B
:4004800099B062B602F0BEFC9F49042002F0E2FC9E4801F053FF9E4801F084FF9D4801F0B1FF02F06DF902F03FF8002001F0CEFF01F0D2FF0221002000F088FF012001F05D
:4004C00001F9954D0321084602F012FC2E462C4602F02EFC95F8643043B1EA6EEB689B1A41F28832934201D9FFF722FF00F0A6FF18B98A48FFF7E5FE04E000F0A5FF0028D3
:40050000F7D134E000F09AFF10B902F011FCF9E78348FFF7D6FE032001F0C0F88148FFF7D0FE002386F86730FFF73EFFFFF74FFE86F87400FFF7FCFE012386F86730FFF701
:4005400033FFFFF744FE86F87500FFF7F1FE96F874007549754B96F87520002A14BF0A461A46002808BF19467148FFF7AAFE032000F076FF0128ABD16E48FFF7EBFE6E49CB
:400580000320FFF731FE94F876106C48FFF799FE94F87630023B142B00F2D783DFE813F01500D5031E00D5032400D5035000D5037600D503DA00D503C201D5030903D50356
:4005C0002D03D5033403D5034E0303238DF820308DF8213011238DF822302BE394F87800FFF700FF564B22E340F2DC57FFF7DCFE00232375E068227D02F0FF012AB9EB680B
:400600001B1ABB42F7DD0B4611E083B10022174696F87810F068277594F814E0BEF1000F02D1EB681B1AF7E701329142F3DA07228DF8202004228DF82120ADF82230F9E2BF
:400640000220FFF77FFD4FF000080DF1200A02F06FFB4FF480790027C9EB0803DA1907F80A200137402FF9D10220FFF76BFD3A465146022000F04CFFB9F10109EBD108F10F
:400680000108B8F1400FE2D12E4B38E04FF0010A4FF000080DF1200B02F04AFB4FF0000959460120FFF7A0FD08EB090300270493049B1BF807203B44DBB29A4209D08DE89D
:4006C0000C0041463B464A461F48FFF7FAFD4FF0000A0137402FEBD109F10109B9F5807FDED108F10108B8F1400FD5D151461748FFF7E7FDBAF1000F00F01C81144B1B883D
:4007000007A8ADF81C3096E255010000F900000091000000C50000008081FF1F523A0000653A00006F3A00004B3A00004F3A0000823A0000E581FF1FF681FF1F9A3A000052
:40074000F4380000F6380000A93A0000C53A0000F8380000206FFFF745FE94F8780001F0FFFD94F8780001F0E3FD02F015FCB94BDFF8FC821A78002702F0FB021A701A785F
:4007800042F001021A701A7802F0FE021A701A7802F0FE021A7002F003FC0220FFF7D2FC012141F6FF734FF48042084601F0DEFD84F8B60002F02AFA08F807000137402FA7
:4007C000F8D1DFF8B0A200270AF195091FFA89F80137402F14BF3A4600221AF8010F2244062392F82420402102F044FA424646F24E419AF8000002F04FFA08F14008402FB5
:400800001FFA88F8E4D196F8793053B196F87C30336100233375237D002BFCD000233375336100234FF0FF32236062602372236894F8B600234493F8241002F09FF994F82A
:40084000B60002F05DF9012194F8B60002F030F92368002BFCD0002398467360D6F80CA0012702F065FAE368B4F87A20CAEB030393420DD367B1042195F8B60002F08AF9BC
:4008800094F8B60002F096F90028F9D107463072237AFBB96A682B689A4202D1002FE0D118E00220FFF74EFC6968402209EB8111022000F02DFE6A68674B01321340002BCA
:4008C000BEBF03F1FF3363F03F03013308F101086360C6E70220277AFFF734FC00221146022000F015FE0220FFF72CFCFFB2FFF79BFC002001F01CFD37B15848FFF7E1FC90
:400900000220FFF705FD06E0554B08A81B88ADF82030FFF7EBFC227D4146237A5148FFF7D0FC15E25048FFF7CCFCD4F87A7017F03F0701D0032009E2286FFFF753FD95F83B
:40094000780001F00DFD95F8780001F0F1FC012001F098FD02F020FB444BDFF814811A7842F004021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F0BF
:400980000FFB01214FF4804341F6FF72084601F01DFD85F8B60002F039F908F807000137402FF8D1DFF8CC90002709F195031FFA83F804930137402F14BF3A46002219F8F2
:4009C000010F2244052392F82420402102F052F9414646F2484299F8000002F05DF908F14008402F1FFA88F8E4D100274FF0FF33376098467360BB463B46D6F87A903772EA
:400A00005FEA99190CBF4FF0010A4FF0000A2168114A01310A40002ABCBF02F1FF3262F03F026068B8BF013282426FD02BB1227A002A7AD12A7D002A77D12068049A059368
:400A400002EB8010BAF1000F16D040223F2102F003FB1CE09E6400403F000080CF3A0000FA380000E93A0000FC3A000098640040A481FF1FA381FF1F014601370120FFF7BF
:400A8000B3FBC7EB0903D3F1000A4AEB030A2168B34A01310A40002ABEBF02F1FF3262F03F02013222606268059B01322ED12A683F2A2BD14FF00008C5F8048001F0B4FCE9
:400AC00085F808806B6895F8B6002B4493F8241002F054F895F8B60002F012F8012195F8B60001F0E5FF95F87E302B6185F81480237D002BFCD04FF00008012086F814800D
:400B000001F09EFC404601F0BDFC00E023B1237A5BB92B7D4BB90123626842453FF477AF0BF1010BD5F8048071E701F083FC012001F0A8FC002001F083FC042194F8B6004D
:400B400002F028F894F8B60002F034F880460028F8D196F8B60001F0C1FF337D327A0293012303920193CDF800A05B463A4649467C48FFF7A6FBC6F81080BAF1000F0BD0C6
:400B8000FFF752FB002001F0D3FB237A63B17648FFF797FB0220D9E0B945F1D073490120FFF722FB0137F7E77148FFF78AFB714B3DE094F8780001F0D3FB206FFFF712FC19
:400BC0006D48FFF77EFB94F87930236100232375237D002BFCD0012001F032FC00233375237D002BFCD0002001F02AFC002363483361FFF766FB624B19E0002084F86A0088
:400C0000FFF7F0FB5F4B12E094F8743023B195F875200AB985F8782094F875201AB113B9012385F878305848FFF794FB574B1B88ADF8203008A8FFF759FB89E0FFF778FB0D
:400C400001F01CFE002001F0BFFD2A2701F0EAFC002001F08DFC3A46002108A802F0FCF917238DF820308DF8217002F061F8002001F052FB002001F0E9FBC82002F01AF8F3
:400C80000DF12200FFF7E8FA0DF13600FFF705FB02F04EF8012001F0D9FB322002F00AF80DF12600FFF7D8FA0DF13A00FFF7F5FA012001F031FB4FF4967001F0FBFF02F041
:400CC00037F80DF12E00FFF7C7FA0DF14200FFF7E4FA002001F020FB4FF4967001F0EAFF02F026F8022001F0B1FB322001F0E2FF0DEB0700FFF7B0FA0DF13E00FFF7CDFAF4
:400D0000012001F009FB4FF4967001F0D3FF02F00FF80DF13200FFF79FFA0DF14600FFF7BCFA002001F0F8FA4FF4967001F0C2FF01F0FEFF002001F089FB002384F86A302F
:400D400001F01EFD01F0F0FB74E70120FFF7E0FA032000F0A3FC0E48FFF7B3FAFFF7B8BB3F000080063B0000363B00003892FF1F403B0000FC380000483B0000563B00000F
:400D8000FE38000000390000F681FF1F02390000633B00000F4B1A78120616D55878C0B2012814D11B79DBB2042B02D0052B05D07047094A094B5A60282203E0084A074B07
:400DC0005A60E0221A8000F043BE002070470120704700BF0060004004390000BC92FF1F2C3900002DE9F04172B6884B61221A70A3F5F06301221A801924854A9C7092E807
:400E000003008033062283F8002283E80300522203F580731A707F4B7F4A1B787F4EDBB2137040F618027E4B00251A8041F2512223F8022C33784FF4F07003F0010343EAA4
:400E4000450502F0A1F8013C05F003052ED0032DF0D1744B4FF480721A8007221A70724A002548211570917002221D705D7103F8032C0422DA716D4A6D4C13786D4E43F060
:400E80000103137012F8013C062743F0030302F8013C2378012243F0800323705B4B1A70654A137843F02003137000E0FEE707FB056300219A881868013502F0CDF8072D6A
:400EC000F5D15E485E4E002550F8041F05F1105303F14A0221F0FF074B33C9B20B4452005B0002329A4206D012F802EC12F801CC0EF807C0F5E7B0420D44E5D1514A002390
:400F000013609360136193614F4B504F1A68504BDFF888811A604F4B1A684F4B1A604F4A137843F002031370137C43F0020313742378A2F5863243F040032370413A1378DD
:400F400043F010031370464A464B07CA03C31A80454A2833106843F8250C127903F8212C424A07CA03C31A80414AE83B07CA03C31A80404A083307CA03C31A803E4A3F4B11
:400F8000A2F5616203CBC2F8100EC2F8141E1378042043F008031370394B02F5AA521B783D78DBB298F80060EDB203F007010C321B091170F6B2537045F003033B7046F095
:400FC000030388F800302F4B48221A702E4A402313702E49937013729372082382F81F3220220A7048710A72294A0A20137001F077FE284B88F8006044223D70264D1A709B
:4010000094E80F0007C52B80BDE8F08100480040680A00480F010049A146004025420040224200400440004006400040A2430040A0430040683B0000E8460040FCFFFF47E7
:401040009000004800760040700A0048F846004020760040740A00482876004003500140280A0048C0510040340A00483C0A0048480A0048540A004832510040600A004800
:40108000CF0100491D51004001590040235B0040585B004076580040B0430040F946004008B501F0ADFF03680C2B00D1FEE7FEE7084908B50B68084A1844821A802A01DC75
:4010C000086005E001F09CFF0C2303604FF0FF33184608BDCC80FF1F8893FF1F80B51148114B0025C0B1A3F1100192C922460439161BB74204D051F8046F42F8046BF7E7E8
:40110000114653F8046C8C1AA64202D041F8045BF9E701381033E5E701F078FFFFF7AEF9FEE700BF01000000FC3C0000124A134B10B51A60124A134C1368134843F4007367
:4011400013600023032B98BF54F823204FEA830188BF0E4A0133302B4250F3D10C4B1A780C4B1A700C4B084A1A60FFF73BFEBDE8104001F087BC00BF0004FA050CED00E0A4
:4011800014ED00E0000000000080FF1FA1100000BC760040C080FF1F08ED00E0F8B501F0FBFE4B4A01271378022643F001031370137C484C43F001031374474B02F5E352FA
:4011C0001F700B3203F8946C1378054603F07F031370002001F084FD2378404A03F0F90323701378384603F0DF03137023783B43237001F075FD282001F072FD384B304610
:401200001A7802F07F021A701A7802F0BF021A7023783343237001F063FD2378314A43F0040323700023137053702F4AFF2199540133092BFBD1284601F0B2FE07211720E7
:4012400001F096FD2949172001F084FD0721182001F08EFD2649182001F07CFD0721152001F086FD2349152001F074FD0721052001F07EFD2049052001F06CFD072106201F
:4012800001F076FD1D49062001F064FD0721084601F06EFD1A49072001F05CFD0721082001F066FD1749082001F054FD0021162001F05EFD1449162001F04CFD07210C2014
:4012C00001F056FDBDE8F84010490C2001F042BDA5430040944300409D60004012600040F851004084600040AD92FF1F6B1B0000A5190000691B00009D1A0000C91A0000BE
:40130000F91A0000311B0000711B0000E51B0000214B224A10B5187000231370204A40201370204A0F2413701F4A13701F4A13701F4A13701F4A13701F4B4FF400021A60B1
:401340004FF080721A604FF400121A6020221A601860802018604FF480701860174804704FF480001860164B1A70933B19B91A7802F0FE0202E01A7842F001021A70114B50
:4013800003221A70802203F8202C012001F0FCFD0D4B04221A7010BDC892FF1FCE92FF1FCC92FF1FCD92FF1FC992FF1FB892FF1FCB92FF1F4093FF1F00E100E09E6000407A
:4013C0009C600040286000401260004070B5074C054623780E461BB9FFF7E0FE0123237031462846BDE87040FFF792BF7892FF1F0A4A002313700A4A13700A4A13700A4A81
:4014000013700A4A13700A4A13700A4A13700A4B03221A70802203F8202C7047CE92FF1FCC92FF1FCD92FF1FC992FF1FB892FF1FCB92FF1F4093FF1F28600040014B187898
:40144000704700BFCD92FF1F044B1A7802F0FF001AB118780022C0B21A707047CC92FF1F024A0C2303FB002040787047D492FF1F431E072B0CD8074A064B00010344805C32
:401480005B7800F00F0043EA0020023880B2704700207047FC5F00401A4A38B50C2303FB00231B79090C13F0800F00F1FF35044619BF8AB24FF480438BB24FF48042032DA1
:4014C00018D8DFE805F002070C110021084601F0A1FA0DE00021084601F080FA08E00021084601F05FFA03E00021084601F03EFA054B1855EDB2072D03D801F087FB034B10
:40150000185538BDD492FF1FA492FF1FAD92FF1F431E072B2DE9F0470446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB2C3F500739D424FF00C0303FB007388BF8A
:40154000D588DB7884BFC5F50075ADB2254A43EA15230601B354B244EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FB047200215170B9F1000F28D03DB31B4FEB
:40158000385D01F0ABFA11232946FE2218F8040001F070FB06F5C04278321FFA89F118F8040001F079FB124D18F80410385D01F0E5FA0121385D01F07BFA735D43F002030D
:4015C0007355735D03F0FD037355BDE8F08703FB04746379DBB28AF80230BDE8F08700BFD492FF1FFC5F0040AD92FF1FA492FF1F706000402DE9F047044615468846002945
:4016000040D0431E072B3FD8FFF732FFA84203D22046FFF72DFF05461D4E335DFF2B03D141462046FFF738FFDFF868A027011AF8040001F053FA1223FE222946305D01F087
:4016400019FB07F5C0411FFA88F27831305D01F023FBDFF84490315D1AF8040001F08EFA01211AF8040001F023FA17F8093043F0020307F8093017F8093003F0FD0307F881
:40168000093002E00D4600E000252846BDE8F087AD92FF1FA492FF1F70600040431E072B0AD8064A0C2303FB002300225A705A79034BD2B200011A54704700BFD492FF1F5D
:4016C000FE5F0040431E072B9FBF024B000108221A547047FE5F004030B51A4A1A491B4D0878138803449BB21380194A00231488D8B2A4B27CB1082B0CD050680078C0B2EC
:40170000E85450680133013050601088013880B21080ECE718460B780E4C082B0E4A00D040B10E4D2B7883F080032B700F232370022301E0022323701370094B18700870CA
:4017400030BD00BF4493FF1F4093FF1F00600040BC92FF1FB992FF1FCE92FF1FCA92FF1F4193FF1F074B02221A70074B80221A70064B0F221A70064A00231370054A012088
:4017800013707047CE92FF1FCA92FF1FB992FF1F4093FF1F4193FF1F30B5164B16491B780A8803F00F03023BDBB21A4492B20A80124C134A0020118889B279B173B155682C
:4017C000215C013BC9B229705168DBB20131516011880130013989B21180ECE7094A1370094A137883F080031370084B0B221A7030BD00BF296000404493FF1F006000400F
:40180000BC92FF1F4193FF1FCA92FF1FB992FF1F064A06231370064A01201370054B80221A70054B00221A70704700BFCE92FF1FB992FF1FCA92FF1F4193FF1F054B9A68E4
:401840003AB19A68044910709A680988518000229A607047BC92FF1F4493FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A137008BD0220FFF7E1FF0D4B1B7803F0CE
:401880006003202B05D0402B06D043B900F012FC04E001F089FB01E0FFF77CFA10B9034B03221A7008BD00BF28600040B992FF1F0060004008B5084A084B012019781388FA
:4018C0000B449BB21380064B00221A70FFF7B6FF044B03221A7008BD4493FF1F4093FF1FCE92FF1FB992FF1F08B50C4B1B78DBB2042B07D0062B09D0022B0DD1BDE8084045
:40190000FFF7D8BFBDE80840FFF746BF0320FFF795FF034B03221A7008BD00BFCE92FF1FB992FF1F08B5054B002201201A70FFF785FF034B03221A7008BD00BFCE92FF1FCA
:40194000B992FF1F08B50A4B1A7832B11A78094942F080020A7000221A70074B002201201A70FFF76BFF054B03221A7008BD00BFB892FF1F08600040CE92FF1FB992FF1FC0
:40198000074B1B78DBB2042B05D0062B05D0022B05D1FFF7A1BEFFF7C5BFFFF7D3BF7047CE92FF1F38B51D4C2378DBB2DD0634D518060AD503F00F03012B2ED1FFF74EFF42
:4019C000174B1B78190609D538BD5A0602D5FFF7D7FF03E09D0620D5FFF786FF23781B061BD4104B1A78104B1B7813430F4A13701278934211D10A4A0849154613782078EB
:401A0000DBB2000605D41378DBB20B700B7803F00F0328788342F1D138BD38BD28600040B992FF1FCA92FF1F4193FF1F29600040054A00231380054A916819B191680B701D
:401A400092685380704700BF4493FF1FBC92FF1F0E4808B503889BB213B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FF094AD1799379028843EA012392B2934229
:401A800038BF0380FFF728FE012008BDBC92FF1FCE92FF1FCA92FF1F00600040084B01221A700F3B9B7C074B1A7B02F00302012A1EBFDA7B82F08002DA7301225A73704722
:401AC0000B600040D492FF1F094B02221A700F3B93F82230074B1A7E02F00302012A1EBFDA7E82F08002DA7601225A76704700BF0B600040D492FF1F0B4B04221A700F3B21
:401B000093F83230094B93F8242002F00302012A1EBF93F8272082F0800283F82720012283F82520704700BF0B600040D492FF1F0B4B08221A700F3B93F84230094B93F856
:401B4000302002F00302012A1EBF93F8332082F0800283F83320012283F83120704700BF0B600040D492FF1F7047FFF741BC0000F0B5184B184E19780C27C9B201234FF028
:401B8000000C31B3CA0720D5144A4FEA031E7244947850782040C5070DD507FB03652C79240608D5147804F0FE0414706D790C4CEDB204F80E50840706D507FB036425795F
:401BC0002D0658BF84F801C090700133DBB24908D7E7F0BD9F600040D492FF1F70600040FE5F004000F032BF70B50446184B88B003AA03F11006154618685968083303C530
:401C0000B3422A46F7D11B782B70FCB12223237001AD03232846637001F024F9002220461146AB5C08AC04EB131414F8144C03F00F03847008AC234413F8143C0132082AB1
:401C4000C1700371417100F10400EAD108B070BD923B00002DE9F0431C4D01222E460C201F274FF0800E4FF0080C194B00FB02581401234418705F70164998F8059021445B
:401C8000B9F1000F07D098F8044024064CBF887081F802C001E081F802E000FB0261CC880132E4B29C71CC88092AC4F30724DC71CC88E4B21C71C988C1F307215971D4D1CB
:401CC000054BFF221A70BDE8F08300BFD492FF1F70600040FC5F00400A600040064B074A1B7802EBC30253681A7C824286BF03EBC003586900207047C892FF1FB83B000044
:401D00002DE9F84F424B1A78002A7ED01878414D0138C0B2FFF7E2FFA8463F4AC3681478007ADFF800C1E4B203EBC0000C2600274FF0010E834268D01A78A24263D11CF829
:401D40000420597891425ED19A7893F8039002F07F0206FB02FA05EB0A01CF7093F802B009F0030981F804B093F803B005F80AB0B3F804A0A1F808A093F902A0BAF1000FB6
:401D80000BDAB9F1010F0CBF4FF007094FF00D0981F8059081F801E009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEA02191A4906FB0282494481F802E0B2F806
:401DC00008A0CAF3072A81F800A0B2F808A05FFA8AFA81F801A0B2F806A011495FFA8AFA494481F806A0B2F80690C9F3072981F80790B2F806905FFA89F981F80490D288FA
:401E0000C2F307224A71083394E7BDE8F88F00BFCD92FF1FD492FF1FC992FF1FFC5F004070600040BA92FF1F08B5064B18780138C0B2FFF753FF20B143681B7900EBC3008A
:401E4000406908BDCD92FF1F00212DE9F84F0B464E4E0C2707FB01F401313219092933554FF000059370494CD3701381937253705371EFD118B1464B1D70464B1D70464B16
:401E80001A78002A7FD0187801250138C0B2FFF725FFA8464368DFF8F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010202F0FE021A70027AD20007FB0541C368D0
:401EC00003EB02094B4531D093F802A00AF07F06AE4229D10E89B3F804B0B6B25E4538BFA1F808B01E7893F801B01EF80660B3451AD181F804A0DE780E7093F902A0DE78D3
:401F0000BAF1000F06F0030607DA012E0CBF07260D264E7181F8018006E0012E0CBF052609264E7181F801C00833CBE70135092DC3D1C1680A328B1C0A440C20083393423E
:401F400009D013F8081C13F80A5C01F07F0100FB01418D72F2E7FFF767FF114B0121186000230C2000FB0142D3801289013113449BB203F00102134409299BB2F2D1BDE88B
:401F8000F84FFFF767BEBDE8F88F00BFD492FF1FBA92FF1F4293FF1FCD92FF1FCB92FF1FD092FF1F114B1B7903F07F035A1E072A19D80F490C2202FB031291781B0141F08E
:401FC000010191700021D170517841F002015170127912F0800F074A1A4414BF8D2389239370FFF715BC0020704700BF00600040D492FF1FFC5F004030B4194B1A7902F0D8
:402000007F02531E072B27D8164B0C2404FB02339978154D01F0FE0199700021D97029461201505D114400F07F0050555A7802F0FD025A701A795B78120605D5012B01D167
:402040008C7006E00D2303E0012B0CBF082309238B7030BCFFF7DCBB002030BC704700BF00600040D492FF1FFC5F004010B50D4B0D4C21791878C9B20138C0B2FFF72EFE80
:4020800043681B798B4201D2012909D8074A0848535CDBB24354A3780120DBB2535410BD002010BDCD92FF1F00600040BA92FF1F4293FF1F38B5874A874C13780021DBB254
:4020C00021801806517840F188800A2900F20081DFE811F05800FE00FE00FE00FE00FE000B00FE007900FE007D00D3787949012B09D17A4B1A787A4B03EBC2035B685B68FE
:402100006360122310E0CB78022B12D18878FFF7E5FD002800F0DC80436863606368DA7863689B7843EA02232380BDE83840FFF78FBCCB78032B21D16A4B00228878D5B29B
:40214000854203D3634A92783AB910E0187801320028F7D018780344F0E75E4A62499278097C914203D16148FFF73EFD5F4B1A78002A00F0AD801A78228018E0BDE8384099
:4021800000F012BF13F0030313D0022B40F0A0802380504B0C211B7903F07F02544B01FB02339A78534BD2B21A7000225A706360BBE702222280504A11784E4AC9B2117026
:4021C00053706260B1E7012323804C4BEFE70123238013794A4A1344E9E701390A2977D8DFE801F037764F76067676760A7620009378444ADBB25AE0937803F0FF0153B928
:402200003E4B1A7891425FD019703F4B01201870FFF71AFE58E0481EC0B2FFF75FFD0028EED155E0FFF722FF002851D0294A374913791279DBB2D2B20A70354A3049D25C1E
:40224000CB5C9A4240D0304B01221A70FFF758FD3AE003F00303012B2BD009D3022B37D11C4B9B78002B33D1BDE83840FFF7C4BE184B9B78012B2BD11F4A137803F0FD03CA
:4022800015E003F00303012B13D008D3022B1FD1104B9B78E3B9BDE83840FFF783BE0D4B9B78012B14D1144A137843F0020313700AE0084B1A795AB998781B791549DBB239
:4022C000CA5C22EA0002CA54BDE83840FFF7A0BA002038BD00600040BC92FF1FC892FF1FB83B00001C3C00008F3C00006093FF1FD492FF1F7992FF1FCB92FF1FCD92FF1FBA
:40230000BA92FF1FB892FF1FCC92FF1FC992FF1F4293FF1FCF92FF1F014B1870704700BF72640040014B1878704700BF68650040014B1870704700BF7A650040064A0123C8
:40234000136002F688321268E0211064034A1170A2F540721360704780E100E000E400E0014B1870704700BF7A64004073B515461E460B4C04230022019200920A46014658
:402380001846237000F022FC32462946207800F0DDFB0221207800F0C7FB207802B070BDD080FF1F074A0223136002F688321268E0215064044A11706FF440710A44136017
:4023C000704700BF80E100E001E400E073B515461E460B4C05230022019200920A4601461846237000F0F2FB32462946207800F0ADFB0221207800F097FB207802B070BD84
:40240000D180FF1F064A0423136002F688321268E0219064034A1170A2F202321360704780E100E002E400E0014B04221A60704700E100E0014B04221A60704780E100E013
:40244000014B1870704700BF7B650040014B1870704700BF73640040704738B505460078012428B100F038FD285D0134E4B2F8E738BD08B50D2000F02FFDBDE808400A20C6
:4024800000F02ABD014B1870704700BF7865004010B500F081FD204A044613780A2043F002031370137C43F00203137412F80A3C43F0010302F80A3C937943F0010393712B
:4024C00002F5AB52137843F003031370134B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222183B1A70094A137843F0AA
:402500000803137000F0ECFB064B10222046BDE810401A6000F044BDAB4300400E5900402F5B004080E200E008B500F035FD0F4A137803F0FE031370A2F5AA521D3A13785D
:4025400003F0FD031370137C03F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE039371BDE8084000F01BBD00BF08590040044A137803F03F0343EA8010C0B2107082
:40258000704700BF08590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2E4
:4025C000883103F6C41393FBF1F305490B60054B1A8070470A590040A43B00004A93FF1F4C93FF1F5093FF1F08B5102000F036FA0721042000F0BCFB0749042000F0AAFB02
:40260000064A0C20137843F006031370FFF7BCFF034B00221A8008BDE1260000095900404893FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF72ABF7B92FF1FA6
:40264000044B1A7802F0FB021A701A7842F001021A7070470859004010B5084B1C7814F0010403D10028F9D0002404E0204600F037FB024B1B78204610BD00BF09590040D9
:40268000034A044B1B881088181A00B2704700BF5093FF1FA25B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F20223F1
:4026C00091FBF3F30028D8BF5B42134493FBF1F000B270474A93FF1F4C93FF1F4893FF1F7047000010B500F057FC214A044613780A2043F001031370137C43F001031374BC
:4027000012F80A3C43F0020302F80A3C937943F00203937102F5AA521832137843F003031370144B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CBE
:40274000CE2203F8062CA3F597530222123B1A70094A137843F00803137000F0C1FA074B08222046BDE810401A6000F019BC00BFAB43004006590040275B004080E200E0CF
:4027800008B500F009FC0F4A137803F0FE031370A2F5AA52153A137803F0FE031370137C03F0FE03137412F80A3C03F0FD0302F80A3C937903F0FD039371BDE8084000F0BB
:4027C000EFBB00BF00590040044A137803F03F0343EA8010C0B21070704700BF00590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF095
:40280000000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A80704702590040AE3B00005693FF1F5C93FF1F5493FF1FFC
:4028400008B5102000F014F90721032000F090FA0749032000F07EFA064A0C20137843F006031370FFF7BCFF034B00221A8008BD39290000015900405893FF1F10B5054C6D
:4028800023781BB9FFF7DCFF01232370BDE81040FFF728BF7C92FF1F044B1A7802F0FB021A701A7842F001021A7070470059004010B5084B1C7814F0010403D10028F9D0AE
:4028C000002404E0204600F00BFA024B1B78204610BD00BF01590040034A044B1B881088181A00B2704700BF5493FF1FA05B00400E4A13881BB223B111880A2309B25943E7
:4029000001E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475693FF1F5C93FF1F5893FF1F70470000014B1870E9
:40294000704700BF7B640040014B1870704700BF7F640040014B1870704700BF7C640040014B1870704700BF7E640040F7B516461F460B4C00230325019300930A460146B2
:402980002846257000F022F93A463146207800F0DDF80221207800F0C7F8207803B0F0BDE080FF1FF7B516461F460B4C00230225019300930A4601462846257000F006F917
:4029C0003A463146207800F0C1F82946207800F0ABF8207803B0F0BDE180FF1FF7B516461F460B4C00230125019300930A4601462846257000F0EAF83A463146207800F06F
:402A0000A5F80221207800F08FF8207803B0F0BDE280FF1F73B515461E460B4C0023019300930A4601461846237000F0CFF832462946207800F08AF80221207800F074F880
:402A4000207802B070BD00BFE380FF1F024B1878C0F38010704700BF8F450040034A00F0F800137803431370704700BF02410040034A00F0F800137803431370704700BF74
:402A800006410040074A7F23802113705170064A013BDBB202F80839002BF9D1034A1370704700BFE480FF1FF87B00400078004017280FD8084B0001C25C11B142F020028D
:402AC00001E002F0DF02C254C25C42F00102C25400207047012070471070004017280BD8064B0001C25C02F0FE02C254C25C02F0DF02C25400207047012070471070004024
:402B000017280DD8074900010B4603441A7942F004021A71435C43F00103435400207047012070471070004017280BD8064A0001835C490003F0F10301F00E0119438154A3
:402B400000207047012070471070004041F6FF73994208BF4FF400519A4208BF4FF4005217289FBFC00000F1804000F5EC4081809ABFC280002001207047000017289FBF6F
:402B8000034B00011954002088BF0120704700BF1970004017289FBF054B00011A5C01F007019DBF1143195400200120704700BF1470004017289FBF034B0001185C00F04D
:402BC000070088BFFF20704714700040172810B51AD8C00001F07F0100F1804441EAC21204F5EC44D2B222709DF8082003F00F0343EA0213DBB263709DF80C30002003F08B
:402C00000F03A370E07010BD012010BD10B500F0C3F90A4A5378182B0AD91478013B5370E30003F1804303F5F0431B78137000E0FF2400F0B5F9204610BD00BFE480FF1F33
:402C4000030610B5044611D400F0A6F9084AE300117803F1804303F5F04319705378147001335370BDE8104000F09AB910BD00BFE480FF1F30B504060CD411F4704509D1B0
:402C8000C40004F1804404F5F0442180A270E370284630BD012030BD03065FBFC00000F1804000F5F04081805ABFC280002001207047000038B50446084DB4F5004F05D988
:402CC000286800F061F9A4F50044F6E7034B58686043BDE8384000F057B900BFEC80FF1F024B1B7A584300F04FB900BFEC80FF1F0E4B00F003001A78490102F0FC02104300
:402D000018701A7801F0600142F080021A701A7802F07F021A701A7802F09F020A431A701A7842F010021A70704700BF83430040014B01221A70704784430040044B00F08C
:402D40000F021B6853F8220043F82210704700BF08ED00E0054A00F01F00126800F1100352F8230042F82310704700BF08ED00E000F01F0000F16040490100F56440C9B29B
:402D8000017070470F4B10B50F4900240F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040313600A4B4FF47A72DB68B3FBF2F3084A1360084B4FF400421C60C3F883
:402DC000E82010BD8092FF1F312E000010E000E0EC80FF1F14E000E018E000E0024A136843F002031360704710E000E008B5FFF7F5FF034A136843F00103136008BD00BFD3
:402E000010E000E010B5054CA3691BB9FFF7BAFF0123A361BDE81040FFF7E8BF8092FF1F024B1868C0F30040704700BF10E000E038B5FFF7F5FF012808D1054D002455F891
:402E4000243003B198470134052CF8D138BD00BF8492FF1F024B03EB80035868596070478092FF1F134B144A1B78DBB20360127843EA0223114A0360127843EA0243104A07
:402E80000360127843EA026303600E4B0E4A1B78DBB24360127843EA02230C4A4360127843EA02430A4A4360127843EA02634360704700BF0301004904010049EC460040B2
:402EC000020100490101004900010049050100490601004900000000FEB5494652465B460EB40746244909688A46244A12682448022100F071F8030020480068C018204936
:402F000000F06AF8143883460121C9430C460125002600F041F8814651460B7823400B705846013000F030F83800F04028400B78234003430B70584600F026F80136072E00
:402F4000F2D9002001300138013001200B78234003430B705846043000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF30080D480FF1FF880FF1F6B
:402F800000C20100000000000230800803D000BF01380046FCD17047EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D113790C2103F07F021C
:402FC000044B01FB02339B7A00E013790020704700600040D492FF1F002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB11217047704700BFA1
:40300000014B1868704700BF5C81FF1F0E4B70B51E460E4C0025E41AA410A54204D056F8253098470135F8E700F04EFE084B094C1E46E41AA4100025A54204D056F8253071
:4030400098470135F8E770BDD43C0000D43C0000D43C0000DC3C000003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B604FF0FF301DE04FF432
:403080000273ADF80C300CBF234604F1FF33029305934FF6FF7300910491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B00221A701BB030BD000022
:4030C00007B5009313460A46014603480068FFF7CBFF03B05DF804FB5C81FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD02568096902236F1A656977
:4031000005EB450595FBF3F57B1C43449D4238BF1D4653050FD5294600F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A4600F098FB064670B9E0
:403140002169504600F0E8FA0C23CAF80030A3894FF0FF3043F04003A381BDE8F08726613E44266046466561ED1BA560464528BF464649463246206800F0B3FAA3680020A9
:403180009B1BA36023681E442660BDE8F08700002DE9F04F9DB003938B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F80030CDE040236B6150
:4031C0000023099320238DF82930DFF89CB130238DF82A3037463C4614F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF771FF013000F0A780FE
:40320000099B4B4409933B78002B00F0A08000234FF0FF3204930793059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46801A06FA00F0184379
:403240000490EFE7D90644BF20228DF853201A0744BF2B228DF8532022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F00200079204900BE027468C
:403280003B780134303B092B03D800FB02320121F5E701B107923B782E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E0002319460593781CA7
:4032C0000A2407463A780130303A092A03D804FB01210123F5E703B1059103223978224800F0E6F940B14023CBEB000003FA00F0049B013718430490397806221B487E1CEB
:403300008DF8281000F0D4F988B1194B33B9039B073323F007030833039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A9404600F093F8B0F12A
:40334000FF3F824603D0099B5344099342E7AB895B0601D4099801E04FF0FF301DB0BDE8F08F00BFA33C0000A93C0000AD3C000000000000DD3000002DE9F04791461F4627
:403380000A698B6806469342B8BF1346C9F8003091F843200C46DDF8208012B10133C9F800302368990642BFD9F800300233C9F80030256815F0060510D104F1190A07E05B
:4033C0000123524639463046C04701301AD00135E368D9F800209B1A9D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F845102244023382F884
:40340000431003E04FF0FF30BDE8F08704F1430239463046C0470130F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500259342C4BF9B1AED1843
:403440004FF000091A344D4509D00123224639463046C0470130D5D009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9B01F1430E00F0AE8054
:4034800011D8632A22D009D8002A00F0BB80582A40F0CA8081F84520834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B8800A6842F020020A6091
:4034C0003EE0752A24D0782A3AD0ADE01A6801F14205111D1960136884F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F10401196002D0B2F9003000E045
:403500001368002B3CDA2D225B4284F8432037E021681A6811F0800F02D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A14BF0A2208221BE068
:4035400078225A4984F845202268186812F0800F00F104051D6003D1550601D5038800E00368D00744BF42F0200222601BB9226822F0200222601022002084F8430001E08A
:4035800049490A226568002DA56008DB206820F0040020602BB9002D7DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D1082A0BD12368DA0737
:4035C00008D5236962689A42DEBF302305F8013C05F1FF35C5EB0E0323612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F104001860136801D0198064
:4036000000E0196000232361754616E01A68111D1960156800216268284600F049F808B1401B6060636804E004F1420584F8422001232361002384F84330CDF800803B468C
:4036400003AA21463046FFF797FE013002D14FF0FF3026E023692A4639463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046C0470130E7D00135CC
:40368000E368039A9B1A9D42F2DBE068039B9842B8BF184605E00B7804F1420584F842308AE705B0BDE8F083923B0000B43C000010B5C9B202449042034605D01C78013064
:4036C0008C42F8D1184610BD002010BD10B5431E0A44914204D011F8014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21AD34204D013F8014DA2
:4037000001F8014DF8E710BD994204D011F8014B02F8014FF8E710BD38B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B96360146030E0A342E7
:403740000DD92268A018834201BF18685B681218226063600C6023E0A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D11068526801441960EE
:403780005A600DE002D90C232B6009E021686018824201BF106852680918216062605C602846BDE8384000F098B838BDA092FF1F70B5CD1C25F0030508350C2D38BF0C2534
:4037C000002D064601DBA94202D90C23336046E000F082F8234B1C681A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B681360636018BF0C4695
:4038000015E00C464968E9E7174C23681BB9304600F052F820602946304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B00231D20F00700C31A48
:403840000ED05A42E25070BD211A304600F034F80130EBD10C233360304600F03EF8002070BD00BFA092FF1F9C92FF1FF8B5074615460E4621B91146BDE8F840FFF798BFAB
:403880001AB9FFF749FF2846F8BD00F027F885420ED929463846FFF78BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD38B5064C002305467D
:4038C00008462360FDF7F4FB431C02D1236803B12B6038BD8493FF1F7047704751F8040C0028BEBF091851F8043CC0180438704700000000050209020B020D020F021102BB
:403900001302150228000000000104000100000000000000000157494E55534200003030303031000000000000000000E0000000000105000100D6000000070000002A0075
:4039400044006500760069006300650049006E0074006500720066006100630065004700550049004400730000009E0000007B00330064003200370035006300660065004E
:403980002D0035003400330035002D0034006400640035002D0061006300630061002D003900660062003900390035006500320066003600330038007D0000007B00330058
:4039C00064003200370035006300660065002D0035003400330035002D0034006400640035002D0061006300630061002D00390066006200390039003500650032006600B4
:403A00003600330038007D00000000007265706C792030782530327800686F6D696E6700626567696E6E696E67207365656B2066726F6D20256420746F2025640066696E14
:403A40006973686564207365656B00796573006E6F0057616974696E6720666F72205553422E2E2E00555342207265616479005363616E6E696E67206472697665732E2E1C
:403A80002E00647269766520303A20257320647269766520313A20257300636F6D6D616E6420307825303278006661696C2025642B25642B2564203D3D2025642C206E6F40
:403AC00074202564007061737365643D256400756E64657272756E206166746572202564207061636B65747300636F756E743D256420693D256420643D256400636D645FEF
:403B0000777269746500703D25642063723D25642063773D256420663D256420773D256420696E6465783D256420756E64657272756E3D256400756E64657272756E210015
:403B4000737563636573730073746172742065726173696E670073746F702065726173696E670069646C650000510040100040510040300000000140001000140140000858
:403B800000400140000A004C01400002005001402000303132333435363738394142434445460000000100000004000000100001000000040000001001000000C03B000072
:403BC000010000008F3C0000000000000000000001000000D83B000001000000613C000004000000FA3B0000000000000000000000000000F83B0000FF0000000102400099
:403C0000FF00000082024000FF00000003034000FF00000084034000FF00020304030904160346006C007500780045006E00670069006E0065002A0343006F0077006C00CE
:403C4000610072006B00200054006500630068006E006F006C006F0067006900650073000009022E0001010080320904000004FF00000107050102400000070582024000E5
:403C8000000705030340000A0705840340000A12010002FF0001080912006E0100020180014300232D302B2000686C4C00656667454647003031323334353637383961629E
:403CC0006364656600000000F8B500BFF8BC08BC9E467047590000002D110000F8B500BFF8BC08BC9E46704735000000003D0000C880FF1F980000002812000000000000F3
:403D0000000000008893FF1FFFFF0000675000400C00000007000000FFFFFFFF7F8000003F0000000000007D00FA0000400000000090D003000000000000000000000000EE
:403D40000000000000000000000000000000000000000000A13C00000000000000000000000000000000000000000000000000000000000000000000000000000000000066
:403D80000000000000000000000000000000000000000000FC80FF1F0000000000000000000000000000000000000000000000000000000000000000000000000000000069
:403DC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C3
:403E00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082
:403E40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042
@@ -4098,48 +4098,48 @@
:40FF80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041
:40FFC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
:0200000480007A
:400000000145004009520040015B0040016500400101014003030140010501400307014052080140500901404F0A0140480B0140490C0140470D0140400E0140360F01409E
:4000400002150140031701405C18014057190140471A0140531B01400B400140124101400B4201400D43014002440140074501400B4601400C470140104801401149014069
:40008000154C01400D4D014005500140045101407E02080209411080110218041902600C61157C402721290AE204E601EA20EE02E202E608EA10EE04000801080404050568
:4000C0000708080809080C400D080E1011011201130E143A16401708180819081C021E042305240825042701280629082A782C202D012E402F02307F310F327F330F380AE4
:400100003B0A580459045B045C995F018203830187018A108C018D018E248F08907C9282930E961097019A809D019E1C9F04A101A210A302A510A610AA10AB01AC01AE48FD
:40014000B01FB10FB4F0B510BA20BB02BF10D608D80BD90BDB04DC99DD90DF01012202010488051007800A860B100C020DA00E2011011208134414021504162017201804BE
:4001800019021A041B011CA01E202008220126012790282029202A012C022E282F4032493514360139013B443C803D2A5980614069906F0278417B0288049141928093D450
:4001C00094249602970A982299249A019B409C019D019EC0A188A208A402A504A709B404B502B604C0FBC2FFC4EFCAF7CCEBCEFBD608D808DE01E608EA10EE0400FC0108A1
:4002000002020310071408200AD00BE40CE10D100E080F8212031314161017141D1F1E101F4020802110223C2321261028402A302B142CE12E04301F310F33F034E03B08A2
:40024000580B590B5B045C995F0180368209852A86128740882D89098A128B148E098F209080924093C09440968098079C249D089F13A03FA180A320A840A908ADEAB0076D
:40028000B1E0B280B301B438B518B640B706BE44D808D904DB04DC99DF0100820120040A07050A420B100EA80F01110112091304171219201A401D8A1E081F4020602101B0
:4002C000231025102652275029202A012C022F483249364239033B543D283E806102620169406C0C6F0178017F0187058C028D04900292809345946495029642971298206E
:4003000099049B069C019D219E449F01A041A208A418A701B040B204B7C0C0FBC2FDC45FCAB5CC9BCE7FDE11E40800020104040806140A010C1C101012081301140215050D
:400340001601170220032B022D062E022F0130043107320336183A083F015608580459045B045C095D905F018128829083118401853F86448A9C8C208D3F8EC090C191FFA2
:40038000922895149690972199019A039B0E9D409E90A0FCA202A902AA90AB01ADC0AE90B140B330B41FB50FB6E0B780BA80D608D80BD90BDB04DC99DD90DF0101A604240E
:4003C0000501070208400A240B400EA0120113121401164019801A041B401D841EA02080229C2344260129602C082F0230023294370539063A053B403F01580259025A0213
:400400005B405F806140670168036A806B026C016F06780182019440950296E4974698209C429D209E049F02A412A580A741B240B420B540C0FFC23FC49BCA3CCCCFCE8BAA
:40044000D618D818DE01EA20EE0203900401060407900A100B9C0D010E100F4812101520161017C01A031B031DFC1E1C1F02201C2202239025C127242A102B902C012E08A9
:400480002F9033E0341F351F3B08420147E0482049FF4AFF4BFF4F83580859085A045B045C995D095F018310871C8B108D018F0893109F03A310A501A704AB10AD1CAF0254
:4004C000B71FD908DB04DC90DF0100400120032008420A281110134219101A0820042120220826802701284229202A042B202C422D202F20312032483402368039123B4544
:400500003C203D103F454180520159105A445B0160046208638269406E8078807C807F018201C007C20FC40BCAFFCC9ECEFFD008D60FD80FDE18EA01EE08E020E623EE0BF2
:40054000022005880612083209880C080E010F011049118812321432151017A0182119461A1C1BB81D9A1E091F20206321422204230424322588282029882A022C322D04B6
:400580003020313F324033C1341F37C1398A3A203E055608580459045B045C995D905F018401860289018D028E039002920199089B049D049F08A004B002B102B204B304D8
:4005C000B401B501B708B822B988BE15BF55C043C520C802C9FFCAFFCBFFCD20CEF0D110D804D904DA04DB04DD09DF01E108E240E340E480E640E740000801400320051496
:40060000060107400A800D800EA8108016441799181019021B101C401E081F0420012111220424A02505264227A02D202F52324033203504360137A039903C243E4040504B
:40064000488049204A0459405A205D045E085F4064016580670268046A806C036E406F01831085118B4491D0938094A89504970A984099449B509C019D119EC89F20A0500A
:40068000A188A211A402A535A708A980C0F5C2F8C4F8CAF0CCFCCE7CD003D61CD810E022E620EA04EE0B010129022C013102350136013E403F115608580459045B045D9048
:4006C0005F0180478208830885218626874E880189018A388B708D4E8E40914E92029426974E98269B029D809E11A010A201A321A520A604A701A811AA26AB04AC26AD0FF1
:40070000AF10B040B380B43FB57FB63FBAA0BB20BF04C003C50EC70CC811C9FFCAFFCBFFD004D601D804D904DA04DB04DC99DD09DF01E2C0040205940DA20E201002150580
:40074000162017A01B401D111E011F102040221024042530260827082B042E642F40344035043602371038013C803D283E0145084F04570858905C406001630868026C042B
:400780006D016E046F02768981028410860888808D028E049002910492409380940C96209724984099869A049BB09D119E899F08A040A18CA212A530A74AA881AE04B74049
:4007C000C0F0C2F0C4F1CAF4CCF0CEF1D040D61CD80CE008E404E602E8041B011F083180330836843B408340C630CCF0CE10E22032043380364037023B043F808180A0042B
:40080000A340A580A604AE80AF41B004CCF0CE60E680EE40531057208510960897049F02A004A644A780D460E240860491209608972498809F02A004A640A7A0AA04B48062
:40084000E610EE201680C40458405E019A80A404AC04D401D6011B04844096019C40A404A710B680B710C608EA08EE0808080B080E020F4087048A209601A404A710AB043D
:40088000C20FE00425808004871089808B04912097249880A004A720AB02AE40AF80C820E6C0EED0511054045880700477809008912098809B80A004AB80AF20D4E0DC8000
:4008C000DE20EA80EE1005200A400C100F201C0852225620610186029641A220A404A710AA41AC08AF40C001C20DC601D407D80270018001852086018810960198109920A0
:40090000AA20B501DC01E204EC0201010D010F0111011D0100FF01AB02021105BF0000A09F001F000000000000000000100000004000000000000000C0000000FF0000B88D
:4009400047004700000100008000000282008200000000000007070007000000270018012700180100040000000500000000000000000000000000000000000000000000C4
:400980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037
:4009C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F7
:400A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B6
:400A40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076
:400000000145004008520040015B004001650040020101402C0201402D0301400F0501400B0701406308014056090140570A01404A0B0140500C01404A0D0140480E014042
:40004000390F01400415014004170140671801404B190140361A0140471B01400D40014010410140134201400D43014004440140064501400A460140084701400B48014097
:4000800010490140144C01400C4D014006500140045101407E02080109441180180219016009610A7C402721290AE204E6080F011001150126012E012F013001310138026A
:4000C00039023E013F01580459045B045F01810883048C018D0E910194029701A404AD04AE04AF0AB004B102B301B402B50CB601B802B908BE51BF04D608D804D909DB0401
:40010000DC90DD90DF01020208080C02100213081540170818081A801B801C80200821012210231026012A802C02300233043708388039023D405840600268026B0C6D4004
:40014000782088028B109840B040C001C214C4A5CA18CC43CE19D608D808DE04E208E68080088201928094809C289E80A201A602B044B380B404B610E604EA01EE44804068
:40018000842094809C209E80A602AA40E211E608EA01EE0C00800121020403020403052106FC0704080109210A020B080C040E100F4010FF11011404152F162019201A0495
:4001C0001B011C041D211EF91F0E220224042608271028042A402B2F2D0F2E02311F336034FF39083A0C3E104003450C470E481149FF4AFF4BFF50045609581859045A044E
:400200005B045C995D995F0162C081028407850186388702880189048C028D048E04902091049208940895029610983F99029C089D049E30A105A503A704A802A902AA0417
:40024000AC3FAD02B43FB507B63FBF10D608D804D904DB04DC09DD90DF010010010803010490050909240A420C030E020F24100A15081602172119401A021B501E101F107B
:400280002120242025052603272029982B402C022F18300831803202332035983701388039013B183C803D113E0447084F0856085A805F4062806302670269806B026C0881
:4002C0006D116E846F21759076467F01814087018E08908094209581971499049C019DC99E449F03A382A408A682A729C0F7C2FFC4F3CA7FCCFFCEFFD040D618D818DE1080
:40030000E210EA01EE0C0101041205010624084209800A100B200C040D700F80107011081201130414061508170219071A081B081D801E601F102140221023802404250F16
:4003400027F028042B0F2C802D01300F320F348035FF3670380A3B0C3E103F105804590A5B045C995F01824185F186418704888089208A018B118CF18E0291F1924E930218
:40038000954096419711980199109A149BE19F0EA1F1A308A580A641A711A801AA28B00FB10FB3F0B4F0BA02BB02D80BD90BDB04DC99DF010108020203080510076208017B
:4003C00009200A220D020E1410021240131414081508188819041A221B821C801D121F1021282501277029892B082D022F103008314033213608376238A039013A043E1454
:4004000068026D407A107E80800481408D108F019108921495809702980899029D109F72A218A440A509A701A809B340B408B502B640C0F7C27FC46FCACFCCFFCE6FDE8298
:40044000E401E8F0EC20EE40020103C4070808050A020B040C380D200F82101011C7120813181602170418081A301B041F302520274128042B042C062E012F043020320715
:40048000330F341837F43B083E04580459085B045C995F0182108303840285628604870489208A108B508DFC8E108F0192039420964097809A109B1C9C409E209F10A07C22
:4004C000A162A201A308A61CA710A940AA10AB30AC02AE08AF10B01FB11FB360B460B780D808D908DC99DF0100A001800204036004020706081009080A800B010C080E8878
:400500001218134215401608180419201A841B201C081EA02105220923112504268429922A242D802E102F20301031083350340136883722380439823B103D0A3E203F4070
:40054000611062106D406F047B037F028A08900292509508968297129C019D329F62A018A444A501A690A721C0DFC27FC4CFCA7FCCFECEFFDE18E020E208EA8000020120A4
:40058000024803C004C20502062407440A030B900E9011FC129013011690170318201AC01B901E901F9020FC21C222012328269027902A9C2B9C2F90321F331F34E037E015
:4005C0003A203B80420647E0482049FF4AFF4BFF4F83580859085A045B045C995D095F0180E28208861088E28A049210942096D09A03A0FCA201A440A630A880AA30AE1C91
:40060000B2E0B41FD808DB04DC09DF01009001C6032005260610072009010A140B420D200E100F40121013121548171218101A241B101C201D201E101F0820022204230148
:4006400029862B08300131103204335039043B1148054A0252015AAA6120628263206A406C097B037F0282018610C07FC27FC4FECA0FCC0FCE07D204D60FD80FDE188A02E6
:40068000A602E604EE02A602B680E401EE020010022003020406063807040802090C0A040BF10C080D0C0F20100813081408150C17101808190C1B401C081F0221802204B3
:4006C000230C250327FC281A29012A202B022DFF2E01303F31FF3E013F01580459045B045F0181238280830C842286CC870188308A028B2F8CC88D0F9123924894C8952367
:40070000970898D499239B049CC89F10A0C8A120A302A4C8A52FA802AB40AC80AE01B080B261B31FB41FB560B760B808B9A0BE11C046C620C808C9FFCAFFCBFFCD20CEF004
:40074000D110D804D904DA04DB04DC99DD09DF01E108E240E340E480E640E7400010010803820490050909080A420B040E020F941002110A120415401721184019181C80B4
:40078000218C2314274029182A022D4A2F0830083361350436413720381039413A043D013F9440404A085010584059205F806C026D4082409080910192029410950896C023
:4007C000971C99049A069B219D099F08A008A105A2C1A3E6A442A580A708B340B610C0FFC2FFC4DFCAF7CCFFCEFFD001D204D61CEE120008060209020D01100411041202D8
:4008000018041F04220428012C063006310132013302340835043A023E143F055608580459045B045C995D905F0181028405881090019236930198019C319E0AAC05AD012B
:40084000B00CB102B210B420B501B603BE04BF11D608D804D904DB04DC09DD90DF0101080240051809200A400E820F08100811401280154019231A501C101D101E821F0E2C
:4008800020402120220827882F02300232803601382039803F0158205A805F40600862406701680869806F0180408210844887408B018E4090209140941096409708982064
:4008C00099089C409D239F88A002A280A304A608A708B020B410C06CC2DAC48BCA10CC89CE8CD61CD81CE001E622EE021B011F083140330836843B4083408540C630CCF07B
:40090000CE10E220E61030803204364037043A083F80848088029740A340A604AE80AF01CCF0CE60E2105004570880049002960897409A049E089F04A002A120A644A80182
:40094000AA04B520D460E680EA80EE208E08900297409A049E08A002A120A640AA04AB0CAE04EA80EEB014407008C404DC0160808740A408B040D802EA011B0482209780E6
:400980009940B008B140B480C608E809EC040B880F4197899940A220AB05AF04C20F24088004900297409A04A002A120AE40C820E480EE4050015120560470027F018301EC
:4009C00090029A04A002A120AF40D4E0DC80DE20E620EE40070208080D010E021F10520854085940622095029940A220AF40B101C001C20DC601D407D802EA04800887021E
:400A00008E019B02A201A408AF10B008B608E208EA01EC02010109010D010F0111011D0100FF01AB02021105BF0000A09F001F0000000000000000001000000040000000B7
:400A400000000000C0000000FF0000B84700470000010000800000008000800000000000000707000700000027001801270018010004000000050000000000000000000052
:400A80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036
:400AC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F6
:400B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B5
@@ -4615,12 +4615,12 @@
:0200000490105A
:04000000BC90ACAF55
:0200000490303A
:0200000096F078
:0200000021B02D
:0200000490402A
:4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0
:400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
:400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
:4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
:0200000490501A
:0C00000000012E16106900002E2FA759D9
:0C00000000012E16106900002E3032198D
:00000001FF

View File

@@ -814,9 +814,9 @@
</Group>
<Group key="Component">
<Group key="v1">
<Data key="cy_boot" value="cy_boot_v5_81" />
<Data key="cy_boot" value="cy_boot_v6_10" />
<Data key="Em_EEPROM_Dynamic" value="Em_EEPROM_Dynamic_v2_20" />
<Data key="LIN_Dynamic" value="LIN_Dynamic_v5_0" />
<Data key="LIN_Dynamic" value="LIN_Dynamic_v6_0" />
</Group>
</Group>
<Data key="DataVersionKey" value="2" />

View File

File diff suppressed because it is too large Load Diff

View File

@@ -23,36 +23,49 @@ module Sampler (
reg [5:0] counter;
reg index_q;
reg rdata_q;
reg index_edge;
reg rdata_edge;
reg req_toggle;
reg rdata_toggle;
reg old_rdata_toggle;
reg index_toggle;
reg old_index_toggle;
always @(posedge rdata)
begin
rdata_toggle <= ~rdata_toggle;
end
always @(posedge index)
begin
index_toggle <= ~index_toggle;
end
always @(posedge sampleclock)
begin
if (reset)
begin
old_rdata_toggle <= 0;
old_index_toggle <= 0;
index_edge <= 0;
rdata_edge <= 0;
index_q <= 0;
rdata_q <= 0;
counter <= 0;
req_toggle <= 0;
end
else
begin
/* Both index and rdata are active high -- positive-going edges
* indicate the start of an index pulse and read pulse, respectively.
*/
/* If data_toggle or index_toggle have changed state, this means that they've
* gone high since the last sampleclock. */
index_edge <= index && !index_q;
index_q <= index;
index_edge <= index_toggle != old_index_toggle;
old_index_toggle <= index_toggle;
rdata_edge <= rdata && !rdata_q;
rdata_q <= rdata;
rdata_edge <= rdata_toggle != old_rdata_toggle;
old_rdata_toggle <= rdata_toggle;
if (rdata_edge || index_edge || (counter == 6'h3f)) begin
opcode <= { rdata_edge, index_edge, counter };

View File

@@ -65,7 +65,10 @@ begin
if (dataclocked)
begin
pulsepending <= opcode[7];
countdown <= opcode[5:0];
if (opcode[5:0] == 0)
countdown <= 0;
else
countdown <= opcode[5:0] - 1; /* compensate for extra tick in state machine */
state <= STATE_WRITING;
end

View File

Binary file not shown.

View File

@@ -21,6 +21,10 @@ static bool drive1_present;
static volatile uint32_t clock = 0; /* ms */
static volatile bool index_irq = false;
/* Duration in ms. 0 causes every pulse to be an index pulse. Durations since
* last pulse greater than this value imply sector pulse. Otherwise is an index
* pulse. */
static volatile uint32_t hardsec_index_threshold = 0;
static bool motor_on = false;
static uint32_t motor_on_time = 0;
@@ -69,12 +73,41 @@ static void system_timer_cb(void)
CY_ISR(index_irq_cb)
{
index_irq = true;
/* Hard sectored media has sector pulses at the beginning of every sector
* and the index pulse is an extra pulse in the middle of the last sector.
* When the extra pulse is seen, the next sector pulse is also the start of
* the track. */
static bool hardsec_index_irq_primed = false;
static uint32_t hardsec_last_pulse_time = 0;
uint32_t index_pulse_duration = clock - hardsec_last_pulse_time;
if (!hardsec_index_threshold)
{
index_irq = true;
hardsec_index_irq_primed = false;
}
else
{
/* It's only an index pulse if the previous pulse is less than
* the threshold.
*/
index_irq = (index_pulse_duration <= hardsec_index_threshold) ?
hardsec_index_irq_primed : false;
if (index_irq)
hardsec_index_irq_primed = false;
else
hardsec_index_irq_primed =
index_pulse_duration <= hardsec_index_threshold;
hardsec_last_pulse_time = clock;
}
/* Stop writing the instant the index pulse comes along; it may take a few
* moments for the main code to notice the pulse, and we don't want to overwrite
* the beginning of the track. */
ERASE_REG_Write(0);
if (index_irq)
ERASE_REG_Write(0);
}
CY_ISR(capture_dma_finished_irq_cb)
@@ -114,7 +147,7 @@ static void set_drive_flags(struct set_drive_frame* flags)
current_drive_flags = *flags;
DRIVESELECT_REG_Write(flags->drive ? 2 : 1); /* select drive 1 or 0 */
DENSITY_REG_Write(flags->high_density); /* density bit */
DENSITY_REG_Write(!flags->high_density); /* double density bit */
INDEX_REG_Write(flags->index_mode);
}
@@ -188,7 +221,7 @@ static int usb_read(int ep, uint8_t buffer[FRAME_SIZE])
static void cmd_get_version(struct any_frame* f)
{
DECLARE_REPLY_FRAME(struct version_frame, F_FRAME_GET_VERSION_REPLY);
r.version = FLUXENGINE_VERSION;
r.version = FLUXENGINE_PROTOCOL_VERSION;
send_reply((struct any_frame*) &r);
}
@@ -249,7 +282,6 @@ static void seek_to(int track)
CyWdtClear();
}
CyDelay(STEP_SETTLING_TIME);
TK43_REG_Write(track < 43); /* high if 0..42, low if 43 or up */
print("finished seek");
}
@@ -269,7 +301,7 @@ static void cmd_recalibrate(void)
send_reply(&r);
}
static void cmd_measure_speed(struct any_frame* f)
static void cmd_measure_speed(struct measurespeed_frame* f)
{
start_motor();
@@ -279,7 +311,7 @@ static void cmd_measure_speed(struct any_frame* f)
while (!index_irq)
{
elapsed = clock - start_clock;
if (elapsed > 1000)
if (elapsed > 1500)
{
elapsed = 0;
break;
@@ -288,10 +320,14 @@ static void cmd_measure_speed(struct any_frame* f)
if (elapsed != 0)
{
index_irq = false;
int target_pulse_count = f->hard_sector_count + 1;
start_clock = clock;
while (!index_irq)
elapsed = clock - start_clock;
for (int x=0; x<target_pulse_count; x++)
{
index_irq = false;
while (!index_irq)
elapsed = clock - start_clock;
}
}
DECLARE_REPLY_FRAME(struct speed_frame, F_FRAME_MEASURE_SPEED_REPLY);
@@ -386,7 +422,6 @@ static void cmd_read(struct read_frame* f)
seek_to(current_track);
SIDE_REG_Write(f->side);
STEP_REG_Write(f->side); /* for drives which multiplex SIDE and DIR */
/* Do slow setup *before* we go into the real-time bit. */
{
@@ -404,10 +439,12 @@ static void cmd_read(struct read_frame* f)
if (f->synced)
{
hardsec_index_threshold = f->hardsec_threshold_ms;
index_irq = false;
while (!index_irq)
;
index_irq = false;
hardsec_index_threshold = 0;
}
dma_writing_to_td = 0;
@@ -530,7 +567,6 @@ static void cmd_write(struct write_frame* f)
seek_to(current_track);
SIDE_REG_Write(f->side);
STEP_REG_Write(f->side); /* for drives which multiplex SIDE and DIR */
SEQUENCER_CONTROL_Write(1); /* put the sequencer into reset */
{
uint8_t i = CyEnterCriticalSection();
@@ -595,7 +631,7 @@ static void cmd_write(struct write_frame* f)
/* Wait for the index marker. While this happens, the DMA engine
* will prime the FIFO. */
hardsec_index_threshold = f->hardsec_threshold_ms;
index_irq = false;
while (!index_irq)
;
@@ -630,6 +666,7 @@ abort:
}
print("p=%d cr=%d cw=%d f=%d w=%d index=%d underrun=%d", packets, count_read, count_written, finished, writing, index_irq, dma_underrun);
hardsec_index_threshold = 0;
if (!finished)
{
/* There's still some data to read, so just read and blackhole it ---
@@ -659,10 +696,11 @@ abort:
static void cmd_erase(struct erase_frame* f)
{
SIDE_REG_Write(f->side);
seek_to(current_track);
seek_to(current_track);
/* Disk is now spinning. */
print("start erasing");
hardsec_index_threshold = f->hardsec_threshold_ms;
index_irq = false;
while (!index_irq)
;
@@ -671,6 +709,7 @@ static void cmd_erase(struct erase_frame* f)
while (!index_irq)
;
ERASE_REG_Write(0);
hardsec_index_threshold = 0;
print("stop erasing");
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_ERASE_REPLY);
@@ -796,7 +835,7 @@ static void handle_command(void)
break;
case F_FRAME_MEASURE_SPEED_CMD:
cmd_measure_speed(f);
cmd_measure_speed((struct measurespeed_frame*) f);
break;
case F_FRAME_BULK_WRITE_TEST_CMD:
@@ -866,7 +905,6 @@ int main(void)
USBFS_Start(0, USBFS_DWR_VDDD_OPERATION);
USBFS_DisableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
detect_drives();
CyWdtStart(CYWDT_1024_TICKS, CYWDT_LPMODE_DISABLED);
for (;;)
@@ -887,6 +925,8 @@ int main(void)
CyWdtClear();
print("USB ready");
USBFS_EnableOutEP(FLUXENGINE_CMD_OUT_EP_NUM);
print("Scanning drives...");
detect_drives();
}
if (USBFS_GetEPState(FLUXENGINE_CMD_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL)
@@ -898,3 +938,75 @@ int main(void)
}
}
}
const uint8_t USBFS_MSOS_CONFIGURATION_DESCR[USBFS_MSOS_CONF_DESCR_LENGTH] = {
/* Length of the descriptor 4 bytes */ 0x28u, 0x00u, 0x00u, 0x00u,
/* Version of the descriptor 2 bytes */ 0x00u, 0x01u,
/* wIndex - Fixed:INDEX_CONFIG_DESCRIPTOR */ 0x04u, 0x00u,
/* bCount - Count of device functions. */ 0x01u,
/* Reserved : 7 bytes */ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
/* bFirstInterfaceNumber */ 0x00u,
/* Reserved */ 0x01u,
/* compatibleId - "WINUSB\0\0" */ 'W', 'I', 'N', 'U', 'S', 'B', 0, 0,
/* subcompatibleID - "00001\0\0" */ '0', '0', '0', '0', '1',
0x00u, 0x00u, 0x00u,
/* Reserved : 6 bytes */ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u
};
const uint8_t USBFS_MSOS_EXTENDED_PROPERTIES_DESCR[224] = {
/* Length; 4 bytes */ 224, 0, 0, 0,
/* Version; 2 bytes */ 0x00, 0x01, /* 1.0 */
/* wIndex */ 0x05, 0x00,
/* Number of sections */ 0x01, 0x00,
/* Property section size */ 214, 0, 0, 0,
/* Property data type */ 0x07, 0x00, 0x00, 0x00, /* 7 = REG_MULTI_SZ Unicode */
/* Property name length */ 42, 0,
/* Property name */ 'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0,
'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0,
'a', 0, 'c', 0, 'e', 0, 'G', 0, 'U', 0, 'I', 0,
'D', 0, 's', 0, 0, 0,
/* Property data length */ 158, 0, 0, 0,
/* GUID #1 data */ '{', 0, '3', 0, 'd', 0, '2', 0, '7', 0, '5', 0,
'c', 0, 'f', 0, 'e', 0, '-', 0, '5', 0, '4', 0,
'3', 0, '5', 0, '-', 0, '4', 0, 'd', 0, 'd', 0,
'5', 0, '-', 0, 'a', 0, 'c', 0, 'c', 0, 'a', 0,
'-', 0, '9', 0, 'f', 0, 'b', 0, '9', 0, '9', 0,
'5', 0, 'e', 0, '2', 0, 'f', 0, '6', 0, '3', 0,
'8', 0, '}', 0, '\0', 0,
/* GUID #2 data */ '{', 0, '3', 0, 'd', 0, '2', 0, '7', 0, '5', 0,
'c', 0, 'f', 0, 'e', 0, '-', 0, '5', 0, '4', 0,
'3', 0, '5', 0, '-', 0, '4', 0, 'd', 0, 'd', 0,
'5', 0, '-', 0, 'a', 0, 'c', 0, 'c', 0, 'a', 0,
'-', 0, '9', 0, 'f', 0, 'b', 0, '9', 0, '9', 0,
'5', 0, 'e', 0, '2', 0, 'f', 0, '6', 0, '3', 0,
'8', 0, '}', 0, '\0', 0, '\0', 0
};
uint8 USBFS_HandleVendorRqst(void)
{
if (!(USBFS_bmRequestTypeReg & USBFS_RQST_DIR_D2H))
return false;
switch (USBFS_bRequestReg)
{
case USBFS_GET_EXTENDED_CONFIG_DESCRIPTOR:
switch (USBFS_wIndexLoReg)
{
case 4:
USBFS_currentTD.pData = (volatile uint8 *) &USBFS_MSOS_CONFIGURATION_DESCR[0u];
USBFS_currentTD.count = USBFS_MSOS_CONFIGURATION_DESCR[0u];
return USBFS_InitControlRead();
case 5:
USBFS_currentTD.pData = (volatile uint8 *) &USBFS_MSOS_EXTENDED_PROPERTIES_DESCR[0u];
USBFS_currentTD.count = USBFS_MSOS_EXTENDED_PROPERTIES_DESCR[0u];
return USBFS_InitControlRead();
}
default:
break;
}
return true;
}

View File

@@ -1,18 +0,0 @@
const READ = 1
const WRITE = 2
filename = "Generated_Source\PSoC5\USBFS_descr.c"
set fso = CreateObject("Scripting.FileSystemObject")
set file = fso.OpenTextFile(filename, READ)
text = file.ReadAll
file.Close
set r = New RegExp
r.MultiLine = True
r.Pattern = "/\* +compatibleID.*\n.*"
text = r.replace(text, "'W', 'I', 'N', 'U', 'S', 'B', 0, 0,")
set file = fso.CreateTextFile(filename, True)
file.Write text
file.Close

325
Makefile
View File

@@ -1,52 +1,299 @@
PACKAGES = zlib sqlite3 libusb-1.0
export CFLAGS = --std=c++14 -ffunction-sections -fdata-sections
export LDFLAGS =
export COPTFLAGS = -Os
export LDOPTFLAGS = -Os -s
export CDBGFLAGS = -O0 -g
export LDDBGFLAGS = -O0 -g
#Special Windows settings.
ifeq ($(OS), Windows_NT)
export CXX = /mingw32/bin/g++
export AR = /mingw32/bin/ar rcs
export STRIP = /mingw32/bin/strip
export CFLAGS += -I/mingw32/include/libusb-1.0
export LDFLAGS +=
export LIBS = -static -lz -lsqlite3 -lusb-1.0
export EXTENSION = .exe
else
MINGWBIN = /mingw32/bin
CCPREFIX = $(MINGWBIN)/
LUA = $(MINGWBIN)/lua
PKG_CONFIG = $(MINGWBIN)/pkg-config
WX_CONFIG = /usr/bin/sh $(MINGWBIN)/wx-config --static=yes
PROTOC = $(MINGWBIN)/protoc
PLATFORM = WINDOWS
LDFLAGS += \
-static
CXXFLAGS += \
-std=c++17 \
-fext-numeric-literals \
-Wno-deprecated-enum-float-conversion \
-Wno-deprecated-enum-enum-conversion
packages-exist = $(shell pkg-config --exists $(PACKAGES) && echo yes)
ifneq ($(packages-exist),yes)
$(warning These pkg-config packages are installed: $(shell pkg-config --list-all | sort | awk '{print $$1}'))
$(error You must have these pkg-config packages installed: $(PACKAGES))
#Required to get the gcc run - time libraries on the path.
export PATH := $(PATH):$(MINGWBIN)
EXT ?= .exe
endif
export CXX = g++
export AR = ar rcs
export STRIP = strip
export CFLAGS += $(shell pkg-config --cflags $(PACKAGES))
export LDFLAGS +=
export LIBS = $(shell pkg-config --libs $(PACKAGES))
export EXTENSION =
#Special OSX settings.
ifeq ($(shell uname),Darwin)
PLATFORM = OSX
LDFLAGS += \
-framework IOKit \
-framework Foundation
endif
CFLAGS += -Ilib -Idep/fmt -Iarch
#Check the Make version.
export OBJDIR = .obj
all: .obj/build.ninja
@ninja -f .obj/build.ninja
ifeq ($(findstring 4.,$(MAKE_VERSION)),)
$(error You need GNU Make 4.x for this (if you're on OSX, use gmake).)
endif
#Normal settings.
OBJDIR ?= .obj
CCPREFIX ?=
LUA ?= lua
CC ?= $(CCPREFIX)gcc
CXX ?= $(CCPREFIX)g++
AR ?= $(CCPREFIX)ar
PKG_CONFIG ?= pkg-config
WX_CONFIG ?= wx-config
PROTOC ?= protoc
CFLAGS ?= -g -O3
CXXFLAGS += -std=c++17
LDFLAGS ?=
PLATFORM ?= UNIX
TESTS ?= yes
EXT ?=
DESTDIR ?=
PREFIX ?= /usr/local
BINDIR ?= $(PREFIX)/bin
CFLAGS += \
-Iarch \
-Ilib \
-I. \
-I$(OBJDIR)/arch \
-I$(OBJDIR)/lib \
-I$(OBJDIR) \
-Wno-deprecated-declarations \
LDFLAGS += \
-lz \
-lfmt
.SUFFIXES:
.DELETE_ON_ERROR:
define nl
endef
empty :=
space := $(empty) $(empty)
use-library = $(eval $(use-library-impl))
define use-library-impl
$1: $(call $3_LIB)
$1: private LDFLAGS += $(call $3_LDFLAGS)
$2: private CFLAGS += $(call $3_CFLAGS)
endef
use-pkgconfig = $(eval $(use-pkgconfig-impl))
define use-pkgconfig-impl
ifneq ($(strip $(shell $(PKG_CONFIG) $3; echo $$?)),0)
$$(error Missing required pkg-config dependency: $3)
endif
$(1): private LDFLAGS += $(shell $(PKG_CONFIG) --libs $(3))
$(2): private CFLAGS += $(shell $(PKG_CONFIG) --cflags $(3))
endef
.PHONY: all binaries tests clean install install-bin
all: binaries tests docs
PROTOS = \
arch/aeslanier/aeslanier.proto \
arch/agat/agat.proto \
arch/amiga/amiga.proto \
arch/apple2/apple2.proto \
arch/brother/brother.proto \
arch/c64/c64.proto \
arch/f85/f85.proto \
arch/fb100/fb100.proto \
arch/ibm/ibm.proto \
arch/macintosh/macintosh.proto \
arch/micropolis/micropolis.proto \
arch/mx/mx.proto \
arch/northstar/northstar.proto \
arch/rolandd20/rolandd20.proto \
arch/smaky6/smaky6.proto \
arch/tids990/tids990.proto \
arch/victor9k/victor9k.proto \
arch/zilogmcz/zilogmcz.proto \
lib/common.proto \
lib/config.proto \
lib/decoders/decoders.proto \
lib/drive.proto \
lib/encoders/encoders.proto \
lib/fl2.proto \
lib/fluxsink/fluxsink.proto \
lib/fluxsource/fluxsource.proto \
lib/imagereader/imagereader.proto \
lib/imagewriter/imagewriter.proto \
lib/layout.proto \
lib/usb/usb.proto \
lib/vfs/vfs.proto \
tests/testproto.proto \
PROTO_HDRS = $(patsubst %.proto, $(OBJDIR)/%.pb.h, $(PROTOS))
PROTO_SRCS = $(patsubst %.proto, $(OBJDIR)/%.pb.cc, $(PROTOS))
PROTO_OBJS = $(patsubst %.cc, %.o, $(PROTO_SRCS))
PROTO_CFLAGS = $(shell $(PKG_CONFIG) --cflags protobuf)
$(PROTO_SRCS): | $(PROTO_HDRS)
$(PROTO_OBJS): CFLAGS += $(PROTO_CFLAGS)
PROTO_LIB = $(OBJDIR)/libproto.a
$(PROTO_LIB): $(PROTO_OBJS)
PROTO_LDFLAGS = $(shell $(PKG_CONFIG) --libs protobuf) -pthread
.PRECIOUS: $(PROTO_HDRS) $(PROTO_SRCS)
include dep/agg/build.mk
include dep/libusbp/build.mk
include dep/stb/build.mk
include dep/emu/build.mk
include dep/fatfs/build.mk
include dep/adflib/build.mk
include dep/hfsutils/build.mk
include scripts/build.mk
include lib/build.mk
include arch/build.mk
include src/build.mk
include src/gui/build.mk
include tools/build.mk
include tests/build.mk
do-encodedecodetest = $(eval $(do-encodedecodetest-impl))
define do-encodedecodetest-impl
tests: $(OBJDIR)/$1$$(subst $$(space),_,$3).flux.encodedecode
$(OBJDIR)/$1$$(subst $$(space),_,$3).flux.encodedecode: scripts/encodedecodetest.sh $(FLUXENGINE_BIN) $2
@mkdir -p $(dir $$@)
@echo ENCODEDECODETEST $1 flux $(FLUXENGINE_BIN) $2 $3
@scripts/encodedecodetest.sh $1 flux $(FLUXENGINE_BIN) $2 $3 > $$@
tests: $(OBJDIR)/$1$$(subst $$(space),_,$3).scp.encodedecode
$(OBJDIR)/$1$$(subst $$(space),_,$3).scp.encodedecode: scripts/encodedecodetest.sh $(FLUXENGINE_BIN) $2
@mkdir -p $(dir $$@)
@echo ENCODEDECODETEST $1 scp $(FLUXENGINE_BIN) $2 $3
@scripts/encodedecodetest.sh $1 scp $(FLUXENGINE_BIN) $2 $3 > $$@
endef
$(call do-encodedecodetest,agat,,--drive.tpi=96)
$(call do-encodedecodetest,amiga,,--drive.tpi=135)
$(call do-encodedecodetest,apple2,,--140 --drive.tpi=96)
$(call do-encodedecodetest,atarist,,--360 --drive.tpi=135)
$(call do-encodedecodetest,atarist,,--370 --drive.tpi=135)
$(call do-encodedecodetest,atarist,,--400 --drive.tpi=135)
$(call do-encodedecodetest,atarist,,--410 --drive.tpi=135)
$(call do-encodedecodetest,atarist,,--720 --drive.tpi=135)
$(call do-encodedecodetest,atarist,,--740 --drive.tpi=135)
$(call do-encodedecodetest,atarist,,--800 --drive.tpi=135)
$(call do-encodedecodetest,atarist,,--820 --drive.tpi=135)
$(call do-encodedecodetest,bk)
$(call do-encodedecodetest,brother,,--120 --drive.tpi=135)
$(call do-encodedecodetest,brother,,--240 --drive.tpi=135)
$(call do-encodedecodetest,commodore,scripts/commodore1541_test.textpb,--171 --drive.tpi=96)
$(call do-encodedecodetest,commodore,scripts/commodore1541_test.textpb,--192 --drive.tpi=96)
$(call do-encodedecodetest,commodore,,--800 --drive.tpi=135)
$(call do-encodedecodetest,commodore,,--1620 --drive.tpi=135)
$(call do-encodedecodetest,hplif,,--264 --drive.tpi=135)
$(call do-encodedecodetest,hplif,,--616 --drive.tpi=135)
$(call do-encodedecodetest,hplif,,--770 --drive.tpi=135)
$(call do-encodedecodetest,ibm,,--1200 --drive.tpi=96)
$(call do-encodedecodetest,ibm,,--1232 --drive.tpi=96)
$(call do-encodedecodetest,ibm,,--1440 --drive.tpi=135)
$(call do-encodedecodetest,ibm,,--1680 --drive.tpi=135)
$(call do-encodedecodetest,ibm,,--180 --drive.tpi=96)
$(call do-encodedecodetest,ibm,,--160 --drive.tpi=96)
$(call do-encodedecodetest,ibm,,--320 --drive.tpi=96)
$(call do-encodedecodetest,ibm,,--360 --drive.tpi=96)
$(call do-encodedecodetest,ibm,,--720_96 --drive.tpi=96)
$(call do-encodedecodetest,ibm,,--720_135 --drive.tpi=135)
$(call do-encodedecodetest,mac,scripts/mac400_test.textpb,--400 --drive.tpi=135)
$(call do-encodedecodetest,mac,scripts/mac800_test.textpb,--800 --drive.tpi=135)
$(call do-encodedecodetest,n88basic,,--drive.tpi=96)
$(call do-encodedecodetest,rx50,,--drive.tpi=96)
$(call do-encodedecodetest,tids990,,--drive.tpi=48)
$(call do-encodedecodetest,victor9k,,--612 --drive.tpi=96)
$(call do-encodedecodetest,victor9k,,--1224 --drive.tpi=96)
do-corpustest = $(eval $(do-corpustest-impl))
define do-corpustest-impl
tests: $(OBJDIR)/corpustest/$2
$(OBJDIR)/corpustest/$2: $(FLUXENGINE_BIN) \
../fluxengine-testdata/data/$1 ../fluxengine-testdata/data/$2
@mkdir -p $(OBJDIR)/corpustest
@echo CORPUSTEST $1 $2 $3
@$(FLUXENGINE_BIN) read $3 -s ../fluxengine-testdata/data/$1 -o $$@ > $$@.log
@cmp $$@ ../fluxengine-testdata/data/$2
endef
ifneq ($(wildcard ../fluxengine-testdata/data),)
$(call do-corpustest,amiga.flux,amiga.adf,amiga --drive.tpi=135)
$(call do-corpustest,atarist360.flux,atarist360.st,atarist --360 --drive.tpi=135)
$(call do-corpustest,atarist720.flux,atarist720.st,atarist --720 --drive.tpi=135)
$(call do-corpustest,brother120.flux,brother120.img,brother --120 --drive.tpi=135)
$(call do-corpustest,cmd-fd2000.flux,cmd-fd2000.img,commodore --1620 --drive.tpi=135)
$(call do-corpustest,ibm1232.flux,ibm1232.img,ibm --1232 --drive.tpi=96)
$(call do-corpustest,ibm1440.flux,ibm1440.img,ibm --1440 --drive.tpi=135)
$(call do-corpustest,mac800.flux,mac800.dsk,mac --800 --drive.tpi=135)
$(call do-corpustest,micropolis315.flux,micropolis315.img,micropolis --315 --drive.tpi=100)
$(call do-corpustest,northstar87-synthetic.flux,northstar87-synthetic.nsi,northstar --87 --drive.tpi=48)
$(call do-corpustest,northstar175-synthetic.flux,northstar175-synthetic.nsi,northstar --175 --drive.tpi=48)
$(call do-corpustest,northstar350-synthetic.flux,northstar350-synthetic.nsi,northstar --350 --drive.tpi=48)
$(call do-corpustest,victor9k_ss.flux,victor9k_ss.img,victor9k --612 --drive.tpi=96)
$(call do-corpustest,victor9k_ds.flux,victor9k_ds.img,victor9k --1224 --drive.tpi=96)
endif
$(OBJDIR)/%.a:
@mkdir -p $(dir $@)
@echo AR $@
@$(AR) rc $@ $^
%.exe:
@mkdir -p $(dir $@)
@echo LINK $@
@$(CXX) -o $@ $(filter %.o,$^) $(filter %.a,$^) $(LDFLAGS) $(filter %.a,$^) $(LDFLAGS)
$(OBJDIR)/%.o: %.cpp
@mkdir -p $(dir $@)
@echo CXX $<
@$(CXX) $(CFLAGS) $(CXXFLAGS) -MMD -MP -MF $(@:.o=.d) -c -o $@ $<
$(OBJDIR)/%.o: %.cc
@mkdir -p $(dir $@)
@echo CXX $<
@$(CXX) $(CFLAGS) $(CXXFLAGS) -MMD -MP -MF $(@:.o=.d) -c -o $@ $<
$(OBJDIR)/%.o: $(OBJDIR)/%.cc
@mkdir -p $(dir $@)
@echo CXX $<
@$(CXX) $(CFLAGS) $(CXXFLAGS) -MMD -MP -MF $(@:.o=.d) -c -o $@ $<
$(OBJDIR)/%.o: %.c
@mkdir -p $(dir $@)
@echo CC $<
@$(CC) $(CFLAGS) $(CFLAGS) -MMD -MP -MF $(@:.o=.d) -c -o $@ $<
$(OBJDIR)/%.pb.h: %.proto
@mkdir -p $(dir $@)
@echo PROTOC $@
@$(PROTOC) -I. --cpp_out=$(OBJDIR) $<
clean:
@echo CLEAN
@rm -rf $(OBJDIR)
rm -rf $(OBJDIR)
.obj/build.ninja: mkninja.sh Makefile
@echo MKNINJA $@
@mkdir -p $(OBJDIR)
@sh $< > $@
install: install-bin # install-man install-docs ...
install-bin: fluxengine$(EXT) fluxengine-gui$(EXT) brother120tool$(EXT) brother240tool$(EXT) upgrade-flux-file$(EXT)
install -d "$(DESTDIR)$(BINDIR)"
for target in $^; do \
install $$target "$(DESTDIR)$(BINDIR)/$$target"; \
done
-include $(OBJS:%.o=%.d)

187
README.md
View File

@@ -2,7 +2,17 @@ FluxEngine
==========
(If you're reading this on GitHub, the formatting's a bit messed up. [Try the
version on cowlark.com instead.](http://cowlark.com/fluxengine/)
version on cowlark.com instead.](http://cowlark.com/fluxengine/))
**Breaking news!** As of 2022-09-09, there's new [filesystem
support](doc/filesystem.md). Read (and sometimes write) files directly from
(and to) your disks, with eight different file systems! It works in the GUI,
too, which is available for Linux (and other Unix clones), Windows and OSX. See
the details below.
<div style="text-align: center">
<a href="doc/screenshot.jpg"><img src="doc/screenshot.jpg" style="width:60%" alt="screenshot of the GUI in action"></a>
</div>
What?
-----
@@ -24,10 +34,13 @@ Don't believe me? Watch the demo reel!
<iframe width="373" height="210" src="https://www.youtube.com/embed/m_s1iw8eW7o" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
**Important note.** On 2020-04-02 I changed the bytecode format (and firmware).
Flux files will need to be upgraded with `fluxengine upgradefluxfile`. The new
format should be more reliable and use way, way less bandwidth. Sorry for the
inconvenience.
**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.
Where?
------
@@ -52,62 +65,83 @@ following friendly articles:
- [Using a FluxEngine](doc/using.md) ∾ what to do with your new hardware ∾
flux files and image files ∾ knowing what you're doing
- [Troubleshooting dubious disks](doc/problems.md) ∾ it's not an exact science ∾
the sector map ∾ clock detection and the histogram
- [Using Greaseweazle hardware with the FluxEngine client
software](doc/greaseweazle.md) ∾ what works ∾ what doesn't work ∾ where to
go for help
- [Configuring for your drive](doc/drives.md) ∾ but I don't have a 80 track
drive! ∾ reading and writing 40 track disks ∾ Shugart and Apple II
- [Direct filesystem access](doc/filesystem.md) ∾ imaging files is a pain
∾ accessing files directly ∾ features and limitation ∾ it works on disk
images too, you say?
- [Troubleshooting dubious disks](doc/problems.md) ∾ it's not an exact
science ∾ the sector map ∾ clock detection and the histogram
- [Disk densities](doc/driveresponse.md) ∾ what's the difference between an HD
and DD disk? ∾ you can't do that with that ∾ measuring your drive's ability to
work with exotic formats ∾ I think my drive is broken
Which?
------
The current support state is as follows.
Dinosaurs (🦖) have yet to be observed in real life --- I've written the
decoder based on Kryoflux (or other) dumps I've found. I don't (yet) have
real, physical disks in my hand to test the capture process.
Dinosaurs (🦖) have yet to be observed in real life --- I've written the encoder
and/or decoder based on Kryoflux (or other) dumps I've found. I don't (yet) have
real, physical disks in my hand to test the capture process, or hardware to
verify that written disks work.
Unicorns (🦄) are completely real --- this means that I've read actual,
physical disks with these formats and so know they work (or had reports from
people who've had it work).
Unicorns (🦄) are completely real --- this means that I've read actual, physical
disks with these formats and/or written real, physical disks and then used them
on real hardware, and so know they work (or had reports from people who've had
it work).
### Old disk formats
If a filesystem is listed, this means that FluxEngine natively supports that
particular filesystem and can read (and sometimes write, support varies) files
directly from disks, flux files or disk images. Some formats have multiple
choices because they can store multiple types of file system.
| Format | Read? | Write? | Notes |
|:-----------------------------------------|:-----:|:------:|-------|
| [IBM PC compatible](doc/disk-ibm.md) | 🦄 | 🦄 | and compatibles (like the Atari ST) |
| [Acorn ADFS](doc/disk-acornadfs.md) | 🦄 | 🦖* | single- and double- sided |
| [Acorn DFS](doc/disk-acorndfs.md) | 🦄 | 🦖* | |
| [Ampro Little Board](doc/disk-ampro.md) | 🦖 | 🦖* | |
| [Apple II DOS 3.3](doc/disk-apple2.md) | 🦄 | | doesn't do logical sector remapping |
| [Amiga](doc/disk-amiga.md) | 🦄 | | |
| [Commodore 64 1541](doc/disk-c64.md) | 🦖 | | and probably the other GCR formats |
| [Brother 120kB](doc/disk-brother.md) | 🦄 | | |
| [Brother 240kB](doc/disk-brother.md) | 🦄 | 🦄 | |
| [Brother FB-100](doc/disk-fb100.md) | 🦖 | | Tandy Model 100, Husky Hunter, knitting machines |
| [Macintosh 800kB](doc/disk-macintosh.md) | 🦄 | 🦄 | and probably the 400kB too |
| [TRS-80](doc/disk-trs80.md) | 🦖 | 🦖* | a minor variation of the IBM scheme |
<!-- FORMATSSTART -->
<!-- This section is automatically generated. Do not edit. -->
| Profile | Format | Read? | Write? | Filesystem? |
|:--------|:-------|:-----:|:------:|:------------|
| [`acornadfs`](doc/disk-acornadfs.md) | Acorn ADFS: BBC Micro, Archimedes | 🦖 | | |
| [`acorndfs`](doc/disk-acorndfs.md) | Acorn DFS: Acorn Atom, BBC Micro series | 🦄 | | ACORNDFS |
| [`aeslanier`](doc/disk-aeslanier.md) | AES Lanier "No Problem": 616kB 5.25" 77-track SSDD hard sectored | 🦖 | | |
| [`agat`](doc/disk-agat.md) | Agat: 840kB 5.25" 80-track DS | 🦖 | 🦖 | |
| [`amiga`](doc/disk-amiga.md) | Amiga: 880kB 3.5" DSDD | 🦄 | 🦄 | AMIGAFFS |
| [`ampro`](doc/disk-ampro.md) | Ampro Little Board: CP/M | 🦖 | | CPMFS |
| [`apple2`](doc/disk-apple2.md) | Apple II: Prodos, Appledos, and CP/M | 🦄 | 🦄 | APPLEDOS CPMFS PRODOS |
| [`atarist`](doc/disk-atarist.md) | Atari ST: Almost PC compatible | 🦄 | 🦄 | |
| [`bk`](doc/disk-bk.md) | BK: 800kB 5.25"/3.5" 80-track 10-sector DSDD | 🦖 | 🦖 | |
| [`brother`](doc/disk-brother.md) | Brother word processors: GCR family | 🦄 | 🦄 | BROTHER120 FATFS |
| [`commodore`](doc/disk-commodore.md) | Commodore: 1541, 1581, 8050 and variations | 🦄 | 🦄 | CBMFS |
| [`eco1`](doc/disk-eco1.md) | VDS Eco1: CP/M; 1210kB 77-track mixed format DSHD | 🦖 | | CPMFS |
| [`epsonpf10`](doc/disk-epsonpf10.md) | Epson PF-10: CP/M; 3.5" 40-track DSDD | 🦖 | | CPMFS |
| [`f85`](doc/disk-f85.md) | Durango F85: 461kB 5.25" 77-track SS | 🦖 | | |
| [`fb100`](doc/disk-fb100.md) | Brother FB-100: 100kB 3.5" 40-track SSSD | 🦖 | | |
| [`hplif`](doc/disk-hplif.md) | Hewlett-Packard LIF: a variety of disk formats used by HP | 🦄 | 🦄 | LIF |
| [`ibm`](doc/disk-ibm.md) | IBM PC: Generic PC 3.5"/5.25" disks | 🦄 | 🦄 | FATFS |
| [`icl30`](doc/disk-icl30.md) | ICL Model 30: CP/M; 263kB 35-track DSSD | 🦖 | | CPMFS |
| [`mac`](doc/disk-mac.md) | Macintosh: 400kB/800kB 3.5" GCR | 🦄 | 🦄 | MACHFS |
| [`micropolis`](doc/disk-micropolis.md) | Micropolis: 100tpi MetaFloppy disks | 🦄 | 🦄 | |
| [`mx`](doc/disk-mx.md) | DVK MX: Soviet-era PDP-11 clone | 🦖 | | |
| [`n88basic`](doc/disk-n88basic.md) | N88-BASIC: PC8800/PC98 5.25" 77-track 26-sector DSHD | 🦄 | 🦄 | |
| [`northstar`](doc/disk-northstar.md) | Northstar: 5.25" hard sectored | 🦄 | 🦄 | |
| [`psos`](doc/disk-psos.md) | pSOS: 800kB DSDD with PHILE | 🦄 | 🦄 | PHILE |
| [`rolandd20`](doc/disk-rolandd20.md) | Roland D20: 3.5" electronic synthesiser disks | 🦖 | | |
| [`rx50`](doc/disk-rx50.md) | Digital RX50: 400kB 5.25" 80-track 10-sector SSDD | 🦖 | 🦖 | |
| [`smaky6`](doc/disk-smaky6.md) | Smaky 6: 308kB 5.25" 77-track 16-sector SSDD, hard sectored | 🦖 | | SMAKY6 |
| [`tids990`](doc/disk-tids990.md) | Texas Instruments DS990: 1126kB 8" DSSD | 🦖 | 🦖 | |
| [`tiki`](doc/disk-tiki.md) | Tiki 100: CP/M | | | CPMFS |
| [`victor9k`](doc/disk-victor9k.md) | Victor 9000 / Sirius One: 1224kB 5.25" DSDD GCR | 🦖 | 🦖 | |
| [`zilogmcz`](doc/disk-zilogmcz.md) | Zilog MCZ: 320kB 8" 77-track SSSD hard-sectored | 🦖 | | |
{: .datatable }
`*`: these formats are variations of the generic IBM format, and since the
IBM writer is completely generic, it should be configurable for these
formats... theoretically. I don't have the hardware to try it.
### Even older disk formats
These formats are for particularly old, weird architectures, even by the
standards of floppy disks. They've largely been implemented from single flux
files with no access to physical hardware. Typically the reads were pretty
bad and I've had to make a number of guesses as to how things work. They do,
at least, check the CRC so what data's there is probably good.
| Format | Read? | Write? | Notes |
|:-----------------------------------------|:-----:|:------:|-------|
| [AES Superplus / No Problem](doc/disk-aeslanier.md) | 🦖 | | hard sectors! |
| [Durango F85](doc/disk-durangof85.md) | 🦖 | | 5.25" |
| [DVK MX](doc/disk-mx.md) | 🦖 | | Soviet PDP-11 clone |
| [Micropolis](doc/disk-micropolis.md) | 🦄 | | Micropolis 100tpi drives |
| [TI DS990 FD1000](doc/disk-tids990.md) | 🦄 | 🦄 | 8" |
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8" |
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8" _and_ hard sectors |
{: .datatable }
<!-- FORMATSEND -->
### Notes
@@ -124,7 +158,7 @@ at least, check the CRC so what data's there is probably good.
There hasn't been a lot of demand for this yet; if you have a pressing
need to write weird disks, [please
ask](https://github.com/davidgiven/fluxengine/issues/new). I haven't
implement write support for PC disks because they're boring and I'm lazy,
implemented write support for PC disks because they're boring and I'm lazy,
and also because they vary so much that figuring out how to specify them
is hard.
@@ -178,18 +212,51 @@ There may or may not be anything interesting there.
License
-------
Everything here _except the contents of the `dep` directory_ is © 2019 David
Given and is licensed under the MIT open source license. Please see
Everything here _except the contents of the `dep` directory_ is © 2022 The
FluxEngine Authors (mostly me, David Given; see the VCS history for the other
people) and is licensed under the MIT open source license. Please see
[COPYING](COPYING) for the full text. The tl;dr is: you can do what you like
with it provided you don't claim you wrote it.
As an exception, `dep/fmt` contains a copy of [fmt](http://fmtlib.net),
maintained by Victor Zverovich (`vitaut <https://github.com/vitaut>`) and
Jonathan Müller (`foonathan <https://github.com/foonathan>`) with
contributions from many other people. It is licensed under the terms of the
BSD license. Please see the contents of the directory for the full text.
As an exception, `dep/emu` contains parts of the OpenBSD C library
code, Todd Miller and William A. Rowe (and probably others). It is licensed
code, maintained by Todd Miller and William A. Rowe (and probably others). It is licensed
under the terms of the 3-clause BSD license. Please see the contents of the
directory for the full text. It's been lightly modified by me.
As an exception, `dep/agg` contains parts of the Anti-Grain Antialiasing
library, written by Maxim Semanarev (and others). It is licensed under the
terms of the 3-clause BSD license. Please see the contents of the directory for
the full text. It's been lightly modified by me.
As an exception, `dep/stb` contains parts of the libstb utility library,
written by Sean T Barett (and others). It is public domain/Unlicense/MIT
licensed, at your choice. Please see the contents of the directory for the full
text.
As an exception, `dep/snowhouse` contains the snowhouse assertion library,
taken from https://github.com/banditcpp/snowhouse. It is Boost Standard License
1.0 licensed. Please see the contents of the directory for the full text. Note
that this is only used during the build and no code ends up in the output
binaries.
As an exception, `dep/libusbp` contains the libusbp library, taken from
https://github.com/pololu/libusbp. It is MIT licensed. Please see the contents
of the directory for the full text.
As an exception, `dep/fatfs` contains the fatfs library, taken from
http://elm-chan.org/fsw/ff/00index_e.html. It is single-clause BSD licensed.
Please see the contents of the directory for the full text.
As an exception, `dep/adflib` contains the adflib library, written by Laurent
Clevy et al, taken from https://github.com/lclevy/ADFlib. It is GPL 2.0
licensed. Please see the contents of the directory for the full text.
As an exception, `dep/hfsutils` contains a partial copy of the hfsutils
package, written by Robert Leslie et al, taken from
https://www.mars.org/home/rob/proj/hfs. It is GPL 2.0 licensed. Please see the
contents of the directory for the full text.
__Important:__ Because of all these exceptions, if you distribute the
FluxEngine package as a whole, you must comply with the terms of _all_ of the
licensing terms. This means that __effectively the FluxEngine package is
distributable under the terms of the GPL 2.0__.

View File

@@ -2,19 +2,10 @@
#define AESLANIER_H
#define AESLANIER_RECORD_SEPARATOR 0x55555122
#define AESLANIER_SECTOR_LENGTH 256
#define AESLANIER_RECORD_SIZE (AESLANIER_SECTOR_LENGTH + 5)
#define AESLANIER_SECTOR_LENGTH 256
#define AESLANIER_RECORD_SIZE (AESLANIER_SECTOR_LENGTH + 5)
class Sector;
class Fluxmap;
class AesLanierDecoder : public AbstractDecoder
{
public:
virtual ~AesLanierDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
extern std::unique_ptr<Decoder> createAesLanierDecoder(
const DecoderProto& config);
#endif

View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message AesLanierDecoderProto {}

View File

@@ -6,60 +6,59 @@
#include "decoders/fluxmapreader.h"
#include "sector.h"
#include "bytes.h"
#include "record.h"
#include "fmt/format.h"
#include <string.h>
static const FluxPattern SECTOR_PATTERN(32, AESLANIER_RECORD_SEPARATOR);
/* This is actually M2FM, rather than MFM, but it our MFM/FM decoder copes fine with it. */
/* This is actually M2FM, rather than MFM, but it our MFM/FM decoder copes fine
* with it. */
static Bytes reverse_bits(const Bytes& input)
class AesLanierDecoder : public Decoder
{
Bytes output;
ByteWriter bw(output);
for (uint8_t b : input)
bw.write_8(reverse_bits(b));
return output;
}
AbstractDecoder::RecordType AesLanierDecoder::advanceToNextRecord()
{
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
if (_fmr->eof() || !_sector->clock)
return UNKNOWN_RECORD;
return SECTOR_RECORD;
}
void AesLanierDecoder::decodeSectorRecord()
{
/* Skip ID mark. */
readRawBits(16);
const auto& rawbits = readRawBits(AESLANIER_RECORD_SIZE*16);
const auto& bytes = decodeFmMfm(rawbits).slice(0, AESLANIER_RECORD_SIZE);
const auto& reversed = reverse_bits(bytes);
_sector->logicalTrack = reversed[1];
_sector->logicalSide = 0;
_sector->logicalSector = reversed[2];
/* Check header 'checksum' (which seems far too simple to mean much). */
public:
AesLanierDecoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
uint8_t wanted = reversed[3];
uint8_t got = reversed[1] + reversed[2];
if (wanted != got)
return;
return seekToPattern(SECTOR_PATTERN);
}
/* Check data checksum, which also includes the header and is
* significantly better. */
void decodeSectorRecord() override
{
/* Skip ID mark (we know it's a AESLANIER_RECORD_SEPARATOR). */
_sector->data = reversed.slice(1, AESLANIER_SECTOR_LENGTH);
uint16_t wanted = reversed.reader().seek(0x101).read_le16();
uint16_t got = crc16ref(MODBUS_POLY_REF, _sector->data);
_sector->status = (wanted == got) ? Sector::OK : Sector::BAD_CHECKSUM;
readRawBits(16);
const auto& rawbits = readRawBits(AESLANIER_RECORD_SIZE * 16);
const auto& bytes =
decodeFmMfm(rawbits).slice(0, AESLANIER_RECORD_SIZE);
const auto& reversed = bytes.reverseBits();
_sector->logicalTrack = reversed[1];
_sector->logicalSide = 0;
_sector->logicalSector = reversed[2];
/* Check header 'checksum' (which seems far too simple to mean much). */
{
uint8_t wanted = reversed[3];
uint8_t got = reversed[1] + reversed[2];
if (wanted != got)
return;
}
/* Check data checksum, which also includes the header and is
* significantly better. */
_sector->data = reversed.slice(1, AESLANIER_SECTOR_LENGTH);
uint16_t wanted = reversed.reader().seek(0x101).read_le16();
uint16_t got = crc16ref(MODBUS_POLY_REF, _sector->data);
_sector->status = (wanted == got) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createAesLanierDecoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new AesLanierDecoder(config));
}

20
arch/agat/agat.cc Normal file
View File

@@ -0,0 +1,20 @@
#include "globals.h"
#include "decoders/decoders.h"
#include "agat.h"
#include "bytes.h"
#include "fmt/format.h"
uint8_t agatChecksum(const Bytes& bytes)
{
uint16_t checksum = 0;
for (uint8_t b : bytes)
{
if (checksum > 0xff)
checksum = (checksum + 1) & 0xff;
checksum += b;
}
return checksum & 0xff;
}

19
arch/agat/agat.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef AGAT_H
#define AGAT_H
#define AGAT_SECTOR_SIZE 256
static constexpr uint64_t SECTOR_ID = 0x8924555549111444;
static constexpr uint64_t DATA_ID = 0x8924555514444911;
class Encoder;
class EncoderProto;
class Decoder;
class DecoderProto;
extern std::unique_ptr<Decoder> createAgatDecoder(const DecoderProto& config);
extern std::unique_ptr<Encoder> createAgatEncoder(const EncoderProto& config);
extern uint8_t agatChecksum(const Bytes& bytes);
#endif

19
arch/agat/agat.proto Normal file
View File

@@ -0,0 +1,19 @@
syntax = "proto2";
import "lib/common.proto";
message AgatDecoderProto {}
message AgatEncoderProto {
optional double target_clock_period_us = 1
[default=2.00, (help)="Data clock period of target format."];
optional double target_rotational_period_ms = 2
[default=200.0, (help)="Rotational period of target format."];
optional int32 post_index_gap_bytes = 3
[default=40, (help)="Post-index gap before first sector header."];
optional int32 pre_sector_gap_bytes = 4
[default=11, (help)="Gap before each sector header."];
optional int32 pre_data_gap_bytes = 5
[default=2, (help)="Gap before each sector data record."];
}

89
arch/agat/decoder.cc Normal file
View File

@@ -0,0 +1,89 @@
#include "globals.h"
#include "decoders/decoders.h"
#include "agat.h"
#include "crc.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "sector.h"
#include "bytes.h"
#include "fmt/format.h"
#include <string.h>
// clang-format off
/*
* data: X X X X X X X X X - - X - X - X - X X - X - X - = 0xff956a
* flux: 01 01 01 01 01 01 01 01 01 00 10 01 00 01 00 01 00 01 01 00 01 00 01 00 = 0x555549111444
*
* data: X X X X X X X X - X X - X - X - X - - X - X - X = 0xff6a95
* flux: 01 01 01 01 01 01 01 01 00 01 01 00 01 00 01 00 01 00 10 01 00 01 00 01 = 0x555514444911
*
* Each pattern is prefixed with this one:
*
* data: - - - X - - X - = 0x12
* flux: (10) 10 10 10 01 00 10 01 00 = 0xa924
* magic: (10) 10 00 10 01 00 10 01 00 = 0x8924
* ^
*
* This seems to be generated by emitting A4 in MFM and then a single 0 bit to
* shift it out of phase, so the data bits become clock bits and vice versa.
*
* X - X - - X - - = 0xA4
* 0100010010010010 = MFM encoded
* 1000100100100100 = with trailing zero
* - - - X - - X - = effective bitstream = 0x12
*/
// clang-format on
static const FluxPattern SECTOR_PATTERN(64, SECTOR_ID);
static const FluxPattern DATA_PATTERN(64, DATA_ID);
static const FluxMatchers ALL_PATTERNS = {&SECTOR_PATTERN, &DATA_PATTERN};
class AgatDecoder : public Decoder
{
public:
AgatDecoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ALL_PATTERNS);
}
void decodeSectorRecord() override
{
if (readRaw64() != SECTOR_ID)
return;
auto bytes = decodeFmMfm(readRawBits(64)).slice(0, 4);
if (bytes[3] != 0x5a)
return;
_sector->logicalTrack = bytes[1] >> 1;
_sector->logicalSector = bytes[2];
_sector->logicalSide = bytes[1] & 1;
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord() override
{
if (readRaw64() != DATA_ID)
return;
Bytes bytes = decodeFmMfm(readRawBits((AGAT_SECTOR_SIZE + 2) * 16))
.slice(0, AGAT_SECTOR_SIZE + 2);
if (bytes[AGAT_SECTOR_SIZE + 1] != 0x5a)
return;
_sector->data = bytes.slice(0, AGAT_SECTOR_SIZE);
uint8_t wantChecksum = bytes[AGAT_SECTOR_SIZE];
uint8_t gotChecksum = agatChecksum(_sector->data);
_sector->status =
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createAgatDecoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new AgatDecoder(config));
}

118
arch/agat/encoder.cc Normal file
View File

@@ -0,0 +1,118 @@
#include "lib/globals.h"
#include "lib/decoders/decoders.h"
#include "lib/encoders/encoders.h"
#include "agat.h"
#include "lib/crc.h"
#include "lib/readerwriter.h"
#include "lib/image.h"
#include "lib/layout.h"
#include "arch/agat/agat.pb.h"
#include "lib/encoders/encoders.pb.h"
class AgatEncoder : public Encoder
{
public:
AgatEncoder(const EncoderProto& config):
Encoder(config),
_config(config.agat())
{
}
private:
void writeRawBits(uint64_t data, int width)
{
_cursor += width;
_lastBit = data & 1;
for (int i = 0; i < width; i++)
{
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
void writeBytes(const Bytes& bytes)
{
encodeMfm(_bits, _cursor, bytes, _lastBit);
}
void writeByte(uint8_t byte)
{
Bytes b;
b.writer().write_8(byte);
writeBytes(b);
}
void writeFillerRawBytes(int count, uint16_t byte)
{
for (int i = 0; i < count; i++)
writeRawBits(byte, 16);
};
void writeFillerBytes(int count, uint8_t byte)
{
Bytes b{byte};
for (int i = 0; i < count; i++)
writeBytes(b);
};
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
auto trackLayout = Layout::getLayoutOfTrack(
trackInfo->logicalTrack, trackInfo->logicalSide);
double clockRateUs = _config.target_clock_period_us() / 2.0;
int bitsPerRevolution =
(_config.target_rotational_period_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
writeFillerRawBytes(_config.post_index_gap_bytes(), 0xaaaa);
for (const auto& sector : sectors)
{
/* Header */
writeFillerRawBytes(_config.pre_sector_gap_bytes(), 0xaaaa);
writeRawBits(SECTOR_ID, 64);
writeByte(0x5a);
writeByte((sector->logicalTrack << 1) | sector->logicalSide);
writeByte(sector->logicalSector);
writeByte(0x5a);
/* Data */
writeFillerRawBytes(_config.pre_data_gap_bytes(), 0xaaaa);
auto data = sector->data.slice(0, AGAT_SECTOR_SIZE);
writeRawBits(DATA_ID, 64);
writeBytes(data);
writeByte(agatChecksum(data));
writeByte(0x5a);
}
if (_cursor >= _bits.size())
error("track data overrun");
fillBitmapTo(_bits, _cursor, _bits.size(), {true, false});
auto fluxmap = std::make_unique<Fluxmap>();
fluxmap->appendBits(_bits,
calculatePhysicalClockPeriod(_config.target_clock_period_us() * 1e3,
_config.target_rotational_period_ms() * 1e6));
return fluxmap;
}
private:
const AgatEncoderProto& _config;
uint32_t _cursor;
bool _lastBit;
std::vector<bool> _bits;
};
std::unique_ptr<Encoder> createAgatEncoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new AgatEncoder(config));
}

View File

@@ -1,5 +1,4 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "amiga.h"
#include "bytes.h"
@@ -19,61 +18,61 @@ uint32_t amigaChecksum(const Bytes& bytes)
static uint8_t everyother(uint16_t x)
{
/* aabb ccdd eeff gghh */
x &= 0x6666; /* 0ab0 0cd0 0ef0 0gh0 */
x >>= 1; /* 00ab 00cd 00ef 00gh */
x |= x << 2; /* abab cdcd efef ghgh */
x &= 0x3c3c; /* 00ab cd00 00ef gh00 */
x >>= 2; /* 0000 abcd 0000 efgh */
x |= x >> 4; /* 0000 abcd abcd efgh */
return x;
/* aabb ccdd eeff gghh */
x &= 0x6666; /* 0ab0 0cd0 0ef0 0gh0 */
x >>= 1; /* 00ab 00cd 00ef 00gh */
x |= x << 2; /* abab cdcd efef ghgh */
x &= 0x3c3c; /* 00ab cd00 00ef gh00 */
x >>= 2; /* 0000 abcd 0000 efgh */
x |= x >> 4; /* 0000 abcd abcd efgh */
return x;
}
Bytes amigaInterleave(const Bytes& input)
{
Bytes output;
ByteWriter bw(output);
Bytes output;
ByteWriter bw(output);
/* Write all odd bits. (Numbering starts at 0...) */
/* Write all odd bits. (Numbering starts at 0...) */
{
ByteReader br(input);
while (!br.eof())
{
uint16_t x = br.read_be16();
x &= 0xaaaa; /* a0b0 c0d0 e0f0 g0h0 */
x |= x >> 1; /* aabb ccdd eeff gghh */
x = everyother(x); /* 0000 0000 abcd efgh */
bw.write_8(x);
}
}
{
ByteReader br(input);
while (!br.eof())
{
uint16_t x = br.read_be16();
x &= 0xaaaa; /* a0b0 c0d0 e0f0 g0h0 */
x |= x >> 1; /* aabb ccdd eeff gghh */
x = everyother(x); /* 0000 0000 abcd efgh */
bw.write_8(x);
}
}
/* Write all even bits. */
/* Write all even bits. */
{
ByteReader br(input);
while (!br.eof())
{
uint16_t x = br.read_be16();
x &= 0x5555; /* 0a0b 0c0d 0e0f 0g0h */
x |= x << 1; /* aabb ccdd eeff gghh */
x = everyother(x); /* 0000 0000 abcd efgh */
bw.write_8(x);
}
}
{
ByteReader br(input);
while (!br.eof())
{
uint16_t x = br.read_be16();
x &= 0x5555; /* 0a0b 0c0d 0e0f 0g0h */
x |= x << 1; /* aabb ccdd eeff gghh */
x = everyother(x); /* 0000 0000 abcd efgh */
bw.write_8(x);
}
}
return output;
return output;
}
Bytes amigaDeinterleave(const uint8_t*& input, size_t len)
{
assert(!(len & 1));
const uint8_t* odds = &input[0];
const uint8_t* evens = &input[len/2];
const uint8_t* evens = &input[len / 2];
Bytes output;
ByteWriter bw(output);
for (size_t i=0; i<len/2; i++)
for (size_t i = 0; i < len / 2; i++)
{
uint8_t o = *odds++;
uint8_t e = *evens++;
@@ -82,11 +81,15 @@ Bytes amigaDeinterleave(const uint8_t*& input, size_t len)
* http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
*/
uint16_t result =
(((e * 0x0101010101010101ULL & 0x8040201008040201ULL)
* 0x0102040810204081ULL >> 49) & 0x5555) |
(((o * 0x0101010101010101ULL & 0x8040201008040201ULL)
* 0x0102040810204081ULL >> 48) & 0xAAAA);
(((e * 0x0101010101010101ULL & 0x8040201008040201ULL) *
0x0102040810204081ULL >>
49) &
0x5555) |
(((o * 0x0101010101010101ULL & 0x8040201008040201ULL) *
0x0102040810204081ULL >>
48) &
0xAAAA);
bw.write_be16(result);
}
@@ -96,6 +99,6 @@ Bytes amigaDeinterleave(const uint8_t*& input, size_t len)
Bytes amigaDeinterleave(const Bytes& input)
{
const uint8_t* ptr = input.cbegin();
return amigaDeinterleave(ptr, input.size());
const uint8_t* ptr = input.cbegin();
return amigaDeinterleave(ptr, input.size());
}

View File

@@ -7,33 +7,10 @@
#define AMIGA_TRACKS_PER_DISK 80
#define AMIGA_SECTORS_PER_TRACK 11
#define AMIGA_RECORD_SIZE 0x21f
#define AMIGA_RECORD_SIZE 0x21c
class Sector;
class Fluxmap;
class SectorSet;
class AmigaDecoder : public AbstractDecoder
{
public:
virtual ~AmigaDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
std::set<unsigned> requiredSectors(Track& track) const;
};
class AmigaEncoder : public AbstractEncoder
{
public:
virtual ~AmigaEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
};
extern FlagGroup amigaEncoderFlags;
extern std::unique_ptr<Decoder> createAmigaDecoder(const DecoderProto& config);
extern std::unique_ptr<Encoder> createAmigaEncoder(const EncoderProto& config);
extern uint32_t amigaChecksum(const Bytes& bytes);
extern Bytes amigaInterleave(const Bytes& input);

13
arch/amiga/amiga.proto Normal file
View File

@@ -0,0 +1,13 @@
syntax = "proto2";
import "lib/common.proto";
message AmigaDecoderProto {}
message AmigaEncoderProto {
optional double clock_rate_us = 1
[default=2.00, (help)="Encoded data clock rate."];
optional double post_index_gap_ms = 2
[default=0.5, (help)="Post-index gap before first sector header."];
}

View File

@@ -2,66 +2,83 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "amiga.h"
#include "bytes.h"
#include "fmt/format.h"
#include "lib/decoders/decoders.pb.h"
#include <string.h>
#include <algorithm>
/*
/*
* Amiga disks use MFM but it's not quite the same as IBM MFM. They only use
* a single type of record with a different marker byte.
*
*
* See the big comment in the IBM MFM decoder for the gruesome details of how
* MFM works.
*/
static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD);
AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord()
class AmigaDecoder : public Decoder
{
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
if (_fmr->eof() || !_sector->clock)
return UNKNOWN_RECORD;
return SECTOR_RECORD;
}
public:
AmigaDecoder(const DecoderProto& config):
Decoder(config),
_config(config.amiga())
{
}
void AmigaDecoder::decodeSectorRecord()
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(SECTOR_PATTERN);
}
void decodeSectorRecord() override
{
if (readRaw48() != AMIGA_SECTOR_RECORD)
return;
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE * 16);
if (rawbits.size() < (AMIGA_RECORD_SIZE * 16))
return;
const auto& rawbytes = toBytes(rawbits).slice(0, AMIGA_RECORD_SIZE * 2);
const auto& bytes = decodeFmMfm(rawbits).slice(0, AMIGA_RECORD_SIZE);
const uint8_t* ptr = bytes.begin();
Bytes header = amigaDeinterleave(ptr, 4);
Bytes recoveryinfo = amigaDeinterleave(ptr, 16);
_sector->logicalTrack = header[1] >> 1;
_sector->logicalSide = header[1] & 1;
_sector->logicalSector = header[2];
uint32_t wantedheaderchecksum =
amigaDeinterleave(ptr, 4).reader().read_be32();
uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(0, 40));
if (gotheaderchecksum != wantedheaderchecksum)
return;
uint32_t wanteddatachecksum =
amigaDeinterleave(ptr, 4).reader().read_be32();
uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(56, 1024));
Bytes data;
data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo);
_sector->data = data;
_sector->status = (gotdatachecksum == wanteddatachecksum)
? Sector::OK
: Sector::BAD_CHECKSUM;
}
private:
const AmigaDecoderProto& _config;
nanoseconds_t _clock;
};
std::unique_ptr<Decoder> createAmigaDecoder(const DecoderProto& config)
{
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
if (rawbits.size() < (AMIGA_RECORD_SIZE*16))
return;
const auto& rawbytes = toBytes(rawbits).slice(0, AMIGA_RECORD_SIZE*2);
const auto& bytes = decodeFmMfm(rawbits).slice(0, AMIGA_RECORD_SIZE);
const uint8_t* ptr = bytes.begin() + 3;
Bytes header = amigaDeinterleave(ptr, 4);
Bytes recoveryinfo = amigaDeinterleave(ptr, 16);
_sector->logicalTrack = header[1] >> 1;
_sector->logicalSide = header[1] & 1;
_sector->logicalSector = header[2];
uint32_t wantedheaderchecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(6, 40));
if (gotheaderchecksum != wantedheaderchecksum)
return;
uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(62, 1024));
_sector->data.clear();
_sector->data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo);
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
return std::unique_ptr<Decoder>(new AmigaDecoder(config));
}
std::set<unsigned> AmigaDecoder::requiredSectors(Track& track) const
{
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
return sectors;
}

View File

@@ -1,129 +1,149 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "amiga.h"
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
FlagGroup amigaEncoderFlags;
static DoubleFlag clockRateUs(
{ "--clock-rate" },
"Encoded data clock rate (microseconds).",
2.00);
static DoubleFlag postIndexGapMs(
{ "--post-index-gap" },
"Post-index gap before first sector header (milliseconds).",
0.5);
#include "readerwriter.h"
#include "image.h"
#include "arch/amiga/amiga.pb.h"
#include "lib/encoders/encoders.pb.h"
static bool lastBit;
static int charToInt(char c)
{
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
{
for (bool bit : src)
{
if (cursor < bits.size())
bits[cursor++] = bit;
}
for (bool bit : src)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = bit;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
{
cursor += width;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
cursor += width;
lastBit = data & 1;
for (int i = 0; i < width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
{
assert(!(bytes.size() & 3));
Bytes interleaved = amigaInterleave(bytes);
encodeMfm(bits, cursor, interleaved, lastBit);
ByteReader br(bytes);
BitReader bitr(br);
while (!bitr.eof())
{
if (cursor < bits.size())
bits[cursor++] = bitr.get();
}
}
static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, uint32_t data)
static void write_sector(std::vector<bool>& bits,
unsigned& cursor,
const std::shared_ptr<const Sector>& sector)
{
Bytes b(4);
ByteWriter bw(b);
bw.write_be32(data);
write_interleaved_bytes(bits, cursor, b);
if ((sector->data.size() != 512) && (sector->data.size() != 528))
error("unsupported sector size --- you must pick 512 or 528");
uint32_t checksum = 0;
auto write_interleaved_bytes = [&](const Bytes& bytes)
{
Bytes interleaved = amigaInterleave(bytes);
Bytes mfm = encodeMfm(interleaved, lastBit);
checksum ^= amigaChecksum(mfm);
checksum &= 0x55555555;
write_bits(bits, cursor, mfm);
};
auto write_interleaved_word = [&](uint32_t word)
{
Bytes b(4);
b.writer().write_be32(word);
write_interleaved_bytes(b);
};
write_bits(bits, cursor, 0xaaaa, 2 * 8);
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6 * 8);
checksum = 0;
Bytes header = {0xff, /* Amiga 1.0 format byte */
(uint8_t)((sector->logicalTrack << 1) | sector->logicalSide),
(uint8_t)sector->logicalSector,
(uint8_t)(AMIGA_SECTORS_PER_TRACK - sector->logicalSector)};
write_interleaved_bytes(header);
Bytes recoveryInfo(16);
if (sector->data.size() == 528)
recoveryInfo = sector->data.slice(512, 16);
write_interleaved_bytes(recoveryInfo);
write_interleaved_word(checksum);
Bytes data = sector->data.slice(0, 512);
write_interleaved_word(
amigaChecksum(encodeMfm(amigaInterleave(data), lastBit)));
write_interleaved_bytes(data);
}
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector)
class AmigaEncoder : public Encoder
{
if ((sector->data.size() != 512) && (sector->data.size() != 528))
Error() << "unsupported sector size --- you must pick 512 or 528";
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6*8);
std::vector<bool> headerBits(20*16);
unsigned headerCursor = 0;
Bytes header =
{
0xff, /* Amiga 1.0 format byte */
(uint8_t) ((sector->logicalTrack<<1) | sector->logicalSide),
(uint8_t) sector->logicalSector,
(uint8_t) (AMIGA_SECTORS_PER_TRACK - sector->logicalSector)
};
write_interleaved_bytes(headerBits, headerCursor, header);
Bytes recoveryInfo(16);
if (sector->data.size() == 528)
recoveryInfo = sector->data.slice(512, 16);
write_interleaved_bytes(headerBits, headerCursor, recoveryInfo);
std::vector<bool> dataBits(512*16);
unsigned dataCursor = 0;
write_interleaved_bytes(dataBits, dataCursor, sector->data);
write_bits(bits, cursor, headerBits);
uint32_t headerChecksum = amigaChecksum(toBytes(headerBits));
write_interleaved_bytes(bits, cursor, headerChecksum);
uint32_t dataChecksum = amigaChecksum(toBytes(dataBits));
write_interleaved_bytes(bits, cursor, dataChecksum);
write_bits(bits, cursor, dataBits);
}
std::unique_ptr<Fluxmap> AmigaEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, postIndexGapMs * 1000 / clockRateUs, { true, false });
lastBit = false;
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
public:
AmigaEncoder(const EncoderProto& config):
Encoder(config),
_config(config.amiga())
{
}
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
/* Number of bits for one nominal revolution of a real 200ms Amiga disk.
*/
int bitsPerRevolution = 200e3 / _config.clock_rate_us();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
fillBitmapTo(bits,
cursor,
_config.post_index_gap_ms() * 1000 / _config.clock_rate_us(),
{true, false});
lastBit = false;
for (const auto& sector : sectors)
write_sector(bits, cursor, sector);
if (cursor >= bits.size())
error("track data overrun");
fillBitmapTo(bits, cursor, bits.size(), {true, false});
auto fluxmap = std::make_unique<Fluxmap>();
fluxmap->appendBits(bits,
calculatePhysicalClockPeriod(_config.clock_rate_us() * 1e3, 200e6));
return fluxmap;
}
private:
const AmigaEncoderProto& _config;
};
std::unique_ptr<Encoder> createAmigaEncoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new AmigaEncoder(config));
}

View File

@@ -1,25 +1,19 @@
#ifndef APPLE2_H
#define APPLE2_H
#define APPLE2_SECTOR_RECORD 0xd5aa96
#define APPLE2_DATA_RECORD 0xd5aaad
#include <memory.h>
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#define APPLE2_SECTOR_LENGTH 256
#define APPLE2_SECTOR_RECORD 0xd5aa96
#define APPLE2_DATA_RECORD 0xd5aaad
#define APPLE2_SECTOR_LENGTH 256
#define APPLE2_ENCODED_SECTOR_LENGTH 342
class Sector;
class Fluxmap;
class Apple2Decoder : public AbstractDecoder
{
public:
virtual ~Apple2Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
#define APPLE2_SECTORS 16
extern std::unique_ptr<Decoder> createApple2Decoder(const DecoderProto& config);
extern std::unique_ptr<Encoder> createApple2Encoder(const EncoderProto& config);
#endif

22
arch/apple2/apple2.proto Normal file
View File

@@ -0,0 +1,22 @@
syntax = "proto2";
import "lib/common.proto";
message Apple2DecoderProto {
optional uint32 side_one_track_offset = 1
[ default = 0, (help) = "offset to apply to track numbers on side 1" ];
}
message Apple2EncoderProto
{
/* 245kHz. */
optional double clock_period_us = 1
[ default = 4, (help) = "clock rate on the real device" ];
/* Apple II disk drives spin at 300rpm. */
optional double rotational_period_ms = 2
[ default = 200.0, (help) = "rotational period on the real device" ];
optional uint32 side_one_track_offset = 3
[ default = 0, (help) = "offset to apply to track numbers on side 1" ];
}

View File

@@ -2,10 +2,11 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "apple2.h"
#include "arch/apple2/apple2.pb.h"
#include "lib/decoders/decoders.pb.h"
#include "bytes.h"
#include "fmt/format.h"
#include <string.h>
@@ -13,22 +14,25 @@
const FluxPattern SECTOR_RECORD_PATTERN(24, APPLE2_SECTOR_RECORD);
const FluxPattern DATA_RECORD_PATTERN(24, APPLE2_DATA_RECORD);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
const FluxMatchers ANY_RECORD_PATTERN(
{&SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN});
static int decode_data_gcr(uint8_t gcr)
{
switch (gcr)
{
#define GCR_ENTRY(gcr, data) \
case gcr: return data;
#include "data_gcr.h"
#undef GCR_ENTRY
#define GCR_ENTRY(gcr, data) \
case gcr: \
return data;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
}
/* This is extremely inspired by the MESS implementation, written by Nathan Woods
* and R. Belmont: https://github.com/mamedev/mame/blob/7914a6083a3b3a8c243ae6c3b8cb50b023f21e0e/src/lib/formats/ap2_dsk.cpp
/* This is extremely inspired by the MESS implementation, written by Nathan
* Woods and R. Belmont:
* https://github.com/mamedev/mame/blob/7914a6083a3b3a8c243ae6c3b8cb50b023f21e0e/src/lib/formats/ap2_dsk.cpp
*/
static Bytes decode_crazy_data(const uint8_t* inp, Sector::Status& status)
{
@@ -48,9 +52,11 @@ static Bytes decode_crazy_data(const uint8_t* inp, Sector::Status& status)
{
/* 3 * 2 bit */
output[i + 0] = ((checksum >> 1) & 0x01) | ((checksum << 1) & 0x02);
output[i + 86] = ((checksum >> 3) & 0x01) | ((checksum >> 1) & 0x02);
output[i + 86] =
((checksum >> 3) & 0x01) | ((checksum >> 1) & 0x02);
if ((i + 172) < APPLE2_SECTOR_LENGTH)
output[i + 172] = ((checksum >> 5) & 0x01) | ((checksum >> 3) & 0x02);
output[i + 172] =
((checksum >> 5) & 0x01) | ((checksum >> 3) & 0x02);
}
}
@@ -60,53 +66,110 @@ static Bytes decode_crazy_data(const uint8_t* inp, Sector::Status& status)
return output;
}
uint8_t combine(uint16_t word)
static uint8_t combine(uint16_t word)
{
return word & (word >> 7);
}
AbstractDecoder::RecordType Apple2Decoder::advanceToNextRecord()
class Apple2Decoder : public Decoder
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
public:
Apple2Decoder(const DecoderProto& config): Decoder(config) {}
void Apple2Decoder::decodeSectorRecord()
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
if (readRaw24() != APPLE2_SECTOR_RECORD)
return;
/* Read header. */
auto header = toBytes(readRawBits(8 * 8)).slice(0, 8);
ByteReader br(header);
uint8_t volume = combine(br.read_be16());
_sector->logicalTrack = combine(br.read_be16());
_sector->logicalSide = _sector->physicalSide;
_sector->logicalSector = combine(br.read_be16());
uint8_t checksum = combine(br.read_be16());
// If the checksum is correct, upgrade the sector from MISSING
// to DATA_MISSING in anticipation of its data record
if (checksum ==
(volume ^ _sector->logicalTrack ^ _sector->logicalSector))
_sector->status =
Sector::DATA_MISSING; /* unintuitive but correct */
if (_sector->logicalSide == 1)
_sector->logicalTrack -= _config.apple2().side_one_track_offset();
/* Sanity check. */
if (_sector->logicalTrack > 100)
{
_sector->status = Sector::MISSING;
return;
}
}
void decodeDataRecord() override
{
/* Check ID. */
if (readRaw24() != APPLE2_DATA_RECORD)
return;
// Sometimes there's a 1-bit gap between APPLE2_DATA_RECORD and
// the data itself. This has been seen on real world disks
// such as the Apple II Operating System Kit from Apple2Online.
// However, I haven't seen it described in any of the various
// references.
//
// This extra '0' bit would not affect the real disk interface,
// as it was a '1' reaching the top bit of a shift register
// that triggered a byte to be available, but it affects the
// way the data is read here.
//
// While the floppies tested only seemed to need this applied
// to the first byte of the data record, applying it
// consistently to all of them doesn't seem to hurt, and
// simplifies the code.
/* Read and decode data. */
auto readApple8 = [&]()
{
auto result = 0;
while ((result & 0x80) == 0)
{
auto b = readRawBits(1);
if (b.empty())
break;
result = (result << 1) | b[0];
}
return result;
};
constexpr unsigned recordLength = APPLE2_ENCODED_SECTOR_LENGTH + 2;
uint8_t bytes[recordLength];
for (auto& byte : bytes)
{
byte = readApple8();
}
// Upgrade the sector from MISSING to BAD_CHECKSUM.
// If decode_crazy_data succeeds, it upgrades the sector to
// OK.
_sector->status = Sector::BAD_CHECKSUM;
_sector->data = decode_crazy_data(&bytes[0], _sector->status);
}
};
std::unique_ptr<Decoder> createApple2Decoder(const DecoderProto& config)
{
/* Skip ID (as we know it's a APPLE2_SECTOR_RECORD). */
readRawBits(24);
/* Read header. */
auto header = toBytes(readRawBits(8*8)).slice(0, 8);
ByteReader br(header);
uint8_t volume = combine(br.read_be16());
_sector->logicalTrack = combine(br.read_be16());
_sector->logicalSector = combine(br.read_be16());
uint8_t checksum = combine(br.read_be16());
if (checksum == (volume ^ _sector->logicalTrack ^ _sector->logicalSector))
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void Apple2Decoder::decodeDataRecord()
{
/* Check ID. */
Bytes bytes = toBytes(readRawBits(3*8)).slice(0, 3);
if (bytes.reader().read_be24() != APPLE2_DATA_RECORD)
return;
/* Read and decode data. */
unsigned recordLength = APPLE2_ENCODED_SECTOR_LENGTH + 2;
bytes = toBytes(readRawBits(recordLength*8)).slice(0, recordLength);
_sector->status = Sector::BAD_CHECKSUM;
_sector->data = decode_crazy_data(&bytes[0], _sector->status);
return std::unique_ptr<Decoder>(new Apple2Decoder(config));
}

192
arch/apple2/encoder.cc Normal file
View File

@@ -0,0 +1,192 @@
#include "globals.h"
#include "arch/apple2/apple2.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "sector.h"
#include "readerwriter.h"
#include "image.h"
#include "fmt/format.h"
#include "lib/encoders/encoders.pb.h"
#include <ctype.h>
#include "bytes.h"
static int encode_data_gcr(uint8_t data)
{
switch (data)
{
#define GCR_ENTRY(gcr, data) \
case data: \
return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
class Apple2Encoder : public Encoder
{
public:
Apple2Encoder(const EncoderProto& config):
Encoder(config),
_config(config.apple2())
{
}
private:
const Apple2EncoderProto& _config;
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
int bitsPerRevolution =
(_config.rotational_period_ms() * 1e3) / _config.clock_period_us();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (const auto& sector : sectors)
writeSector(bits, cursor, *sector);
if (cursor >= bits.size())
error("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), {true, false});
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits,
calculatePhysicalClockPeriod(_config.clock_period_us() * 1e3,
_config.rotational_period_ms() * 1e6));
return fluxmap;
}
private:
uint8_t volume_id = 254;
/* This is extremely inspired by the MESS implementation, written by Nathan
* Woods and R. Belmont:
* https://github.com/mamedev/mame/blob/7914a6083a3b3a8c243ae6c3b8cb50b023f21e0e/src/lib/formats/ap2_dsk.cpp
* as well as Understanding the Apple II (1983) Chapter 9
* https://archive.org/details/Understanding_the_Apple_II_1983_Quality_Software/page/n230/mode/1up?view=theater
*/
void writeSector(
std::vector<bool>& bits, unsigned& cursor, const Sector& sector) const
{
if ((sector.status == Sector::OK) or
(sector.status == Sector::BAD_CHECKSUM))
{
auto write_bit = [&](bool val)
{
if (cursor <= bits.size())
{
bits[cursor] = val;
}
cursor++;
};
auto write_bits = [&](uint32_t bits, int width)
{
for (int i = width; i--;)
{
write_bit(bits & (1u << i));
}
};
auto write_gcr44 = [&](uint8_t value)
{
write_bits((value << 7) | value | 0xaaaa, 16);
};
auto write_gcr6 = [&](uint8_t value)
{
write_bits(encode_data_gcr(value), 8);
};
// The special "FF40" sequence is used to synchronize the receiving
// shift register. It's written as "1111 1111 00"; FF indicates the
// 8 consecutive 1-bits, while "40" indicates the total number of
// microseconds.
auto write_ff40 = [&](int n = 1)
{
for (; n--;)
{
write_bits(0xff << 2, 10);
}
};
// There is data to encode to disk.
if ((sector.data.size() != APPLE2_SECTOR_LENGTH))
error("unsupported sector size {} --- you must pick 256",
sector.data.size());
// Write address syncing leader : A sequence of "FF40"s; 5 of them
// are said to suffice to synchronize the decoder.
// "FF40" indicates that the actual data written is "1111
// 1111 00" i.e., 8 1s and a total of 40 microseconds
//
// In standard formatting, the first logical sector apparently gets
// extra padding.
write_ff40(sector.logicalSector == 0 ? 32 : 8);
int track = sector.logicalTrack;
if (sector.logicalSide == 1)
track += _config.side_one_track_offset();
// Write address field: APPLE2_SECTOR_RECORD + sector identifier +
// DE AA EB
write_bits(APPLE2_SECTOR_RECORD, 24);
write_gcr44(volume_id);
write_gcr44(track);
write_gcr44(sector.logicalSector);
write_gcr44(volume_id ^ track ^ sector.logicalSector);
write_bits(0xDEAAEB, 24);
// Write data syncing leader: FF40 + APPLE2_DATA_RECORD + sector
// data + sum + DE AA EB (+ mystery bits cut off of the scan?)
write_ff40(8);
write_bits(APPLE2_DATA_RECORD, 24);
// Convert the sector data to GCR, append the checksum, and write it
// out
constexpr auto TWOBIT_COUNT =
0x56; // Size of the 'twobit' area at the start of the GCR data
uint8_t checksum = 0;
for (int i = 0; i < APPLE2_ENCODED_SECTOR_LENGTH; i++)
{
int value;
if (i >= TWOBIT_COUNT)
{
value = sector.data[i - TWOBIT_COUNT] >> 2;
}
else
{
uint8_t tmp = sector.data[i];
value = ((tmp & 1) << 1) | ((tmp & 2) >> 1);
tmp = sector.data[i + TWOBIT_COUNT];
value |= ((tmp & 1) << 3) | ((tmp & 2) << 1);
if (i + 2 * TWOBIT_COUNT < APPLE2_SECTOR_LENGTH)
{
tmp = sector.data[i + 2 * TWOBIT_COUNT];
value |= ((tmp & 1) << 5) | ((tmp & 2) << 3);
}
}
checksum ^= value;
// assert(checksum & ~0x3f == 0);
write_gcr6(checksum);
checksum = value;
}
if (sector.status == Sector::BAD_CHECKSUM)
checksum ^= 0x3f;
write_gcr6(checksum);
write_bits(0xDEAAEB, 24);
}
}
};
std::unique_ptr<Encoder> createApple2Encoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new Apple2Encoder(config));
}

View File

@@ -3,46 +3,19 @@
/* Brother word processor format (or at least, one of them) */
#define BROTHER_SECTOR_RECORD 0xFFFFFD57
#define BROTHER_DATA_RECORD 0xFFFFFDDB
#define BROTHER_DATA_RECORD_PAYLOAD 256
#define BROTHER_DATA_RECORD_CHECKSUM 3
#define BROTHER_SECTOR_RECORD 0xFFFFFD57
#define BROTHER_DATA_RECORD 0xFFFFFDDB
#define BROTHER_DATA_RECORD_PAYLOAD 256
#define BROTHER_DATA_RECORD_CHECKSUM 3
#define BROTHER_DATA_RECORD_ENCODED_SIZE 415
#define BROTHER_TRACKS_PER_240KB_DISK 78
#define BROTHER_TRACKS_PER_120KB_DISK 39
#define BROTHER_SECTORS_PER_TRACK 12
#define BROTHER_TRACKS_PER_240KB_DISK 78
#define BROTHER_TRACKS_PER_120KB_DISK 39
#define BROTHER_SECTORS_PER_TRACK 12
class Sector;
class Fluxmap;
class BrotherDecoder : public AbstractDecoder
{
public:
virtual ~BrotherDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
class BrotherEncoder : public AbstractEncoder
{
public:
BrotherEncoder(int format, int bias):
_format(format),
_bias(bias)
{}
virtual ~BrotherEncoder() {}
private:
int _format;
int _bias;
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
};
extern FlagGroup brotherEncoderFlags;
extern std::unique_ptr<Decoder> createBrotherDecoder(
const DecoderProto& config);
extern std::unique_ptr<Encoder> createBrotherEncoder(
const EncoderProto& config);
#endif

View File

@@ -0,0 +1,18 @@
syntax = "proto2";
message BrotherDecoderProto {}
enum BrotherFormat {
BROTHER240 = 0;
BROTHER120 = 1;
};
message BrotherEncoderProto {
optional double clock_rate_us = 1 [default = 3.83];
optional double post_index_gap_ms = 2 [default = 1.0];
optional double sector_spacing_ms = 3 [default = 16.2];
optional double post_header_spacing_ms = 4 [default = 0.69];
optional BrotherFormat format = 6 [default = BROTHER240];
}

View File

@@ -1,28 +0,0 @@
#ifndef BROTHER_H
#define BROTHER_H
/* Brother word processor format (or at least, one of them) */
#define BROTHER_SECTOR_RECORD 0xFFFFFD57
#define BROTHER_DATA_RECORD 0xFFFFFDDB
#define BROTHER_DATA_RECORD_PAYLOAD 256
#define BROTHER_DATA_RECORD_CHECKSUM 3
class Sector;
class Fluxmap;
class BrotherDecoder : public AbstractDecoder
{
public:
virtual ~BrotherDecoder() {}
SectorVector decodeToSectors(const RawRecordVector& rawRecords);
int recordMatcher(uint64_t fifo) const;
};
extern void writeBrotherSectorHeader(std::vector<bool>& bits, unsigned& cursor,
int track, int sector);
extern void writeBrotherSectorData(std::vector<bool>& bits, unsigned& cursor,
const std::vector<uint8_t>& data);
#endif

View File

@@ -1,13 +1,13 @@
GCR_ENTRY(0x55, 0) // 00000
GCR_ENTRY(0x57, 1) // 00001
GCR_ENTRY(0x5b, 2) // 00010
GCR_ENTRY(0x5d, 3) // 00011
GCR_ENTRY(0x5f, 4) // 00100
GCR_ENTRY(0x6b, 5) // 00101
GCR_ENTRY(0x6d, 6) // 00110
GCR_ENTRY(0x6f, 7) // 00111
GCR_ENTRY(0x75, 8) // 01000
GCR_ENTRY(0x77, 9) // 01001
GCR_ENTRY(0x55, 0) // 00000
GCR_ENTRY(0x57, 1) // 00001
GCR_ENTRY(0x5b, 2) // 00010
GCR_ENTRY(0x5d, 3) // 00011
GCR_ENTRY(0x5f, 4) // 00100
GCR_ENTRY(0x6b, 5) // 00101
GCR_ENTRY(0x6d, 6) // 00110
GCR_ENTRY(0x6f, 7) // 00111
GCR_ENTRY(0x75, 8) // 01000
GCR_ENTRY(0x77, 9) // 01001
GCR_ENTRY(0x7b, 10) // 01010
GCR_ENTRY(0x7d, 11) // 01011
GCR_ENTRY(0x7f, 12) // 01100
@@ -30,4 +30,3 @@ GCR_ENTRY(0xef, 28) // 11100
GCR_ENTRY(0xf5, 29) // 11101
GCR_ENTRY(0xf7, 30) // 11110
GCR_ENTRY(0xfb, 31) // 11111

View File

@@ -1,10 +1,8 @@
#include "globals.h"
#include "sql.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "record.h"
#include "brother.h"
#include "sector.h"
#include "bytes.h"
@@ -13,7 +11,8 @@
const FluxPattern SECTOR_RECORD_PATTERN(32, BROTHER_SECTOR_RECORD);
const FluxPattern DATA_RECORD_PATTERN(32, BROTHER_DATA_RECORD);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
const FluxMatchers ANY_RECORD_PATTERN(
{&SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN});
static std::vector<uint8_t> outputbuffer;
@@ -34,76 +33,89 @@ static int decode_data_gcr(uint8_t gcr)
{
switch (gcr)
{
#define GCR_ENTRY(gcr, data) \
case gcr: return data;
#include "data_gcr.h"
#undef GCR_ENTRY
#define GCR_ENTRY(gcr, data) \
case gcr: \
return data;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
}
static int decode_header_gcr(uint16_t word)
{
switch (word)
{
#define GCR_ENTRY(gcr, data) \
case gcr: return data;
#include "header_gcr.h"
#undef GCR_ENTRY
}
return -1;
switch (word)
{
#define GCR_ENTRY(gcr, data) \
case gcr: \
return data;
#include "header_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
class BrotherDecoder : public Decoder
{
public:
BrotherDecoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
if (readRaw32() != BROTHER_SECTOR_RECORD)
return;
const auto& rawbits = readRawBits(32);
const auto& bytes = toBytes(rawbits).slice(0, 4);
ByteReader br(bytes);
_sector->logicalTrack = decode_header_gcr(br.read_be16());
_sector->logicalSector = decode_header_gcr(br.read_be16());
/* Sanity check the values read; there's no header checksum and
* occasionally we get garbage due to bit errors. */
if (_sector->logicalSector > 11)
return;
if (_sector->logicalTrack > 79)
return;
_sector->status = Sector::DATA_MISSING;
}
void decodeDataRecord() override
{
if (readRaw32() != BROTHER_DATA_RECORD)
return;
const auto& rawbits = readRawBits(BROTHER_DATA_RECORD_ENCODED_SIZE * 8);
const auto& rawbytes =
toBytes(rawbits).slice(0, BROTHER_DATA_RECORD_ENCODED_SIZE);
Bytes bytes;
ByteWriter bw(bytes);
BitWriter bitw(bw);
for (uint8_t b : rawbytes)
{
uint32_t nibble = decode_data_gcr(b);
bitw.push(nibble, 5);
}
bitw.flush();
_sector->data = bytes.slice(0, BROTHER_DATA_RECORD_PAYLOAD);
uint32_t realCrc = crcbrother(_sector->data);
uint32_t wantCrc =
bytes.reader().seek(BROTHER_DATA_RECORD_PAYLOAD).read_be24();
_sector->status =
(realCrc == wantCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
AbstractDecoder::RecordType BrotherDecoder::advanceToNextRecord()
std::unique_ptr<Decoder> createBrotherDecoder(const DecoderProto& config)
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
void BrotherDecoder::decodeSectorRecord()
{
readRawBits(32);
const auto& rawbits = readRawBits(32);
const auto& bytes = toBytes(rawbits).slice(0, 4);
ByteReader br(bytes);
_sector->logicalTrack = decode_header_gcr(br.read_be16());
_sector->logicalSector = decode_header_gcr(br.read_be16());
/* Sanity check the values read; there's no header checksum and
* occasionally we get garbage due to bit errors. */
if (_sector->logicalSector > 11)
return;
if (_sector->logicalTrack > 79)
return;
_sector->status = Sector::DATA_MISSING;
}
void BrotherDecoder::decodeDataRecord()
{
readRawBits(32);
const auto& rawbits = readRawBits(BROTHER_DATA_RECORD_ENCODED_SIZE*8);
const auto& rawbytes = toBytes(rawbits).slice(0, BROTHER_DATA_RECORD_ENCODED_SIZE);
Bytes bytes;
ByteWriter bw(bytes);
BitWriter bitw(bw);
for (uint8_t b : rawbytes)
{
uint32_t nibble = decode_data_gcr(b);
bitw.push(nibble, 5);
}
bitw.flush();
_sector->data = bytes.slice(0, BROTHER_DATA_RECORD_PAYLOAD);
uint32_t realCrc = crcbrother(_sector->data);
uint32_t wantCrc = bytes.reader().seek(BROTHER_DATA_RECORD_PAYLOAD).read_be24();
_sector->status = (realCrc == wantCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
return std::unique_ptr<Decoder>(new BrotherDecoder(config));
}

View File

@@ -1,183 +1,154 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "brother.h"
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
FlagGroup brotherEncoderFlags;
static DoubleFlag clockRateUs(
{ "--clock-rate" },
"Encoded data clock rate (microseconds).",
3.83);
static DoubleFlag postIndexGapMs(
{ "--post-index-gap" },
"Post-index gap before first sector header (milliseconds).",
1.0);
static DoubleFlag sectorSpacingMs(
{ "--sector-spacing" },
"Time between successive sector headers (milliseconds).",
16.2);
static DoubleFlag postHeaderSpacingMs(
{ "--post-header-spacing" },
"Time between a sector's header and data records (milliseconds).",
0.69);
static StringFlag sectorSkew(
{ "--sector-skew" },
"Order in which to write sectors.",
"05a3816b4927");
#include "readerwriter.h"
#include "image.h"
#include "arch/brother/brother.pb.h"
#include "lib/encoders/encoders.pb.h"
static int encode_header_gcr(uint16_t word)
{
switch (word)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "header_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
switch (word)
{
#define GCR_ENTRY(gcr, data) \
case data: \
return gcr;
#include "header_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
static int encode_data_gcr(uint8_t data)
{
switch (data)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width)
{
cursor += width;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
switch (data)
{
#define GCR_ENTRY(gcr, data) \
case data: \
return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
static void write_sector_header(std::vector<bool>& bits, unsigned& cursor,
int track, int sector)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width)
{
write_bits(bits, cursor, 0xffffffff, 31);
write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32);
write_bits(bits, cursor, encode_header_gcr(track), 16);
write_bits(bits, cursor, encode_header_gcr(sector), 16);
write_bits(bits, cursor, encode_header_gcr(0x2f), 16);
cursor += width;
for (int i = 0; i < width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
static void write_sector_data(std::vector<bool>& bits, unsigned& cursor, const Bytes& data)
static void write_sector_header(
std::vector<bool>& bits, unsigned& cursor, int track, int sector)
{
write_bits(bits, cursor, 0xffffffff, 32);
write_bits(bits, cursor, BROTHER_DATA_RECORD, 32);
write_bits(bits, cursor, 0xffffffff, 31);
write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32);
write_bits(bits, cursor, encode_header_gcr(track), 16);
write_bits(bits, cursor, encode_header_gcr(sector), 16);
write_bits(bits, cursor, encode_header_gcr(0x2f), 16);
}
uint16_t fifo = 0;
int width = 0;
static void write_sector_data(
std::vector<bool>& bits, unsigned& cursor, const Bytes& data)
{
write_bits(bits, cursor, 0xffffffff, 32);
write_bits(bits, cursor, BROTHER_DATA_RECORD, 32);
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
Error() << "unsupported sector size";
uint16_t fifo = 0;
int width = 0;
auto write_byte = [&](uint8_t byte)
{
fifo |= (byte << (8 - width));
width += 8;
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
error("unsupported sector size");
while (width >= 5)
{
uint8_t quintet = fifo >> 11;
fifo <<= 5;
width -= 5;
auto write_byte = [&](uint8_t byte)
{
fifo |= (byte << (8 - width));
width += 8;
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
}
};
while (width >= 5)
{
uint8_t quintet = fifo >> 11;
fifo <<= 5;
width -= 5;
for (uint8_t byte : data)
write_byte(byte);
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
}
};
uint32_t realCrc = crcbrother(data);
write_byte(realCrc>>16);
write_byte(realCrc>>8);
write_byte(realCrc);
write_byte(0x58); /* magic */
for (uint8_t byte : data)
write_byte(byte);
uint32_t realCrc = crcbrother(data);
write_byte(realCrc >> 16);
write_byte(realCrc >> 8);
write_byte(realCrc);
write_byte(0x58); /* magic */
write_byte(0xd4);
while (width != 0)
write_byte(0);
}
static int charToInt(char c)
class BrotherEncoder : public Encoder
{
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
}
public:
BrotherEncoder(const EncoderProto& config):
Encoder(config),
_config(config.brother())
{
}
std::unique_ptr<Fluxmap> BrotherEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
int sectorCount = 0;
for (const auto& sectorData : sectors)
{
double headerMs = _config.post_index_gap_ms() +
sectorCount * _config.sector_spacing_ms();
unsigned headerCursor = headerMs * 1e3 / _config.clock_rate_us();
double dataMs = headerMs + _config.post_header_spacing_ms();
unsigned dataCursor = dataMs * 1e3 / _config.clock_rate_us();
fillBitmapTo(bits, cursor, headerCursor, {true, false});
write_sector_header(bits,
cursor,
sectorData->logicalTrack,
sectorData->logicalSector);
fillBitmapTo(bits, cursor, dataCursor, {true, false});
write_sector_data(bits, cursor, sectorData->data);
sectorCount++;
}
if (cursor >= bits.size())
error("track data overrun");
fillBitmapTo(bits, cursor, bits.size(), {true, false});
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, _config.clock_rate_us() * 1e3);
return fluxmap;
}
private:
const BrotherEncoderProto& _config;
};
std::unique_ptr<Encoder> createBrotherEncoder(const EncoderProto& config)
{
int logicalTrack;
if (physicalSide != 0)
return std::unique_ptr<Fluxmap>();
physicalTrack -= _bias;
switch (_format)
{
case 120:
if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2))
|| (physicalTrack & 1))
return std::unique_ptr<Fluxmap>();
logicalTrack = physicalTrack/2;
break;
case 240:
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
return std::unique_ptr<Fluxmap>();
logicalTrack = physicalTrack;
break;
}
int bitsPerRevolution = 200000.0 / clockRateUs;
const std::string& skew = sectorSkew.get();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (int sectorCount=0; sectorCount<BROTHER_SECTORS_PER_TRACK; sectorCount++)
{
int sectorId = charToInt(skew.at(sectorCount));
double headerMs = postIndexGapMs + sectorCount*sectorSpacingMs;
unsigned headerCursor = headerMs*1e3 / clockRateUs;
double dataMs = headerMs + postHeaderSpacingMs;
unsigned dataCursor = dataMs*1e3 / clockRateUs;
const auto& sectorData = allSectors.get(logicalTrack, 0, sectorId);
fillBitmapTo(bits, cursor, headerCursor, { true, false });
write_sector_header(bits, cursor, logicalTrack, sectorId);
fillBitmapTo(bits, cursor, dataCursor, { true, false });
write_sector_data(bits, cursor, sectorData->data);
}
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
// The pre-index gap is not normally reported.
// std::cerr << "pre-index gap " << 200.0 - (double)cursor*clockRateUs/1e3 << std::endl;
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
return std::unique_ptr<Encoder>(new BrotherEncoder(config));
}

View File

@@ -76,4 +76,3 @@ GCR_ENTRY(0x6BAB, 74)
GCR_ENTRY(0xAD5F, 75)
GCR_ENTRY(0xDBED, 76)
GCR_ENTRY(0x55BB, 77)

43
arch/build.mk Normal file
View File

@@ -0,0 +1,43 @@
LIBARCH_SRCS = \
arch/aeslanier/decoder.cc \
arch/agat/agat.cc \
arch/agat/decoder.cc \
arch/agat/encoder.cc \
arch/amiga/amiga.cc \
arch/amiga/decoder.cc \
arch/amiga/encoder.cc \
arch/apple2/decoder.cc \
arch/apple2/encoder.cc \
arch/brother/decoder.cc \
arch/brother/encoder.cc \
arch/c64/c64.cc \
arch/c64/decoder.cc \
arch/c64/encoder.cc \
arch/f85/decoder.cc \
arch/fb100/decoder.cc \
arch/ibm/decoder.cc \
arch/ibm/encoder.cc \
arch/macintosh/decoder.cc \
arch/macintosh/encoder.cc \
arch/micropolis/decoder.cc \
arch/micropolis/encoder.cc \
arch/mx/decoder.cc \
arch/northstar/decoder.cc \
arch/northstar/encoder.cc \
arch/rolandd20/decoder.cc \
arch/smaky6/decoder.cc \
arch/tids990/decoder.cc \
arch/tids990/encoder.cc \
arch/victor9k/decoder.cc \
arch/victor9k/encoder.cc \
arch/zilogmcz/decoder.cc \
LIBARCH_OBJS = $(patsubst %.cc, $(OBJDIR)/%.o, $(LIBARCH_SRCS))
OBJS += $(LIBARCH_OBJS)
$(LIBARCH_SRCS): | $(PROTO_HDRS)
$(LIBARCH_SRCS): CFLAGS += $(PROTO_CFLAGS)
LIBARCH_LIB = $(OBJDIR)/libarch.a
LIBARCH_LDFLAGS =
$(LIBARCH_LIB): $(LIBARCH_OBJS)
$(call use-pkgconfig, $(LIBARCH_LIB), $(LIBARCH_OBJS), fmt)

28
arch/c64/c64.cc Normal file
View File

@@ -0,0 +1,28 @@
#include "globals.h"
#include "c64.h"
/*
* Track Sectors/track # Sectors Storage in Bytes Clock rate
* ----- ------------- --------- ---------------- ----------
* 1-17 21 357 7820 3.25
* 18-24 19 133 7170 3.5
* 25-30 18 108 6300 3.75
* 31-40(*) 17 85 6020 4
* ---
* 683 (for a 35 track image)
*
* The clock rate is normalised for a 200ms drive.
*/
nanoseconds_t clockPeriodForC64Track(unsigned track)
{
constexpr double b = 8.0;
if (track < 17)
return 26.0 / b;
if (track < 24)
return 28.0 / b;
if (track < 30)
return 30.0 / b;
return 32.0 / b;
}

View File

@@ -1,21 +1,37 @@
#ifndef C64_H
#define C64_H
#define C64_SECTOR_RECORD 0xffd49
#define C64_DATA_RECORD 0xffd57
#define C64_SECTOR_LENGTH 256
#include "decoders/decoders.h"
#include "encoders/encoders.h"
class Sector;
class Fluxmap;
#define C64_SECTOR_RECORD 0xffd49
#define C64_DATA_RECORD 0xffd57
#define C64_SECTOR_LENGTH 256
class Commodore64Decoder : public AbstractDecoder
{
public:
virtual ~Commodore64Decoder() {}
/* Source: http://www.unusedino.de/ec64/technical/formats/g64.html
1. Header sync FF FF FF FF FF (40 'on' bits, not GCR)
2. Header info 52 54 B5 29 4B 7A 5E 95 55 55 (10 GCR bytes)
3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read)
4. Data sync FF FF FF FF FF (40 'on' bits, not GCR)
5. Data block 55...4A (325 GCR bytes)
6. Inter-sector gap 55 55 55 55...55 55 (4 to 12 bytes, never read)
1. Header sync (SYNC for the next sector)
*/
#define C64_HEADER_DATA_SYNC 0xFF
#define C64_HEADER_BLOCK_ID 0x08
#define C64_DATA_BLOCK_ID 0x07
#define C64_HEADER_GAP 0x55
#define C64_INTER_SECTOR_GAP 0x55
#define C64_PADDING 0x0F
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
#define C64_TRACKS_PER_DISK 40
#define C64_BAM_TRACK 17
extern std::unique_ptr<Decoder> createCommodore64Decoder(
const DecoderProto& config);
extern std::unique_ptr<Encoder> createCommodore64Encoder(
const EncoderProto& config);
extern nanoseconds_t clockPeriodForC64Track(unsigned track);
#endif

11
arch/c64/c64.proto Normal file
View File

@@ -0,0 +1,11 @@
syntax = "proto2";
import "lib/common.proto";
message Commodore64DecoderProto {}
message Commodore64EncoderProto {
optional double post_index_gap_us = 1 [default=0.0,
(help) = "post-index gap before first sector header."];
}

View File

@@ -2,7 +2,6 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "c64.h"
@@ -14,19 +13,21 @@
const FluxPattern SECTOR_RECORD_PATTERN(20, C64_SECTOR_RECORD);
const FluxPattern DATA_RECORD_PATTERN(20, C64_DATA_RECORD);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
const FluxMatchers ANY_RECORD_PATTERN(
{&SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN});
static int decode_data_gcr(uint8_t gcr)
{
switch (gcr)
{
#define GCR_ENTRY(gcr, data) \
case gcr: return data;
#include "data_gcr.h"
#undef GCR_ENTRY
#define GCR_ENTRY(gcr, data) \
case gcr: \
return data;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
}
static Bytes decode(const std::vector<bool>& bits)
{
@@ -38,11 +39,11 @@ static Bytes decode(const std::vector<bool>& bits)
while (ii != bits.end())
{
uint8_t inputfifo = 0;
for (size_t i=0; i<5; i++)
for (size_t i = 0; i < 5; i++)
{
if (ii == bits.end())
break;
inputfifo = (inputfifo<<1) | *ii++;
inputfifo = (inputfifo << 1) | *ii++;
}
bitw.push(decode_data_gcr(inputfifo), 4);
@@ -52,41 +53,50 @@ static Bytes decode(const std::vector<bool>& bits)
return output;
}
AbstractDecoder::RecordType Commodore64Decoder::advanceToNextRecord()
class Commodore64Decoder : public Decoder
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
public:
Commodore64Decoder(const DecoderProto& config): Decoder(config) {}
void Commodore64Decoder::decodeSectorRecord()
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
if (readRaw20() != C64_SECTOR_RECORD)
return;
const auto& bits = readRawBits(5 * 10);
const auto& bytes = decode(bits).slice(0, 5);
uint8_t checksum = bytes[0];
_sector->logicalSector = bytes[1];
_sector->logicalSide = 0;
_sector->logicalTrack = bytes[2] - 1;
if (checksum == xorBytes(bytes.slice(1, 4)))
_sector->status =
Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord() override
{
if (readRaw20() != C64_DATA_RECORD)
return;
const auto& bits = readRawBits(259 * 10);
const auto& bytes = decode(bits).slice(0, 259);
_sector->data = bytes.slice(0, C64_SECTOR_LENGTH);
uint8_t gotChecksum = xorBytes(_sector->data);
uint8_t wantChecksum = bytes[256];
_sector->status =
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createCommodore64Decoder(const DecoderProto& config)
{
readRawBits(20);
const auto& bits = readRawBits(5*10);
const auto& bytes = decode(bits).slice(0, 5);
uint8_t checksum = bytes[0];
_sector->logicalSector = bytes[1];
_sector->logicalSide = 0;
_sector->logicalTrack = bytes[2] - 1;
if (checksum == xorBytes(bytes.slice(1, 4)))
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void Commodore64Decoder::decodeDataRecord()
{
readRawBits(20);
const auto& bits = readRawBits(259*10);
const auto& bytes = decode(bits).slice(0, 259);
_sector->data = bytes.slice(0, C64_SECTOR_LENGTH);
uint8_t gotChecksum = xorBytes(_sector->data);
uint8_t wantChecksum = bytes[256];
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
return std::unique_ptr<Decoder>(new Commodore64Decoder(config));
}

338
arch/c64/encoder.cc Normal file
View File

@@ -0,0 +1,338 @@
#include "globals.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "c64.h"
#include "crc.h"
#include "sector.h"
#include "readerwriter.h"
#include "image.h"
#include "fmt/format.h"
#include "arch/c64/c64.pb.h"
#include "lib/encoders/encoders.pb.h"
#include "lib/layout.h"
#include <ctype.h>
#include "bytes.h"
static bool lastBit;
static int encode_data_gcr(uint8_t data)
{
switch (data)
{
#define GCR_ENTRY(gcr, data) \
case data: \
return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
{
for (bool bit : src) // Range-based for loop
{
if (cursor < bits.size())
bits[cursor++] = bit;
}
}
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
{
cursor += width;
for (int i = 0; i < width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
void bindump(std::ostream& stream, std::vector<bool>& buffer)
{
size_t pos = 0;
while ((pos < buffer.size()) and (pos < 520))
{
stream << fmt::format("{:5d} : ", pos);
for (int i = 0; i < 40; i++)
{
if ((pos + i) < buffer.size())
stream << fmt::format("{:01b}", (buffer[pos + i]));
else
stream << "-- ";
if ((((pos + i + 1) % 8) == 0) and i != 0)
stream << " ";
}
stream << std::endl;
pos += 40;
}
}
static std::vector<bool> encode_data(uint8_t input)
{
/*
* Four 8-bit data bytes are converted to four 10-bit GCR bytes at a time by
* the 1541 DOS. RAM is only an 8-bit storage device though. This hardware
* limitation prevents a 10-bit GCR byte from being stored in a single
* memory location. Four 10-bit GCR bytes total 40 bits - a number evenly
* divisible by our overriding 8-bit constraint. Commodore sub- divides the
* 40 GCR bits into five 8-bit bytes to solve this dilemma. This explains
* why four 8-bit data bytes are converted to GCR form at a time. The
* following step by step example demonstrates how this bit manipulation is
* performed by the DOS.
*
* STEP 1. Four 8-bit Data Bytes
* $08 $10 $00 $12
*
* STEP 2. Hexadecimal to Binary Conversion
* 1. Binary Equivalents
* $08 $10 $00 $12
* 00001000 00010000 00000000 00010010
*
* STEP 3. Binary to GCR Conversion
* 1. Four 8-bit Data Bytes
* 00001000 00010000 00000000 00010010
* 2. High and Low Nybbles
* 0000 1000 0001 0000 0000 0000 0001 0010
* 3. High and Low Nybble GCR Equivalents
* 01010 01001 01011 01010 01010 01010 01011 10010
* 4. Four 10-bit GCR Bytes
* 0101001001 0101101010 0101001010 0101110010
*
* STEP 4. 10-bit GCR to 8-bit GCR Conversion
* 1. Concatenate Four 10-bit GCR Bytes
* 0101001001010110101001010010100101110010
* 2. Five 8-bit Subdivisions
* 01010010 01010110 10100101 00101001 01110010
*
* STEP 5. Binary to Hexadecimal Conversion
* 1. Hexadecimal Equivalents
* 01010010 01010110 10100101 00101001 01110010
* $52 $56 $A5 $29 $72
*
* STEP 6. Four 8-bit Data Bytes are Recorded as Five 8-bit GCR Bytes
* $08 $10 $00 $12
*
* are recorded as
* $52 $56 $A5 $29 $72
*/
std::vector<bool> output(10, false);
uint8_t hi = 0;
uint8_t lo = 0;
uint8_t lo_GCR = 0;
uint8_t hi_GCR = 0;
// Convert the byte in high and low nibble
lo = input >> 4; // get the lo nibble shift the bits 4 to the right
hi = input & 15; // get the hi nibble bij masking the lo bits (00001111)
lo_GCR = encode_data_gcr(lo); // example value: 0000 GCR = 01010
hi_GCR = encode_data_gcr(hi); // example value: 1000 GCR = 01001
// output = [0,1,2,3,4,5,6,7,8,9]
// value = [0,1,0,1,0,0,1,0,0,1]
// 01010 01001
int b = 4;
for (int i = 0; i < 10; i++)
{
if (i < 5) // 01234
{ // i = 0 op
output[4 - i] = (lo_GCR & 1); // 01010
// 01010 -> & 00001 -> 00000 output[4] = 0
// 00101 -> & 00001 -> 00001 output[3] = 1
// 00010 -> & 00001 -> 00000 output[2] = 0
// 00001 -> & 00001 -> 00001 output[1] = 1
// 00000 -> & 00001 -> 00000 output[0] = 0
lo_GCR >>= 1;
}
else
{
output[i + b] = (hi_GCR & 1); // 01001
// 01001 -> & 00001 -> 00001 output[9] = 1
// 00100 -> & 00001 -> 00000 output[8] = 0
// 00010 -> & 00001 -> 00000 output[7] = 0
// 00001 -> & 00001 -> 00001 output[6] = 1
// 00000 -> & 00001 -> 00000 output[5] = 0
hi_GCR >>= 1;
b = b - 2;
}
}
return output;
}
class Commodore64Encoder : public Encoder
{
public:
Commodore64Encoder(const EncoderProto& config):
Encoder(config),
_config(config.c64())
{
}
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
/* The format ID Character # 1 and # 2 are in the .d64 image only
* present in track 18 sector zero which contains the BAM info in byte
* 162 and 163. it is written in every header of every sector and track.
* headers are not stored in a d64 disk image so we have to get it from
* track 18 which contains the BAM.
*/
const auto& sectorData = image.get(
C64_BAM_TRACK, 0, 0); // Read de BAM to get the DISK ID bytes
if (sectorData)
{
ByteReader br(sectorData->data);
br.seek(162); // goto position of the first Disk ID Byte
_formatByte1 = br.read_8();
_formatByte2 = br.read_8();
}
else
_formatByte1 = _formatByte2 = 0;
double clockRateUs = clockPeriodForC64Track(trackInfo->logicalTrack);
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits,
cursor,
_config.post_index_gap_us() / clockRateUs,
{true, false});
lastBit = false;
for (const auto& sector : sectors)
writeSector(bits, cursor, sector);
if (cursor >= bits.size())
error("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), {true, false});
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(
bits, calculatePhysicalClockPeriod(clockRateUs * 1e3, 200e6));
return fluxmap;
}
private:
void writeSector(std::vector<bool>& bits,
unsigned& cursor,
std::shared_ptr<const Sector> sector) const
{
/* Source: http://www.unusedino.de/ec64/technical/formats/g64.html
* 1. Header sync FF FF FF FF FF (40 'on' bits, not GCR)
* 2. Header info 52 54 B5 29 4B 7A 5E 95 55 55 (10 GCR bytes)
* 3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read)
* 4. Data sync FF FF FF FF FF (40 'on' bits, not GCR)
* 5. Data block 55...4A (325 GCR bytes)
* 6. Inter-sector gap 55 55 55 55...55 55 (4 to 12 bytes, never read)
* 1. Header sync (SYNC for the next sector)
*/
if ((sector->status == Sector::OK) or
(sector->status == Sector::BAD_CHECKSUM))
{
// There is data to encode to disk.
if ((sector->data.size() != C64_SECTOR_LENGTH))
error("unsupported sector size {} --- you must pick 256",
sector->data.size());
// 1. Write header Sync (not GCR)
for (int i = 0; i < 6; i++)
write_bits(
bits, cursor, C64_HEADER_DATA_SYNC, 1 * 8); /* sync */
// 2. Write Header info 10 GCR bytes
/*
* The 10 byte header info (#2) is GCR encoded and must be decoded
* to it's normal 8 bytes to be understood. Once decoded, its
* breakdown is as follows:
*
* Byte $00 - header block ID ($08)
* 01 - header block checksum 16 (EOR of $02-$05)
* 02 - Sector
* 03 - Track
* 04 - Format ID byte #2
* 05 - Format ID byte #1
* 06-07 - $0F ("off" bytes)
*/
uint8_t encodedTrack =
((sector->logicalTrack) +
1); // C64 track numbering starts with 1. Fluxengine with 0.
uint8_t encodedSector = sector->logicalSector;
// uint8_t formatByte1 = C64_FORMAT_ID_BYTE1;
// uint8_t formatByte2 = C64_FORMAT_ID_BYTE2;
uint8_t headerChecksum =
(encodedTrack ^ encodedSector ^ _formatByte1 ^ _formatByte2);
write_bits(bits, cursor, encode_data(C64_HEADER_BLOCK_ID));
write_bits(bits, cursor, encode_data(headerChecksum));
write_bits(bits, cursor, encode_data(encodedSector));
write_bits(bits, cursor, encode_data(encodedTrack));
write_bits(bits, cursor, encode_data(_formatByte2));
write_bits(bits, cursor, encode_data(_formatByte1));
write_bits(bits, cursor, encode_data(C64_PADDING));
write_bits(bits, cursor, encode_data(C64_PADDING));
// 3. Write header GAP not GCR
for (int i = 0; i < 9; i++)
write_bits(
bits, cursor, C64_HEADER_GAP, 1 * 8); /* header gap */
// 4. Write Data sync not GCR
for (int i = 0; i < 6; i++)
write_bits(
bits, cursor, C64_HEADER_DATA_SYNC, 1 * 8); /* sync */
// 5. Write data block 325 GCR bytes
/*
* The 325 byte data block (#5) is GCR encoded and must be decoded
* to its normal 260 bytes to be understood. The data block is made
* up of the following:
*
* Byte $00 - data block ID ($07)
* 01-100 - 256 bytes data
* 101 - data block checksum (EOR of $01-100)
* 102-103 - $00 ("off" bytes, to make the sector size a
* multiple of 5)
*/
write_bits(bits, cursor, encode_data(C64_DATA_BLOCK_ID));
uint8_t dataChecksum = xorBytes(sector->data);
ByteReader br(sector->data);
int i = 0;
for (i = 0; i < C64_SECTOR_LENGTH; i++)
{
uint8_t val = br.read_8();
write_bits(bits, cursor, encode_data(val));
}
write_bits(bits, cursor, encode_data(dataChecksum));
write_bits(bits, cursor, encode_data(C64_PADDING));
write_bits(bits, cursor, encode_data(C64_PADDING));
// 6. Write inter-sector gap 9 - 12 bytes nor gcr
for (int i = 0; i < 9; i++)
write_bits(
bits, cursor, C64_INTER_SECTOR_GAP, 1 * 8); /* sync */
}
}
private:
const Commodore64EncoderProto& _config;
uint8_t _formatByte1;
uint8_t _formatByte2;
};
std::unique_ptr<Encoder> createCommodore64Encoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new Commodore64Encoder(config));
}
// vim: sw=4 ts=4 et

View File

@@ -2,7 +2,6 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "f85.h"
@@ -14,19 +13,21 @@
const FluxPattern SECTOR_RECORD_PATTERN(24, F85_SECTOR_RECORD);
const FluxPattern DATA_RECORD_PATTERN(24, F85_DATA_RECORD);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
const FluxMatchers ANY_RECORD_PATTERN(
{&SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN});
static int decode_data_gcr(uint8_t gcr)
{
switch (gcr)
{
#define GCR_ENTRY(gcr, data) \
case gcr: return data;
#include "data_gcr.h"
#undef GCR_ENTRY
#define GCR_ENTRY(gcr, data) \
case gcr: \
return data;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
}
static Bytes decode(const std::vector<bool>& bits)
{
@@ -38,11 +39,11 @@ static Bytes decode(const std::vector<bool>& bits)
while (ii != bits.end())
{
uint8_t inputfifo = 0;
for (size_t i=0; i<5; i++)
for (size_t i = 0; i < 5; i++)
{
if (ii == bits.end())
break;
inputfifo = (inputfifo<<1) | *ii++;
inputfifo = (inputfifo << 1) | *ii++;
}
bitw.push(decode_data_gcr(inputfifo), 4);
@@ -52,49 +53,58 @@ static Bytes decode(const std::vector<bool>& bits)
return output;
}
AbstractDecoder::RecordType DurangoF85Decoder::advanceToNextRecord()
class DurangoF85Decoder : public Decoder
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
public:
DurangoF85Decoder(const DecoderProto& config): Decoder(config) {}
void DurangoF85Decoder::decodeSectorRecord()
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
/* Skip sync bits and ID byte. */
if (readRaw24() != F85_SECTOR_RECORD)
return;
/* Read header. */
const auto& bytes = decode(readRawBits(6 * 10));
_sector->logicalSector = bytes[2];
_sector->logicalSide = 0;
_sector->logicalTrack = bytes[0];
uint16_t wantChecksum = bytes.reader().seek(4).read_be16();
uint16_t gotChecksum = crc16(CCITT_POLY, 0xef21, bytes.slice(0, 4));
if (wantChecksum == gotChecksum)
_sector->status =
Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord() override
{
/* Skip sync bits ID byte. */
if (readRaw24() != F85_DATA_RECORD)
return;
const auto& bytes = decode(readRawBits((F85_SECTOR_LENGTH + 3) * 10))
.slice(0, F85_SECTOR_LENGTH + 3);
ByteReader br(bytes);
_sector->data = br.read(F85_SECTOR_LENGTH);
uint16_t wantChecksum = br.read_be16();
uint16_t gotChecksum = crc16(CCITT_POLY, 0xbf84, _sector->data);
_sector->status =
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createDurangoF85Decoder(const DecoderProto& config)
{
/* Skip sync bits and ID byte. */
readRawBits(24);
/* Read header. */
const auto& bytes = decode(readRawBits(6*10));
_sector->logicalSector = bytes[2];
_sector->logicalSide = 0;
_sector->logicalTrack = bytes[0];
uint16_t wantChecksum = bytes.reader().seek(4).read_be16();
uint16_t gotChecksum = crc16(CCITT_POLY, 0xef21, bytes.slice(0, 4));
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void DurangoF85Decoder::decodeDataRecord()
{
/* Skip sync bits ID byte. */
readRawBits(24);
const auto& bytes = decode(readRawBits((F85_SECTOR_LENGTH+3)*10))
.slice(0, F85_SECTOR_LENGTH+3);
ByteReader br(bytes);
_sector->data = br.read(F85_SECTOR_LENGTH);
uint16_t wantChecksum = br.read_be16();
uint16_t gotChecksum = crc16(CCITT_POLY, 0xbf84, _sector->data);
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
return std::unique_ptr<Decoder>(new DurangoF85Decoder(config));
}

View File

@@ -2,20 +2,10 @@
#define F85_H
#define F85_SECTOR_RECORD 0xffffce /* 1111 1111 1111 1111 1100 1110 */
#define F85_DATA_RECORD 0xffffcb /* 1111 1111 1111 1111 1100 1101 */
#define F85_SECTOR_LENGTH 512
#define F85_DATA_RECORD 0xffffcb /* 1111 1111 1111 1111 1100 1101 */
#define F85_SECTOR_LENGTH 512
class Sector;
class Fluxmap;
class DurangoF85Decoder : public AbstractDecoder
{
public:
virtual ~DurangoF85Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
extern std::unique_ptr<Decoder> createDurangoF85Decoder(
const DecoderProto& config);
#endif

4
arch/f85/f85.proto Normal file
View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message F85DecoderProto {}

View File

@@ -2,24 +2,22 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "fb100.h"
#include "crc.h"
#include "bytes.h"
#include "decoders/rawbits.h"
#include "track.h"
#include "fmt/format.h"
#include <string.h>
#include <algorithm>
const FluxPattern SECTOR_ID_PATTERN(16, 0xabaa);
/*
/*
* Reverse engineered from a dump of the floppy drive's ROM. I have no idea how
* it works.
*
*
* LF8BA:
* clra
* staa X00B0
@@ -99,37 +97,46 @@ static uint16_t checksum(const Bytes& bytes)
return (crchi << 8) | crclo;
}
AbstractDecoder::RecordType Fb100Decoder::advanceToNextRecord()
class Fb100Decoder : public Decoder
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(SECTOR_ID_PATTERN, matcher);
if (matcher == &SECTOR_ID_PATTERN)
return RecordType::SECTOR_RECORD;
return RecordType::UNKNOWN_RECORD;
public:
Fb100Decoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(SECTOR_ID_PATTERN);
}
void decodeSectorRecord() override
{
auto rawbits = readRawBits(FB100_RECORD_SIZE * 16);
const Bytes bytes = decodeFmMfm(rawbits).slice(0, FB100_RECORD_SIZE);
ByteReader br(bytes);
br.seek(1);
const Bytes id = br.read(FB100_ID_SIZE);
uint16_t wantIdCrc = br.read_be16();
uint16_t gotIdCrc = checksum(id);
const Bytes payload = br.read(FB100_PAYLOAD_SIZE);
uint16_t wantPayloadCrc = br.read_be16();
uint16_t gotPayloadCrc = checksum(payload);
if (wantIdCrc != gotIdCrc)
return;
uint8_t abssector = id[2];
_sector->logicalTrack = abssector >> 1;
_sector->logicalSide = 0;
_sector->logicalSector = abssector & 1;
_sector->data.writer().append(id.slice(5, 12)).append(payload);
_sector->status = (wantPayloadCrc == gotPayloadCrc)
? Sector::OK
: Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createFb100Decoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new Fb100Decoder(config));
}
void Fb100Decoder::decodeSectorRecord()
{
auto rawbits = readRawBits(FB100_RECORD_SIZE*16);
const Bytes bytes = decodeFmMfm(rawbits).slice(0, FB100_RECORD_SIZE);
ByteReader br(bytes);
br.seek(1);
const Bytes id = br.read(FB100_ID_SIZE);
uint16_t wantIdCrc = br.read_be16();
uint16_t gotIdCrc = checksum(id);
const Bytes payload = br.read(FB100_PAYLOAD_SIZE);
uint16_t wantPayloadCrc = br.read_be16();
uint16_t gotPayloadCrc = checksum(payload);
if (wantIdCrc != gotIdCrc)
return;
uint8_t abssector = id[2];
_sector->logicalTrack = abssector >> 1;
_sector->logicalSide = 0;
_sector->logicalSector = abssector & 1;
_sector->data.writer().append(id.slice(5, 12)).append(payload);
_sector->status = (wantPayloadCrc == gotPayloadCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
}

View File

@@ -5,18 +5,6 @@
#define FB100_ID_SIZE 17
#define FB100_PAYLOAD_SIZE 0x500
class Sector;
class Fluxmap;
class Track;
class Fb100Decoder : public AbstractDecoder
{
public:
virtual ~Fb100Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
extern std::unique_ptr<Decoder> createFb100Decoder(const DecoderProto& config);
#endif

4
arch/fb100/fb100.proto Normal file
View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message Fb100DecoderProto {}

View File

@@ -5,25 +5,27 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "sector.h"
#include "record.h"
#include "arch/ibm/ibm.pb.h"
#include "proto.h"
#include "lib/layout.h"
#include <string.h>
static_assert(std::is_trivially_copyable<IbmIdam>::value,
"IbmIdam is not trivially copyable");
"IbmIdam is not trivially copyable");
/*
* The markers at the beginning of records are special, and have
* missing clock pulses, allowing them to be found by the logic.
*
*
* IAM record:
* flux: XXXX-XXX-XXXX-X- = 0xf77a
* clock: X X - X - X X X = 0xd7
* data: X X X X X X - - = 0xfc
*
*
* (We just ignore this one --- it's useless and optional.)
*/
/*
/*
* IDAM record:
* flux: XXXX-X-X-XXXXXX- = 0xf57e
* clock: X X - - - X X X = 0xc7
@@ -31,7 +33,7 @@ static_assert(std::is_trivially_copyable<IbmIdam>::value,
*/
const FluxPattern FM_IDAM_PATTERN(16, 0xf57e);
/*
/*
* DAM1 record:
* flux: XXXX-X-X-XX-X-X- = 0xf56a
* clock: X X - - - X X X = 0xc7
@@ -39,7 +41,7 @@ const FluxPattern FM_IDAM_PATTERN(16, 0xf57e);
*/
const FluxPattern FM_DAM1_PATTERN(16, 0xf56a);
/*
/*
* DAM2 record:
* flux: XXXX-X-X-XX-XXXX = 0xf56f
* clock: X X - - - X X X = 0xc7
@@ -47,7 +49,7 @@ const FluxPattern FM_DAM1_PATTERN(16, 0xf56a);
*/
const FluxPattern FM_DAM2_PATTERN(16, 0xf56f);
/*
/*
* TRS80DAM1 record:
* flux: XXXX-X-X-XX-X-XX = 0xf56b
* clock: X X - - - X X X = 0xc7
@@ -55,7 +57,7 @@ const FluxPattern FM_DAM2_PATTERN(16, 0xf56f);
*/
const FluxPattern FM_TRS80DAM1_PATTERN(16, 0xf56b);
/*
/*
* TRS80DAM2 record:
* flux: XXXX-X-X-XX-XXX- = 0xf56e
* clock: X X - - - X X X = 0xc7
@@ -71,89 +73,178 @@ const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56e);
* ^^^^^
* When shifted out of phase, the special 0xa1 byte becomes an illegal
* encoding (you can't do 10 00). So this can't be spoofed by user data.
*
*
* shifted: 10 00 10 01 00 01 00 1
*
*
* It's repeated three times.
*/
const FluxPattern MFM_PATTERN(48, 0x448944894489LL);
const FluxMatchers ANY_RECORD_PATTERN(
const FluxMatchers ANY_RECORD_PATTERN({
&MFM_PATTERN,
&FM_IDAM_PATTERN,
&FM_DAM1_PATTERN,
&FM_DAM2_PATTERN,
&FM_TRS80DAM1_PATTERN,
&FM_TRS80DAM2_PATTERN,
});
class IbmDecoder : public Decoder
{
public:
IbmDecoder(const DecoderProto& config):
Decoder(config),
_config(config.ibm())
{
&MFM_PATTERN,
&FM_IDAM_PATTERN,
&FM_DAM1_PATTERN,
&FM_DAM2_PATTERN,
&FM_TRS80DAM1_PATTERN,
&FM_TRS80DAM2_PATTERN,
}
);
AbstractDecoder::RecordType IbmDecoder::advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
/* If this is the MFM prefix byte, the the decoder is going to expect three
* extra bytes on the front of the header. */
_currentHeaderLength = (matcher == &MFM_PATTERN) ? 3 : 0;
Fluxmap::Position here = tell();
if (_currentHeaderLength > 0)
readRawBits(_currentHeaderLength*16);
auto idbits = readRawBits(16);
const Bytes idbytes = decodeFmMfm(idbits);
uint8_t id = idbytes.slice(0, 1)[0];
seek(here);
switch (id)
nanoseconds_t advanceToNextRecord() override
{
case IBM_IDAM:
return RecordType::SECTOR_RECORD;
case IBM_DAM1:
case IBM_DAM2:
case IBM_TRS80DAM1:
case IBM_TRS80DAM2:
return RecordType::DATA_RECORD;
return seekToPattern(ANY_RECORD_PATTERN);
}
return RecordType::UNKNOWN_RECORD;
}
void IbmDecoder::decodeSectorRecord()
void decodeSectorRecord() override
{
/* This is really annoying because the IBM record scheme has a
* variable-sized header _and_ the checksum covers this header too. So
* we have to read and decode a byte at a time until we know where the
* record itself starts, saving the bytes for the checksumming later.
*/
Bytes bytes;
ByteWriter bw(bytes);
auto readByte = [&]()
{
auto bits = readRawBits(16);
auto bytes = decodeFmMfm(bits).slice(0, 1);
uint8_t byte = bytes[0];
bw.write_8(byte);
return byte;
};
uint8_t id = readByte();
if (id == 0xa1)
{
readByte();
readByte();
id = readByte();
}
if (id != IBM_IDAM)
return;
ByteReader br(bytes);
br.seek(bw.pos);
auto bits = readRawBits(IBM_IDAM_LEN * 16);
bw += decodeFmMfm(bits).slice(0, IBM_IDAM_LEN);
IbmDecoderProto::TrackdataProto trackdata;
getTrackFormat(
trackdata, _sector->physicalTrack, _sector->physicalSide);
_sector->logicalTrack = br.read_8();
_sector->logicalSide = br.read_8();
_sector->logicalSector = br.read_8();
_currentSectorSize = 1 << (br.read_8() + 7);
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, br.pos));
uint16_t wantCrc = br.read_be16();
if (wantCrc == gotCrc)
_sector->status =
Sector::DATA_MISSING; /* correct but unintuitive */
if (trackdata.ignore_side_byte())
_sector->logicalSide =
Layout::remapSidePhysicalToLogical(_sector->physicalSide);
_sector->logicalSide ^= trackdata.invert_side_byte();
if (trackdata.ignore_track_byte())
_sector->logicalTrack = _sector->physicalTrack;
for (int sector : trackdata.ignore_sector())
if (_sector->logicalSector == sector)
{
_sector->status = Sector::MISSING;
break;
}
}
void decodeDataRecord() override
{
/* This is the same deal as the sector record. */
Bytes bytes;
ByteWriter bw(bytes);
auto readByte = [&]()
{
auto bits = readRawBits(16);
auto bytes = decodeFmMfm(bits).slice(0, 1);
uint8_t byte = bytes[0];
bw.write_8(byte);
return byte;
};
uint8_t id = readByte();
if (id == 0xa1)
{
readByte();
readByte();
id = readByte();
}
if ((id != IBM_DAM1) && (id != IBM_DAM2) && (id != IBM_TRS80DAM1) &&
(id != IBM_TRS80DAM2))
return;
ByteReader br(bytes);
br.seek(bw.pos);
auto bits = readRawBits((_currentSectorSize + 2) * 16);
bw += decodeFmMfm(bits).slice(0, _currentSectorSize + 2);
_sector->data = br.read(_currentSectorSize);
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, br.pos));
uint16_t wantCrc = br.read_be16();
_sector->status =
(wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
auto layout = Layout::getLayoutOfTrack(
_sector->logicalTrack, _sector->logicalSide);
if (_currentSectorSize != layout->sectorSize)
std::cerr << fmt::format(
"Warning: configured sector size for t{}.h{}.s{} is {} bytes "
"but that seen on disk is {} bytes\n",
_sector->logicalTrack,
_sector->logicalSide,
_sector->logicalSector,
layout->sectorSize,
_currentSectorSize);
}
private:
void getTrackFormat(IbmDecoderProto::TrackdataProto& trackdata,
unsigned track,
unsigned head) const
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_track() && (f.track() != track))
continue;
if (f.has_head() && (f.head() != head))
continue;
trackdata.MergeFrom(f);
}
}
private:
const IbmDecoderProto& _config;
unsigned _currentSectorSize;
unsigned _currentHeaderLength;
};
std::unique_ptr<Decoder> createIbmDecoder(const DecoderProto& config)
{
unsigned recordSize = _currentHeaderLength + IBM_IDAM_LEN;
auto bits = readRawBits(recordSize*16);
auto bytes = decodeFmMfm(bits).slice(0, recordSize);
ByteReader br(bytes);
br.seek(_currentHeaderLength);
br.read_8(); /* skip ID byte */
_sector->logicalTrack = br.read_8();
_sector->logicalSide = br.read_8();
_sector->logicalSector = br.read_8() - _sectorBase;
_currentSectorSize = 1 << (br.read_8() + 7);
uint16_t wantCrc = br.read_be16();
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, _currentHeaderLength + 5));
if (wantCrc == gotCrc)
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
if (_ignoreSideByte)
_sector->logicalSide = _sector->physicalSide;
}
void IbmDecoder::decodeDataRecord()
{
unsigned recordLength = _currentHeaderLength + _currentSectorSize + 3;
auto bits = readRawBits(recordLength*16);
auto bytes = decodeFmMfm(bits).slice(0, recordLength);
ByteReader br(bytes);
br.seek(_currentHeaderLength);
br.read_8(); /* skip ID byte */
_sector->data = br.read(_currentSectorSize);
uint16_t wantCrc = br.read_be16();
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, recordLength-2));
_sector->status = (wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
return std::unique_ptr<Decoder>(new IbmDecoder(config));
}

View File

@@ -1,12 +1,15 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "ibm.h"
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
#include "readerwriter.h"
#include "image.h"
#include "arch/ibm/ibm.pb.h"
#include "lib/encoders/encoders.pb.h"
#include "fmt/format.h"
#include "lib/proto.h"
#include "lib/layout.h"
#include <ctype.h>
/* IAM record separator:
@@ -38,9 +41,9 @@
* ^^^^^
* When shifted out of phase, the special 0xa1 byte becomes an illegal
* encoding (you can't do 10 00). So this can't be spoofed by user data.
*
*
* shifted: 10 00 10 01 00 01 00 1
*
*
* It's repeated three times.
*/
#define MFM_RECORD_SEPARATOR 0x4489
@@ -56,181 +59,224 @@
* mfm: 01 01 01 01 01 00 01 01 = 0x5545
*/
static int charToInt(char c)
{
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
}
void IbmEncoder::writeRawBits(uint32_t data, int width)
{
_cursor += width;
_lastBit = data & 1;
for (int i=0; i<width; i++)
{
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
void IbmEncoder::writeBytes(const Bytes& bytes)
{
if (_parameters.useFm)
encodeFm(_bits, _cursor, bytes);
else
encodeMfm(_bits, _cursor, bytes, _lastBit);
}
void IbmEncoder::writeBytes(int count, uint8_t byte)
{
Bytes bytes = { byte };
for (int i=0; i<count; i++)
writeBytes(bytes);
}
static uint8_t decodeUint16(uint16_t raw)
{
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
}
std::unique_ptr<Fluxmap> IbmEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
class IbmEncoder : public Encoder
{
double clockRateUs = 1e3 / _parameters.clockRateKhz;
if (!_parameters.useFm)
clockRateUs /= 2.0;
int bitsPerRevolution = (_parameters.trackLengthMs * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
uint8_t idamUnencoded = decodeUint16(_parameters.idamByte);
uint8_t damUnencoded = decodeUint16(_parameters.damByte);
uint8_t sectorSize = 0;
{
int s = _parameters.sectorSize >> 7;
while (s > 1)
{
s >>= 1;
sectorSize += 1;
}
}
uint8_t gapFill = _parameters.useFm ? 0x00 : 0x4e;
writeBytes(_parameters.gap0, gapFill);
if (_parameters.emitIam)
{
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
if (!_parameters.useFm)
{
for (int i=0; i<3; i++)
writeRawBits(MFM_IAM_SEPARATOR, 16);
}
writeRawBits(_parameters.useFm ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
writeBytes(_parameters.gap1, gapFill);
}
bool first = true;
for (char sectorChar : _parameters.sectorSkew)
{
int sectorId = charToInt(sectorChar);
if (!first)
writeBytes(_parameters.gap3, gapFill);
first = false;
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
if (!sectorData)
Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId);
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
{
Bytes header;
ByteWriter bw(header);
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
if (!_parameters.useFm)
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(idamUnencoded);
bw.write_8(sectorData->logicalTrack);
bw.write_8(sectorData->logicalSide);
bw.write_8(sectorData->logicalSector + _parameters.startSectorId);
bw.write_8(sectorSize);
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!_parameters.useFm)
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(_parameters.idamByte, 16);
conventionalHeaderStart += 1;
writeBytes(header.slice(conventionalHeaderStart));
}
writeBytes(_parameters.gap2, gapFill);
{
Bytes data;
ByteWriter bw(data);
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
if (!_parameters.useFm)
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(damUnencoded);
Bytes truncatedData = sectorData->data.slice(0, _parameters.sectorSize);
bw += truncatedData;
hexdump(std::cout, data.slice(0, 64));
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!_parameters.useFm)
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(_parameters.damByte, 16);
conventionalHeaderStart += 1;
writeBytes(data.slice(conventionalHeaderStart));
}
public:
IbmEncoder(const EncoderProto& config):
Encoder(config),
_config(config.ibm())
{
}
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeBytes(1, gapFill);
private:
void writeRawBits(uint32_t data, int width)
{
_cursor += width;
_lastBit = data & 1;
for (int i = 0; i < width; i++)
{
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);
return fluxmap;
void getEncoderTrackData(IbmEncoderProto::TrackdataProto& trackdata,
unsigned track,
unsigned head)
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_track() && (f.track() != track))
continue;
if (f.has_head() && (f.head() != head))
continue;
trackdata.MergeFrom(f);
}
}
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
IbmEncoderProto::TrackdataProto trackdata;
getEncoderTrackData(
trackdata, trackInfo->logicalTrack, trackInfo->logicalSide);
auto trackLayout = Layout::getLayoutOfTrack(
trackInfo->logicalTrack, trackInfo->logicalSide);
auto writeBytes = [&](const Bytes& bytes)
{
if (trackdata.use_fm())
encodeFm(_bits, _cursor, bytes);
else
encodeMfm(_bits, _cursor, bytes, _lastBit);
};
auto writeFillerRawBytes = [&](int count, uint16_t byte)
{
for (int i = 0; i < count; i++)
writeRawBits(byte, 16);
};
auto writeFillerBytes = [&](int count, uint8_t byte)
{
Bytes b{byte};
for (int i = 0; i < count; i++)
writeBytes(b);
};
double clockRateUs = trackdata.target_clock_period_us();
if (!trackdata.use_fm())
clockRateUs /= 2.0;
int bitsPerRevolution =
(trackdata.target_rotational_period_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
uint8_t idamUnencoded = decodeUint16(trackdata.idam_byte());
uint8_t damUnencoded = decodeUint16(trackdata.dam_byte());
uint8_t sectorSize = 0;
{
int s = trackLayout->sectorSize >> 7;
while (s > 1)
{
s >>= 1;
sectorSize += 1;
}
}
uint16_t gapFill = trackdata.gap_fill_byte();
writeFillerRawBytes(trackdata.gap0(), gapFill);
if (trackdata.emit_iam())
{
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i = 0; i < 3; i++)
writeRawBits(MFM_IAM_SEPARATOR, 16);
}
writeRawBits(
trackdata.use_fm() ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
writeFillerRawBytes(trackdata.gap1(), gapFill);
}
bool first = true;
for (const auto& sectorData : sectors)
{
if (!first)
writeFillerRawBytes(trackdata.gap3(), gapFill);
first = false;
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
{
Bytes header;
ByteWriter bw(header);
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i = 0; i < 3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(idamUnencoded);
bw.write_8(sectorData->logicalTrack);
bw.write_8(
sectorData->logicalSide ^ trackdata.invert_side_byte());
bw.write_8(sectorData->logicalSector);
bw.write_8(sectorSize);
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!trackdata.use_fm())
{
for (int i = 0; i < 3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(trackdata.idam_byte(), 16);
conventionalHeaderStart += 1;
writeBytes(header.slice(conventionalHeaderStart));
}
writeFillerRawBytes(trackdata.gap2(), gapFill);
{
Bytes data;
ByteWriter bw(data);
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i = 0; i < 3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(damUnencoded);
Bytes truncatedData =
sectorData->data.slice(0, trackLayout->sectorSize);
bw += truncatedData;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!trackdata.use_fm())
{
for (int i = 0; i < 3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(trackdata.dam_byte(), 16);
conventionalHeaderStart += 1;
writeBytes(data.slice(conventionalHeaderStart));
}
}
if (_cursor >= _bits.size())
error("track data overrun");
while (_cursor < _bits.size())
writeFillerRawBytes(1, gapFill);
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits,
calculatePhysicalClockPeriod(clockRateUs * 1e3,
trackdata.target_rotational_period_ms() * 1e6));
return fluxmap;
}
private:
const IbmEncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
std::unique_ptr<Encoder> createIbmEncoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new IbmEncoder(config));
}

View File

@@ -1,99 +1,37 @@
#ifndef IBM_H
#define IBM_H
#include "decoders/decoders.h"
#include "encoders/encoders.h"
/* IBM format (i.e. ordinary PC floppies). */
#define IBM_MFM_SYNC 0xA1 /* sync byte for MFM */
#define IBM_IAM 0xFC /* start-of-track record */
#define IBM_IAM_LEN 1 /* plus prologue */
#define IBM_IDAM 0xFE /* sector header */
#define IBM_IDAM_LEN 7 /* plus prologue */
#define IBM_DAM1 0xF8 /* sector data (type 1) */
#define IBM_DAM2 0xFB /* sector data (type 2) */
#define IBM_TRS80DAM1 0xF9 /* sector data (TRS-80 directory) */
#define IBM_TRS80DAM2 0xFA /* sector data (TRS-80 directory) */
#define IBM_DAM_LEN 1 /* plus prologue and user data */
#define IBM_MFM_SYNC 0xA1 /* sync byte for MFM */
#define IBM_IAM 0xFC /* start-of-track record */
#define IBM_IAM_LEN 1 /* plus prologue */
#define IBM_IDAM 0xFE /* sector header */
#define IBM_IDAM_LEN 7 /* plus prologue */
#define IBM_DAM1 0xF8 /* sector data (type 1) */
#define IBM_DAM2 0xFB /* sector data (type 2) */
#define IBM_TRS80DAM1 0xF9 /* sector data (TRS-80 directory) */
#define IBM_TRS80DAM2 0xFA /* sector data (TRS-80 directory) */
#define IBM_DAM_LEN 1 /* plus prologue and user data */
/* Length of a DAM record is determined by the previous sector header. */
struct IbmIdam
{
uint8_t id;
uint8_t cylinder;
uint8_t track;
uint8_t side;
uint8_t sector;
uint8_t sectorSize;
uint8_t crc[2];
};
class IbmDecoder : public AbstractDecoder
{
public:
IbmDecoder(unsigned sectorBase, bool ignoreSideByte=false,
const std::set<unsigned> requiredSectors=std::set<unsigned>()):
_sectorBase(sectorBase),
_ignoreSideByte(ignoreSideByte),
_requiredSectors(requiredSectors)
{}
class Encoder;
class Decoder;
class DecoderProto;
class EncoderProto;
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
std::set<unsigned> requiredSectors(Track& track) const
{ return _requiredSectors; }
private:
unsigned _sectorBase;
bool _ignoreSideByte;
std::set<unsigned> _requiredSectors;
unsigned _currentSectorSize;
unsigned _currentHeaderLength;
};
struct IbmParameters
{
int trackLengthMs;
int sectorSize;
bool emitIam;
int startSectorId;
int clockRateKhz;
bool useFm;
uint16_t idamByte;
uint16_t damByte;
int gap0;
int gap1;
int gap2;
int gap3;
std::string sectorSkew;
};
class IbmEncoder : public AbstractEncoder
{
public:
IbmEncoder(const IbmParameters& parameters):
_parameters(parameters)
{}
virtual ~IbmEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
private:
void writeRawBits(uint32_t data, int width);
void writeBytes(const Bytes& bytes);
void writeBytes(int count, uint8_t value);
void writeSync();
private:
IbmParameters _parameters;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
extern std::unique_ptr<Decoder> createIbmDecoder(const DecoderProto& config);
extern std::unique_ptr<Encoder> createIbmEncoder(const EncoderProto& config);
#endif

43
arch/ibm/ibm.proto Normal file
View File

@@ -0,0 +1,43 @@
syntax = "proto2";
import "lib/common.proto";
message IbmDecoderProto {
// Next: 11
message TrackdataProto {
optional int32 track = 7 [(help) = "if set, the format applies only to this track"];
optional int32 head = 8 [(help) = "if set, the format applies only to this head"];
optional bool ignore_side_byte = 2 [default = false, (help) = "ignore side byte in sector header"];
optional bool ignore_track_byte = 6 [default = false, (help) = "ignore track byte in sector header"];
optional bool invert_side_byte = 4 [default = false, (help) = "invert the side byte in the sector header"];
repeated int32 ignore_sector = 10 [(help) = "sectors with these IDs will not be read"];
}
repeated TrackdataProto trackdata = 1;
}
message IbmEncoderProto {
// Next: 20
message TrackdataProto {
optional int32 track = 15 [(help) = "if set, the format applies only to this track"];
optional int32 head = 16 [(help) = "if set, the format applies only to this head"];
optional bool emit_iam = 3 [default=true, (help) = "whether to emit an IAM record"];
optional double target_clock_period_us = 5 [default=4, (help) = "data clock rate on target disk"];
optional bool use_fm = 6 [default=false, (help) = "whether to use FM encoding rather than MFM"];
optional int32 idam_byte = 7 [default=0x5554, (help) = "16-bit raw bit pattern of IDAM byte"];
optional int32 dam_byte = 8 [default=0x5545, (help) = "16-bit raw bit pattern of DAM byte"];
optional int32 gap0 = 9 [default=80, (help) = "size of gap 1 (the post-index gap)"];
optional int32 gap1 = 10 [default=50, (help) = "size of gap 2 (the post-ID gap)"];
optional int32 gap2 = 11 [default=22, (help) = "size of gap 3 (the pre-data gap)"];
optional int32 gap3 = 12 [default=80, (help) = "size of gap 4 (the post-data or format gap)"];
optional bool invert_side_byte = 19 [default=false, (help) = "invert the side byte before writing"];
optional int32 gap_fill_byte = 18 [default=0x9254, (help) = "16-bit raw bit pattern of gap fill byte"];
optional double target_rotational_period_ms = 1 [default=200, (help) = "rotational period of target disk"];
}
repeated TrackdataProto trackdata = 1;
}

View File

@@ -2,10 +2,8 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "track.h"
#include "macintosh.h"
#include "bytes.h"
#include "fmt/format.h"
@@ -14,22 +12,25 @@
const FluxPattern SECTOR_RECORD_PATTERN(24, MAC_SECTOR_RECORD);
const FluxPattern DATA_RECORD_PATTERN(24, MAC_DATA_RECORD);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
const FluxMatchers ANY_RECORD_PATTERN(
{&SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN});
static int decode_data_gcr(uint8_t gcr)
{
switch (gcr)
{
#define GCR_ENTRY(gcr, data) \
case gcr: return data;
#include "data_gcr.h"
#undef GCR_ENTRY
#define GCR_ENTRY(gcr, data) \
case gcr: \
return data;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
}
/* This is extremely inspired by the MESS implementation, written by Nathan Woods
* and R. Belmont: https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp
/* This is extremely inspired by the MESS implementation, written by Nathan
* Woods and R. Belmont:
* https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp
*/
static Bytes decode_crazy_data(const Bytes& input, Sector::Status& status)
{
@@ -43,7 +44,7 @@ static Bytes decode_crazy_data(const Bytes& input, Sector::Status& status)
uint8_t b2[LOOKUP_LEN + 1];
uint8_t b3[LOOKUP_LEN + 1];
for (int i=0; i<=LOOKUP_LEN; i++)
for (int i = 0; i <= LOOKUP_LEN; i++)
{
uint8_t w4 = br.read_8();
uint8_t w1 = br.read_8();
@@ -124,85 +125,71 @@ uint8_t decode_side(uint8_t side)
return !!(side & 0x20);
}
AbstractDecoder::RecordType MacintoshDecoder::advanceToNextRecord()
class MacintoshDecoder : public Decoder
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return DATA_RECORD;
return UNKNOWN_RECORD;
}
public:
MacintoshDecoder(const DecoderProto& config): Decoder(config) {}
void MacintoshDecoder::decodeSectorRecord()
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
if (readRaw24() != MAC_SECTOR_RECORD)
return;
/* Read header. */
auto header = toBytes(readRawBits(7 * 8)).slice(0, 7);
uint8_t encodedTrack = decode_data_gcr(header[0]);
if (encodedTrack != (_sector->physicalTrack & 0x3f))
return;
uint8_t encodedSector = decode_data_gcr(header[1]);
uint8_t encodedSide = decode_data_gcr(header[2]);
uint8_t formatByte = decode_data_gcr(header[3]);
uint8_t wantedsum = decode_data_gcr(header[4]);
if (encodedSector > 11)
return;
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalSide = decode_side(encodedSide);
_sector->logicalSector = encodedSector;
uint8_t gotsum =
(encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
if (wantedsum == gotsum)
_sector->status =
Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord() override
{
if (readRaw24() != MAC_DATA_RECORD)
return;
/* Read data. */
readRawBits(8); /* skip spare byte */
auto inputbuffer = toBytes(readRawBits(MAC_ENCODED_SECTOR_LENGTH * 8))
.slice(0, MAC_ENCODED_SECTOR_LENGTH);
for (unsigned i = 0; i < inputbuffer.size(); i++)
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
_sector->status = Sector::BAD_CHECKSUM;
Bytes userData = decode_crazy_data(inputbuffer, _sector->status);
_sector->data.clear();
_sector->data.writer()
.append(userData.slice(12, 512))
.append(userData.slice(0, 12));
}
};
std::unique_ptr<Decoder> createMacintoshDecoder(const DecoderProto& config)
{
/* Skip ID (as we know it's a MAC_SECTOR_RECORD). */
readRawBits(24);
/* Read header. */
auto header = toBytes(readRawBits(7*8)).slice(0, 7);
uint8_t encodedTrack = decode_data_gcr(header[0]);
if (encodedTrack != (_track->physicalTrack & 0x3f))
return;
uint8_t encodedSector = decode_data_gcr(header[1]);
uint8_t encodedSide = decode_data_gcr(header[2]);
uint8_t formatByte = decode_data_gcr(header[3]);
uint8_t wantedsum = decode_data_gcr(header[4]);
if (encodedSector > 11)
return;
_sector->logicalTrack = _track->physicalTrack;
_sector->logicalSide = decode_side(encodedSide);
_sector->logicalSector = encodedSector;
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
if (wantedsum == gotsum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
return std::unique_ptr<Decoder>(new MacintoshDecoder(config));
}
void MacintoshDecoder::decodeDataRecord()
{
auto id = toBytes(readRawBits(24)).reader().read_be24();
if (id != MAC_DATA_RECORD)
return;
/* Read data. */
readRawBits(8); /* skip spare byte */
auto inputbuffer = toBytes(readRawBits(MAC_ENCODED_SECTOR_LENGTH*8))
.slice(0, MAC_ENCODED_SECTOR_LENGTH);
for (unsigned i=0; i<inputbuffer.size(); i++)
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
_sector->status = Sector::BAD_CHECKSUM;
Bytes userData = decode_crazy_data(inputbuffer, _sector->status);
_sector->data.clear();
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
}
std::set<unsigned> MacintoshDecoder::requiredSectors(Track& track) const
{
int count;
if (track.physicalTrack < 16)
count = 12;
else if (track.physicalTrack < 32)
count = 11;
else if (track.physicalTrack < 48)
count = 10;
else if (track.physicalTrack < 64)
count = 9;
else
count = 8;
std::set<unsigned> sectors;
while (count--)
sectors.insert(count);
return sectors;
}

View File

@@ -1,68 +1,60 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "macintosh.h"
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
#include "readerwriter.h"
#include "image.h"
#include "fmt/format.h"
#include "lib/encoders/encoders.pb.h"
#include "lib/layout.h"
#include "arch/macintosh/macintosh.pb.h"
#include <ctype.h>
FlagGroup macintoshEncoderFlags;
static DoubleFlag postIndexGapUs(
{ "--post-index-gap-us" },
"Post-index gap before first sector header (microseconds).",
0);
static DoubleFlag clockCompensation(
{ "--clock-compensation-factor" },
"Scale the output clock by this much.",
.9724);
static bool lastBit;
static double clockRateUsForTrack(unsigned track)
{
if (track < 16)
return 2.623;
if (track < 32)
return 2.861;
if (track < 48)
return 3.148;
if (track < 64)
return 3.497;
return 3.934;
if (track < 16)
return 2.63;
if (track < 32)
return 2.89;
if (track < 48)
return 3.20;
if (track < 64)
return 3.57;
return 3.98;
}
static unsigned sectorsForTrack(unsigned track)
{
if (track < 16)
return 12;
if (track < 32)
return 11;
if (track < 48)
return 10;
if (track < 64)
return 9;
return 8;
if (track < 16)
return 12;
if (track < 32)
return 11;
if (track < 48)
return 10;
if (track < 64)
return 9;
return 8;
}
static int encode_data_gcr(uint8_t gcr)
{
switch (gcr)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
#define GCR_ENTRY(gcr, data) \
case data: \
return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
}
/* This is extremely inspired by the MESS implementation, written by Nathan Woods
* and R. Belmont: https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp
/* This is extremely inspired by the MESS implementation, written by Nathan
* Woods and R. Belmont:
* https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp
*/
static Bytes encode_crazy_data(const Bytes& input)
{
@@ -70,7 +62,7 @@ static Bytes encode_crazy_data(const Bytes& input)
ByteWriter bw(output);
ByteReader br(input);
uint8_t w1, w2, w3, w4;
uint8_t w1, w2, w3, w4;
static const int LOOKUP_LEN = MAC_SECTOR_LENGTH / 3;
@@ -78,92 +70,94 @@ static Bytes encode_crazy_data(const Bytes& input)
uint8_t b2[LOOKUP_LEN + 1];
uint8_t b3[LOOKUP_LEN + 1];
uint32_t c1 = 0;
uint32_t c2 = 0;
uint32_t c3 = 0;
for (int j=0;; j++)
{
c1 = (c1 & 0xff) << 1;
if (c1 & 0x0100)
c1++;
uint32_t c1 = 0;
uint32_t c2 = 0;
uint32_t c3 = 0;
for (int j = 0;; j++)
{
c1 = (c1 & 0xff) << 1;
if (c1 & 0x0100)
c1++;
uint8_t val = br.read_8();
c3 += val;
if (c1 & 0x0100)
{
c3++;
c1 &= 0xff;
}
b1[j] = (val ^ c1) & 0xff;
uint8_t val = br.read_8();
c3 += val;
if (c1 & 0x0100)
{
c3++;
c1 &= 0xff;
}
b1[j] = (val ^ c1) & 0xff;
val = br.read_8();
c2 += val;
if (c3 > 0xff)
{
c2++;
c3 &= 0xff;
}
b2[j] = (val ^ c3) & 0xff;
val = br.read_8();
c2 += val;
if (c3 > 0xff)
{
c2++;
c3 &= 0xff;
}
b2[j] = (val ^ c3) & 0xff;
if (br.pos == 524)
break;
if (br.pos == 524)
break;
val = br.read_8();
c1 += val;
if (c2 > 0xff)
{
c1++;
c2 &= 0xff;
}
b3[j] = (val ^ c2) & 0xff;
}
uint32_t c4 = ((c1 & 0xc0) >> 6) | ((c2 & 0xc0) >> 4) | ((c3 & 0xc0) >> 2);
b3[LOOKUP_LEN] = 0;
val = br.read_8();
c1 += val;
if (c2 > 0xff)
{
c1++;
c2 &= 0xff;
}
b3[j] = (val ^ c2) & 0xff;
}
uint32_t c4 = ((c1 & 0xc0) >> 6) | ((c2 & 0xc0) >> 4) | ((c3 & 0xc0) >> 2);
b3[LOOKUP_LEN] = 0;
for (int i = 0; i <= LOOKUP_LEN; i++)
{
w1 = b1[i] & 0x3f;
w2 = b2[i] & 0x3f;
w3 = b3[i] & 0x3f;
w4 = ((b1[i] & 0xc0) >> 2);
w4 |= ((b2[i] & 0xc0) >> 4);
w4 |= ((b3[i] & 0xc0) >> 6);
for (int i = 0; i <= LOOKUP_LEN; i++)
{
w1 = b1[i] & 0x3f;
w2 = b2[i] & 0x3f;
w3 = b3[i] & 0x3f;
w4 = ((b1[i] & 0xc0) >> 2);
w4 |= ((b2[i] & 0xc0) >> 4);
w4 |= ((b3[i] & 0xc0) >> 6);
bw.write_8(w4);
bw.write_8(w1);
bw.write_8(w2);
bw.write_8(w4);
bw.write_8(w1);
bw.write_8(w2);
if (i != LOOKUP_LEN)
bw.write_8(w3);
}
if (i != LOOKUP_LEN)
bw.write_8(w3);
}
bw.write_8(c4 & 0x3f);
bw.write_8(c3 & 0x3f);
bw.write_8(c2 & 0x3f);
bw.write_8(c1 & 0x3f);
bw.write_8(c4 & 0x3f);
bw.write_8(c3 & 0x3f);
bw.write_8(c2 & 0x3f);
bw.write_8(c1 & 0x3f);
return output;
return output;
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
{
for (bool bit : src)
{
if (cursor < bits.size())
bits[cursor++] = bit;
}
for (bool bit : src)
{
if (cursor < bits.size())
bits[cursor++] = bit;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
{
cursor += width;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
cursor += width;
for (int i = 0; i < width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
static uint8_t encode_side(uint8_t track, uint8_t side)
@@ -172,71 +166,93 @@ static uint8_t encode_side(uint8_t track, uint8_t side)
* bit 5) and also whether we're above track 0x3f (in bit 0).
*/
return (side ? 0x20 : 0x00) | ((track>0x3f) ? 0x01 : 0x00);
return (side ? 0x20 : 0x00) | ((track > 0x3f) ? 0x01 : 0x00);
}
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector)
static void write_sector(std::vector<bool>& bits,
unsigned& cursor,
const std::shared_ptr<const Sector>& sector)
{
if ((sector->data.size() != 512) && (sector->data.size() != 524))
Error() << "unsupported sector size --- you must pick 512 or 524";
if ((sector->data.size() != 512) && (sector->data.size() != 524))
error("unsupported sector size --- you must pick 512 or 524");
write_bits(bits, cursor, 0xff, 1*8); /* pad byte */
for (int i=0; i<7; i++)
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6*8); /* sync */
write_bits(bits, cursor, MAC_SECTOR_RECORD, 3*8);
write_bits(bits, cursor, 0xff, 1 * 8); /* pad byte */
for (int i = 0; i < 7; i++)
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6 * 8); /* sync */
write_bits(bits, cursor, MAC_SECTOR_RECORD, 3 * 8);
uint8_t encodedTrack = sector->physicalTrack & 0x3f;
uint8_t encodedSector = sector->logicalSector;
uint8_t encodedSide = encode_side(sector->physicalTrack, sector->logicalSide);
uint8_t formatByte = MAC_FORMAT_BYTE;
uint8_t headerChecksum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
uint8_t encodedTrack = sector->logicalTrack & 0x3f;
uint8_t encodedSector = sector->logicalSector;
uint8_t encodedSide =
encode_side(sector->logicalTrack, sector->logicalSide);
uint8_t formatByte = MAC_FORMAT_BYTE;
uint8_t headerChecksum =
(encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
write_bits(bits, cursor, encode_data_gcr(encodedTrack), 1*8);
write_bits(bits, cursor, encode_data_gcr(encodedSector), 1*8);
write_bits(bits, cursor, encode_data_gcr(encodedSide), 1*8);
write_bits(bits, cursor, encode_data_gcr(formatByte), 1*8);
write_bits(bits, cursor, encode_data_gcr(headerChecksum), 1*8);
write_bits(bits, cursor, encode_data_gcr(encodedTrack), 1 * 8);
write_bits(bits, cursor, encode_data_gcr(encodedSector), 1 * 8);
write_bits(bits, cursor, encode_data_gcr(encodedSide), 1 * 8);
write_bits(bits, cursor, encode_data_gcr(formatByte), 1 * 8);
write_bits(bits, cursor, encode_data_gcr(headerChecksum), 1 * 8);
write_bits(bits, cursor, 0xdeaaff, 3*8);
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6*8); /* sync */
write_bits(bits, cursor, MAC_DATA_RECORD, 3*8);
write_bits(bits, cursor, encode_data_gcr(sector->logicalSector), 1*8);
write_bits(bits, cursor, 0xdeaaff, 3 * 8);
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6 * 8); /* sync */
write_bits(bits, cursor, MAC_DATA_RECORD, 3 * 8);
write_bits(bits, cursor, encode_data_gcr(sector->logicalSector), 1 * 8);
Bytes wireData;
wireData.writer().append(sector->data.slice(512, 12)).append(sector->data.slice(0, 512));
for (uint8_t b : encode_crazy_data(wireData))
write_bits(bits, cursor, encode_data_gcr(b), 1*8);
Bytes wireData;
wireData.writer()
.append(sector->data.slice(512, 12))
.append(sector->data.slice(0, 512));
for (uint8_t b : encode_crazy_data(wireData))
write_bits(bits, cursor, encode_data_gcr(b), 1 * 8);
write_bits(bits, cursor, 0xdeaaff, 3*8);
write_bits(bits, cursor, 0xdeaaff, 3 * 8);
}
std::unique_ptr<Fluxmap> MacintoshEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
class MacintoshEncoder : public Encoder
{
if ((physicalTrack < 0) || (physicalTrack >= MAC_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
double clockRateUs = clockRateUsForTrack(physicalTrack) * clockCompensation;
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, postIndexGapUs / clockRateUs, { true, false });
lastBit = false;
unsigned numSectors = sectorsForTrack(physicalTrack);
for (int sectorId=0; sectorId<numSectors; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
public:
MacintoshEncoder(const EncoderProto& config):
Encoder(config),
_config(config.macintosh())
{
}
if (cursor >= bits.size())
Error() << fmt::format("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), { true, false });
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
double clockRateUs = clockRateUsForTrack(trackInfo->logicalTrack);
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
fillBitmapTo(bits,
cursor,
_config.post_index_gap_us() / clockRateUs,
{true, false});
lastBit = false;
for (const auto& sector : sectors)
write_sector(bits, cursor, sector);
if (cursor >= bits.size())
error("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), {true, false});
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(
bits, calculatePhysicalClockPeriod(clockRateUs * 1e3, 200e6));
return fluxmap;
}
private:
const MacintoshEncoderProto& _config;
};
std::unique_ptr<Encoder> createMacintoshEncoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new MacintoshEncoder(config));
}

View File

@@ -1,44 +1,23 @@
#ifndef MACINTOSH_H
#define MACINTOSH_H
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#define MAC_SECTOR_RECORD 0xd5aa96 /* 1101 0101 1010 1010 1001 0110 */
#define MAC_DATA_RECORD 0xd5aaad /* 1101 0101 1010 1010 1010 1101 */
#define MAC_SECTOR_RECORD 0xd5aa96 /* 1101 0101 1010 1010 1001 0110 */
#define MAC_DATA_RECORD 0xd5aaad /* 1101 0101 1010 1010 1010 1101 */
#define MAC_SECTOR_LENGTH 524 /* yes, really */
#define MAC_SECTOR_LENGTH 524 /* yes, really */
#define MAC_ENCODED_SECTOR_LENGTH 703
#define MAC_FORMAT_BYTE 0x22
#define MAC_FORMAT_BYTE 0x22
#define MAC_TRACKS_PER_DISK 80
class Sector;
class Fluxmap;
class MacintoshDecoder : public AbstractDecoder
{
public:
virtual ~MacintoshDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
std::set<unsigned> requiredSectors(Track& track) const;
};
class MacintoshEncoder : public AbstractEncoder
{
public:
virtual ~MacintoshEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
};
extern FlagGroup macintoshEncoderFlags;
class Encoder;
class Decoder;
class DecoderProto;
class EncoderProto;
extern std::unique_ptr<Decoder> createMacintoshDecoder(
const DecoderProto& config);
extern std::unique_ptr<Encoder> createMacintoshEncoder(
const EncoderProto& config);
#endif

View File

@@ -0,0 +1,11 @@
syntax = "proto2";
import "lib/common.proto";
message MacintoshDecoderProto {}
message MacintoshEncoderProto {
optional double post_index_gap_us = 1 [default = 0.0,
(help) = "post-index gap before first sector header (microseconds)."];
}

View File

@@ -6,56 +6,202 @@
#include "micropolis.h"
#include "bytes.h"
#include "fmt/format.h"
#include "lib/decoders/decoders.pb.h"
/* The sector has a preamble of MFM 0x00s and uses 0xFF as a sync pattern. */
static const FluxPattern SECTOR_SYNC_PATTERN(32, 0xaaaa5555);
/* The sector has a preamble of MFM 0x00s and uses 0xFF as a sync pattern.
*
* 00 00 00 F F
* 0000 0000 0000 0000 0000 0000 0101 0101 0101 0101
* A A A A A A 5 5 5 5
*/
static const FluxPattern SECTOR_SYNC_PATTERN(64, 0xAAAAAAAAAAAA5555LL);
AbstractDecoder::RecordType MicropolisDecoder::advanceToNextRecord()
/* Pattern to skip past current SYNC. */
static const FluxPattern SECTOR_ADVANCE_PATTERN(64, 0xAAAAAAAAAAAAAAAALL);
/* Standard Micropolis checksum. Adds all bytes, with carry. */
uint8_t micropolisChecksum(const Bytes& bytes)
{
_fmr->seekToIndexMark();
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(SECTOR_SYNC_PATTERN, matcher);
if (matcher == &SECTOR_SYNC_PATTERN) {
readRawBits(16);
return SECTOR_RECORD;
}
return UNKNOWN_RECORD;
ByteReader br(bytes);
uint16_t sum = 0;
while (!br.eof())
{
if (sum > 0xFF)
{
sum -= 0x100 - 1;
}
sum += br.read_8();
}
/* The last carry is ignored */
return sum & 0xFF;
}
/* Adds all bytes, with carry. */
static uint8_t checksum(const Bytes& bytes) {
ByteReader br(bytes);
uint16_t sum = 0;
while (!br.eof()) {
if (sum > 0xFF) {
sum -= 0x100 - 1;
}
sum += br.read_8();
}
/* The last carry is ignored */
return sum & 0xFF;
}
void MicropolisDecoder::decodeSectorRecord()
/* Vector MZOS does not use the standard Micropolis checksum.
* The checksum is initially 0.
* For each data byte in the 256-byte payload, rotate left,
* carrying bit 7 to bit 0. XOR with the current checksum.
*
* Unlike the Micropolis checksum, this does not cover the 12-byte
* header (track, sector, 10 OS-specific bytes.)
*/
uint8_t mzosChecksum(const Bytes& bytes)
{
auto rawbits = readRawBits(MICROPOLIS_ENCODED_SECTOR_SIZE*16);
auto bytes = decodeFmMfm(rawbits).slice(0, MICROPOLIS_ENCODED_SECTOR_SIZE);
ByteReader br(bytes);
ByteReader br(bytes);
uint8_t checksum = 0;
uint8_t databyte;
br.read_8(); /* sync */
_sector->logicalTrack = br.read_8();
_sector->logicalSide = _sector->physicalSide;
_sector->logicalSector = br.read_8();
if (_sector->logicalSector > 15)
return;
if (_sector->logicalTrack > 77)
return;
while (!br.eof())
{
databyte = br.read_8();
checksum ^= ((databyte << 1) | (databyte >> 7));
}
br.read(10); /* OS data or padding */
_sector->data = br.read(256);
uint8_t wantChecksum = br.read_8();
uint8_t gotChecksum = checksum(bytes.slice(1, 2+266));
br.read(5); /* 4 byte ECC and ECC-present flag */
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
return checksum;
}
class MicropolisDecoder : public Decoder
{
public:
MicropolisDecoder(const DecoderProto& config):
Decoder(config),
_config(config.micropolis())
{
_checksumType = _config.checksum_type();
}
nanoseconds_t advanceToNextRecord() override
{
nanoseconds_t now = tell().ns();
/* For all but the first sector, seek to the next sector pulse.
* The first sector does not contain the sector pulse in the fluxmap.
*/
if (now != 0)
{
seekToIndexMark();
now = tell().ns();
}
/* Discard a possible partial sector at the end of the track.
* This partial sector could be mistaken for a conflicted sector, if
* whatever data read happens to match the checksum of 0, which is
* rare, but has been observed on some disks.
*/
if (now > (getFluxmapDuration() - 12.5e6))
{
seekToIndexMark();
return 0;
}
nanoseconds_t clock = seekToPattern(SECTOR_SYNC_PATTERN);
auto syncDelta = tell().ns() - now;
/* Due to the weak nature of the Micropolis SYNC patern,
* it's possible to detect a false SYNC during the gap
* between the sector pulse and the write gate. If the SYNC
* is detected less than 100uS after the sector pulse, search
* for another valid SYNC.
*
* Reference: Vector Micropolis Disk Controller Board Technical
* Information Manual, pp. 1-16.
*/
if ((syncDelta > 0) && (syncDelta < 100e3))
{
seekToPattern(SECTOR_ADVANCE_PATTERN);
clock = seekToPattern(SECTOR_SYNC_PATTERN);
}
_sector->headerStartTime = tell().ns();
/* seekToPattern() can skip past the index hole, if this happens
* too close to the end of the Fluxmap, discard the sector.
*/
if (_sector->headerStartTime > (getFluxmapDuration() - 12.5e6))
{
return 0;
}
return clock;
}
void decodeSectorRecord() override
{
readRawBits(48);
auto rawbits = readRawBits(MICROPOLIS_ENCODED_SECTOR_SIZE * 16);
auto bytes =
decodeFmMfm(rawbits).slice(0, MICROPOLIS_ENCODED_SECTOR_SIZE);
ByteReader br(bytes);
int syncByte = br.read_8(); /* sync */
if (syncByte != 0xFF)
return;
_sector->logicalTrack = br.read_8();
_sector->logicalSide = _sector->physicalSide;
_sector->logicalSector = br.read_8();
if (_sector->logicalSector > 15)
return;
if (_sector->logicalTrack > 76)
return;
if (_sector->logicalTrack != _sector->physicalTrack)
return;
br.read(10); /* OS data or padding */
auto data = br.read(MICROPOLIS_PAYLOAD_SIZE);
uint8_t wantChecksum = br.read_8();
/* If not specified, automatically determine the checksum type.
* Once the checksum type is determined, it will be used for the
* entire disk.
*/
if (_checksumType == MicropolisDecoderProto::AUTO)
{
/* Calculate both standard Micropolis (MDOS, CP/M, OASIS) and MZOS
* checksums */
if (wantChecksum == micropolisChecksum(bytes.slice(1, 2 + 266)))
{
_checksumType = MicropolisDecoderProto::MICROPOLIS;
}
else if (wantChecksum ==
mzosChecksum(bytes.slice(
MICROPOLIS_HEADER_SIZE, MICROPOLIS_PAYLOAD_SIZE)))
{
_checksumType = MicropolisDecoderProto::MZOS;
std::cout << "Note: MZOS checksum detected." << std::endl;
}
}
uint8_t gotChecksum;
if (_checksumType == MicropolisDecoderProto::MZOS)
{
gotChecksum = mzosChecksum(
bytes.slice(MICROPOLIS_HEADER_SIZE, MICROPOLIS_PAYLOAD_SIZE));
}
else
{
gotChecksum = micropolisChecksum(bytes.slice(1, 2 + 266));
}
br.read(5); /* 4 byte ECC and ECC-present flag */
if (_config.sector_output_size() == MICROPOLIS_PAYLOAD_SIZE)
_sector->data = data;
else if (_config.sector_output_size() == MICROPOLIS_ENCODED_SECTOR_SIZE)
_sector->data = bytes;
else
error("Sector output size may only be 256 or 275");
_sector->status =
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
private:
const MicropolisDecoderProto& _config;
MicropolisDecoderProto_ChecksumType
_checksumType; /* -1 = auto, 1 = Micropolis, 2=MZOS */
};
std::unique_ptr<Decoder> createMicropolisDecoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new MicropolisDecoder(config));
}

111
arch/micropolis/encoder.cc Normal file
View File

@@ -0,0 +1,111 @@
#include "globals.h"
#include "micropolis.h"
#include "sector.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "image.h"
#include "lib/encoders/encoders.pb.h"
static void write_sector(std::vector<bool>& bits,
unsigned& cursor,
const std::shared_ptr<const Sector>& sector)
{
if ((sector->data.size() != 256) &&
(sector->data.size() != MICROPOLIS_ENCODED_SECTOR_SIZE))
error("unsupported sector size --- you must pick 256 or 275");
int fullSectorSize = 40 + MICROPOLIS_ENCODED_SECTOR_SIZE + 40 + 35;
auto fullSector = std::make_shared<std::vector<uint8_t>>();
fullSector->reserve(fullSectorSize);
/* sector preamble */
for (int i = 0; i < 40; i++)
fullSector->push_back(0);
Bytes sectorData;
if (sector->data.size() == MICROPOLIS_ENCODED_SECTOR_SIZE)
{
if (sector->data[0] != 0xFF)
error(
"275 byte sector doesn't start with sync byte 0xFF. "
"Corrupted sector");
uint8_t wantChecksum = sector->data[1 + 2 + 266];
uint8_t gotChecksum =
micropolisChecksum(sector->data.slice(1, 2 + 266));
if (wantChecksum != gotChecksum)
std::cerr << "Warning: checksum incorrect. Sector: "
<< sector->logicalSector << std::endl;
sectorData = sector->data;
}
else
{
ByteWriter writer(sectorData);
writer.write_8(0xff); /* Sync */
writer.write_8(sector->logicalTrack);
writer.write_8(sector->logicalSector);
for (int i = 0; i < 10; i++)
writer.write_8(0); /* Padding */
writer += sector->data;
writer.write_8(micropolisChecksum(sectorData.slice(1)));
for (int i = 0; i < 5; i++)
writer.write_8(0); /* 4 byte ECC and ECC not present flag */
}
for (uint8_t b : sectorData)
fullSector->push_back(b);
/* sector postamble */
for (int i = 0; i < 40; i++)
fullSector->push_back(0);
/* filler */
for (int i = 0; i < 35; i++)
fullSector->push_back(0);
if (fullSector->size() != fullSectorSize)
error("sector mismatched length");
bool lastBit = false;
encodeMfm(bits, cursor, fullSector, lastBit);
/* filler */
for (int i = 0; i < 5; i++)
{
bits[cursor++] = 1;
bits[cursor++] = 0;
}
}
class MicropolisEncoder : public Encoder
{
public:
MicropolisEncoder(const EncoderProto& config):
Encoder(config),
_config(config.micropolis())
{
}
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
int bitsPerRevolution =
(_config.rotational_period_ms() * 1e3) / _config.clock_period_us();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (const auto& sectorData : sectors)
write_sector(bits, cursor, sectorData);
if (cursor != bits.size())
error("track data mismatched length");
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits,
calculatePhysicalClockPeriod(_config.clock_period_us() * 1e3,
_config.rotational_period_ms() * 1e6));
return fluxmap;
}
private:
const MicropolisEncoderProto& _config;
};
std::unique_ptr<Encoder> createMicropolisEncoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new MicropolisEncoder(config));
}

View File

@@ -1,18 +1,21 @@
#ifndef ZILOGMCZ_H
#define ZILOGMCZ_H
#ifndef MICROPOLIS_H
#define MICROPOLIS_H
#define MICROPOLIS_ENCODED_SECTOR_SIZE (1+2+266+6)
#define MICROPOLIS_PAYLOAD_SIZE (256)
#define MICROPOLIS_HEADER_SIZE (1 + 2 + 10)
#define MICROPOLIS_ENCODED_SECTOR_SIZE \
(MICROPOLIS_HEADER_SIZE + MICROPOLIS_PAYLOAD_SIZE + 6)
class Sector;
class Fluxmap;
class Decoder;
class Encoder;
class EncoderProto;
class DecoderProto;
class MicropolisDecoder : public AbstractDecoder
{
public:
virtual ~MicropolisDecoder() {}
extern std::unique_ptr<Decoder> createMicropolisDecoder(
const DecoderProto& config);
extern std::unique_ptr<Encoder> createMicropolisEncoder(
const EncoderProto& config);
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
extern uint8_t micropolisChecksum(const Bytes& bytes);
#endif

View File

@@ -0,0 +1,24 @@
syntax = "proto2";
import "lib/common.proto";
message MicropolisDecoderProto {
enum ChecksumType {
AUTO = 0;
MICROPOLIS = 1;
MZOS = 2;
}
optional int32 sector_output_size = 1 [default = 256,
(help) = "How much of the raw sector should be saved. Must be 256 or 275"];
optional ChecksumType checksum_type = 2 [default = AUTO,
(help) = "Checksum type to use: AUTO, MICROPOLIS, MZOS"];
}
message MicropolisEncoderProto {
optional double clock_period_us = 1
[ default = 2.0, (help) = "clock rate on the real device" ];
optional double rotational_period_ms = 2
[ default = 200.0, (help) = "rotational period on the real device" ];
}

View File

@@ -5,8 +5,6 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "sector.h"
#include "record.h"
#include "track.h"
#include <string.h>
const int SECTOR_SIZE = 256;
@@ -18,58 +16,68 @@ const int SECTOR_SIZE = 256;
*/
/* FM beginning of track marker:
* 0 0 f 3 decoded nibbles
* 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1
* 1010 1010 1010 1010 1111 1111 1010 1111
* a a a a f f a f
* a a a a f f a f encoded nibbles
*/
const FluxPattern ID_PATTERN(32, 0xaaaaffaf);
void MxDecoder::beginTrack()
class MxDecoder : public Decoder
{
_currentSector = -1;
_clock = 0;
}
public:
MxDecoder(const DecoderProto& config): Decoder(config) {}
AbstractDecoder::RecordType MxDecoder::advanceToNextRecord()
{
if (_currentSector == -1)
void beginTrack() override
{
/* First sector in the track: look for the sync marker. */
const FluxMatcher* matcher = nullptr;
_sector->clock = _clock = _fmr->seekToPattern(ID_PATTERN, matcher);
readRawBits(32); /* skip the ID mark */
_logicalTrack = decodeFmMfm(readRawBits(32)).slice(0, 32).reader().read_be16();
}
else if (_currentSector == 10)
{
/* That was the last sector on the disk. */
return UNKNOWN_RECORD;
}
else
{
/* Otherwise we assume the clock from the first sector is still valid.
* The decoder framwork will automatically stop when we hit the end of
* the track. */
_sector->clock = _clock;
_clock = _sector->clock = seekToPattern(ID_PATTERN);
_currentSector = 0;
}
_currentSector++;
return SECTOR_RECORD;
}
nanoseconds_t advanceToNextRecord() override
{
if (_currentSector == 11)
{
/* That was the last sector on the disk. */
return 0;
}
else
return _clock;
}
void MxDecoder::decodeSectorRecord()
void decodeSectorRecord() override
{
/* Skip the ID pattern and track word, which is only present on the
* first sector. We don't trust the track word because some driver
* don't write it correctly. */
if (_currentSector == 0)
readRawBits(64);
auto bits = readRawBits((SECTOR_SIZE + 2) * 16);
auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE + 2);
uint16_t gotChecksum = 0;
ByteReader br(bytes);
for (int i = 0; i < (SECTOR_SIZE / 2); i++)
gotChecksum += br.read_be16();
uint16_t wantChecksum = br.read_be16();
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalSide = _sector->physicalSide;
_sector->logicalSector = _currentSector;
_sector->data = bytes.slice(0, SECTOR_SIZE).swab();
_sector->status =
(gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
_currentSector++;
}
private:
nanoseconds_t _clock;
int _currentSector;
};
std::unique_ptr<Decoder> createMxDecoder(const DecoderProto& config)
{
auto bits = readRawBits((SECTOR_SIZE+2)*16);
auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE+2).swab();
uint16_t gotChecksum = 0;
ByteReader br(bytes);
for (int i=0; i<(SECTOR_SIZE/2); i++)
gotChecksum += br.read_le16();
uint16_t wantChecksum = br.read_le16();
_sector->logicalTrack = _logicalTrack;
_sector->logicalSide = _track->physicalSide;
_sector->logicalSector = _currentSector;
_sector->data = bytes.slice(0, SECTOR_SIZE);
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
return std::unique_ptr<Decoder>(new MxDecoder(config));
}

View File

@@ -3,19 +3,6 @@
#include "decoders/decoders.h"
class MxDecoder : public AbstractDecoder
{
public:
virtual ~MxDecoder() {}
void beginTrack();
RecordType advanceToNextRecord();
void decodeSectorRecord();
private:
nanoseconds_t _clock;
int _currentSector;
int _logicalTrack;
};
extern std::unique_ptr<Decoder> createMxDecoder(const DecoderProto& config);
#endif

4
arch/mx/mx.proto Normal file
View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message MxDecoderProto {}

186
arch/northstar/decoder.cc Normal file
View File

@@ -0,0 +1,186 @@
/* Decoder for North Star 10-sector hard-sectored disks.
*
* Supports both single- and double-density. For the sector format and
* checksum algorithm, see pp. 33 of the North Star Double Density Controller
* manual:
*
* http://bitsavers.org/pdf/northstar/boards/Northstar_MDS-A-D_1978.pdf
*
* North Star disks do not contain any track/head/sector information
* encoded in the sector record. For this reason, we have to be absolutely
* sure that the hardSectorId is correct.
*/
#include "globals.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "northstar.h"
#include "bytes.h"
#include "lib/decoders/decoders.pb.h"
#include "fmt/format.h"
#define MFM_ID 0xaaaaaaaaaaaa5545LL
#define FM_ID 0xaaaaaaaaaaaaffefLL
/*
* MFM sectors have 32 bytes of 00's followed by two sync characters,
* specified in the North Star MDS manual as 0xFBFB.
*
* This is true for most disks; however, I found a few disks, including an
* original North Star DOS/BASIC v2.2.1 DQ disk) that uses 0xFBnn, where
* nn is an incrementing pattern.
*
* 00 00 00 F B
* 0000 0000 0000 0000 0000 0000 0101 0101 0100 0101
* A A A A A A 5 5 4 5
*/
static const FluxPattern MFM_PATTERN(64, MFM_ID);
/* FM sectors have 16 bytes of 00's followed by 0xFB.
* 00 FB
* 0000 0000 1111 1111 1110 1111
* A A F F E F
*/
static const FluxPattern FM_PATTERN(64, FM_ID);
const FluxMatchers ANY_SECTOR_PATTERN({
&MFM_PATTERN,
&FM_PATTERN,
});
/* Checksum is initially 0.
* For each data byte, XOR with the current checksum.
* Rotate checksum left, carrying bit 7 to bit 0.
*/
uint8_t northstarChecksum(const Bytes& bytes)
{
ByteReader br(bytes);
uint8_t checksum = 0;
while (!br.eof())
{
checksum ^= br.read_8();
checksum = ((checksum << 1) | ((checksum >> 7)));
}
return checksum;
}
class NorthstarDecoder : public Decoder
{
public:
NorthstarDecoder(const DecoderProto& config):
Decoder(config),
_config(config.northstar())
{
}
/* Search for FM or MFM sector record */
nanoseconds_t advanceToNextRecord() override
{
nanoseconds_t now = tell().ns();
/* For all but the first sector, seek to the next sector pulse.
* The first sector does not contain the sector pulse in the fluxmap.
*/
if (now != 0)
{
seekToIndexMark();
now = tell().ns();
}
/* Discard a possible partial sector at the end of the track.
* This partial sector could be mistaken for a conflicted sector, if
* whatever data read happens to match the checksum of 0, which is
* rare, but has been observed on some disks.
*/
if (now > (getFluxmapDuration() - 21e6))
{
seekToIndexMark();
return 0;
}
int msSinceIndex = std::round(now / 1e6);
/* Note that the seekToPattern ignores the sector pulses, so if
* a sector is not found for some reason, the seek will advance
* past one or more sector pulses. For this reason, calculate
* _hardSectorId after the sector header is found.
*/
nanoseconds_t clock = seekToPattern(ANY_SECTOR_PATTERN);
_sector->headerStartTime = tell().ns();
/* Discard a possible partial sector. */
if (_sector->headerStartTime > (getFluxmapDuration() - 21e6))
{
return 0;
}
int sectorFoundTimeRaw = std::round(_sector->headerStartTime / 1e6);
int sectorFoundTime;
/* Round time to the nearest 20ms */
if ((sectorFoundTimeRaw % 20) < 10)
{
sectorFoundTime = (sectorFoundTimeRaw / 20) * 20;
}
else
{
sectorFoundTime = ((sectorFoundTimeRaw + 20) / 20) * 20;
}
/* Calculate the sector ID based on time since the index */
_hardSectorId = (sectorFoundTime / 20) % 10;
return clock;
}
void decodeSectorRecord() override
{
uint64_t id = toBytes(readRawBits(64)).reader().read_be64();
unsigned recordSize, payloadSize, headerSize;
if (id == MFM_ID)
{
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_DD;
payloadSize = NORTHSTAR_PAYLOAD_SIZE_DD;
headerSize = NORTHSTAR_HEADER_SIZE_DD;
}
else
{
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_SD;
payloadSize = NORTHSTAR_PAYLOAD_SIZE_SD;
headerSize = NORTHSTAR_HEADER_SIZE_SD;
}
auto rawbits = readRawBits(recordSize * 16);
auto bytes = decodeFmMfm(rawbits).slice(0, recordSize);
ByteReader br(bytes);
_sector->logicalSide = _sector->physicalSide;
_sector->logicalSector = _hardSectorId;
_sector->logicalTrack = _sector->physicalTrack;
if (headerSize == NORTHSTAR_HEADER_SIZE_DD)
{
br.read_8(); /* MFM second Sync char, usually 0xFB */
}
_sector->data = br.read(payloadSize);
uint8_t wantChecksum = br.read_8();
uint8_t gotChecksum =
northstarChecksum(bytes.slice(headerSize - 1, payloadSize));
_sector->status =
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
private:
const NorthstarDecoderProto& _config;
uint8_t _hardSectorId;
};
std::unique_ptr<Decoder> createNorthstarDecoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new NorthstarDecoder(config));
}

168
arch/northstar/encoder.cc Normal file
View File

@@ -0,0 +1,168 @@
#include "globals.h"
#include "northstar.h"
#include "sector.h"
#include "bytes.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "image.h"
#include "lib/encoders/encoders.pb.h"
#define GAP_FILL_SIZE_SD 30
#define PRE_HEADER_GAP_FILL_SIZE_SD 9
#define GAP_FILL_SIZE_DD 62
#define PRE_HEADER_GAP_FILL_SIZE_DD 16
#define GAP1_FILL_BYTE (0x4F)
#define GAP2_FILL_BYTE (0x4F)
#define TOTAL_SECTOR_BYTES ()
static void write_sector(std::vector<bool>& bits,
unsigned& cursor,
const std::shared_ptr<const Sector>& sector)
{
int preambleSize = 0;
int encodedSectorSize = 0;
int gapFillSize = 0;
int preHeaderGapFillSize = 0;
bool doubleDensity;
switch (sector->data.size())
{
case NORTHSTAR_PAYLOAD_SIZE_SD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD +
NORTHSTAR_ENCODED_SECTOR_SIZE_SD +
GAP_FILL_SIZE_SD;
gapFillSize = GAP_FILL_SIZE_SD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD;
doubleDensity = false;
break;
case NORTHSTAR_PAYLOAD_SIZE_DD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD +
NORTHSTAR_ENCODED_SECTOR_SIZE_DD +
GAP_FILL_SIZE_DD;
gapFillSize = GAP_FILL_SIZE_DD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD;
doubleDensity = true;
break;
default:
error("unsupported sector size --- you must pick 256 or 512");
break;
}
int fullSectorSize = preambleSize + encodedSectorSize;
auto fullSector = std::make_shared<std::vector<uint8_t>>();
fullSector->reserve(fullSectorSize);
/* sector gap after index pulse */
for (int i = 0; i < preHeaderGapFillSize; i++)
fullSector->push_back(GAP1_FILL_BYTE);
/* sector preamble */
for (int i = 0; i < preambleSize; i++)
fullSector->push_back(0);
Bytes sectorData;
if (sector->data.size() == encodedSectorSize)
sectorData = sector->data;
else
{
ByteWriter writer(sectorData);
writer.write_8(0xFB); /* sync character */
if (doubleDensity == true)
{
writer.write_8(0xFB); /* Double-density has two sync characters */
}
writer += sector->data;
if (doubleDensity == true)
{
writer.write_8(northstarChecksum(sectorData.slice(2)));
}
else
{
writer.write_8(northstarChecksum(sectorData.slice(1)));
}
}
for (uint8_t b : sectorData)
fullSector->push_back(b);
if (sector->logicalSector != 9)
{
/* sector postamble */
for (int i = 0; i < gapFillSize; i++)
fullSector->push_back(GAP2_FILL_BYTE);
if (fullSector->size() != fullSectorSize)
error("sector mismatched length ({}); expected {}, got {}",
sector->data.size(),
fullSector->size(),
fullSectorSize);
}
else
{
/* sector postamble */
for (int i = 0; i < gapFillSize; i++)
fullSector->push_back(GAP2_FILL_BYTE);
}
bool lastBit = false;
if (doubleDensity == true)
{
encodeMfm(bits, cursor, fullSector, lastBit);
}
else
{
encodeFm(bits, cursor, fullSector);
}
}
class NorthstarEncoder : public Encoder
{
public:
NorthstarEncoder(const EncoderProto& config):
Encoder(config),
_config(config.northstar())
{
}
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
int bitsPerRevolution = 100000;
double clockRateUs = _config.clock_period_us();
const auto& sector = *sectors.begin();
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD)
bitsPerRevolution /= 2; // FM
else
clockRateUs /= 2.00;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (const auto& sectorData : sectors)
write_sector(bits, cursor, sectorData);
if (cursor > bits.size())
error("track data overrun");
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits,
calculatePhysicalClockPeriod(
clockRateUs * 1e3, _config.rotational_period_ms() * 1e6));
return fluxmap;
}
private:
const NorthstarEncoderProto& _config;
};
std::unique_ptr<Encoder> createNorthstarEncoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new NorthstarEncoder(config));
}

View File

@@ -0,0 +1,42 @@
#ifndef NORTHSTAR_H
#define NORTHSTAR_H
/* Northstar floppies are 10-hard sectored disks with a sector format as
* follows:
*
* |----------------------------------|
* | SYNC Byte | Payload | Checksum |
* |------------+----------+----------|
* | 1 (0xFB) | 256 (SD) | 1 |
* | 2 (0xFBFB) | 512 (DD) | |
* |----------------------------------|
*
*/
#define NORTHSTAR_PREAMBLE_SIZE_SD (16)
#define NORTHSTAR_PREAMBLE_SIZE_DD (32)
#define NORTHSTAR_HEADER_SIZE_SD (1)
#define NORTHSTAR_HEADER_SIZE_DD (2)
#define NORTHSTAR_PAYLOAD_SIZE_SD (256)
#define NORTHSTAR_PAYLOAD_SIZE_DD (512)
#define NORTHSTAR_CHECKSUM_SIZE (1)
#define NORTHSTAR_ENCODED_SECTOR_SIZE_SD \
(NORTHSTAR_HEADER_SIZE_SD + NORTHSTAR_PAYLOAD_SIZE_SD + \
NORTHSTAR_CHECKSUM_SIZE)
#define NORTHSTAR_ENCODED_SECTOR_SIZE_DD \
(NORTHSTAR_HEADER_SIZE_DD + NORTHSTAR_PAYLOAD_SIZE_DD + \
NORTHSTAR_CHECKSUM_SIZE)
class Decoder;
class Encoder;
class EncoderProto;
class DecoderProto;
extern uint8_t northstarChecksum(const Bytes& bytes);
extern std::unique_ptr<Decoder> createNorthstarDecoder(
const DecoderProto& config);
extern std::unique_ptr<Encoder> createNorthstarEncoder(
const EncoderProto& config);
#endif /* NORTHSTAR */

View File

@@ -0,0 +1,13 @@
syntax = "proto2";
import "lib/common.proto";
message NorthstarDecoderProto {}
message NorthstarEncoderProto {
optional double clock_period_us = 1
[ default = 4.0, (help) = "clock rate on the real device (for FM)" ];
optional double rotational_period_ms = 2
[ default = 166.0, (help) = "rotational period on the real device" ];
}

51
arch/rolandd20/decoder.cc Normal file
View File

@@ -0,0 +1,51 @@
#include "lib/globals.h"
#include "lib/decoders/decoders.h"
#include "lib/crc.h"
#include "lib/fluxmap.h"
#include "lib/decoders/fluxmapreader.h"
#include "lib/sector.h"
#include "lib/bytes.h"
#include "rolandd20.h"
#include <string.h>
/* Sector header record:
*
* BF FF FF FF FF FF FE AB
*
* This encodes to:
*
* e d 5 5 5 5 5 5
* 1110 1101 0101 0101 0101 0101 0101 0101
* 5 5 5 5 5 5 5 5
* 0101 0101 0101 0101 0101 0101 0101 0101
* 5 5 5 5 5 5 5 5
* 0101 0101 0101 0101 0101 0101 0101 0101
* 5 5 5 4 4 4 4 5
* 0101 0101 0101 0100 0100 0100 0100 0101
*/
static const FluxPattern SECTOR_PATTERN(64, 0xed55555555555555LL);
class RolandD20Decoder : public Decoder
{
public:
RolandD20Decoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(SECTOR_PATTERN);
}
void decodeSectorRecord() override
{
auto rawbits = readRawBits(256);
const auto& bytes = decodeFmMfm(rawbits);
fmt::print("{} ", _sector->clock);
hexdump(std::cout, bytes);
}
};
std::unique_ptr<Decoder> createRolandD20Decoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new RolandD20Decoder(config));
}

View File

@@ -0,0 +1,4 @@
#pragma once
extern std::unique_ptr<Decoder> createRolandD20Decoder(
const DecoderProto& config);

View File

@@ -0,0 +1,5 @@
syntax = "proto2";
message RolandD20DecoderProto {}

154
arch/smaky6/decoder.cc Normal file
View File

@@ -0,0 +1,154 @@
#include "globals.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "smaky6.h"
#include "bytes.h"
#include "crc.h"
#include "fmt/format.h"
#include "lib/decoders/decoders.pb.h"
#include <string.h>
#include <algorithm>
static const FluxPattern SECTOR_PATTERN(32, 0x54892aaa);
class Smaky6Decoder : public Decoder
{
public:
Smaky6Decoder(const DecoderProto& config):
Decoder(config),
_config(config.smaky6())
{
}
private:
/* Returns the sector ID of the _current_ sector. */
int advanceToNextSector()
{
auto previous = tell();
seekToIndexMark();
auto now = tell();
if ((now.ns() - previous.ns()) < 9e6)
{
seekToIndexMark();
auto next = tell();
if ((next.ns() - now.ns()) < 9e6)
{
/* We just found sector 0. */
_sectorId = 0;
}
else
{
/* Spurious... */
seek(now);
}
}
return _sectorId++;
}
public:
void beginTrack() override
{
/* Find the start-of-track index marks, which will be an interval
* of about 6ms. */
seekToIndexMark();
_sectorId = 99;
for (;;)
{
auto pos = tell();
advanceToNextSector();
if (_sectorId < 99)
{
seek(pos);
break;
}
if (eof())
return;
}
/* Now we know where to start counting, start finding sectors. */
_sectorStarts.clear();
for (;;)
{
auto now = tell();
if (eof())
break;
int id = advanceToNextSector();
if (id < 16)
_sectorStarts.push_back(std::make_pair(id, now));
}
_sectorIndex = 0;
}
nanoseconds_t advanceToNextRecord() override
{
if (_sectorIndex == _sectorStarts.size())
{
seekToIndexMark();
return 0;
}
const auto& p = _sectorStarts[_sectorIndex++];
_sectorId = p.first;
seek(p.second);
nanoseconds_t clock = seekToPattern(SECTOR_PATTERN);
_sector->headerStartTime = tell().ns();
return clock;
}
void decodeSectorRecord() override
{
readRawBits(33);
const auto& rawbits = readRawBits(SMAKY6_RECORD_SIZE * 16);
if (rawbits.size() < SMAKY6_SECTOR_SIZE)
return;
const auto& rawbytes =
toBytes(rawbits).slice(0, SMAKY6_RECORD_SIZE * 16);
/* The Smaky bytes are stored backwards! Backwards! */
const auto& bytes =
decodeFmMfm(rawbits).slice(0, SMAKY6_RECORD_SIZE).reverseBits();
ByteReader br(bytes);
uint8_t track = br.read_8();
Bytes data = br.read(SMAKY6_SECTOR_SIZE);
uint8_t wantedChecksum = br.read_8();
uint8_t gotChecksum = sumBytes(data) & 0xff;
if (track != _sector->physicalTrack)
return;
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalSide = _sector->physicalSide;
_sector->logicalSector = _sectorId;
_sector->data = data;
_sector->status =
(wantedChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
private:
const Smaky6DecoderProto& _config;
nanoseconds_t _startOfTrack;
std::vector<std::pair<int, Fluxmap::Position>> _sectorStarts;
int _sectorId;
int _sectorIndex;
};
std::unique_ptr<Decoder> createSmaky6Decoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new Smaky6Decoder(config));
}

9
arch/smaky6/smaky6.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef SMAKY6_H
#define SMAKY6_H
#define SMAKY6_SECTOR_SIZE 256
#define SMAKY6_RECORD_SIZE (1 + SMAKY6_SECTOR_SIZE + 1)
extern std::unique_ptr<Decoder> createSmaky6Decoder(const DecoderProto& config);
#endif

6
arch/smaky6/smaky6.proto Normal file
View File

@@ -0,0 +1,6 @@
syntax = "proto2";
import "lib/common.proto";
message Smaky6DecoderProto {}

View File

@@ -6,8 +6,6 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "sector.h"
#include "record.h"
#include "track.h"
#include <string.h>
#include <fmt/format.h>
@@ -25,63 +23,78 @@
* When shifted out of phase, the special 0xa1 byte becomes an illegal
* encoding (you can't do 10 00). So this can't be spoofed by user data.
*/
const uint16_t SECTOR_ID = 0x550a;
const FluxPattern SECTOR_RECORD_PATTERN(32, 0x11112244);
/*
* Data record:
* data: 0 1 0 1 0 1 0 1 .0 0 0 0 1 0 1 1 = 0x550c
* data: 0 1 0 1 0 1 0 1 .0 0 0 0 1 0 1 1 = 0x550b
* mfm: 00 01 00 01 00 01 00 01.00 10 10 10 01 00 01 01 = 0x11112a45
* special: 00 01 00 01 00 01 00 01.00 10 00 10 01 00 01 01 = 0x11112245
* ^^
* When shifted out of phase, the special 0xa1 byte becomes an illegal
* encoding (you can't do 10 00). So this can't be spoofed by user data.
*/
const uint16_t DATA_ID = 0x550b;
const FluxPattern DATA_RECORD_PATTERN(32, 0x11112245);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
const FluxMatchers ANY_RECORD_PATTERN(
{&SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN});
AbstractDecoder::RecordType TiDs990Decoder::advanceToNextRecord()
class Tids990Decoder : public Decoder
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
public:
Tids990Decoder(const DecoderProto& config): Decoder(config) {}
void TiDs990Decoder::decodeSectorRecord()
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE * 16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE);
ByteReader br(bytes);
if (br.read_be16() != SECTOR_ID)
return;
uint16_t gotChecksum =
crc16(CCITT_POLY, bytes.slice(1, TIDS990_SECTOR_RECORD_SIZE - 3));
_sector->logicalSide = br.read_8() >> 3;
_sector->logicalTrack = br.read_8();
br.read_8(); /* number of sectors per track */
_sector->logicalSector = br.read_8();
br.read_be16(); /* sector size */
uint16_t wantChecksum = br.read_be16();
if (wantChecksum == gotChecksum)
_sector->status =
Sector::DATA_MISSING; /* correct but unintuitive */
}
void decodeDataRecord() override
{
auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE * 16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE);
ByteReader br(bytes);
if (br.read_be16() != DATA_ID)
return;
uint16_t gotChecksum =
crc16(CCITT_POLY, bytes.slice(1, TIDS990_DATA_RECORD_SIZE - 3));
_sector->data = br.read(TIDS990_PAYLOAD_SIZE);
uint16_t wantChecksum = br.read_be16();
_sector->status =
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createTids990Decoder(const DecoderProto& config)
{
auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE*16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE);
ByteReader br(bytes);
uint16_t gotChecksum = crc16(CCITT_POLY, bytes.slice(1, TIDS990_SECTOR_RECORD_SIZE-3));
br.seek(2);
_sector->logicalSide = br.read_8() >> 3;
_sector->logicalTrack = br.read_8();
br.read_8(); /* number of sectors per track */
_sector->logicalSector = br.read_8();
br.read_be16(); /* sector size */
uint16_t wantChecksum = br.read_be16();
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
return std::unique_ptr<Decoder>(new Tids990Decoder(config));
}
void TiDs990Decoder::decodeDataRecord()
{
auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE*16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE);
ByteReader br(bytes);
uint16_t gotChecksum = crc16(CCITT_POLY, bytes.slice(1, TIDS990_DATA_RECORD_SIZE-3));
br.seek(2);
_sector->data = br.read(TIDS990_PAYLOAD_SIZE);
uint16_t wantChecksum = br.read_be16();
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}

View File

@@ -1,176 +1,151 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "tids990.h"
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
#include "readerwriter.h"
#include "image.h"
#include "arch/tids990/tids990.pb.h"
#include "lib/encoders/encoders.pb.h"
#include <fmt/format.h>
FlagGroup tids990EncoderFlags;
static IntFlag trackLengthMs(
{ "--tids990-track-length-ms" },
"Length of a track in milliseconds.",
166);
static IntFlag sectorCount(
{ "--tids990-sector-count" },
"Number of sectors per track.",
26);
static IntFlag clockRateKhz(
{ "--tids990-clock-rate-khz" },
"Clock rate of data to write.",
500);
static HexIntFlag am1Byte(
{ "--tids990-am1-byte" },
"16-bit RAW bit pattern to use for the AM1 ID byte",
0x2244);
static HexIntFlag am2Byte(
{ "--tids990-am2-byte" },
"16-bit RAW bit pattern to use for the AM2 ID byte",
0x2245);
static IntFlag gap1(
{ "--tids990-gap1-bytes" },
"Size of gap 1 (the post-index gap).",
80);
static IntFlag gap2(
{ "--tids990-gap2-bytes" },
"Size of gap 2 (the post-ID gap).",
21);
static IntFlag gap3(
{ "--tids990-gap3-bytes" },
"Size of gap 3 (the post-data or format gap).",
51);
static StringFlag sectorSkew(
{ "--tids990-sector-skew" },
"Order to emit sectors.",
"1mhc72nid83oje94pkfa50lgb6");
static int charToInt(char c)
{
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
}
void TiDs990Encoder::writeRawBits(uint32_t data, int width)
{
_cursor += width;
_lastBit = data & 1;
for (int i=0; i<width; i++)
{
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
void TiDs990Encoder::writeBytes(const Bytes& bytes)
{
encodeMfm(_bits, _cursor, bytes, _lastBit);
}
void TiDs990Encoder::writeBytes(int count, uint8_t byte)
{
Bytes bytes = { byte };
for (int i=0; i<count; i++)
writeBytes(bytes);
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
}
static uint8_t decodeUint16(uint16_t raw)
{
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
}
std::unique_ptr<Fluxmap> TiDs990Encoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
class Tids990Encoder : public Encoder
{
double clockRateUs = 1e3 / clockRateKhz / 2.0;
int bitsPerRevolution = (trackLengthMs * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
uint8_t am1Unencoded = decodeUint16(am1Byte);
uint8_t am2Unencoded = decodeUint16(am2Byte);
writeBytes(gap1, 0x55);
bool first = true;
for (char sectorChar : sectorSkew.get())
{
int sectorId = charToInt(sectorChar);
if (!first)
writeBytes(gap3, 0x55);
first = false;
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
if (!sectorData)
Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId);
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
{
Bytes header;
ByteWriter bw(header);
writeBytes(12, 0x55);
bw.write_8(am1Unencoded);
bw.write_8(sectorData->logicalSide << 3);
bw.write_8(sectorData->logicalTrack);
bw.write_8(sectorCount);
bw.write_8(sectorData->logicalSector);
bw.write_be16(sectorData->data.size());
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
writeRawBits(am1Byte, 16);
writeBytes(header.slice(1));
}
writeBytes(gap2, 0x55);
{
Bytes data;
ByteWriter bw(data);
writeBytes(12, 0x55);
bw.write_8(am2Unencoded);
bw += sectorData->data;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
writeRawBits(am2Byte, 16);
writeBytes(data.slice(1));
}
public:
Tids990Encoder(const EncoderProto& config):
Encoder(config),
_config(config.tids990())
{
}
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeBytes(1, 0x55);
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);
return fluxmap;
}
private:
void writeRawBits(uint32_t data, int width)
{
_cursor += width;
_lastBit = data & 1;
for (int i = 0; i < width; i++)
{
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
void writeBytes(const Bytes& bytes)
{
encodeMfm(_bits, _cursor, bytes, _lastBit);
}
void writeBytes(int count, uint8_t byte)
{
Bytes bytes = {byte};
for (int i = 0; i < count; i++)
writeBytes(bytes);
}
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
double clockRateUs = _config.clock_period_us() / 2.0;
int bitsPerRevolution =
(_config.rotational_period_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
uint8_t am1Unencoded = decodeUint16(_config.am1_byte());
uint8_t am2Unencoded = decodeUint16(_config.am2_byte());
writeBytes(_config.gap1_bytes(), 0x55);
bool first = true;
for (const auto& sectorData : sectors)
{
if (!first)
writeBytes(_config.gap3_bytes(), 0x55);
first = false;
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
{
Bytes header;
ByteWriter bw(header);
writeBytes(12, 0x55);
bw.write_8(am1Unencoded);
bw.write_8(sectorData->logicalSide << 3);
bw.write_8(sectorData->logicalTrack);
bw.write_8(_config.sector_count());
bw.write_8(sectorData->logicalSector);
bw.write_be16(sectorData->data.size());
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
writeRawBits(_config.am1_byte(), 16);
writeBytes(header.slice(1));
}
writeBytes(_config.gap2_bytes(), 0x55);
{
Bytes data;
ByteWriter bw(data);
writeBytes(12, 0x55);
bw.write_8(am2Unencoded);
bw += sectorData->data;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
writeRawBits(_config.am2_byte(), 16);
writeBytes(data.slice(1));
}
}
if (_cursor >= _bits.size())
error("track data overrun");
while (_cursor < _bits.size())
writeBytes(1, 0x55);
auto fluxmap = std::make_unique<Fluxmap>();
fluxmap->appendBits(_bits,
calculatePhysicalClockPeriod(
clockRateUs * 1e3, _config.rotational_period_ms() * 1e6));
return fluxmap;
}
private:
const Tids990EncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
std::unique_ptr<Encoder> createTids990Encoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new Tids990Encoder(config));
}

View File

@@ -1,47 +1,18 @@
#ifndef TIDS990_H
#define TIDS990_H
#define TIDS990_PAYLOAD_SIZE 288 /* bytes */
#define TIDS990_SECTOR_RECORD_SIZE 10 /* bytes */
#define TIDS990_DATA_RECORD_SIZE (TIDS990_PAYLOAD_SIZE + 4) /* bytes */
#define TIDS990_PAYLOAD_SIZE 288 /* bytes */
#define TIDS990_SECTOR_RECORD_SIZE 10 /* bytes */
#define TIDS990_DATA_RECORD_SIZE (TIDS990_PAYLOAD_SIZE + 4) /* bytes */
class Sector;
class SectorSet;
class Fluxmap;
class Track;
class Encoder;
class Decoder;
class DecoderProto;
class EncoderProto;
class TiDs990Decoder : public AbstractDecoder
{
public:
virtual ~TiDs990Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
class TiDs990Encoder : public AbstractEncoder
{
public:
virtual ~TiDs990Encoder() {}
private:
void writeRawBits(uint32_t data, int width);
void writeBytes(const Bytes& bytes);
void writeBytes(int count, uint8_t value);
void writeSync();
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
private:
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
extern FlagGroup tids990EncoderFlags;
extern std::unique_ptr<Decoder> createTids990Decoder(
const DecoderProto& config);
extern std::unique_ptr<Encoder> createTids990Encoder(
const EncoderProto& config);
#endif

View File

@@ -0,0 +1,25 @@
syntax = "proto2";
import "lib/common.proto";
message Tids990DecoderProto {}
message Tids990EncoderProto {
optional double rotational_period_ms = 1 [ default = 166,
(help) = "length of a track" ];
optional int32 sector_count = 2 [ default = 26,
(help) = "number of sectors per track" ];
optional double clock_period_us = 3 [ default = 2,
(help) = "clock rate of data to write" ];
optional int32 am1_byte = 4 [ default = 0x2244,
(help) = "16-bit RAW bit pattern to use for the AM1 ID byte" ];
optional int32 am2_byte = 5 [ default = 0x2245,
(help) = "16-bit RAW bit pattern to use for the AM2 ID byte" ];
optional int32 gap1_bytes = 6 [ default = 80,
(help) = "size of gap 1 (the post-index gap)" ];
optional int32 gap2_bytes = 7 [ default = 21,
(help) = "size of gap 2 (the post-ID gap)" ];
optional int32 gap3_bytes = 8 [ default = 51,
(help) = "size of gap 3 (the post-data or format gap)" ];
}

View File

@@ -2,32 +2,32 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "victor9k.h"
#include "crc.h"
#include "bytes.h"
#include "track.h"
#include "fmt/format.h"
#include <string.h>
#include <algorithm>
const FluxPattern SECTOR_RECORD_PATTERN(32, VICTOR9K_SECTOR_RECORD);
const FluxPattern DATA_RECORD_PATTERN(32, VICTOR9K_DATA_RECORD);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
const FluxMatchers ANY_RECORD_PATTERN(
{&SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN});
static int decode_data_gcr(uint8_t gcr)
{
switch (gcr)
{
#define GCR_ENTRY(gcr, data) \
case gcr: return data;
#include "data_gcr.h"
#undef GCR_ENTRY
#define GCR_ENTRY(gcr, data) \
case gcr: \
return data;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
}
static Bytes decode(const std::vector<bool>& bits)
{
@@ -39,11 +39,11 @@ static Bytes decode(const std::vector<bool>& bits)
while (ii != bits.end())
{
uint8_t inputfifo = 0;
for (size_t i=0; i<5; i++)
for (size_t i = 0; i < 5; i++)
{
if (ii == bits.end())
break;
inputfifo = (inputfifo<<1) | *ii++;
inputfifo = (inputfifo << 1) | *ii++;
}
uint8_t decoded = decode_data_gcr(inputfifo);
@@ -54,58 +54,65 @@ static Bytes decode(const std::vector<bool>& bits)
return output;
}
AbstractDecoder::RecordType Victor9kDecoder::advanceToNextRecord()
class Victor9kDecoder : public Decoder
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return DATA_RECORD;
return UNKNOWN_RECORD;
}
public:
Victor9kDecoder(const DecoderProto& config): Decoder(config) {}
void Victor9kDecoder::decodeSectorRecord()
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
/* Check the ID. */
if (readRaw32() != VICTOR9K_SECTOR_RECORD)
return;
/* Read header. */
auto bytes = decode(readRawBits(3 * 10)).slice(0, 3);
uint8_t rawTrack = bytes[0];
_sector->logicalSector = bytes[1];
uint8_t gotChecksum = bytes[2];
_sector->logicalTrack = rawTrack & 0x7f;
_sector->logicalSide = rawTrack >> 7;
uint8_t wantChecksum = bytes[0] + bytes[1];
if ((_sector->logicalSector > 20) || (_sector->logicalTrack > 85) ||
(_sector->logicalSide > 1))
return;
if (wantChecksum == gotChecksum)
_sector->status =
Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord() override
{
/* Check the ID. */
if (readRaw32() != VICTOR9K_DATA_RECORD)
return;
/* Read data. */
auto bytes = decode(readRawBits((VICTOR9K_SECTOR_LENGTH + 4) * 10))
.slice(0, VICTOR9K_SECTOR_LENGTH + 4);
ByteReader br(bytes);
_sector->data = br.read(VICTOR9K_SECTOR_LENGTH);
uint16_t gotChecksum = sumBytes(_sector->data);
uint16_t wantChecksum = br.read_le16();
_sector->status =
(gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createVictor9kDecoder(const DecoderProto& config)
{
/* Skip the sync marker bit. */
readRawBits(23);
/* Read header. */
auto bytes = decode(readRawBits(4*10)).slice(0, 4);
uint8_t rawTrack = bytes[1];
_sector->logicalSector = bytes[2];
uint8_t gotChecksum = bytes[3];
_sector->logicalTrack = rawTrack & 0x7f;
_sector->logicalSide = rawTrack >> 7;
uint8_t wantChecksum = bytes[1] + bytes[2];
if ((_sector->logicalSector > 20) || (_sector->logicalTrack > 85) || (_sector->logicalSide > 1))
return;
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void Victor9kDecoder::decodeDataRecord()
{
/* Skip the sync marker bit. */
readRawBits(23);
/* Read data. */
auto bytes = decode(readRawBits((VICTOR9K_SECTOR_LENGTH+5)*10))
.slice(0, VICTOR9K_SECTOR_LENGTH+5);
ByteReader br(bytes);
/* Check that this is actually a data record. */
if (br.read_8() != 8)
return;
_sector->data = br.read(VICTOR9K_SECTOR_LENGTH);
uint16_t gotChecksum = sumBytes(_sector->data);
uint16_t wantChecksum = br.read_le16();
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
return std::unique_ptr<Decoder>(new Victor9kDecoder(config));
}

210
arch/victor9k/encoder.cc Normal file
View File

@@ -0,0 +1,210 @@
#include "globals.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "victor9k.h"
#include "crc.h"
#include "sector.h"
#include "readerwriter.h"
#include "image.h"
#include "fmt/format.h"
#include "arch/victor9k/victor9k.pb.h"
#include "lib/encoders/encoders.pb.h"
#include "lib/layout.h"
#include <ctype.h>
#include "bytes.h"
static bool lastBit;
static void write_zero_bits(
std::vector<bool>& bits, unsigned& cursor, unsigned count)
{
while (count--)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = 0;
}
}
static void write_one_bits(
std::vector<bool>& bits, unsigned& cursor, unsigned count)
{
while (count--)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = 1;
}
}
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
{
for (bool bit : src)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = bit;
}
}
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
{
cursor += width;
lastBit = data & 1;
for (int i = 0; i < width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
{
ByteReader br(bytes);
BitReader bitr(br);
while (!bitr.eof())
{
if (cursor < bits.size())
bits[cursor++] = bitr.get();
}
}
static int encode_data_gcr(uint8_t data)
{
switch (data & 0x0f)
{
#define GCR_ENTRY(gcr, data) \
case data: \
return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
static void write_byte(std::vector<bool>& bits, unsigned& cursor, uint8_t b)
{
write_bits(bits, cursor, encode_data_gcr(b >> 4), 5);
write_bits(bits, cursor, encode_data_gcr(b), 5);
}
static void write_bytes(
std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
{
for (uint8_t b : bytes)
write_byte(bits, cursor, b);
}
static void write_gap(std::vector<bool>& bits, unsigned& cursor, int length)
{
for (int i = 0; i < length / 10; i++)
write_byte(bits, cursor, '0');
}
static void write_sector(std::vector<bool>& bits,
unsigned& cursor,
const Victor9kEncoderProto::TrackdataProto& trackdata,
const Sector& sector)
{
write_one_bits(bits, cursor, trackdata.pre_header_sync_bits());
write_bits(bits, cursor, VICTOR9K_SECTOR_RECORD, 10);
uint8_t encodedTrack = sector.logicalTrack | (sector.logicalSide << 7);
uint8_t encodedSector = sector.logicalSector;
write_bytes(bits,
cursor,
Bytes{
encodedTrack,
encodedSector,
(uint8_t)(encodedTrack + encodedSector),
});
write_gap(bits, cursor, trackdata.post_header_gap_bits());
write_one_bits(bits, cursor, trackdata.pre_data_sync_bits());
write_bits(bits, cursor, VICTOR9K_DATA_RECORD, 10);
write_bytes(bits, cursor, sector.data);
Bytes checksum(2);
checksum.writer().write_le16(sumBytes(sector.data));
write_bytes(bits, cursor, checksum);
write_gap(bits, cursor, trackdata.post_data_gap_bits());
}
class Victor9kEncoder : public Encoder
{
public:
Victor9kEncoder(const EncoderProto& config):
Encoder(config),
_config(config.victor9k())
{
}
private:
void getTrackFormat(Victor9kEncoderProto::TrackdataProto& trackdata,
unsigned track,
unsigned head)
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_min_track() && (track < f.min_track()))
continue;
if (f.has_max_track() && (track > f.max_track()))
continue;
if (f.has_head() && (head != f.head()))
continue;
trackdata.MergeFrom(f);
}
}
public:
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
Victor9kEncoderProto::TrackdataProto trackdata;
getTrackFormat(
trackdata, trackInfo->logicalTrack, trackInfo->logicalSide);
unsigned bitsPerRevolution = (trackdata.rotational_period_ms() * 1e3) /
trackdata.clock_period_us();
std::vector<bool> bits(bitsPerRevolution);
nanoseconds_t clockPeriod =
calculatePhysicalClockPeriod(trackdata.clock_period_us() * 1e3,
trackdata.rotational_period_ms() * 1e6);
unsigned cursor = 0;
fillBitmapTo(bits,
cursor,
trackdata.post_index_gap_us() * 1e3 / clockPeriod,
{true, false});
lastBit = false;
for (const auto& sector : sectors)
write_sector(bits, cursor, trackdata, *sector);
if (cursor >= bits.size())
error("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), {true, false});
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockPeriod);
return fluxmap;
}
private:
const Victor9kEncoderProto& _config;
};
std::unique_ptr<Encoder> createVictor9kEncoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new Victor9kEncoder(config));
}
// vim: sw=4 ts=4 et

View File

@@ -1,22 +1,26 @@
#ifndef VICTOR9K_H
#define VICTOR9K_H
#define VICTOR9K_SECTOR_RECORD 0xfffffeab
#define VICTOR9K_DATA_RECORD 0xfffffea4
class Encoder;
class Decoder;
class EncoderProto;
class DecoderProto;
/* ... 1101 0101 0111
* ^^ ^^^^ ^^^^ ten bit IO byte */
#define VICTOR9K_SECTOR_RECORD 0xfffffd57
#define VICTOR9K_HEADER_ID 0x7
/* ... 1101 0100 1001
* ^^ ^^^^ ^^^^ ten bit IO byte */
#define VICTOR9K_DATA_RECORD 0xfffffd49
#define VICTOR9K_DATA_ID 0x8
#define VICTOR9K_SECTOR_LENGTH 512
class Sector;
class Fluxmap;
class Victor9kDecoder : public AbstractDecoder
{
public:
virtual ~Victor9kDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
extern std::unique_ptr<Decoder> createVictor9kDecoder(
const DecoderProto& config);
extern std::unique_ptr<Encoder> createVictor9kEncoder(
const EncoderProto& config);
#endif

View File

@@ -0,0 +1,36 @@
syntax = "proto2";
import "lib/common.proto";
message Victor9kDecoderProto {}
// NEXT: 12
message Victor9kEncoderProto
{
message TrackdataProto
{
optional int32 min_track = 1
[ (help) = "minimum track this format applies to" ];
optional int32 max_track = 2
[ (help) = "maximum track this format applies to" ];
optional int32 head = 3
[ (help) = "which head this format applies to" ];
optional double rotational_period_ms = 4
[ (help) = "original rotational period of this track" ];
optional double clock_period_us = 5
[ (help) = "original data rate of this track" ];
optional double post_index_gap_us = 6
[ (help) = "size of post-index gap" ];
optional int32 pre_header_sync_bits = 10
[ (help) = "number of sync bits before the sector header" ];
optional int32 pre_data_sync_bits = 8
[ (help) = "number of sync bits before the sector data" ];
optional int32 post_data_gap_bits = 9
[ (help) = "size of gap between data and the next header" ];
optional int32 post_header_gap_bits = 11
[ (help) = "size of gap between header and the data" ];
}
repeated TrackdataProto trackdata = 1;
}

View File

@@ -2,7 +2,6 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "zilogmcz.h"
@@ -14,35 +13,43 @@
static const FluxPattern SECTOR_START_PATTERN(16, 0xaaab);
AbstractDecoder::RecordType ZilogMczDecoder::advanceToNextRecord()
class ZilogMczDecoder : public Decoder
{
const FluxMatcher* matcher = nullptr;
_fmr->seekToIndexMark();
_sector->clock = _fmr->seekToPattern(SECTOR_START_PATTERN, matcher);
if (matcher == &SECTOR_START_PATTERN)
return SECTOR_RECORD;
return UNKNOWN_RECORD;
}
public:
ZilogMczDecoder(const DecoderProto& config): Decoder(config) {}
void ZilogMczDecoder::decodeSectorRecord()
nanoseconds_t advanceToNextRecord() override
{
seekToIndexMark();
return seekToPattern(SECTOR_START_PATTERN);
}
void decodeSectorRecord() override
{
readRawBits(14);
auto rawbits = readRawBits(140 * 16);
auto bytes = decodeFmMfm(rawbits).slice(0, 140);
ByteReader br(bytes);
_sector->logicalSector = br.read_8() & 0x1f;
_sector->logicalSide = 0;
_sector->logicalTrack = br.read_8() & 0x7f;
if (_sector->logicalSector > 31)
return;
if (_sector->logicalTrack > 80)
return;
_sector->data = br.read(132);
uint16_t wantChecksum = br.read_be16();
uint16_t gotChecksum = crc16(MODBUS_POLY, 0x0000, bytes.slice(0, 134));
_sector->status =
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createZilogMczDecoder(const DecoderProto& config)
{
readRawBits(14);
auto rawbits = readRawBits(140*16);
auto bytes = decodeFmMfm(rawbits).slice(0, 140);
ByteReader br(bytes);
_sector->logicalSector = br.read_8() & 0x1f;
_sector->logicalSide = 0;
_sector->logicalTrack = br.read_8() & 0x7f;
if (_sector->logicalSector > 31)
return;
if (_sector->logicalTrack > 80)
return;
_sector->data = br.read(132);
uint16_t wantChecksum = br.read_be16();
uint16_t gotChecksum = crc16(MODBUS_POLY, 0x0000, bytes.slice(0, 134));
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
return std::unique_ptr<Decoder>(new ZilogMczDecoder(config));
}

View File

@@ -1,18 +1,7 @@
#ifndef ZILOGMCZ_H
#define ZILOGMCZ_H
class Sector;
class Fluxmap;
class ZilogMczDecoder : public AbstractDecoder
{
public:
virtual ~ZilogMczDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
extern std::unique_ptr<Decoder> createZilogMczDecoder(
const DecoderProto& config);
#endif

View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message ZilogMczDecoderProto {}

194
build.lua Normal file
View File

@@ -0,0 +1,194 @@
vars.cflags = { "$(CFLAGS)" }
vars.cxxflags = { "$(CXXFLAGS)" }
vars.ldflags = { "-pthread" }
include "build/protobuf.lua"
include "build/dependency.lua"
include "build/tests.lua"
dependency {
name = "fmt_dep",
pkg_config = "fmt",
}
dependency {
name = "stb_dep",
pkg_config = "stb",
fallback = "dep/stb+stb"
}
dependency {
name = "protobuf_dep",
pkg_config = "protobuf"
}
dependency {
name = "zlib_dep",
pkg_config = "zlib"
}
proto_cc_library {
name = "config_lib",
srcs = {
"./lib/common.proto",
"./lib/config.proto",
"./lib/decoders/decoders.proto",
"./lib/drive.proto",
"./lib/encoders/encoders.proto",
"./lib/fl2.proto",
"./lib/fluxsink/fluxsink.proto",
"./lib/fluxsource/fluxsource.proto",
"./lib/imagereader/imagereader.proto",
"./lib/imagewriter/imagewriter.proto",
"./lib/mapper.proto",
"./lib/usb/usb.proto",
"./arch/aeslanier/aeslanier.proto",
"./arch/agat/agat.proto",
"./arch/amiga/amiga.proto",
"./arch/apple2/apple2.proto",
"./arch/brother/brother.proto",
"./arch/c64/c64.proto",
"./arch/f85/f85.proto",
"./arch/fb100/fb100.proto",
"./arch/ibm/ibm.proto",
"./arch/macintosh/macintosh.proto",
"./arch/micropolis/micropolis.proto",
"./arch/mx/mx.proto",
"./arch/northstar/northstar.proto",
"./arch/rolandd20/rolandd20.proto",
"./arch/tids990/tids990.proto",
"./arch/victor9k/victor9k.proto",
"./arch/zilogmcz/zilogmcz.proto",
}
}
clibrary {
name = "protocol_lib",
hdrs = { "./protocol.h" }
}
clibrary {
name = "libfluxengine",
srcs = {
"./arch/aeslanier/decoder.cc",
"./arch/agat/agat.cc",
"./arch/agat/decoder.cc",
"./arch/amiga/amiga.cc",
"./arch/amiga/decoder.cc",
"./arch/amiga/encoder.cc",
"./arch/apple2/decoder.cc",
"./arch/apple2/encoder.cc",
"./arch/brother/decoder.cc",
"./arch/brother/encoder.cc",
"./arch/c64/c64.cc",
"./arch/c64/decoder.cc",
"./arch/c64/encoder.cc",
"./arch/f85/decoder.cc",
"./arch/fb100/decoder.cc",
"./arch/ibm/decoder.cc",
"./arch/ibm/encoder.cc",
"./arch/macintosh/decoder.cc",
"./arch/macintosh/encoder.cc",
"./arch/micropolis/decoder.cc",
"./arch/micropolis/encoder.cc",
"./arch/mx/decoder.cc",
"./arch/northstar/decoder.cc",
"./arch/northstar/encoder.cc",
"./arch/rolandd20/rolandd20.cc",
"./arch/tids990/decoder.cc",
"./arch/tids990/encoder.cc",
"./arch/victor9k/decoder.cc",
"./arch/victor9k/encoder.cc",
"./arch/zilogmcz/decoder.cc",
"./lib/bitmap.cc",
"./lib/bytes.cc",
"./lib/crc.cc",
"./lib/csvreader.cc",
"./lib/decoders/decoders.cc",
"./lib/decoders/fluxdecoder.cc",
"./lib/decoders/fluxmapreader.cc",
"./lib/decoders/fmmfm.cc",
"./lib/encoders/encoders.cc",
"./lib/flags.cc",
"./lib/fluxmap.cc",
"./lib/fluxsink/aufluxsink.cc",
"./lib/fluxsink/fl2fluxsink.cc",
"./lib/fluxsink/fluxsink.cc",
"./lib/fluxsink/hardwarefluxsink.cc",
"./lib/fluxsink/scpfluxsink.cc",
"./lib/fluxsink/vcdfluxsink.cc",
"./lib/fluxsource/cwffluxsource.cc",
"./lib/fluxsource/erasefluxsource.cc",
"./lib/fluxsource/fl2fluxsource.cc",
"./lib/fluxsource/fluxsource.cc",
"./lib/fluxsource/hardwarefluxsource.cc",
"./lib/fluxsource/kryoflux.cc",
"./lib/fluxsource/kryofluxfluxsource.cc",
"./lib/fluxsource/scpfluxsource.cc",
"./lib/fluxsource/testpatternfluxsource.cc",
"./lib/globals.cc",
"./lib/hexdump.cc",
"./lib/image.cc",
"./lib/imagereader/d64imagereader.cc",
"./lib/imagereader/d88imagereader.cc",
"./lib/imagereader/dimimagereader.cc",
"./lib/imagereader/diskcopyimagereader.cc",
"./lib/imagereader/fdiimagereader.cc",
"./lib/imagereader/imagereader.cc",
"./lib/imagereader/imdimagereader.cc",
"./lib/imagereader/imgimagereader.cc",
"./lib/imagereader/jv3imagereader.cc",
"./lib/imagereader/nfdimagereader.cc",
"./lib/imagereader/nsiimagereader.cc",
"./lib/imagereader/td0imagereader.cc",
"./lib/imagewriter/d64imagewriter.cc",
"./lib/imagewriter/d88imagewriter.cc",
"./lib/imagewriter/diskcopyimagewriter.cc",
"./lib/imagewriter/imagewriter.cc",
"./lib/imagewriter/imgimagewriter.cc",
"./lib/imagewriter/ldbsimagewriter.cc",
"./lib/imagewriter/nsiimagewriter.cc",
"./lib/imagewriter/rawimagewriter.cc",
"./lib/imginputoutpututils.cc",
"./lib/ldbs.cc",
"./lib/logger.cc",
"./lib/mapper.cc",
"./lib/proto.cc",
"./lib/readerwriter.cc",
"./lib/sector.cc",
"./lib/usb/fluxengineusb.cc",
"./lib/usb/greaseweazle.cc",
"./lib/usb/greaseweazleusb.cc",
"./lib/usb/serial.cc",
"./lib/usb/usb.cc",
"./lib/usb/usbfinder.cc",
"./lib/utils.cc",
"protocol.h",
},
deps = {
"+config_lib",
"+protocol_lib",
"+fmt_dep",
"+protobuf_dep",
"+zlib_dep",
"dep/libusbp+libusbp",
},
dep_cflags = { "-Ilib", "-Iarch", "-I." },
vars = {
["+cflags"] = { "-Ilib", "-Iarch", "-I." }
}
}
installable {
name = "all",
map = {
["fluxengine"] = "src+fluxengine",
["fluxengine-gui"] = "src/gui+fluxengine",
["brother120tool"] = "tools+brother120tool",
["brother240tool"] = "tools+brother240tool",
["upgrade-flux-file"] = "tools+upgrade-flux-file",
}
}
include "tests/build.lua"

251
build/build.lua Normal file
View File

@@ -0,0 +1,251 @@
local OBJDIR = "$(OBJDIR)"
local function objdir(e)
return concatpath(OBJDIR, e.cwd, e.name)
end
definerule("normalrule",
{
ins = { type="targets" },
deps = { type="targets", default={} },
outs = { type="targets", default={} },
outleaves = { type="strings" },
label = { type="string", optional=true },
objdir = { type="string", optional=true },
commands = { type="strings" },
},
function (e)
local dir = e.objdir or objdir(e)
local realouts = {}
for _, v in pairs(e.outleaves) do
realouts[#realouts+1] = concatpath(dir, v)
end
local vars = inherit(e.vars, {
dir = dir
})
local result = simplerule {
name = e.name,
ins = e.ins,
deps = e.deps,
outs = concat(realouts, filenamesof(e.outs)),
label = e.label,
commands = e.commands,
vars = vars,
}
result.dir = dir
return result
end
)
local function is_clike(f)
return f:find("%.c$") or f:find("%.cc$") or f:find("%.cpp$")
end
definerule("cfile",
{
srcs = { type="targets" },
deps = { type="targets", default={} }
},
function (e)
local cflags = e.vars.cflags
local cxxflags = e.vars.cxxflags
for _, target in ipairs(targetsof(e.deps)) do
if target.is.clibrary then
cflags = concat(cflags, target.dep_cflags)
cxxflags = concat(cxxflags, target.dep_cxxflags)
end
end
local src = filter(filenamesof(e.srcs), is_clike)
local cmd
local cxx = false
if src[1]:find("%.c$") then
cmd = "$(CC) -c -o %{outs[1]} %{ins[1]} %{hdrpaths} %{cflags}"
else
cmd = "$(CXX) -c -o %{outs[1]} %{ins[1]} %{hdrpaths} %{cflags} %{cxxflags}"
cxx = true
end
local outleaf = basename(e.name)..".o"
local rule = normalrule {
name = e.name,
cwd = e.cwd,
ins = e.srcs,
deps = e.deps,
outleaves = {outleaf},
label = e.label,
commands = cmd,
vars = {
hdrpaths = {},
cflags = cflags,
cxxflags = cxxflags,
}
}
rule.is.cxxfile = cxx
return rule
end
)
local function do_cfiles(e)
local outs = {}
local srcs = filenamesof(e.srcs)
for _, f in ipairs(sorted(filter(srcs, is_clike))) do
local ofile
if f:find(OBJDIR, 1, true) == 1 then
ofile = e.name.."/"..f:sub(#OBJDIR+1)..".o"
else
ofile = e.name.."/"..f..".o"
end
outs[#outs+1] = cfile {
name = ofile,
srcs = { f },
deps = e.deps
}
end
return outs
end
definerule("clibrary",
{
srcs = { type="targets", default={} },
deps = { type="targets", default={} },
hdrs = { type="targets", default={} },
dep_cflags = { type="strings", default={} },
dep_cxxflags = { type="strings", default={} },
dep_ldflags = { type="strings", default={} },
dep_libs = { type="strings", default={} },
},
function (e)
local ins = do_cfiles(e)
local cxx = false
for _, f in ipairs(ins) do
if f.is.cxxfile then
cxx = true
break
end
end
local mkdirs = {}
local copies = {}
local outs = {}
local function copy_file(src, dest)
mkdirs[#mkdirs+1] = "mkdir -p %{dir}/"..dirname(dest)
copies[#copies+1] = "cp "..src.." %{dir}/"..dest
outs[#outs+1] = objdir(e).."/"..dest
end
local deps = {}
for k, v in pairs(e.hdrs) do
deps[#deps+1] = v
if type(k) == "number" then
v = filenamesof(v)
for _, v in ipairs(v) do
if not startswith(e.cwd, v) then
error(string.format("filename '%s' is not local to '%s' --- "..
"you'll have to specify the output filename manually", v, e.cwd))
end
copy_file(v, v:gsub("^"..e.cwd, ""))
end
else
v = filenamesof(v)
if #v ~= 1 then
error("each mapped hdrs item can only cope with a single file")
end
copy_file(v[1], k)
end
end
ins = sorted(filenamesof(ins))
local has_ar = (#ins ~= 0)
local lib = normalrule {
name = e.name,
cwd = e.cwd,
ins = sorted(filenamesof(ins)),
deps = deps,
outs = outs,
outleaves = { e.name..".a" },
label = e.label,
commands = {
sorted(mkdirs),
sorted(copies),
has_ar and "rm -f %{outs[1]} && $(AR) cqs %{outs[1]} %{ins}" or {},
}
}
lib.dep_cflags = concat(e.dep_cflags, "-I"..lib.dir)
lib.dep_cxxflags = e.dep_cxxflags
lib.dep_ldflags = e.dep_ldflags
lib.dep_libs = concat(e.dep_libs, has_ar and matching(filenamesof(lib), "%.a$") or {})
lib.dep_cxx = cxx
for _, d in pairs(targetsof(e.deps)) do
lib.dep_cflags = concat(lib.dep_cflags, d.dep_cflags)
lib.dep_cxxflags = concat(lib.dep_cxxflags, d.dep_cxxflags)
lib.dep_ldflags = concat(lib.dep_ldflags, d.dep_ldflags)
lib.dep_libs = concat(lib.dep_libs, d.dep_libs)
lib.dep_cxx = lib.dep_cxx or d.dep_cxx
end
return lib
end
)
definerule("cprogram",
{
srcs = { type="targets", default={} },
deps = { type="targets", default={} },
},
function (e)
local deps = e.deps
local ins = {}
local cxx = false
if (#e.srcs > 0) then
local objs = do_cfiles(e)
for _, obj in pairs(objs) do
if obj.is.cxxfile then
cxx = true
end
ins[#ins+1] = obj
end
end
local libs = {}
local cflags = {}
local cxxflags = {}
local ldflags = {}
for _, lib in pairs(e.deps) do
cflags = concat(cflags, lib.dep_cflags)
cxxflags = concat(cxxflags, lib.dep_cxxflags)
ldflags = concat(ldflags, lib.dep_ldflags)
libs = concat(libs, lib.dep_libs)
cxx = cxx or lib.dep_cxx
end
local command
if cxx then
command = "$(CXX) $(LDFLAGS) %{ldflags} -o %{outs[1]} %{ins} %{libs} %{libs}"
else
command = "$(CC) $(LDFLAGS) %{ldflags} -o %{outs[1]} %{ins} %{libs} %{libs}"
end
return normalrule {
name = e.name,
cwd = e.cwd,
deps = deps,
ins = ins,
outleaves = { e.name },
commands = { command },
vars = {
cflags = cflags,
cxxflags = cxxflags,
ldflags = ldflags,
libs = libs,
}
}
end
)

18
build/tests.lua Normal file
View File

@@ -0,0 +1,18 @@
definerule("test",
{
srcs = { type="targets", default={} },
},
function (e)
if vars.TESTS == "yes" then
normalrule {
name = e.name,
ins = e.srcs,
outleaves = { "log.txt" },
commands = {
"%{ins} > %{outs}",
}
}
end
end
)

45
dep/adflib/.gitignore vendored Normal file
View File

@@ -0,0 +1,45 @@
# Object files
*.o
*.lo
# Libraries
.libs
*.lib
*.a
*.la
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
# generated by autogen.sh
INSTALL
Makefile.in
aclocal.m4
autom4te.cache
compile
config.guess
config.h.in
config.sub
configure
depcomp
install-sh
ltmain.sh
missing
# generated by configure
.deps
Makefile
adflib.pc
config.h
config.log
config.status
libtool
stamp-h1

13
dep/adflib/AUTHORS Normal file
View File

@@ -0,0 +1,13 @@
The main developper is
Laurent Clévy (laurent.clevy@club-internet.fr)
Contributors are:
Bjarne Viksoe
(C++ wrapper, lot of bug fixes)
Gary Harris
(bug fixes and W32 support)
Dan Sutherland
(bug fixes and W32 support)
See CHANGES.txt for detailed contributions.

339
dep/adflib/COPYING Normal file
View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

128
dep/adflib/README Normal file
View File

@@ -0,0 +1,128 @@
The ADFlib is a free, portable and open implementation of the Amiga filesystem.
initial release in 1999
It supports :
- floppy dumps
- multiple partitions harddisk dumps
- UAE hardfiles
- WinNT devices with the 'native driver' written by Dan Sutherland
- mount/unmount/create a device (real one or a dump file),
- mount/unmount/create a volume (partition),
- create/open/close/delete/rename/undel a file,
- read/write bytes from/to a file,
- create/delete/rename/move/undel a directory,
- get directory contents, change current directory, get parent directory
- use dir cache to get directory contents.
It is written in portable C, and support the WinNT platform to access
real drives.
See also :
https://packages.debian.org/source/sid/unadf
https://www.cvedetails.com/cve/CVE-2016-1243/
https://www.cvedetails.com/cve/CVE-2016-1244/
---
unADF is a unzip like for .ADF files :
unadf [-lrcsp -v n] dumpname.adf [files-with-path] [-d extractdir]
-l : lists root directory contents
-r : lists directory tree contents
-c : use dircache data (must be used with -l)
-s : display entries logical block pointer (must be used with -l)
-m : display file comments, if exists (must be used with -l)
-v n : mount volume #n instead of default #0 volume
-p : send extracted files to pipe (unadf -p dump.adf Pics/pic1.gif | xv -)
-d dir : extract to 'dir' directory
Credits:
--------
main design and code Laurent Clevy
Bug fixes and C++ wrapper Bjarke Viksoe (adfwrapper.h)
WinNT native driver Dan Sutherland and Gary Harris
New versions and contact e-mail can be found at :
http://lclevy.free.fr/adflib
COMPILATION
-----------
The following commands should automatically detect the system
configuration and build the library and examples/unadf,
the ADF extractor binary.
./autogen.sh
./configure
make
FEATURES NEEDING FURTHER TESTS
------------------------------
* Native driver
The NATIV_DIR variable is used to choose the (only one) target platform
of the native driver. The default is :
NATIV_DIR = ./Generic
This one do not give access to any real device. The other one available is
Win32, to access real devices under WinNT.
* Win32DLL
The 'prefix.h' is used to create the Win32 DLL version of the library.
If the WIN32DLL variable is defined in the library code, public functions
are preceded by the '__declspec(dllexport)' directive. If this same
variable is defined, the '__declspec(dllimport)' is put before the functions
prototypes in the 'adflib.h' library include file.
FILES
-----
AUTHORS Contributors
README The file you are reading
TODO Future improvements and bugfixes
CHANGES Detailed changes
src/ main library files
src/win32/ WinNT native driver
src/generic/ native files templates
boot/ Bootblocks that might by used to put on floppy disks
doc/ The library developpers documentation
doc/FAQ/ The Amiga Filesystem explained
examples/ unadf.c
Possible bugs
-------------
- in dircache updates
- when a volume is becoming full
- lost memory releases
Please report any bugs or mistakes in the documentation !
Have fun anyway !

2
dep/adflib/UPSTREAM.md Normal file
View File

@@ -0,0 +1,2 @@
This is head of git downloaded from https://github.com/lclevy/ADFlib on
2022-08-28.

37
dep/adflib/adf_nativ.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef ADF_NATIV_H
#define ADF_NATIV_H
#ifdef __cplusplus
extern "C" {
#endif
#include "adf_str.h"
#define NATIVE_FILE 8001
struct nativeDevice
{
FILE* fd;
};
struct nativeFunctions
{
/* called by adfMount() */
RETCODE (*adfInitDevice)(struct Device*, char*, BOOL);
/* called by adfReadBlock() */
RETCODE (*adfNativeReadSector)(struct Device*, int32_t, int, uint8_t*);
/* called by adfWriteBlock() */
RETCODE (*adfNativeWriteSector)(struct Device*, int32_t, int, uint8_t*);
/* called by adfMount() */
BOOL (*adfIsDevNative)(char*);
/* called by adfUnMount() */
RETCODE (*adfReleaseDevice)(struct Device*);
};
extern void adfInitNativeFct();
#ifdef __cplusplus
}
#endif
#endif

22
dep/adflib/build.mk Normal file
View File

@@ -0,0 +1,22 @@
ADFLIB_SRCS = \
dep/adflib/src/adf_bitm.c \
dep/adflib/src/adf_cache.c \
dep/adflib/src/adf_dir.c \
dep/adflib/src/adf_disk.c \
dep/adflib/src/adf_dump.c \
dep/adflib/src/adf_env.c \
dep/adflib/src/adf_file.c \
dep/adflib/src/adf_hd.c \
dep/adflib/src/adf_link.c \
dep/adflib/src/adf_raw.c \
dep/adflib/src/adf_salv.c \
dep/adflib/src/adf_util.c \
ADFLIB_OBJS = $(patsubst %.c, $(OBJDIR)/%.o, $(ADFLIB_SRCS))
$(ADFLIB_OBJS): CFLAGS += -Idep/adflib/src -Idep/adflib
ADFLIB_LIB = $(OBJDIR)/libadflib.a
$(ADFLIB_LIB): $(ADFLIB_OBJS)
ADFLIB_CFLAGS = -Idep/adflib/src
ADFLIB_LDFLAGS =
OBJS += $(ADFLIB_OBJS)

2
dep/adflib/config.h Normal file
View File

@@ -0,0 +1,2 @@
/* empty config.h to keep the source happy */

View File

@@ -0,0 +1,44 @@
NATIVE_DIR = generic
lib_LTLIBRARIES = libadf.la
libadf_la_SOURCES = adf_hd.c \
adf_disk.c \
adf_raw.c \
adf_bitm.c \
adf_dump.c \
adf_util.c \
adf_env.c \
$(NATIVE_DIR)/adf_nativ.c \
adf_dir.c \
adf_file.c \
adf_cache.c \
adf_link.c \
adf_salv.c
include_HEADERS = adf_defs.h \
adf_blk.h \
adf_err.h \
adf_str.h \
adflib.h \
adf_bitm.h \
adf_cache.h \
adf_dir.h \
adf_disk.h \
adf_dump.h \
adf_env.h \
adf_file.h \
adf_hd.h \
adf_link.h \
adf_raw.h \
adf_salv.h \
adf_util.h \
defendian.h \
hd_blk.h \
prefix.h \
$(NATIVE_DIR)/adf_nativ.h
libadf_la_LDFLAGS = -version-info 0:12:0
AM_CPPFLAGS = -D_XOPEN_SOURCE -D_SVID_SOURCE -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_GNU_SOURCE -I$(srcdir) -I$(srcdir)/$(NATIVE_DIR)
AM_CFLAGS = -std=c99 -pedantic -Wall

561
dep/adflib/src/adf_bitm.c Normal file
View File

@@ -0,0 +1,561 @@
/*
* ADF Library. (C) 1997-2002 Laurent Clevy
*
* adf_bitm.c
*
* $Id$
*
* bitmap code
*
* This file is part of ADFLib.
*
* ADFLib is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* ADFLib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <stdlib.h>
#include <string.h>
#include"adf_raw.h"
#include"adf_bitm.h"
#include"adf_err.h"
#include"adf_disk.h"
#include"adf_util.h"
#include"defendian.h"
extern uint32_t bitMask[32];
extern struct Env adfEnv;
/*
* adfUpdateBitmap
*
*/
RETCODE adfUpdateBitmap(struct Volume *vol)
{
int i;
struct bRootBlock root;
/*printf("adfUpdateBitmap\n");*/
if (adfReadRootBlock(vol, vol->rootBlock,&root)!=RC_OK)
return RC_ERROR;
root.bmFlag = BM_INVALID;
if (adfWriteRootBlock(vol,vol->rootBlock,&root)!=RC_OK)
return RC_ERROR;
for(i=0; i<vol->bitmapSize; i++)
if (vol->bitmapBlocksChg[i]) {
if (adfWriteBitmapBlock(vol, vol->bitmapBlocks[i], vol->bitmapTable[i])!=RC_OK)
return RC_ERROR;
vol->bitmapBlocksChg[i] = FALSE;
}
root.bmFlag = BM_VALID;
adfTime2AmigaTime(adfGiveCurrentTime(),&(root.days),&(root.mins),&(root.ticks));
if (adfWriteRootBlock(vol,vol->rootBlock,&root)!=RC_OK)
return RC_ERROR;
return RC_OK;
}
/*
* adfCountFreeBlocks
*
*/
int32_t adfCountFreeBlocks(struct Volume* vol)
{
int32_t freeBlocks;
int j;
freeBlocks = 0L;
for(j=vol->firstBlock+2; j<=(vol->lastBlock - vol->firstBlock); j++)
if ( adfIsBlockFree(vol,j) )
freeBlocks++;
return freeBlocks;
}
/*
* adfReadBitmap
*
*/
RETCODE adfReadBitmap(struct Volume* vol, int32_t nBlock, struct bRootBlock* root)
{
int32_t mapSize, nSect;
int32_t j, i;
struct bBitmapExtBlock bmExt;
mapSize = nBlock / (127*32);
if ( (nBlock%(127*32))!=0 )
mapSize++;
vol->bitmapSize = mapSize;
vol->bitmapTable = (struct bBitmapBlock**) malloc(sizeof(struct bBitmapBlock*)*mapSize);
if (!vol->bitmapTable) {
(*adfEnv.eFct)("adfReadBitmap : malloc, vol->bitmapTable");
return RC_MALLOC;
}
vol->bitmapBlocks = (SECTNUM*) malloc(sizeof(SECTNUM)*mapSize);
if (!vol->bitmapBlocks) {
free(vol->bitmapTable);
(*adfEnv.eFct)("adfReadBitmap : malloc, vol->bitmapBlocks");
return RC_MALLOC;
}
vol->bitmapBlocksChg = (BOOL*) malloc(sizeof(BOOL)*mapSize);
if (!vol->bitmapBlocksChg) {
free(vol->bitmapTable); free(vol->bitmapBlocks);
(*adfEnv.eFct)("adfReadBitmap : malloc, vol->bitmapBlocks");
return RC_MALLOC;
}
for(i=0; i<mapSize; i++) {
vol->bitmapBlocksChg[i] = FALSE;
vol->bitmapTable[i] = (struct bBitmapBlock*)malloc(sizeof(struct bBitmapBlock));
if (!vol->bitmapTable[i]) {
free(vol->bitmapBlocksChg); free(vol->bitmapBlocks);
for(j=0; j<i; j++)
free(vol->bitmapTable[j]);
free(vol->bitmapTable);
(*adfEnv.eFct)("adfReadBitmap : malloc, vol->bitmapBlocks");
return RC_MALLOC;
}
}
j=0; i=0;
/* bitmap pointers in rootblock : 0 <= i <BM_SIZE */
while(i<BM_SIZE && root->bmPages[i]!=0) {
vol->bitmapBlocks[j] = nSect = root->bmPages[i];
if ( !isSectNumValid(vol,nSect) ) {
(*adfEnv.wFct)("adfReadBitmap : sector out of range");
}
if (adfReadBitmapBlock(vol, nSect, vol->bitmapTable[j])!=RC_OK) {
adfFreeBitmap(vol);
return RC_ERROR;
}
j++; i++;
}
nSect = root->bmExt;
while(nSect!=0) {
/* bitmap pointers in bitmapExtBlock, j <= mapSize */
if (adfReadBitmapExtBlock(vol, nSect, &bmExt)!=RC_OK) {
adfFreeBitmap(vol);
return RC_ERROR;
}
i=0;
while(i<127 && j<mapSize) {
nSect = bmExt.bmPages[i];
if ( !isSectNumValid(vol,nSect) )
(*adfEnv.wFct)("adfReadBitmap : sector out of range");
vol->bitmapBlocks[j] = nSect;
if (adfReadBitmapBlock(vol, nSect, vol->bitmapTable[j])!=RC_OK) {
adfFreeBitmap(vol);
return RC_ERROR;
}
i++; j++;
}
nSect = bmExt.nextBlock;
}
return RC_OK;
}
/*
* adfIsBlockFree
*
*/
BOOL adfIsBlockFree(struct Volume* vol, SECTNUM nSect)
{
int sectOfMap = nSect-2;
int block = sectOfMap/(127*32);
int indexInMap = (sectOfMap/32)%127;
/*printf("sect=%d block=%d ind=%d, ",sectOfMap,block,indexInMap);
printf("bit=%d, ",sectOfMap%32);
printf("bitm=%x, ",bitMask[ sectOfMap%32]);
printf("res=%x, ",vol->bitmapTable[ block ]->map[ indexInMap ]
& bitMask[ sectOfMap%32 ]);
*/
return ( (vol->bitmapTable[ block ]->map[ indexInMap ]
& bitMask[ sectOfMap%32 ])!=0 );
}
/*
* adfSetBlockFree OK
*
*/
void adfSetBlockFree(struct Volume* vol, SECTNUM nSect)
{
uint32_t oldValue;
int sectOfMap = nSect-2;
int block = sectOfMap/(127*32);
int indexInMap = (sectOfMap/32)%127;
/*printf("sect=%d block=%d ind=%d, ",sectOfMap,block,indexInMap);
printf("bit=%d, ",sectOfMap%32);
*printf("bitm=%x, ",bitMask[ sectOfMap%32]);*/
oldValue = vol->bitmapTable[ block ]->map[ indexInMap ];
/*printf("old=%x, ",oldValue);*/
vol->bitmapTable[ block ]->map[ indexInMap ]
= oldValue | bitMask[ sectOfMap%32 ];
/*printf("new=%x, ",vol->bitmapTable[ block ]->map[ indexInMap ]);*/
vol->bitmapBlocksChg[ block ] = TRUE;
}
/*
* adfSetBlockUsed
*
*/
void adfSetBlockUsed(struct Volume* vol, SECTNUM nSect)
{
uint32_t oldValue;
int sectOfMap = nSect-2;
int block = sectOfMap/(127*32);
int indexInMap = (sectOfMap/32)%127;
oldValue = vol->bitmapTable[ block ]->map[ indexInMap ];
vol->bitmapTable[ block ]->map[ indexInMap ]
= oldValue & (~bitMask[ sectOfMap%32 ]);
vol->bitmapBlocksChg[ block ] = TRUE;
}
/*
* adfGet1FreeBlock
*
*/
SECTNUM adfGet1FreeBlock(struct Volume *vol) {
SECTNUM block[1];
if (!adfGetFreeBlocks(vol,1,block))
return(-1);
else
return(block[0]);
}
/*
* adfGetFreeBlocks
*
*/
BOOL adfGetFreeBlocks(struct Volume* vol, int nbSect, SECTNUM* sectList)
{
int i, j;
BOOL diskFull;
int32_t block = vol->rootBlock;
i = 0;
diskFull = FALSE;
/*printf("lastblock=%ld\n",vol->lastBlock);*/
while( i<nbSect && !diskFull ) {
if ( adfIsBlockFree(vol, block) ) {
sectList[i] = block;
i++;
}
/* if ( block==vol->lastBlock )
block = vol->firstBlock+2;*/
if ( (block+vol->firstBlock)==vol->lastBlock )
block = 2;
else if (block==vol->rootBlock-1)
diskFull = TRUE;
else
block++;
}
if (!diskFull)
for(j=0; j<nbSect; j++)
adfSetBlockUsed( vol, sectList[j] );
return (i==nbSect);
}
/*
* adfCreateBitmap
*
* create bitmap structure in vol
*/
RETCODE adfCreateBitmap(struct Volume *vol)
{
int32_t nBlock, mapSize ;
int i, j;
nBlock = vol->lastBlock - vol->firstBlock +1 - 2;
mapSize = nBlock / (127*32);
if ( (nBlock%(127*32))!=0 )
mapSize++;
vol->bitmapSize = mapSize;
vol->bitmapTable = (struct bBitmapBlock**)malloc( sizeof(struct bBitmapBlock*)*mapSize );
if (!vol->bitmapTable) {
(*adfEnv.eFct)("adfCreateBitmap : malloc, vol->bitmapTable");
return RC_MALLOC;
}
vol->bitmapBlocksChg = (BOOL*) malloc(sizeof(BOOL)*mapSize);
if (!vol->bitmapBlocksChg) {
free(vol->bitmapTable);
(*adfEnv.eFct)("adfCreateBitmap : malloc, vol->bitmapBlocksChg");
return RC_MALLOC;
}
vol->bitmapBlocks = (SECTNUM*) malloc(sizeof(SECTNUM)*mapSize);
if (!vol->bitmapBlocks) {
free(vol->bitmapTable); free(vol->bitmapBlocksChg);
(*adfEnv.eFct)("adfCreateBitmap : malloc, vol->bitmapBlocks");
return RC_MALLOC;
}
for(i=0; i<mapSize; i++) {
vol->bitmapTable[i] = (struct bBitmapBlock*)malloc(sizeof(struct bBitmapBlock));
if (!vol->bitmapTable[i]) {
free(vol->bitmapTable); free(vol->bitmapBlocksChg);
for(j=0; j<i; j++)
free(vol->bitmapTable[j]);
free(vol->bitmapTable);
(*adfEnv.eFct)("adfCreateBitmap : malloc");
return RC_MALLOC;
}
}
for(i=vol->firstBlock+2; i<=(vol->lastBlock - vol->firstBlock); i++)
adfSetBlockFree(vol, i);
return RC_OK;
}
/*
* adfWriteNewBitmap
*
* write ext blocks and bitmap
*
* uses vol->bitmapSize,
*/
RETCODE adfWriteNewBitmap(struct Volume *vol)
{
struct bBitmapExtBlock bitme;
SECTNUM *bitExtBlock;
int n, i, k;
int nExtBlock;
int nBlock;
SECTNUM *sectList;
struct bRootBlock root;
sectList=(SECTNUM*)malloc(sizeof(SECTNUM)*vol->bitmapSize);
if (!sectList) {
(*adfEnv.eFct)("adfCreateBitmap : sectList");
return RC_MALLOC;
}
if (!adfGetFreeBlocks(vol, vol->bitmapSize, sectList)) {
free(sectList);
return RC_ERROR;
}
if (adfReadRootBlock(vol, vol->rootBlock, &root)!=RC_OK) {
free(sectList);
return RC_ERROR;
}
nBlock = 0;
n = min( vol->bitmapSize, BM_SIZE );
for(i=0; i<n; i++) {
root.bmPages[i] = vol->bitmapBlocks[i] = sectList[i];
}
nBlock = n;
/* for devices with more than 25*127 blocks == hards disks */
if (vol->bitmapSize>BM_SIZE) {
nExtBlock = (vol->bitmapSize-BM_SIZE)/127;
if ((vol->bitmapSize-BM_SIZE)%127)
nExtBlock++;
bitExtBlock=(SECTNUM*)malloc(sizeof(SECTNUM)*nExtBlock);
if (!bitExtBlock) {
free(sectList);
adfEnv.eFct("adfWriteNewBitmap : malloc failed");
return RC_MALLOC;
}
if (!adfGetFreeBlocks(vol, nExtBlock, bitExtBlock)) {
free(sectList); free(bitExtBlock);
return RC_MALLOC;
}
k = 0;
root.bmExt = bitExtBlock[ k ];
while( nBlock<vol->bitmapSize ) {
i=0;
while( i<127 && nBlock<vol->bitmapSize ) {
bitme.bmPages[i] = vol->bitmapBlocks[nBlock] = sectList[i];
i++;
nBlock++;
}
if ( k+1<nExtBlock )
bitme.nextBlock = bitExtBlock[ k+1 ];
else
bitme.nextBlock = 0;
if (adfWriteBitmapExtBlock(vol, bitExtBlock[ k ], &bitme)!=RC_OK) {
free(sectList); free(bitExtBlock);
return RC_ERROR;
}
k++;
}
free( bitExtBlock );
}
free( sectList);
if (adfWriteRootBlock(vol,vol->rootBlock,&root)!=RC_OK)
return RC_ERROR;
return RC_OK;
}
/*
* adfReadBitmapBlock
*
* ENDIAN DEPENDENT
*/
RETCODE
adfReadBitmapBlock(struct Volume* vol, SECTNUM nSect, struct bBitmapBlock* bitm)
{
uint8_t buf[LOGICAL_BLOCK_SIZE];
/*printf("bitmap %ld\n",nSect);*/
if (adfReadBlock(vol, nSect, buf)!=RC_OK)
return RC_ERROR;
memcpy(bitm, buf, LOGICAL_BLOCK_SIZE);
#ifdef LITT_ENDIAN
/* big to little = 68000 to x86 */
swapEndian((uint8_t*)bitm, SWBL_BITMAP);
#endif
if (bitm->checkSum!=adfNormalSum(buf,0,LOGICAL_BLOCK_SIZE))
(*adfEnv.wFct)("adfReadBitmapBlock : invalid checksum");
return RC_OK;
}
/*
* adfWriteBitmapBlock
*
* OK
*/
RETCODE
adfWriteBitmapBlock(struct Volume* vol, SECTNUM nSect, struct bBitmapBlock* bitm)
{
uint8_t buf[LOGICAL_BLOCK_SIZE];
uint32_t newSum;
memcpy(buf,bitm,LOGICAL_BLOCK_SIZE);
#ifdef LITT_ENDIAN
/* little to big */
swapEndian(buf, SWBL_BITMAP);
#endif
newSum = adfNormalSum(buf, 0, LOGICAL_BLOCK_SIZE);
swLong(buf,newSum);
/* dumpBlock((uint8_t*)buf);*/
if (adfWriteBlock(vol, nSect, (uint8_t*)buf)!=RC_OK)
return RC_ERROR;
return RC_OK;
}
/*
* adfReadBitmapExtBlock
*
* ENDIAN DEPENDENT
*/
RETCODE
adfReadBitmapExtBlock(struct Volume* vol, SECTNUM nSect, struct bBitmapExtBlock* bitme)
{
uint8_t buf[LOGICAL_BLOCK_SIZE];
if (adfReadBlock(vol, nSect, buf)!=RC_OK)
return RC_ERROR;
memcpy(bitme, buf, LOGICAL_BLOCK_SIZE);
#ifdef LITT_ENDIAN
swapEndian((uint8_t*)bitme, SWBL_BITMAP);
#endif
return RC_OK;
}
/*
* adfWriteBitmapExtBlock
*
*/
RETCODE
adfWriteBitmapExtBlock(struct Volume* vol, SECTNUM nSect, struct bBitmapExtBlock* bitme)
{
uint8_t buf[LOGICAL_BLOCK_SIZE];
memcpy(buf,bitme, LOGICAL_BLOCK_SIZE);
#ifdef LITT_ENDIAN
/* little to big */
swapEndian(buf, SWBL_BITMAPE);
#endif
/* dumpBlock((uint8_t*)buf);*/
if (adfWriteBlock(vol, nSect, (uint8_t*)buf)!=RC_OK)
return RC_ERROR;
return RC_OK;
}
/*
* adfFreeBitmap
*
*/
void adfFreeBitmap(struct Volume* vol)
{
int i;
for(i=0; i<vol->bitmapSize; i++)
free(vol->bitmapTable[i]);
vol->bitmapSize = 0;
free(vol->bitmapTable);
vol->bitmapTable = 0;
free(vol->bitmapBlocks);
vol->bitmapBlocks = 0;
free(vol->bitmapBlocksChg);
vol->bitmapBlocksChg = 0;
}
/*#######################################################################################*/

Some files were not shown because too many files have changed in this diff Show More