Compare commits

..

190 Commits

Author SHA1 Message Date
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
275 changed files with 16118 additions and 11890 deletions

View File

@@ -18,17 +18,19 @@ AlwaysBreakBeforeMultilineStrings: 'true'
AlwaysBreakTemplateDeclarations: 'Yes'
BinPackArguments: 'false'
BinPackParameters: 'false'
BreakConstructorInitializers: 'AfterColon'
BreakBeforeBraces: Allman
BreakConstructorInitializers: 'AfterColon'
BreakInheritanceList: AfterColon
BreakStringLiterals: 'true'
IndentCaseLabels: 'true'
IndentWidth: '4'
ColumnLimit: '80'
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
FixNamespaceComments: 'false'
IncludeBlocks: Preserve
IndentCaseLabels: 'true'
IndentWidth: '4'
IndentWrappedFunctionNames: 'false'
KeepEmptyLinesAtTheStartOfBlocks: 'true'
NamespaceIndentation: All
PointerAlignment: Left
ReflowComments: 'true'
SortIncludes: 'false'

View File

@@ -6,26 +6,40 @@ jobs:
build-linux:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
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 libudev-dev libsqlite3-dev protobuf-compiler libwxgtk3.0-gtk3-dev libfmt-dev
- name: make
run: CXXFLAGS="-Wp,-D_GLIBCXX_ASSERTIONS" make
run: CXXFLAGS="-Wp,-D_GLIBCXX_ASSERTIONS" make -j2 -C fluxengine
build-macos:
build-macos-current:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
with:
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 protobuf wxwidgets fmt make coreutils
run: brew install sqlite pkg-config libusb protobuf wxwidgets fmt make coreutils dylibbundler libjpeg
- name: make
run: gmake
run: gmake -j2 -C fluxengine
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: ${{ github.event.repository.name }}.${{ github.sha }}
path: FluxEngine.pkg
path: fluxengine.FluxEngine.pkg
build-windows:
runs-on: windows-latest
@@ -50,22 +64,32 @@ jobs:
mingw-w64-i686-zlib
mingw-w64-i686-nsis
zip
- uses: actions/checkout@v1
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
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: |
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 fluxengine-installer.exe
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-windows.zip
path: fluxengine/fluxengine-windows.zip

View File

@@ -29,11 +29,12 @@ jobs:
mingw-w64-i686-zlib
mingw-w64-i686-nsis
zip
vim
- uses: actions/checkout@v3
- name: build
run: |
make
make -j2
- name: nsis
run: |
@@ -83,7 +84,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: brew
run: brew install sqlite pkg-config libusb protobuf wxwidgets fmt make coreutils
run: brew install sqlite pkg-config libusb protobuf wxwidgets fmt make coreutils dylibbundler libjpeg
- name: make
run: gmake

104
Makefile
View File

@@ -48,7 +48,7 @@ AR ?= $(CCPREFIX)ar
PKG_CONFIG ?= pkg-config
WX_CONFIG ?= wx-config
PROTOC ?= protoc
CFLAGS ?= -g -O0
CFLAGS ?= -g -O3
CXXFLAGS += -std=c++17
LDFLAGS ?=
PLATFORM ?= UNIX
@@ -77,6 +77,9 @@ define nl
endef
empty :=
space := $(empty) $(empty)
use-library = $(eval $(use-library-impl))
define use-library-impl
$1: $(call $3_LIB)
@@ -95,7 +98,7 @@ $(2): private CFLAGS += $(shell $(PKG_CONFIG) --cflags $(3))
endef
.PHONY: all binaries tests clean install install-bin
all: binaries tests
all: binaries tests docs
PROTOS = \
arch/aeslanier/aeslanier.proto \
@@ -111,6 +114,7 @@ PROTOS = \
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 \
@@ -163,48 +167,86 @@ define do-encodedecodetest-impl
tests: $(OBJDIR)/$1$3.flux.encodedecode
$(OBJDIR)/$1$3.flux.encodedecode: scripts/encodedecodetest.sh $(FLUXENGINE_BIN) $2
@mkdir -p $(dir $$@)
@echo ENCODEDECODETEST .flux $1 $3
@echo ENCODEDECODETEST $1 flux $(FLUXENGINE_BIN) $2 $3
@scripts/encodedecodetest.sh $1 flux $(FLUXENGINE_BIN) $2 $3 > $$@
tests: $(OBJDIR)/$1$3.scp.encodedecode
$(OBJDIR)/$1$3.scp.encodedecode: scripts/encodedecodetest.sh $(FLUXENGINE_BIN) $2
@mkdir -p $(dir $$@)
@echo ENCODEDECODETEST .scp $1 $3
@echo ENCODEDECODETEST $1 scp $(FLUXENGINE_BIN) $2 $3
@scripts/encodedecodetest.sh $1 scp $(FLUXENGINE_BIN) $2 $3 > $$@
endef
$(call do-encodedecodetest,agat)
$(call do-encodedecodetest,amiga)
$(call do-encodedecodetest,apple2)
$(call do-encodedecodetest,atarist360)
$(call do-encodedecodetest,atarist370)
$(call do-encodedecodetest,atarist400)
$(call do-encodedecodetest,atarist410)
$(call do-encodedecodetest,atarist720)
$(call do-encodedecodetest,atarist740)
$(call do-encodedecodetest,atarist800)
$(call do-encodedecodetest,atarist820)
$(call do-encodedecodetest,bk800)
$(call do-encodedecodetest,brother120)
$(call do-encodedecodetest,brother240)
$(call do-encodedecodetest,commodore1541,scripts/commodore1541_test.textpb,--35)
$(call do-encodedecodetest,commodore1541,scripts/commodore1541_test.textpb,--40)
$(call do-encodedecodetest,commodore1581)
$(call do-encodedecodetest,cmd_fd2000)
$(call do-encodedecodetest,hp9121)
$(call do-encodedecodetest,ibm1200)
$(call do-encodedecodetest,ibm1232)
$(call do-encodedecodetest,ibm1440)
$(call do-encodedecodetest,ibm180)
$(call do-encodedecodetest,ibm360)
$(call do-encodedecodetest,ibm720)
$(call do-encodedecodetest,mac400,scripts/mac400_test.textpb)
$(call do-encodedecodetest,mac800,scripts/mac800_test.textpb)
$(call do-encodedecodetest,apple2,,--140)
$(call do-encodedecodetest,atarist,,--360)
$(call do-encodedecodetest,atarist,,--370)
$(call do-encodedecodetest,atarist,,--400)
$(call do-encodedecodetest,atarist,,--410)
$(call do-encodedecodetest,atarist,,--720)
$(call do-encodedecodetest,atarist,,--740)
$(call do-encodedecodetest,atarist,,--800)
$(call do-encodedecodetest,atarist,,--820)
$(call do-encodedecodetest,bk)
$(call do-encodedecodetest,brother,,--120)
$(call do-encodedecodetest,brother,,--240)
$(call do-encodedecodetest,commodore,scripts/commodore1541_test.textpb,--171)
$(call do-encodedecodetest,commodore,scripts/commodore1541_test.textpb,--192)
$(call do-encodedecodetest,commodore,,--800)
$(call do-encodedecodetest,commodore,,--1620)
$(call do-encodedecodetest,hplif,,--264)
$(call do-encodedecodetest,hplif,,--616)
$(call do-encodedecodetest,hplif,,--770)
$(call do-encodedecodetest,ibm,,--1200)
$(call do-encodedecodetest,ibm,,--1232)
$(call do-encodedecodetest,ibm,,--1440)
$(call do-encodedecodetest,ibm,,--1680)
$(call do-encodedecodetest,ibm,,--180)
$(call do-encodedecodetest,ibm,,--160)
$(call do-encodedecodetest,ibm,,--320)
$(call do-encodedecodetest,ibm,,--360)
$(call do-encodedecodetest,ibm,,--720)
$(call do-encodedecodetest,mac,scripts/mac400_test.textpb,--400)
$(call do-encodedecodetest,mac,scripts/mac800_test.textpb,--800)
$(call do-encodedecodetest,n88basic)
$(call do-encodedecodetest,rx50)
$(call do-encodedecodetest,tids990)
$(call do-encodedecodetest,victor9k_ss)
$(call do-encodedecodetest,victor9k_ds)
$(call do-encodedecodetest,victor9k,,--612)
$(call do-encodedecodetest,victor9k,,--1224)
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)
$(call do-corpustest,atarist360.flux,atarist360.st,atarist --360)
$(call do-corpustest,atarist720.flux,atarist720.st,atarist --720)
$(call do-corpustest,brother120.flux,brother120.img,brother --120)
$(call do-corpustest,cmd-fd2000.flux,cmd-fd2000.img,commodore --1620)
$(call do-corpustest,ibm1232.flux,ibm1232.img,ibm --1232)
$(call do-corpustest,ibm1440.flux,ibm1440.img,ibm --1440)
$(call do-corpustest,mac800.flux,mac800.dsk,mac --800)
$(call do-corpustest,micropolis315.flux,micropolis315.img,micropolis --315)
$(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)
$(call do-corpustest,victor9k_ds.flux,victor9k_ds.img,victor9k --1224)
endif
$(OBJDIR)/%.a:
@mkdir -p $(dir $@)

100
README.md
View File

@@ -88,62 +88,60 @@ 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) |
| [Atari ST](doc/disk-atarist.md) | 🦄 | 🦄 | technically the same as IBM, almost |
| [Acorn ADFS](doc/disk-acornadfs.md) | 🦄 | 🦖* | single- and double- sided |
| [Acorn DFS](doc/disk-acorndfs.md) | 🦄 | 🦖* | |
| [Ampro Little Board](doc/disk-ampro.md) | 🦖 | 🦖* | |
| [Agat](doc/disk-agat.md) | 🦖 | | Soviet Union Apple-II-like computer |
| [Apple II](doc/disk-apple2.md) | 🦄 | 🦄 | |
| [Amiga](doc/disk-amiga.md) | 🦄 | 🦄 | |
| [Commodore 64 1541/1581](doc/disk-c64.md) | 🦄 | 🦄 | and probably the other formats |
| [Brother 120kB](doc/disk-brother.md) | 🦄 | 🦄 | |
| [Brother 240kB](doc/disk-brother.md) | 🦄 | 🦄 | |
| [Brother FB-100](doc/disk-fb100.md) | 🦖 | | Tandy Model 100, Husky Hunter, knitting machines |
| [Elektronika BK](doc/disk-bd.md) | 🦄 | 🦄 | Soviet Union PDP-11 clone |
| [Macintosh 400kB/800kB](doc/disk-macintosh.md) | 🦄 | 🦄 | |
| [NEC PC-98](doc/disk-ibm.md) | 🦄 | 🦄 | trimode drive not required |
| [Sharp X68000](doc/disk-ibm.md) | 🦄 | 🦄 | |
| [Smaky 6](doc/disk-smaky6.md) | 🦖 | | 5.25" hard sectored |
| [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"/3.5" 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 |
| [VDS Eco1](doc/disk-eco1.md) | 🦖 | | 8" mixed format |
| [Micropolis](doc/disk-micropolis.md) | 🦄 | | Micropolis 100tpi drives |
| [Northstar](doc/disk-northstar.md) | 🦖 | 🦖 | 5.25" hard sectors |
| [TI DS990 FD1000](doc/disk-tids990.md) | 🦄 | 🦄 | 8" |
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 5.25" GCR encoded |
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8" _and_ hard sectors |
{: .datatable }
<!-- FORMATSEND -->
### Notes

View File

@@ -3,7 +3,16 @@
#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);

View File

@@ -1,5 +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."];
}

View File

@@ -33,10 +33,7 @@
*
*/
static const uint64_t SECTOR_ID = 0x8924555549111444;
static const FluxPattern SECTOR_PATTERN(64, SECTOR_ID);
static const uint64_t DATA_ID = 0x8924555514444911;
static const FluxPattern DATA_PATTERN(64, DATA_ID);
static const FluxMatchers ALL_PATTERNS = {

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

@@ -2,7 +2,10 @@ syntax = "proto2";
import "lib/common.proto";
message Apple2DecoderProto {}
message Apple2DecoderProto {
optional uint32 side_one_track_offset = 1
[ default = 0, (help) = "offset to apply to track numbers on side 1" ];
}
message Apple2EncoderProto
{
@@ -13,4 +16,7 @@ message Apple2EncoderProto
/* 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

@@ -5,6 +5,8 @@
#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>
@@ -12,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)
{
@@ -47,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);
}
}
@@ -67,88 +74,102 @@ static uint8_t combine(uint16_t word)
class Apple2Decoder : public Decoder
{
public:
Apple2Decoder(const DecoderProto& config):
Decoder(config)
{}
Apple2Decoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
if (readRaw24() != APPLE2_SECTOR_RECORD)
return;
{
if (readRaw24() != APPLE2_SECTOR_RECORD)
return;
/* Read header. */
/* Read header. */
auto header = toBytes(readRawBits(8*8)).slice(0, 8);
ByteReader br(header);
auto header = toBytes(readRawBits(8 * 8)).slice(0, 8);
ByteReader br(header);
uint8_t volume = combine(br.read_be16());
_sector->logicalTrack = combine(br.read_be16());
_sector->logicalSector = combine(br.read_be16());
uint8_t checksum = combine(br.read_be16());
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 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. */
{
/* Check ID. */
if (readRaw24() != APPLE2_DATA_RECORD)
return;
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.
// 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. */
/* 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;
};
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();
}
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);
}
// 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)
{
return std::unique_ptr<Decoder>(new Apple2Decoder(config));
return std::unique_ptr<Decoder>(new Apple2Decoder(config));
}

View File

@@ -56,8 +56,7 @@ public:
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits,
calculatePhysicalClockPeriod(
_config.clock_period_us() * 1e3,
calculatePhysicalClockPeriod(_config.clock_period_us() * 1e3,
_config.rotational_period_ms() * 1e6));
return fluxmap;
}
@@ -132,13 +131,17 @@ private:
// 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(sector.logicalTrack);
write_gcr44(track);
write_gcr44(sector.logicalSector);
write_gcr44(volume_id ^ sector.logicalTrack ^ sector.logicalSector);
write_gcr44(volume_id ^ track ^ sector.logicalSector);
write_bits(0xDEAAEB, 24);
// Write data syncing leader: FF40 + APPLE2_DATA_RECORD + sector

View File

@@ -2,6 +2,7 @@ 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 \
@@ -23,6 +24,7 @@ LIBARCH_SRCS = \
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 \

View File

@@ -147,6 +147,7 @@ public:
_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)
@@ -206,6 +207,18 @@ public:
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:

View File

@@ -16,14 +16,14 @@ static bool lastBit;
static double clockRateUsForTrack(unsigned track)
{
if (track < 16)
return 2.623;
return 2.63;
if (track < 32)
return 2.861;
return 2.89;
if (track < 48)
return 3.148;
return 3.20;
if (track < 64)
return 3.497;
return 3.934;
return 3.57;
return 3.98;
}
static unsigned sectorsForTrack(unsigned track)

View File

@@ -19,6 +19,6 @@ 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 = 166.0, (help) = "rotational period on the real device" ];
[ default = 200.0, (help) = "rotational period on the real device" ];
}

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

@@ -0,0 +1,56 @@
#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 {}

View File

@@ -55,6 +55,7 @@ proto_cc_library {
"./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",
@@ -93,6 +94,7 @@ clibrary {
"./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",

View File

@@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 2.8.11)
cmake_minimum_required (VERSION 3.10.0)
# Fix behavior of CMAKE_CXX_STANDARD when targeting macOS.
if (POLICY CMP0025)
@@ -18,7 +18,7 @@ endif ()
project (libusbp)
set (LIBUSBP_VERSION_MAJOR 1)
set (LIBUSBP_VERSION_MINOR 2)
set (LIBUSBP_VERSION_MINOR 3)
set (LIBUSBP_VERSION_PATCH 0)
# Make 'Release' be the default build type, since the debug builds
@@ -49,28 +49,8 @@ set(VBOX_LINUX_ON_WINDOWS FALSE CACHE BOOL
set(ENABLE_GCOV FALSE CACHE BOOL
"Compile with special options needed for gcov.")
# Our C code uses features from the C99 standard.
macro(use_c99)
if (CMAKE_VERSION VERSION_LESS "3.1")
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
set (CMAKE_C_FLAGS "--std=gnu99 ${CMAKE_C_FLAGS}")
endif ()
else ()
set (CMAKE_C_STANDARD 99)
endif ()
endmacro(use_c99)
# Our C++ code uses features from the C++11 standard.
macro(use_cxx11)
if (CMAKE_VERSION VERSION_LESS "3.1")
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
# Use --std=gnu++0x instead of --std=gnu++11 in order to support GCC 4.6.
set (CMAKE_CXX_FLAGS "--std=gnu++0x ${CMAKE_C_FLAGS}")
endif ()
else ()
set (CMAKE_CXX_STANDARD 11)
endif ()
endmacro(use_cxx11)
set (CMAKE_C_STANDARD 99)
set (CMAKE_CXX_STANDARD 11)
set (LIBUSBP_VERSION ${LIBUSBP_VERSION_MAJOR}.${LIBUSBP_VERSION_MINOR}.${LIBUSBP_VERSION_PATCH})

View File

@@ -1,7 +1,5 @@
# libusbp: Pololu USB Library
Version: 1.2.0<br/>
Release date: 2020-11-16<br/>
[www.pololu.com](https://www.pololu.com/)
The **Pololu USB Library** (also known as **libusbp**) is a cross-platform C library for accessing USB devices.
@@ -17,7 +15,7 @@ The **Pololu USB Library** (also known as **libusbp**) is a cross-platform C lib
- Provides detailed error information to the caller.
- Each error includes one or more English sentences describing the error, including error codes from underlying APIs.
- Some errors have libusbp-defined error codes that can be used to programmatically decide how to handle the error.
- Provides an object-oriented C++ wrapper (using features of C++11).
- Provides an object-oriented C++ wrapper.
- Provides access to underlying identifiers, handles, and file descriptors.
@@ -139,9 +137,9 @@ If you are using GCC and a shell that supports Bash-like syntax, here is an exam
gcc program.c `pkg-config --cflags --libs libusbp-1`
Here is an equivalent command for C++. Note that we use the `--std=gnu++11` option because the libusbp C++ API requires features from C++11:
Here is an equivalent command for C++:
g++ --std=gnu++11 program.cpp `pkg-config --cflags --libs libusbp-1`
g++ program.cpp `pkg-config --cflags --libs libusbp-1`
The order of the arguments above matters: the user program must come before libusbp because it relies on symbols that are defined by libusbp.
@@ -167,6 +165,9 @@ For detailed documentation of this library, see the header files `libusb.h` and
## Version history
* 1.3.0 (2023-01-02):
* Windows: Added support for detecting FTDI serial ports. (FTDI devices with more than one port have not been tested and the interface for detecting them might change in the future.)
* macOS: Fixed the detection of serial ports for devices that are not CDC ACM.
* 1.2.0 (2020-11-16):
* Linux: Made the library work with devices attached to the cp210x driver.
* Windows: Made the library work with devices that have lowercase letters in their hardware IDs.

View File

@@ -1,2 +1,3 @@
This was taken from https://github.com/pololu/libusbp on 2021-12-11.
This is version 1.3.0 taken from https://github.com/pololu/libusbp on
2023-05-06.

View File

@@ -1,5 +1,3 @@
use_cxx11()
add_executable(async_in async_in.cpp)
include_directories (

View File

@@ -1,5 +1,3 @@
use_cxx11()
add_executable(lsport lsport.cpp)
include_directories (

View File

@@ -1,5 +1,3 @@
use_cxx11()
add_executable(lsusb lsusb.cpp)
include_directories (

View File

@@ -1,5 +1,3 @@
use_cxx11()
add_executable(port_name port_name.cpp)
include_directories (

View File

@@ -41,7 +41,6 @@ extern "C" {
#ifdef LIBUSBP_STATIC
# define LIBUSBP_API
#else
#error not static
# ifdef LIBUSBP_EXPORTS
# define LIBUSBP_API LIBUSBP_DLL_EXPORT
# else

View File

@@ -107,7 +107,7 @@ namespace libusbp
{
public:
/*! Constructor that takes a pointer. */
explicit unique_pointer_wrapper(T * p = nullptr) noexcept
explicit unique_pointer_wrapper(T * p = NULL) noexcept
: pointer(p)
{
}
@@ -133,9 +133,9 @@ namespace libusbp
/*! Implicit conversion to bool. Returns true if the underlying pointer
* is not NULL. */
explicit operator bool() const noexcept
operator bool() const noexcept
{
return pointer != nullptr;
return pointer != NULL;
}
/*! Returns the underlying pointer. */
@@ -146,19 +146,19 @@ namespace libusbp
/*! Sets the underlying pointer to the specified value, freeing the
* previous pointer and taking ownership of the specified one. */
void pointer_reset(T * p = nullptr) noexcept
void pointer_reset(T * p = NULL) noexcept
{
pointer_free(pointer);
pointer = p;
}
/*! Releases the pointer, transferring ownership of it to the caller and
* resetting the underlying pointer of this object to nullptr. The caller
* resetting the underlying pointer of this object to NULL. The caller
* is responsible for freeing the returned pointer if it is not NULL. */
T * pointer_release() noexcept
{
T * p = pointer;
pointer = nullptr;
pointer = NULL;
return p;
}
@@ -193,14 +193,14 @@ namespace libusbp
{
public:
/*! Constructor that takes a pointer. */
explicit unique_pointer_wrapper_with_copy(T * p = nullptr) noexcept
explicit unique_pointer_wrapper_with_copy(T * p = NULL) noexcept
: unique_pointer_wrapper<T>(p)
{
}
/*! Move constructor. */
unique_pointer_wrapper_with_copy(
unique_pointer_wrapper_with_copy && other) noexcept = default;
unique_pointer_wrapper_with_copy && other) = default;
/*! Copy constructor */
unique_pointer_wrapper_with_copy(
@@ -228,13 +228,14 @@ namespace libusbp
{
public:
/*! Constructor that takes a pointer. */
explicit error(libusbp_error * p = nullptr) noexcept
explicit error(libusbp_error * p = NULL) noexcept
: unique_pointer_wrapper_with_copy(p)
{
}
/*! Wrapper for libusbp_error_get_message(). */
const char * what() const noexcept override {
virtual const char * what() const noexcept
{
return libusbp_error_get_message(pointer);
}
@@ -255,7 +256,7 @@ namespace libusbp
/*! \cond */
inline void throw_if_needed(libusbp_error * err)
{
if (err != nullptr)
if (err != NULL)
{
throw error(err);
}
@@ -267,7 +268,7 @@ namespace libusbp
{
public:
/*! Constructor that takes a pointer. */
explicit async_in_pipe(libusbp_async_in_pipe * pointer = nullptr)
explicit async_in_pipe(libusbp_async_in_pipe * pointer = NULL)
: unique_pointer_wrapper(pointer)
{
}
@@ -303,8 +304,8 @@ namespace libusbp
bool handle_finished_transfer(void * buffer, size_t * transferred,
error * transfer_error)
{
libusbp_error ** error_out = nullptr;
if (transfer_error != nullptr)
libusbp_error ** error_out = NULL;
if (transfer_error != NULL)
{
transfer_error->pointer_reset();
error_out = transfer_error->pointer_to_pointer_get();
@@ -328,7 +329,7 @@ namespace libusbp
{
public:
/*! Constructor that takes a pointer. */
explicit device(libusbp_device * pointer = nullptr) :
explicit device(libusbp_device * pointer = NULL) :
unique_pointer_wrapper_with_copy(pointer)
{
}
@@ -387,7 +388,7 @@ namespace libusbp
std::vector<device> vector;
for(size_t i = 0; i < size; i++)
{
vector.emplace_back(device_list[i]);
vector.push_back(device(device_list[i]));
}
libusbp_list_free(device_list);
return vector;
@@ -408,13 +409,13 @@ namespace libusbp
public:
/*! Constructor that takes a pointer. This object will free the pointer
* when it is destroyed. */
explicit generic_interface(libusbp_generic_interface * pointer = nullptr)
explicit generic_interface(libusbp_generic_interface * pointer = NULL)
: unique_pointer_wrapper_with_copy(pointer)
{
}
/*! Wrapper for libusbp_generic_interface_create. */
explicit generic_interface(const device & device,
generic_interface(const device & device,
uint8_t interface_number = 0, bool composite = false)
{
throw_if_needed(libusbp_generic_interface_create(
@@ -448,13 +449,13 @@ namespace libusbp
public:
/*! Constructor that takes a pointer. This object will free the pointer
* when it is destroyed. */
explicit generic_handle(libusbp_generic_handle * pointer = nullptr) noexcept
explicit generic_handle(libusbp_generic_handle * pointer = NULL) noexcept
: unique_pointer_wrapper(pointer)
{
}
/*! Wrapper for libusbp_generic_handle_open(). */
explicit generic_handle(const generic_interface & gi)
generic_handle(const generic_interface & gi)
{
throw_if_needed(libusbp_generic_handle_open(gi.pointer_get(), &pointer));
}
@@ -486,9 +487,9 @@ namespace libusbp
uint8_t bRequest,
uint16_t wValue,
uint16_t wIndex,
void * buffer = nullptr,
void * buffer = NULL,
uint16_t wLength = 0,
size_t * transferred = nullptr)
size_t * transferred = NULL)
{
throw_if_needed(libusbp_control_transfer(pointer,
bmRequestType, bRequest, wValue, wIndex,
@@ -542,13 +543,13 @@ namespace libusbp
public:
/*! Constructor that takes a pointer. This object will free the pointer
* when it is destroyed. */
explicit serial_port(libusbp_serial_port * pointer = nullptr)
explicit serial_port(libusbp_serial_port * pointer = NULL)
: unique_pointer_wrapper_with_copy(pointer)
{
}
/*! Wrapper for libusbp_serial_port_create(). */
explicit serial_port(const device & device,
serial_port(const device & device,
uint8_t interface_number = 0, bool composite = false)
{
throw_if_needed(libusbp_serial_port_create(

View File

@@ -1,5 +1,3 @@
use_c99()
add_library (install_helper SHARED install_helper_windows.c dll.def)
target_link_libraries (install_helper setupapi msi)

View File

@@ -1,5 +1,3 @@
use_cxx11()
add_executable(test_async_in test_async_in.cpp)
include_directories (

View File

@@ -1,5 +1,3 @@
use_cxx11()
add_executable(test_long_read test_long_read.cpp)
include_directories (

View File

@@ -1,5 +1,3 @@
use_cxx11()
add_executable(test_long_write test_long_write.cpp)
include_directories (

View File

@@ -1,5 +1,3 @@
use_cxx11()
add_executable(test_transitions test_transitions.cpp)
include_directories (

View File

@@ -1,5 +1,3 @@
use_c99()
# Settings for GCC
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
# By default, symbols are not visible outside of the library.

View File

@@ -124,7 +124,7 @@ libusbp_error * error_add_v(libusbp_error * error, const char * format, va_list
int result = vsnprintf(x, 0, format, ap2);
if (result > 0)
{
outer_message_length = (size_t) result;
outer_message_length = result;
}
va_end(ap2);
}

View File

@@ -37,7 +37,10 @@ libusbp_error * libusbp_find_device_with_vid_pid(
libusbp_device ** new_list = NULL;
size_t size = 0;
error = libusbp_list_connected_devices(&new_list, &size);
if (error == NULL)
{
error = libusbp_list_connected_devices(&new_list, &size);
}
assert(error != NULL || new_list != NULL);

View File

@@ -37,6 +37,7 @@
#include <usbioctl.h>
#include <stringapiset.h>
#include <winusb.h>
#include <ntddmodm.h>
#endif
#ifdef __linux__

View File

@@ -51,11 +51,14 @@ libusbp_error * async_in_transfer_create(
libusbp_error * error = NULL;
// Allocate memory for the transfer struct.
async_in_transfer * new_transfer = calloc(1, sizeof(async_in_transfer));
if (new_transfer == NULL)
async_in_transfer * new_transfer = NULL;
if (error == NULL)
{
error = &error_no_memory;
new_transfer = calloc(1, sizeof(async_in_transfer));
if (new_transfer == NULL)
{
error = &error_no_memory;
}
}
// Allocate memory for the buffer.

View File

@@ -25,9 +25,14 @@ libusbp_error * create_device(io_service_t service, libusbp_device ** device)
assert(service != MACH_PORT_NULL);
assert(device != NULL);
libusbp_error * error = NULL;
// Allocate the device.
libusbp_device * new_device = NULL;
libusbp_error * error = device_allocate(&new_device);
if (error == NULL)
{
error = device_allocate(&new_device);
}
// Get the numeric IDs.
if (error == NULL)
@@ -84,7 +89,10 @@ libusbp_error * libusbp_device_copy(const libusbp_device * source, libusbp_devic
// Allocate the device.
libusbp_device * new_device = NULL;
error = device_allocate(&new_device);
if (error == NULL)
{
error = device_allocate(&new_device);
}
// Copy the simple fields, while leaving the pointers owned by the
// device NULL so that libusbp_device_free is still OK to call.

View File

@@ -63,9 +63,8 @@ static libusbp_error * process_pipe_properties(libusbp_generic_handle * handle)
uint8_t transfer_type;
uint16_t max_packet_size;
uint8_t interval;
kr = (*handle->ioh)->GetPipeProperties(handle->ioh, (UInt8) i,
&direction, &endpoint_number, &transfer_type, &max_packet_size, &interval);
kern_return_t kr = (*handle->ioh)->GetPipeProperties(handle->ioh, i,
&direction, &endpoint_number, &transfer_type, &max_packet_size, &interval);
if (kr != KERN_SUCCESS)
{
return error_create_mach(kr, "Failed to get pipe properties for pipe %d.", i);
@@ -75,11 +74,11 @@ static libusbp_error * process_pipe_properties(libusbp_generic_handle * handle)
{
if (direction)
{
handle->in_pipe_index[endpoint_number] = (uint8_t) i;
handle->in_pipe_index[endpoint_number] = i;
}
else
{
handle->out_pipe_index[endpoint_number] = (uint8_t) i;
handle->out_pipe_index[endpoint_number] = i;
}
}
}
@@ -96,11 +95,14 @@ static libusbp_error * set_configuration(io_service_t service)
// Turn io_service_t into something we can actually use.
IOUSBDeviceInterface ** dev_handle = NULL;
IOCFPlugInInterface ** plug_in = NULL;
error = service_to_interface(service,
kIOUSBDeviceUserClientTypeID,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID197),
(void **)&dev_handle,
&plug_in);
if (error == NULL)
{
error = service_to_interface(service,
kIOUSBDeviceUserClientTypeID,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID197),
(void **)&dev_handle,
&plug_in);
}
uint8_t config_num = 0;
if (error == NULL)
@@ -170,7 +172,10 @@ static libusbp_error * set_configuration_and_get_service(
// Get an io_service_t for the physical device.
io_service_t device_service = MACH_PORT_NULL;
error = service_get_from_id(device_id, &device_service);
if (error == NULL)
{
error = service_get_from_id(device_id, &device_service);
}
// Set the configruation to 1 if it is not set.
if (error == NULL)
@@ -209,11 +214,13 @@ libusbp_error * libusbp_generic_handle_open(
// Allocate memory for the handle.
libusbp_generic_handle * new_handle = NULL;
new_handle = calloc(1, sizeof(libusbp_generic_handle));
if (new_handle == NULL)
if (error == NULL)
{
error = &error_no_memory;
new_handle = calloc(1, sizeof(libusbp_generic_handle));
if (new_handle == NULL)
{
error = &error_no_memory;
}
}
// Get the io_service_t representing the IOUSBInterface.
@@ -323,11 +330,14 @@ libusbp_error * libusbp_generic_handle_set_timeout(
libusbp_error * error = NULL;
error = check_pipe_id(pipe_id);
if (error == NULL)
{
error = check_pipe_id(pipe_id);
}
if (error == NULL)
{
uint8_t endpoint_number = pipe_id & (uint8_t) MAX_ENDPOINT_NUMBER;
uint8_t endpoint_number = pipe_id & MAX_ENDPOINT_NUMBER;
if (pipe_id & 0x80)
{
@@ -401,7 +411,7 @@ libusbp_error * libusbp_read_pipe(
libusbp_error * error = NULL;
if (size == 0)
if (error == NULL && size == 0)
{
error = error_create("Transfer size 0 is not allowed.");
}
@@ -423,12 +433,12 @@ libusbp_error * libusbp_read_pipe(
if (error == NULL)
{
uint8_t endpoint_number = pipe_id & (uint8_t) MAX_ENDPOINT_NUMBER;
uint8_t endpoint_number = pipe_id & MAX_ENDPOINT_NUMBER;
uint32_t no_data_timeout = 0;
uint32_t completion_timeout = handle->in_timeout[endpoint_number];
uint32_t iokit_size = (uint32_t) size;
uint32_t iokit_size = size;
uint32_t pipe_index = handle->in_pipe_index[endpoint_number];
kern_return_t kr = (*handle->ioh)->ReadPipeTO(handle->ioh, (UInt8) pipe_index,
kern_return_t kr = (*handle->ioh)->ReadPipeTO(handle->ioh, pipe_index,
buffer, &iokit_size, no_data_timeout, completion_timeout);
if (transferred != NULL) { *transferred = iokit_size; }
if (kr != KERN_SUCCESS)
@@ -464,7 +474,7 @@ libusbp_error * libusbp_write_pipe(
libusbp_error * error = NULL;
if (size > UINT32_MAX)
if (error == NULL && size > UINT32_MAX)
{
error = error_create("Transfer size is too large.");
}
@@ -481,12 +491,12 @@ libusbp_error * libusbp_write_pipe(
if (error == NULL)
{
uint8_t endpoint_number = pipe_id & (uint8_t) MAX_ENDPOINT_NUMBER;
uint8_t endpoint_number = pipe_id & MAX_ENDPOINT_NUMBER;
uint32_t no_data_timeout = 0;
uint32_t completion_timeout = handle->out_timeout[endpoint_number];
uint32_t pipe_index = handle->out_pipe_index[endpoint_number];
kern_return_t kr = (*handle->ioh)->WritePipeTO(handle->ioh, (UInt8) pipe_index,
(void *)buffer, (UInt32) size, no_data_timeout, completion_timeout);
kern_return_t kr = (*handle->ioh)->WritePipeTO(handle->ioh, pipe_index,
(void *)buffer, size, no_data_timeout, completion_timeout);
if (kr != KERN_SUCCESS)
{
error = error_create_mach(kr, "");
@@ -588,7 +598,7 @@ IOUSBInterfaceInterface182 ** generic_handle_get_ioh(const libusbp_generic_handl
uint8_t generic_handle_get_pipe_index(const libusbp_generic_handle * handle, uint8_t pipe_id)
{
uint8_t endpoint_number = pipe_id & (uint8_t) MAX_ENDPOINT_NUMBER;
uint8_t endpoint_number = pipe_id & MAX_ENDPOINT_NUMBER;
if (pipe_id & 0x80)
{
return handle->in_pipe_index[endpoint_number];

View File

@@ -82,7 +82,10 @@ libusbp_error * libusbp_generic_interface_create(
{
// Get an io_service_t for the physical device.
io_service_t device_service = MACH_PORT_NULL;
error = service_get_from_id(new_gi->device_id, &device_service);
if (error == NULL)
{
error = service_get_from_id(new_gi->device_id, &device_service);
}
// Get the io_service_t for the interface.
io_service_t interface_service = MACH_PORT_NULL;
@@ -146,7 +149,10 @@ libusbp_error * libusbp_generic_interface_copy(
// Allocate the generic interface.
libusbp_generic_interface * new_gi = NULL;
error = generic_interface_allocate(&new_gi);
if (error == NULL)
{
error = generic_interface_allocate(&new_gi);
}
// Copy the simple fields.
if (error == NULL)

View File

@@ -42,12 +42,14 @@ libusbp_error * service_get_usb_interface(io_service_t service,
libusbp_error * error = NULL;
io_iterator_t iterator = MACH_PORT_NULL;
kern_return_t result = IORegistryEntryGetChildIterator(
service, kIOServicePlane, &iterator);
if (result != KERN_SUCCESS)
if (error == NULL)
{
error = error_create_mach(result, "Failed to get child iterator.");
kern_return_t result = IORegistryEntryGetChildIterator(
service, kIOServicePlane, &iterator);
if (result != KERN_SUCCESS)
{
error = error_create_mach(result, "Failed to get child iterator.");
}
}
// Loop through the devices to find the right one.
@@ -57,7 +59,7 @@ libusbp_error * service_get_usb_interface(io_service_t service,
if (candidate == MACH_PORT_NULL) { break; }
// Filter out candidates that are not of class IOUSBInterface.
bool conforms = (bool) IOObjectConformsTo(candidate, kIOUSBInterfaceClassName);
bool conforms = IOObjectConformsTo(candidate, kIOUSBInterfaceClassName);
if (!conforms)
{
IOObjectRelease(candidate);
@@ -88,54 +90,6 @@ libusbp_error * service_get_usb_interface(io_service_t service,
return error;
}
libusbp_error * service_get_child_by_class(io_service_t service,
const char * class_name, io_service_t * interface_service)
{
assert(service != MACH_PORT_NULL);
assert(interface_service != NULL);
*interface_service = MACH_PORT_NULL;
libusbp_error * error = NULL;
io_iterator_t iterator = MACH_PORT_NULL;
kern_return_t result = IORegistryEntryCreateIterator(
service, kIOServicePlane, kIORegistryIterateRecursively, &iterator);
if (result != KERN_SUCCESS)
{
error = error_create_mach(result, "Failed to get recursive iterator.");
}
// Loop through the devices to find the right one.
while (error == NULL)
{
io_service_t candidate = IOIteratorNext(iterator);
if (candidate == MACH_PORT_NULL) { break; }
// Filter out candidates that are not the right class.
bool conforms = (bool) IOObjectConformsTo(candidate, class_name);
if (!conforms)
{
IOObjectRelease(candidate);
continue;
}
// This is the right one. Pass it to the caller.
*interface_service = candidate;
break;
}
if (error == NULL && *interface_service == MACH_PORT_NULL)
{
error = error_create("Could not find entry with class %s.", class_name);
error = error_add_code(error, LIBUSBP_ERROR_NOT_READY);
}
if (iterator != MACH_PORT_NULL) { IOObjectRelease(iterator); }
return error;
}
libusbp_error * service_to_interface(
io_service_t service,
CFUUIDRef pluginType,
@@ -154,13 +108,15 @@ libusbp_error * service_to_interface(
// Create the plug-in interface.
IOCFPlugInInterface ** new_plug_in = NULL;
kern_return_t kr = IOCreatePlugInInterfaceForService(service,
pluginType, kIOCFPlugInInterfaceID,
&new_plug_in, &score);
if (kr != KERN_SUCCESS)
if (error == NULL)
{
error = error_create_mach(kr, "Failed to create plug-in interface.");
kern_return_t kr = IOCreatePlugInInterfaceForService(service,
pluginType, kIOCFPlugInInterfaceID,
&new_plug_in, &score);
if (kr != KERN_SUCCESS)
{
error = error_create_mach(kr, "Failed to create plug-in interface.");
}
}
// Create the device interface and pass it to the caller.
@@ -223,7 +179,7 @@ libusbp_error * get_string(io_registry_entry_t entry, CFStringRef name, char **
libusbp_error * error = NULL;
if (CFGetTypeID(cf_value) != CFStringGetTypeID())
if (error == NULL && CFGetTypeID(cf_value) != CFStringGetTypeID())
{
error = error_create("Property is not a string.");
}
@@ -244,7 +200,7 @@ libusbp_error * get_string(io_registry_entry_t entry, CFStringRef name, char **
error = string_copy(buffer, value);
}
CFRelease(cf_value);
if (cf_value != NULL) { CFRelease(cf_value); }
return error;
}
@@ -258,11 +214,14 @@ libusbp_error * get_int32(io_registry_entry_t entry, CFStringRef name, int32_t *
libusbp_error * error = NULL;
CFTypeRef cf_value = IORegistryEntryCreateCFProperty(entry, name, kCFAllocatorDefault, 0);
if (cf_value == NULL)
CFTypeRef cf_value = NULL;
if (error == NULL)
{
error = error_create("Failed to get int32 property from IORegistryEntry.");
cf_value = IORegistryEntryCreateCFProperty(entry, name, kCFAllocatorDefault, 0);
if (cf_value == NULL)
{
error = error_create("Failed to get int32 property from IORegistryEntry.");
}
}
if (error == NULL && CFGetTypeID(cf_value) != CFNumberGetTypeID())
@@ -292,13 +251,16 @@ libusbp_error * get_uint16(io_registry_entry_t entry, CFStringRef name, uint16_t
libusbp_error * error = NULL;
int32_t tmp;
error = get_int32(entry, name, &tmp);
if (error == NULL)
{
error = get_int32(entry, name, &tmp);
}
if (error == NULL)
{
// There is an unchecked conversion of an int32_t to a uint16_t here but
// There is an implicit conversion of an int32_t to a uint16_t here but
// we don't expect any data to be lost.
*value = (uint16_t) tmp;
*value = tmp;
}
return error;

View File

@@ -37,10 +37,13 @@ libusbp_error * libusbp_list_connected_devices(
// Create a dictionary that says "IOProviderClass" => "IOUSBDevice"
// This dictionary is CFReleased by IOServiceGetMatchingServices.
CFMutableDictionaryRef dict = NULL;
dict = IOServiceMatching("IOUSBHostDevice");
if (dict == NULL)
if (error == NULL)
{
error = error_create("IOServiceMatching returned null.");
dict = IOServiceMatching("IOUSBHostDevice");
if (dict == NULL)
{
error = error_create("IOServiceMatching returned null.");
}
}
// Create an iterator for all the connected USB devices.

View File

@@ -2,9 +2,6 @@
struct libusbp_serial_port
{
// The I/O Registry ID of the IOBSDSerialClient.
uint64_t id;
// A port filename like "/dev/cu.usbmodemFD123".
char * port_name;
};
@@ -29,23 +26,16 @@ libusbp_error * libusbp_serial_port_create(
return error_create("Device is null.");
}
// Add one to the interface number because that is what we need for the
// typical case: The user specifies the lower of the two interface numbers,
// which corresponds to the control interface of a CDC ACM device. We
// actually need the data interface because that is the one that the
// IOSerialBSDClient lives under. If this +1 causes any problems, it is
// easy for the user to address it using an an ifdef. Also, we might make
// this function more flexible in the future if we need to handle different
// types of serial devices with different drivers or interface layouts.
interface_number += 1;
libusbp_error * error = NULL;
libusbp_serial_port * new_port = calloc(1, sizeof(libusbp_serial_port));
if (new_port == NULL)
libusbp_serial_port * new_port = NULL;
if (error == NULL)
{
error = &error_no_memory;
new_port = calloc(1, sizeof(libusbp_serial_port));
if (new_port == NULL)
{
error = &error_no_memory;
}
}
// Get the ID for the physical device.
@@ -62,19 +52,67 @@ libusbp_error * libusbp_serial_port_create(
error = service_get_from_id(device_id, &device_service);
}
// Get an io_service_t for the interface.
io_service_t interface_service = MACH_PORT_NULL;
io_iterator_t iterator = MACH_PORT_NULL;
if (error == NULL)
{
error = service_get_usb_interface(device_service, interface_number, &interface_service);
kern_return_t result = IORegistryEntryCreateIterator(
device_service, kIOServicePlane, kIORegistryIterateRecursively, &iterator);
if (result != KERN_SUCCESS)
{
error = error_create_mach(result, "Failed to get recursive iterator.");
}
}
// Get an io_service_t for the IOSerialBSDClient
io_service_t serial_service = MACH_PORT_NULL;
if (error == NULL)
int32_t current_interface = -1;
int32_t last_acm_control_interface_with_no_port = -1;
int32_t last_acm_data_interface = -1;
while (error == NULL)
{
error = service_get_child_by_class(interface_service,
kIOSerialBSDServiceValue, &serial_service);
io_service_t service = IOIteratorNext(iterator);
if (service == MACH_PORT_NULL) { break; }
if (IOObjectConformsTo(service, kIOUSBHostInterfaceClassName))
{
error = get_int32(service, CFSTR("bInterfaceNumber"), &current_interface);
}
else if (IOObjectConformsTo(service, "AppleUSBACMControl"))
{
last_acm_control_interface_with_no_port = current_interface;
}
else if (IOObjectConformsTo(service, "AppleUSBACMData"))
{
last_acm_data_interface = current_interface;
}
else if (IOObjectConformsTo(service, kIOSerialBSDServiceValue))
{
int32_t fixed_interface = current_interface;
if (last_acm_data_interface == current_interface &&
last_acm_control_interface_with_no_port >= 0)
{
// We found an ACM control interface with no serial port, then
// an ACM data interface with a serial port. For consistency with
// other operating systems, we will consider this serial port to
// actually be associated with the control interface instead of the
// data interface.
fixed_interface = last_acm_control_interface_with_no_port;
}
last_acm_control_interface_with_no_port = -1;
if (fixed_interface == interface_number)
{
// We found the serial port the user is looking for.
serial_service = service;
break;
}
}
IOObjectRelease(service);
}
if (error == NULL && serial_service == MACH_PORT_NULL)
{
error = error_create("Could not find entry with class IOSerialBSDClient.");
error = error_add_code(error, LIBUSBP_ERROR_NOT_READY);
}
// Get the port name.
@@ -91,7 +129,7 @@ libusbp_error * libusbp_serial_port_create(
}
if (serial_service != MACH_PORT_NULL) { IOObjectRelease(serial_service); }
if (interface_service != MACH_PORT_NULL) { IOObjectRelease(interface_service); }
if (iterator != MACH_PORT_NULL) { IOObjectRelease(iterator); }
if (device_service != MACH_PORT_NULL) { IOObjectRelease(device_service); }
libusbp_serial_port_free(new_port);
@@ -125,11 +163,14 @@ libusbp_error * libusbp_serial_port_copy(const libusbp_serial_port * source,
libusbp_error * error = NULL;
// Allocate memory for the new object.
libusbp_serial_port * new_port = calloc(1, sizeof(libusbp_serial_port));
if (new_port == NULL)
libusbp_serial_port * new_port = NULL;
if (error == NULL)
{
error = &error_no_memory;
new_port = calloc(1, sizeof(libusbp_serial_port));
if (new_port == NULL)
{
error = &error_no_memory;
}
}
// Copy the port name.

View File

@@ -113,42 +113,79 @@ static libusbp_error * get_interface_composite(
return error;
}
// Get a list of all the USB-related devices.
HDEVINFO new_list = SetupDiGetClassDevs(NULL, "USB", NULL,
DIGCF_ALLCLASSES | DIGCF_PRESENT);
if (new_list == INVALID_HANDLE_VALUE)
{
return error_create_winapi(
"Failed to get list of all USB devices while finding an interface.");
}
unsigned int list_index = 0;
HDEVINFO new_list = INVALID_HANDLE_VALUE;
DWORD i = 0;
// Iterate through the list until we find a device whose
// Iterate through various device lists until we find a device whose
// parent device is ours and which controls the interface
// specified by the caller.
for (DWORD i = 0; ; i++)
while (true)
{
if (new_list == INVALID_HANDLE_VALUE)
{
if (list_index == 0)
{
// Get a list of all the USB-related devices.
// It includes native USB interfaces and usbser.sys ports but
// not FTDI ports.
new_list = SetupDiGetClassDevs(NULL,
"USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
}
else if (list_index == 1)
{
// Get a list of all the COM port devices.
// This includes FTDI and usbser.sys ports.
new_list = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT,
NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
}
else if (list_index == 2)
{
// Get a list of all modem devices.
// Rationale: https://github.com/pyserial/pyserial/commit/7bb1dcc5aea16ca1c957690cb5276df33af1c286
new_list = SetupDiGetClassDevs(&GUID_DEVINTERFACE_MODEM,
NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
}
else
{
// Could not find the child interface in any list.
// This could be a temporary condition.
libusbp_error * error = error_create("Could not find interface %d.",
interface_number);
error = error_add_code(error, LIBUSBP_ERROR_NOT_READY);
return error;
}
if (new_list == INVALID_HANDLE_VALUE)
{
return error_create_winapi(
"Failed to list devices to find an interface (%u).",
list_index);
}
i = 0;
}
SP_DEVINFO_DATA device_info_data;
device_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
bool success = SetupDiEnumDeviceInfo(new_list, i, &device_info_data);
if (!success)
{
libusbp_error * error;
if (GetLastError() == ERROR_NO_MORE_ITEMS)
{
// Could not find the child interface. This could be
// a temporary condition.
error = error_create("Could not find interface %d.",
interface_number);
error = error_add_code(error, LIBUSBP_ERROR_NOT_READY);
// This list is done. Try the next list.
SetupDiDestroyDeviceInfoList(new_list);
new_list = INVALID_HANDLE_VALUE;
list_index++;
continue;
}
else
{
error = error_create_winapi(
"Failed to get device info while finding an interface.");
libusbp_error * error = error_create_winapi(
"Failed to get device info to find an interface.");
SetupDiDestroyDeviceInfoList(new_list);
return error;
}
SetupDiDestroyDeviceInfoList(new_list);
return error;
}
DEVINST parent_dev_inst;
@@ -162,6 +199,7 @@ static libusbp_error * get_interface_composite(
if (parent_dev_inst != dev_inst)
{
// This device is not a child of our device.
i++;
continue;
}
@@ -179,9 +217,21 @@ static libusbp_error * get_interface_composite(
unsigned int actual_interface_number;
int result = sscanf(device_id, "USB\\VID_%*4x&PID_%*4x&MI_%2x\\",
&actual_interface_number);
if (result != 1 || actual_interface_number != interface_number)
if (result != 1)
{
result = sscanf(device_id, "FTDIBUS\\%*[^\\]\\%x",
&actual_interface_number);
if (result != 1)
{
// Could not figure out the interface number.
i++;
continue;
}
}
if (actual_interface_number != interface_number)
{
// This is not the right interface.
i++;
continue;
}

View File

@@ -124,6 +124,26 @@ libusbp_error * libusbp_serial_port_create(
libusbp_string_free(usb_device_id);
libusbp_serial_port_free(new_sp);
// FTDI devices like the FT232RL aren't actually composite but they look
// like it on Windows because the serial port device is a child of the USB
// device. On Linux and macOS, those devices can be detected with
// composite=false (or composite=true and interface_number=0).
// This workaround allows that to work on Windows too, and it might make
// this API easier to use for some non-FTDI devices too.
if (error && !composite)
{
libusbp_error * error2 = libusbp_serial_port_create(device, 0, true, port);
if (error2)
{
libusbp_error_free(error2);
}
else
{
libusbp_error_free(error);
return NULL;
}
}
return error;
}

View File

@@ -1,9 +1,8 @@
INCLUDE (CheckIncludeFileCXX)
# If catch.hpp is not present, we want to simply skip compiling the tests. This
# allows someone to compile and install libusbp without having catch installed.
# The header can either be installed in this directory or in a standard system
# location.
# If catch.hpp is not present, we want to simply skip compiling the tests.
# Download catch.hpp and put it in this directory:
# https://raw.githubusercontent.com/catchorg/Catch2/v2.x/single_include/catch2/catch.hpp
set (CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}")
CHECK_INCLUDE_FILE_CXX (catch.hpp HAVE_CATCH_FRAMEWORK)
if (NOT HAVE_CATCH_FRAMEWORK)
@@ -11,8 +10,6 @@ if (NOT HAVE_CATCH_FRAMEWORK)
return ()
endif ()
use_cxx11 ()
set(USE_TEST_DEVICE_A FALSE CACHE BOOL
"Run tests that require Test Device A.")

View File

@@ -486,6 +486,9 @@ TEST_CASE("async_in_pipe for an interrupt endpoint")
// ms, a three-packet transfer will quickly receive those two packets
// and then keep waiting for more.
// Previous versions of macOS returned one packet instead of two,
// but this is no longer true on Darwin Kernel 22.1.0 (Oct 2022).
// Pause the ADC for 100 ms.
handle.control_transfer(0x40, 0xA0, 100, 0);
@@ -508,9 +511,6 @@ TEST_CASE("async_in_pipe for an interrupt endpoint")
#if defined(VBOX_LINUX_ON_WINDOWS)
CHECK(transferred == 0);
#elif defined(__APPLE__)
CHECK(transferred == transfer_size);
CHECK(buffer[4] == 0xAB);
#else
CHECK(transferred == transfer_size * 2);
CHECK(buffer[4] == 0xAB);
@@ -662,9 +662,10 @@ TEST_CASE("async_in_pipe for an interrupt endpoint")
expected_message = "Asynchronous IN transfer failed. "
"Incorrect function. Windows error code 0x1.";
#elif defined(__linux__)
// This request results in an error in Linux but it is only detected
// after some data is transferred.
expected_transferred = transfer_size + 1;
// This request results in an error in Linux after some data is transferred.
// On some older Linux systems, expected_transferred was transfer_size + 1.
// With Linux 5.15 on a Raspberry Pi 4, expected_transferred is transfer_size.
expected_transferred = transfer_size;
expected_message = "Asynchronous IN transfer failed. "
"The transfer overflowed. Error code 75.";
#elif defined(__APPLE__)

View File

@@ -0,0 +1,4 @@
<!-- This file is automatically generated. Do not edit. -->
# Adjust configuration for a 40-track drive
(This format has no documentation. Please file a bug.)

View File

@@ -1,23 +1,14 @@
Disk: Acorn ADFS
================
<!-- This file is automatically generated. Do not edit. -->
# BBC Micro, Archimedes
Acorn ADFS disks are pretty standard MFM encoded IBM scheme disks, although
with different sector sizes and with the 0-based sector identifiers rather
than 1-based sector identifiers. The index hole is ignored and sectors are
written whereever, requiring FluxEngine to do two revolutions to read a
disk.
Acorn ADFS disks are used by the 6502-based BBC Micro and ARM-based Archimedes
series of computers. They are yet another variation on MFM encoded IBM scheme
disks, although with different sector sizes and with the 0-based sector
identifiers rather than 1-based sector identifiers. The index hole is ignored
and sectors are written whereever, requiring FluxEngine to do two revolutions
to read a disk.
There are various different kinds, which should all work out of the box.
Tested ones are:
- ADFS L: 80 track, 16 sector, 2 sides, 256 bytes per sector == 640kB.
- ADFE D/E: 80 track, 5 sector, 2 sides, 1024 bytes per sector == 800kB.
- ADFS F: 80 track, 10 sector, 2 sides, 1024 bytes per sector == 1600kB.
I expect the others to work, but haven't tried them; [get in
touch](https://github.com/davidgiven/fluxengine/issues/new) if you have any
news. For ADFS S (single-sided 40 track) you'll want `--heads 0 --cylinders
0-79x2`. For ADFS M (single-sided 80 track) you'll want `--heads 0`.
Be aware that Acorn logical block numbering goes all the way up side 0 and
then all the way up side 1. However, FluxEngine uses traditional disk images
@@ -25,15 +16,7 @@ with alternating sides, with the blocks from track 0 side 0 then track 0 side
1 then track 1 side 0 etc. Most Acorn emulators will use both formats, but
they might require nudging as the side order can't be reliably autodetected.
Reading discs
-------------
## Options
Just do:
(no options)
```
fluxengine read acornadfs
```
You should end up with an `acornadfs.img` of the appropriate size for your disk
format. This is an alias for `fluxengine read ibm` with preconfigured
parameters.

View File

@@ -1,34 +1,22 @@
Disk: Acorn DFS
===============
<!-- This file is automatically generated. Do not edit. -->
# Acorn Atom, BBC Micro series
Acorn DFS disks are pretty standard FM encoded IBM scheme disks, with
256-sectors and 0-based sector identifiers. There's nothing particularly
special here.
Acorn DFS disks are used by the Acorn Atom and BBC Micro series of computers.
They are pretty standard FM encoded IBM scheme disks, with 256-sectors and
0-based sector identifiers. There's nothing particularly special here.
DFS disks are all single-sided, but allow the other side of the disk to be
used as another drive. FluxEngine supports these; read one side at a time
with `--heads 0` or `--heads 1`.
used as another volume.
DFS comes in two varieties, 40 track and 80 track. These should both work. For
40 track you'll want `--cylinders 0-79x2`. Some rare disks are both at the same
time. FluxEngine can read these but it requires a bit of fiddling as they have
the same tracks on twice.
They come in two varieties, 40 track and 80 track. These should both work.
Some rare disks are both at the same time. FluxEngine can read these but it
requires a bit of fiddling as they have the same tracks on twice.
Reading discs
-------------
## Options
Just do:
(no options)
```
fluxengine read acorndfs
```
## References
You should end up with an `acorndfs.img` of the appropriate size for your disk
format. This is an alias for `fluxengine read ibm` with preconfigured
parameters.
References
----------
- [The Acord DFS disc format](https://beebwiki.mdfs.net/Acorn_DFS_disc_format)
- [The Acorn DFS disc format](https://beebwiki.mdfs.net/Acorn_DFS_disc_format)

View File

@@ -1,11 +1,11 @@
Disk: AES Lanier word processor
===============================
<!-- This file is automatically generated. Do not edit. -->
# 616kB 5.25" 77-track SSDD hard sectored
Back in 1980 Lanier released a series of very early integrated word processor
appliances, the No Problem. These were actually [rebranded AES Data Superplus
machines](http://vintagecomputers.site90.net/aes/). They were gigantic,
weighed 40kg, and one example I've found cost £13,000 in 1981 (the equivalent
of nearly £50,000 in 2018!).
weighed 40kg, and one example I've found cost ꆲ5bb4
of nearly ㇢1a6e
8080 machines with 32kB of RAM, they ran their own proprietary word
processing software off twin 5.25" drive units, but apparently other software
@@ -27,18 +27,14 @@ disk image, and I've had to make a lot of guesses as to the sector format
based on what looks right. If anyone knows _anything_ about these disks,
[please get in touch](https://github.com/davidgiven/fluxengine/issues/new).
Reading discs
-------------
## Options
Just do:
(no options)
```
fluxengine read aeslanier
```
## References
You'll end up with an `aeslanier.img` file.
* [SA800 Diskette Storage Drive - Theory Of
Operations](http://www.hartetechnologies.com/manuals/Shugart/50664-1_SA800_TheorOp_May78.pdf):
talks about MMFM a lot, but the Lanier machines didn't use this disk
format.
Useful references
-----------------
* [SA800 Diskette Storage Drive - Theory Of Operations](http://www.hartetechnologies.com/manuals/Shugart/50664-1_SA800_TheorOp_May78.pdf): talks about MMFM a lot, but the Lanier machines didn't use this disk format.

View File

@@ -1,34 +1,24 @@
Disk: Agat
==========
<!-- This file is automatically generated. Do not edit. -->
# 840kB 5.25" 80-track DS
The Agat (Russian: Агат) was a series Soviet-era computer, first released about
The Agat (Russian: ↊fd74
1983. These were based around a 6502 and were nominally Apple II-compatible
although with enough differences to be problematic.
They could use either standard Apple II 140kB disks, or a proprietary 840kb
MFM-based double-sided format. FluxEngine supports both of these; this profile
is for the proprietary format. for the Apple II format, use the [Apple II
profile](disk-apple2.md).
is for the proprietary format. for the Apple II format, use the `apple2`
profile.
## Options
(no options)
Reading discs
-------------
Just do:
```
fluxengine read agat840
```
You should end up with an `agat840.img` which is 860160 bytes long.
Useful references
-----------------
## References
- [Magazine article on the
Agat](https://sudonull.com/post/54185-Is-AGAT-a-bad-copy-of-Apple)
Agat](https://sudonull.com/post/54185-Is-AGAT-a-bad-copy-of-Apple)
- [Forum thread with (some) documentation on the
format](https://torlus.com/floppy/forum/viewtopic.php?t=1385)
format](https://torlus.com/floppy/forum/viewtopic.php?t=1385)

View File

@@ -1,65 +1,27 @@
Disk: Amiga
===========
<!-- This file is automatically generated. Do not edit. -->
# 880kB 3.5" DSDD
Amiga disks use MFM, but don't use IBM scheme. Instead, the entire track is
read and written as a unit, with each sector butting up against the previous
one. This saves a lot of space which allows the Amiga to not just store 880kB
on a DD disk, but _also_ allows an extra 16 bytes of metadata per sector.
This metadata is mostly unused, so the default for FluxEngine is to ignore it
and just use the 512 bytes of main sector data. If you want it, specify a
528-byte sector size. The metadata will come after the user data.
Bizarrely, the data in each sector is stored with all the odd bits first, and
then all the even bits. This is tied into the checksum algorithm, which is
distinctly subpar and not particularly good at detecting errors.
Reading disks
-------------
## Options
Just do:
(no options)
```
fluxengine read amiga
```
You should end up with an `amiga.adf` which is 901120 bytes long (for a
normal DD disk) --- it ought to be a perfectly normal ADF file which you can
use in an emulator.
If you want the metadata as well, specify a 528 byte sector size for the
output image:
```
fluxengine read amiga -o amiga.adf --output.image.img.trackdata.sector_size=528
```
You will end up with a 929280 byte long image which you probably _can't_ use
in an emulator; each sector will contain the 512 bytes of user payload
followed by the 16 bytes of metadata.
Writing disks
-------------
Just do:
```
fluxengine write amiga -i amiga.adf
```
This will take a normal 901120 byte long ADF file and write it to a DD disk.
Note that writing to an HD disk will probably not work (this will depend on
your drive and disk and potential FluxEngine bugs I'm still working on ---
please [get in touch](https://github.com/davidgiven/fluxengine/issues/new) if
you have any insight here).
If you want to write the metadata as well, specify a 528 byte sector size for
the output image and supply a 929280 byte long file as described above.
```
fluxengine write amiga -i amiga.adf --input.image.img.trackdata.sector_size=528
```
Useful references
-----------------
## References
- [The Amiga Floppy Boot Process and Physical
Layout](https://wiki.amigaos.net/wiki/Amiga_Floppy_Boot_Process_and_Physical_Layout)
- [The Amiga Disk File FAQ](http://lclevy.free.fr/adflib/adf_info.html)

View File

@@ -1,5 +1,5 @@
Disk: Ampro Little Board
========================
<!-- This file is automatically generated. Do not edit. -->
# CP/M
The Ampro Little Board was a very simple and cheap Z80-based computer from
1984, which ran CP/M. It was, in fact, a single PCB which you could mount
@@ -12,20 +12,8 @@ double-sided 80 track drive. The disk format it used was a slightly quirky
variation of the standard MFM IBM scheme --- sector numbering starts at 17
rather than 1 (or Acorn's 0). FluxEngine supports this.
Reading discs
-------------
Just do:
```
fluxengine read ampro400
```
You should end up with an `ampro.img` which is 409600. If you have a
double-sided disk, use `ampro800`, which will give you a file819200 bytes long.
These is an alias for `fluxengine read ibm` with preconfigured parameters. You
can pass this straight into [cpmtools](http://www.moria.de/~michael/cpmtools/):
FluxEngine has direct filesystem support for these disks, or you can pass the
disk images into [cpmtools](http://www.moria.de/~michael/cpmtools/):
```
$ cpmls -f ampdsdd ampro.img
@@ -43,7 +31,11 @@ kayinfo.lbr
...etc...
```
Useful references
-----------------
## Options
(no options)
## References
- [The Ampro Little Board](http://oldcomputers.net/ampro-little-board.html)

View File

@@ -1,5 +1,5 @@
Disk: Apple II
==============
<!-- This file is automatically generated. Do not edit. -->
# Prodos, Appledos, and CP/M
Apple II disks are nominally fairly sensible 40-track, single-sided, 256
bytes-per-sector jobs. However, they come in two varieties: DOS 3.3/ProDOS and
@@ -30,46 +30,31 @@ FluxEngine can remap the sectors from physical to logical using modifiers. If
you don't specify a remapping modifier, you get the sectors in the order they
appear on the disk.
If you don't want an image in physical sector order, specify one of the
filesystem ordering options. These also select the appropriate file system;
FluxEngine has read-only support for all of these.
Reading discs
-------------
In addition, some third-party systems use 80-track double sides drives, with
the same underlying disk format. The complication here is that the AppleDOS
filesystem only supports up to 50 tracks, so it needs tweaking to support
larger disks. It treats the second side of the disk as a completely different
volume.
Just do:
## Options
```
fluxengine read apple2
```
- Format variant:
- `140`: 140kB 5.25" 35-track SS
- `640`: 640kB 5.25" 80-track DS
- Filesystem and sector skew:
- `nofs`: use physical CHS sector order and no file system
- `appledos`: use AppleDOS soft sector skew and file system
- `prodos`: use ProDOS soft sector skew and filesystem
- `cpm`: use CP/M soft sector skew and filesystem
- `side1`: for AppleDOS file system access, read the volume on side 1 of a disk
You should end up with an `apple2.img` which is 143360 bytes long. It will be in
physical sector ordering. You can specify a sector ordering, `--appledos` or
`--prodos` to get an image intended for use in an emulator, due to the logical
sector mapping issue described above:
```
fluxengine read apple2 --prodos
```
You will also need this for filesystem access.
Writing discs
-------------
Just do:
```
fluxengine write apple2 -i apple2.img
```
If your image is in logical sector ordering (images intended for emulators
usually are), specify a modifier of `--appledos` or `--prodos`:
```
fluxengine write apple2 --prodos -i apple2.img
```
Useful references
-----------------
## References
- [Beneath Apple DOS](https://fabiensanglard.net/fd_proxy/prince_of_persia/Beneath%20Apple%20DOS.pdf)
- [MAME's ap2_dsk.cpp file](https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap2_dsk.cpp)

4
doc/disk-apple2_drive.md Normal file
View File

@@ -0,0 +1,4 @@
<!-- This file is automatically generated. Do not edit. -->
# Adjust configuration for a 40-track Apple II drive
(This format has no documentation. Please file a bug.)

View File

@@ -1,5 +1,5 @@
Disk: Atari ST
==============
<!-- This file is automatically generated. Do not edit. -->
# Almost PC compatible
Atari ST disks are standard MFM encoded IBM scheme disks without an IAM header.
Disks are typically formatted 512 bytes per sector with between 9-10 (sometimes
@@ -11,63 +11,17 @@ Atari profiles below are configured to ignore these.
Be aware that many PC drives (including mine) won't do the 82 track formats.
Reading disks
-------------
## Options
Just do:
(no options)
fluxengine read <format>
...and you'll end up with an `atarist.st` file. The size of the disk image will
vary depending on the format. This is an alias for `fluxengine read ibm` with
preconfigured parameters.
Note that the profile by default assumes a double-sided disk; if you're reading
a single-sided disk, add `--heads 0` to prevent FluxEngine from looking at the
other side and getting confused by any data it sees there.
Writing disks
-------------
FluxEngine can also write Atari ST scheme disks.
The syntax is:
fluxengine write <format> -i input.st
Available formats
-----------------
`<format>` can be one of these:
- `atarist360`: a 360kB 3.5" disk, with 80 cylinders, 1 side, and 9 sectors
per track.
- `atarist370`: a 370kB 3.5" disk, with 82 cylinders, 1 side, and 9 sectors
per track.
- `atarist400`: a 400kB 3.5" disk, with 80 cylinders, 1 side, and 10 sectors
per track.
- `atarist410`: a 410kB 3.5" disk, with 82 cylinders, 1 side, and 10 sectors
per track.
- `atarist720`: a 720kB 3.5" disk, with 80 cylinders, 2 sides, and 9 sectors
per track.
- `atarist740`: a 740kB 3.5" disk, with 82 cylinders, 2 sides, and 9 sectors
per track.
- `atarist800`: a 800kB 3.5" disk, with 80 cylinders, 2 sides, and 10 sectors
per track.
- `atarist820`: a 820kB 3.5" disk, with 82 cylinders, 2 sides, and 10 sectors
per track.
See [the IBM format documentation](disk-ibm.md) for more information. Note that
only some PC 3.5" floppy disk drives are capable of seeking to the 82nd track.
Useful references
-----------------
## References
- [Atari ST Floppy Drive Hardware
Information](https://info-coach.fr/atari/hardware/FD-Hard.php) by Jean
Louis-Guerin
Information](https://info-coach.fr/atari/hardware/FD-Hard.php) by Jean
Louis-Guerin
- [Atari ST Floppy Drive Software
Information](https://info-coach.fr/atari/software/FD-Soft.php) by Jean
Louis-Guerin
Information](https://info-coach.fr/atari/software/FD-Soft.php) by Jean
Louis-Guerin

View File

@@ -1,7 +1,7 @@
Disk: Elektronica BK
====================
<!-- This file is automatically generated. Do not edit. -->
# 800kB 5.25"/3.5" 80-track 10-sector DSDD
The BK (an abbreviation for бытовой компьютер --- 'home computer' in Russian)
The BK (an abbreviation for 1ba9
is a Soviet era personal computer from Elektronika based on a PDP-11
single-chip processor. It was the _only_ official, government approved home
computer in mass production at the time.
@@ -12,26 +12,7 @@ sectors per track, resulting in 800kB disks. The format is, in fact, identical
to the Atari ST 800kB format. Either 5.25" or 3.5" drives were used depending
on what was available at the time, with the same format on both.
Reading disks
-------------
## Options
Just do:
```
fluxengine read bk800 -o bk800.img
```
You should end up with an `bk800.img` containing all the sectors concatenated
one after the other in CHS order. This will work on both 5.25" and 3.5" drives.
Writing disks
-------------
Just do:
```
fluxengine write bk800 -i bk800.img
```
This will write the disk image to either a 5.25" or 3.5" drive.
(no options)

View File

@@ -1,5 +1,5 @@
Disk: Brother word processor
============================
<!-- This file is automatically generated. Do not edit. -->
# GCR family
Brother word processor disks are weird, using custom tooling and chipsets.
They are completely not PC compatible in every possible way other than the
@@ -32,28 +32,9 @@ If you find one of these misaligned disks then *please* [get in
touch](https://github.com/davidgiven/fluxengine/issues/new); I want to
investigate.
Reading disks
-------------
## Options
Just do:
```
fluxengine read `<format>`
```
... where `<format>` can be `brother120` or `brother240`. You should end up
with a `brother.img` which is either 119808 or 239616 bytes long.
Writing disks
-------------
Just do:
```
fluxengine write `<format>` -i brother.img
```
...where `<format>` can be `brother120` or `brother240`.
(no options)
Dealing with misaligned disks
-----------------------------
@@ -63,10 +44,10 @@ _can_. If you have access to a compatible word processor, there's a fairly
simple workaround to allow you to extract the data:
1. Format a disk using FluxEngine (by simply writing a blank filesystem image
to a disk). This will have the correct alignment to work on a PC drive.
to a disk). This will have the correct alignment to work on a PC drive.
2. Use a word processor to copy the misaligned disk to the newly formatted
disk. The machine will happily adjust itself to both sets of alignments.
disk. The machine will happily adjust itself to both sets of alignments.
3. Use FluxEngine to read the data off the correctly aligned disk.
@@ -102,50 +83,19 @@ record.) The sector order is 05a3816b4927, which gives a sector skew of 5.
High level format
-----------------
Once decoded, you end up with a file system image.
Once decoded, you end up with a file system image. FluxEngine supports direct
filesystem access for both kinds of disks.
### 120kB disks
These disks use a proprietary and very simple file system. It's FAT-like
with an obvious directory and allocation table. I have reversed engineered
a very simple tool for extracting files from it. To show the directory, do:
```
brother120tool image.img
```
To extract a file, do:
```
brother120tool image.img filename
```
Wildcards are supported, so use `'*'` for the filename (remember to quote it)
if you want to extract everything.
The files are usually in the format known as WP-1, which aren't well
supported by modern tools (to nobody's great surprise). Matthias Henckell has
[reverse engineered the file
format](https://mathesoft.eu/brother-wp-1-dokumente/) and has produced a
(Windows-only, but runs in Wine) [tool which will convert these files into
RTF](https://mathesoft.eu/sdm_downloads/wp2rtf/). This will only work on WP-1
files.
To create a disk image (note: this creates a _new_ disk image, overwriting the
previous image), do:
```
brother120tool --create image.img filename1 filename2...
```
with an obvious directory and allocation table. It's supported by FluxEngine.
Any files whose names begin with an asterisk (`*`) will be marked as hidden. If
the file is named `*boot`, then a boot sector will be created which will load
and run the file at 0x7000 if the machine is started with CODE+Q pressed. So
far this has only been confirmed to work on a WP-1.
Any questions? please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new).
### 240kB disks
Conversely, the 240kB disks turns out to be a completely normal Microsoft FAT
@@ -157,17 +107,6 @@ reverse engineered it to find out.
Standard Linux mtools will access the filesystem image and allow you to move
files in and out. However, you'll need to change the media type bytes at
offsets 0x015 and 0x100 from 0x58 to 0xf0 before mtools will touch it. The
supplied `brother240tool` will do this. Once done, this will work:
supplied `brother240tool` will do this. Additionally, FluxEngine's own FAT
file system supports this.
```
mdir -i brother.img
mcopy -i brother.img ::brother.doc linux.doc
```
The word processor checks the media byte, unfortunately, so you'll need to
change it back to 0x58 before writing an image to disk. Just run
`brother240tool` on the image again and it will flip it back.
The file format is not WP-1, and currently remains completely unknown,
although it's probably related. If anyone knows anything about this, please
[get in touch](https://github.com/davidgiven/fluxengine/issues/new).

View File

@@ -1,103 +0,0 @@
Disk: Commodore 64
==================
Commodore 64 disks come in two varieties: GCR, which are the overwhelming
majority; and MFM, only used on the 1571 and 1581. The latter were (as far as I
can tell) standard IBM PC format disks with a slightly odd sector count.
The GCR disks are much more interesting. They could store 170kB on a
single-sided disk (although later drives were double-sided), using a proprietary
encoding and record scheme; like [Apple Macintosh disks](macintosh.md) they
stored varying numbers of sectors per track to make the most of the physical
disk area, although unlike them they did it by changing the bitrate rather than
adjusting the motor speed.
The drives were also intelligent and ran DOS on a CPU inside them. The
computer itself knew nothing about file systems. You could even upload
programs onto the drive and run them there, allowing all sorts of custom disk
formats, although this was mostly used to compensate for the [cripplingly
slow connection to the
computer](https://ilesj.wordpress.com/2014/05/14/1541-why-so-complicated/) of
300 bytes per second (!). (The drive itself could transfer data reasonably
quickly.)
A standard 1541 disk has 35 tracks of 17 to 21 sectors, each 256 bytes long
(sometimes 40 tracks).
A standard 1581 disk has 80 tracks and two sides, each with 10 sectors, 512
bytes long.
Reading 1541 disks
------------------
Just do:
```
fluxengine read commodore1541 -o commodore1541.d64
```
You should end up with an `commodore1541.d64` file which is 174848 bytes long.
You can load this straight into a Commodore 64 emulator such as
[VICE](http://vice-emu.sourceforge.net/).
If you have a 40-track disk, add `--40`.
**Big warning!** Commodore 64 disk images are complicated due to the way the
tracks are different sizes and the odd sector size, so you need the special D64
or LDBS output formats to represent them sensibly. Don't use IMG unless you
know what you're doing.
Writing 1541 disks
------------------
Just do:
```
fluxengine write commodore1541 -i file.d64
```
If you have a 40-track disk, add `--40`.
Note that only standard Commodore 64 BAM file systems can be written this way,
as the disk ID in the BAM has to be copied to every sector on the disk.
Reading and writing 1581 disks
------------------------------
1581 disks are just another version of the standard IBM scheme.
Just do:
```
fluxengine read commodore1581 -o commodore1581.d81
```
or:
```
fluxengine write commodore1581 -i commodore1581.img
```
Reading and writing CMD FD2000 disks
------------------------------------
Yet again, these are another IBM scheme variant.
Just do:
```
fluxengine read cmd_fd2000 -o cmd_fd2000.d81
```
or:
```
fluxengine write cmd_fd2000 -i cmd_fd2000.img
```
Useful references
-----------------
- [Ruud's Commodore Site: 1541](http://www.baltissen.org/newhtm/1541c.htm):
documentation on the 1541 disk format.

50
doc/disk-commodore.md Normal file
View File

@@ -0,0 +1,50 @@
<!-- This file is automatically generated. Do not edit. -->
# 1541, 1581, 8050 and variations
Commodore 8-bit computer disks come in two varieties: GCR, which are the
overwhelming majority; and MFM, only used on the 1571 and 1581. The latter were
(as far as I can tell) standard IBM PC format disks with a slightly odd sector
count.
The GCR disks are much more interesting. They could store 170kB on a
single-sided disk (although later drives were double-sided), using a proprietary
encoding and record scheme; like [Apple Macintosh disks](macintosh.md) they
stored varying numbers of sectors per track to make the most of the physical
disk area, although unlike them they did it by changing the bitrate rather than
adjusting the motor speed.
The drives were also intelligent and ran DOS on a CPU inside them. The
computer itself knew nothing about file systems. You could even upload
programs onto the drive and run them there, allowing all sorts of custom disk
formats, although this was mostly used to compensate for the [cripplingly
slow connection to the
computer](https://ilesj.wordpress.com/2014/05/14/1541-why-so-complicated/) of
300 bytes per second (!). (The drive itself could transfer data reasonably
quickly.)
- a 1541 disk has 35 tracks of 17 to 21 sectors, each 256 bytes long
(sometimes 40 tracks), and uses GCR encoding.
- a standard 1581 disk has 80 tracks and two sides, each with 10 sectors, 512
bytes long, and uses normal IBM encoding.
- an 8050 disk has 77 tracks and two sides, with four speed zones; the number
of sectors varies from 23 to 29, using GCR encoding. These will store
1042kB. These drives are peculiar because they are 100tpi and therefore the
disks cannot be read in normal 96tpi drives.
- a CMD FD2000 disk (a popular third-party Commodore disk drive) has 81
tracks and two sides, each with 10 1024-byte sectors, for a massive 1620kB
of storage. This also uses IBM encoding.
A CMD FD2000 disk (a popular third-party Commodore disk drive)
## Options
(no options)
## References
- [Ruud's Commodore Site: 1541](http://www.baltissen.org/newhtm/1541c.htm):
documentation on the 1541 disk format.

View File

@@ -1,5 +1,5 @@
Disk: VDS Eco1
==============
<!-- This file is automatically generated. Do not edit. -->
# CP/M; 1210kB 77-track mixed format DSHD
The Eco1 is a Italian CP/M machine produced in 1982. It had 64kB of RAM, in
later models expandable up to 384kB, and _two_ Z80 processors. One of these was
@@ -23,20 +23,12 @@ to the format confusing the size autodetection the images need postprocessing
to be useful, so there's a custom profile for the Eco1 which produces sensible
images.
Reading discs
-------------
## Options
Just do:
(no options)
```
fluxengine read eco1 -o eco1.img
```
## References
You should end up with an `eco1.img` of 1255168 bytes, containing all the
sectors concatenated one after the other in CHS order, with no padding.
References
----------
- [Apulio Retrocomputing's page on the Eco1](https://www.apuliaretrocomputing.it/wordpress/?p=8976)
- [Apulio Retrocomputing's page on the
Eco1](https://www.apuliaretrocomputing.it/wordpress/?p=8976)

11
doc/disk-epsonpf10.md Normal file
View File

@@ -0,0 +1,11 @@
<!-- This file is automatically generated. Do not edit. -->
# CP/M; 3.5" 40-track DSDD
The Epson PF10 is the disk unit for the Epson Z80 series of 'laptops', running
CP/M. It uses a single-sided 40-track 3.5" format, which is unusual, but the
format itself is yet another IBM scheme variant.
## Options
(no options)

View File

@@ -1,5 +1,5 @@
Disk: Durango F85
=================
<!-- This file is automatically generated. Do not edit. -->
# 461kB 5.25" 77-track SS
The Durango F85 was an early office computer based around a 5MHz 8085 processor,
sold in 1977. It had an impressive 64kB of RAM, upgradable to 128kB, and ran
@@ -26,21 +26,14 @@ disks; I don't have access to an image of one so don't know how they work
store the side). As always, if you have one, please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new).
Reading discs
-------------
## Options
Just do:
(no options)
```
fluxengine read f85
```
You should end up with an `f85.img` which is 472064 bytes long.
Useful references
-----------------
## References
There's amazingly little information about these things.
* [Chuck Guzis' F85 page](http://www.sydex.com/durango/durango.html) with lots of pictures
* [Chuck Guzis' F85 page](http://www.sydex.com/durango/durango.html) with
lots of pictures

View File

@@ -1,5 +1,5 @@
Disk: Brother FB-100
====================
<!-- This file is automatically generated. Do not edit. -->
# 100kB 3.5" 40-track SSSD
The Brother FB-100 is a serial-attached smart floppy drive used by a several
different machines for mass storage, including the Tandy Model 100 and
@@ -20,26 +20,21 @@ ID data used for filesystem management.
There was also apparently a TPDD-2 which could store twice as much data, but
I don't have access to one of those disks.
Reading discs
-------------
## Options
Just do:
(no options)
```
fluxengine read fb100
```
## References
You should end up with an `fb100.img` of the appropriate size. It's a simple
array of 80 1292-byte sectors (12 bytes for the ID record plus 1280 bytes for
the data).
- [Tandy Portable Disk Drive operations
manual](http://www.classiccmp.org/cini/pdf/Tandy/Portable%20Disk%20Drive%20Operation%20Manual.pdf)
References
----------
- [Tandy Portable Disk Drive service
manual](https://archive.org/details/TandyPortableDiskDriveSoftwareManual26-3808s)
- [Tandy Portable Disk Drive operations manual](http://www.classiccmp.org/cini/pdf/Tandy/Portable%20Disk%20Drive%20Operation%20Manual.pdf)
- [TPDD design notes (including a dump of the
ROM)](http://bitchin100.com/wiki/index.php?title=TPDD_Design_Notes)
- [Tandy Portable Disk Drive service manual](https://archive.org/details/TandyPortableDiskDriveSoftwareManual26-3808s)
- [Knitting machine FB-100
resources](http://www.k2g2.org/wiki:brother_fb-100)
- [TPDD design notes (including a dump of the ROM)](http://bitchin100.com/wiki/index.php?title=TPDD_Design_Notes)
- [Knitting machine FB-100 resources](http://www.k2g2.org/wiki:brother_fb-100)

15
doc/disk-hplif.md Normal file
View File

@@ -0,0 +1,15 @@
<!-- This file is automatically generated. Do not edit. -->
# a variety of disk formats used by HP
LIF, a.k.a. Logical Interchange Format, is a series of formats used by
Hewlett-Packard across their entire range of computers, from calculators to
modern servers. It also defines a simple non-hierarchical filesystem which is
bizarrely _still_ supported by HP-UX systems.
Floppy-disk wise, they're yet more variations of the standard IBM floppy
encoding scheme.
## Options
(no options)

View File

@@ -1,5 +1,5 @@
Disk: Generic IBM
=================
<!-- This file is automatically generated. Do not edit. -->
# Generic PC 3.5"/5.25" disks
IBM scheme disks are _the_ most common disk format, ever. They're used by a
huge variety of different systems, and they come in a huge variety of different
@@ -24,76 +24,19 @@ metadata. Systems which use IBM scheme disks include but are not limited to:
FluxEngine supports reading these. However, some variants are more peculiar
than others, and as a result there are specific decoders which set the defaults
correctly for certain formats (for example: on PC disks the sector numbers
start from 1, but on [Acorn](disk-acorndfs.md) disks they start from 0). The
IBM decoder described here is the generic one, and is suited for 'conventional'
PC disks. While you can read all the variant formats with it if you use the
right set of arguments, it's easier to use the specific decoder.
start from 1, but on Acorn disks they start from 0). The IBM decoder described
here is the generic one, and is suited for 'conventional' PC disks. While you
can read all the variant formats with it if you use the right set of arguments,
it's easier to use the specific decoder.
The generic decoder is mostly self-configuring, and will detect the format of
your disk for you.
There is a generic decoder which should adjust automatically to whichever disk
format you are using, but it's unreliable and not recommended. This format
should also be used if you are writing images such as DIM which specify the
image format. FluxEngine will use these parameters.
## Options
Reading disks
-------------
Just do:
fluxengine read `<format>`
...and you'll end up with a `<format>.img` file. This should work on most PC
disks (including FM 360kB disks, 3.5" 1440kB disks, 5.25" 1200kB disks, etc.)
The size of the disk image will vary depending on the format.
The common PC formats are `ibm720` and `ibm1440`, but there are _many_ others,
and there's too many configuration options to usefully list. Use `fluxengine
write` to list all formats, and try `fluxengine write ibm1440 --config` to see
a sample configuration.
Configuration options you'll want include:
- `--decoder.ibm.sector_id_base=N`: specifies the ID of the first sector;
this defaults to 1. Some formats (like the Acorn ones) start at 0. This
can't be autodetected because FluxEngine can't distinguish between a disk
which starts at sector 1 and a disk which starts at sector 0 but all the
sector 0s are missing.
- `--decoder.ibm.ignore_side_byte=true|false`: each sector header describes
the location of the sector: sector ID, track and side. Some formats use the
wrong side ID, so the sectors on side 1 are labelled as belonging to side
0. This causes FluxEngine to see duplicate sectors (as it can't distinguish
between the two sides). This option tells FluxEngine to ignore the side
byte completely and use the physical side instead.
- `--decoder.ibm.required_sectors=range`: if you know how many sectors to
expect per track, you can improve reads by telling FluxEngine what to
expect here. If a track is read and a sector on this list is _not_ present,
then FluxEngine assumes the read failed and will retry. This avoids the
situation where FluxEngine can't tell the difference between a sector
missing because it's bad or a sector missing because it was never written
in the first place. If sectors are seen outside the range here, it will
still be read. You can use the same syntax as for track specifiers: e.g.
`0-9`, `0,1,2,3`, etc.
Writing disks
-------------
FluxEngine can also write IBM scheme disks. Unfortunately the format is
incredibly flexible and you need to specify every single parameter, which
makes things slightly awkward. Preconfigured profiles are available.
The syntax is:
fluxengine write <format> -i input.img <options>
The common PC formats are `ibm720` and `ibm1440`, but there are _many_ others,
and there's too many configuration options to usefully list. Use `fluxengine
write` to list all formats, and try `fluxengine write ibm1440 --config` to see
a sample configuration.
Some image formats, such as DIM, specify the image format, For these you can
specify the `ibm` format and FluxEngine will automatically determine the
correct format to use.
(no options)
Mixed-format disks
------------------
@@ -131,11 +74,7 @@ drives, feature "tri-mode" support which in addition to normal 300rpm modes,
can change their speed to read and write 360rpm DD and HD disks.
Neither the FluxEngine or Greaseweazle hardware can currently command a
tri-mode drive to spin at 360rpm, however an older 360rpm-only drive will work
to read these formats.
tri-mode drive to spin at 360rpm. However on both devices the FluxEngine
software is capable of both reading and writing 300rpm disks at 360rpm and vice
versa, so it shouldn't matter.
Alternately, the FluxEngine software can rescale the flux pulses to enable
reading and writing these formats with a plain 300rpm drive. To do this,
specify the following two additional options:
--flux_source.rescale=1.2 --flux_sink.rescale=1.2

11
doc/disk-icl30.md Normal file
View File

@@ -0,0 +1,11 @@
<!-- This file is automatically generated. Do not edit. -->
# CP/M; 263kB 35-track DSSD
The ICL Model 30 is a reasonably standard CP/M machine using 35-track single
density disks and the traditional CP/M 128-byte secotrs --- 30 of them per
track! Other than that it's another IBM scheme variation.
## Options
(no options)

59
doc/disk-mac.md Normal file
View File

@@ -0,0 +1,59 @@
<!-- This file is automatically generated. Do not edit. -->
# 400kB/800kB 3.5" GCR
Macintosh disks come in two varieties: the newer 1440kB ones, which are
perfectly ordinary PC disks you should use the `ibm` profile to read them, and
the older 800kB disks (and 400kB for the single sides ones). They have 80
tracks and up to 12 sectors per track.
They are also completely insane.
It's not just the weird, custom GCR encoding. It's not just the utterly
bizarre additional encoding/checksum built on top of that where [every byte
is mutated according to the previous bytes in the
sector](https://www.bigmessowires.com/2011/10/02/crazy-disk-encoding-schemes/).
It's not just the odd way in which disks think they have four sides, two on one
side and two on the other, so that the track byte stores only the bottom 6 bits
of the track number. It's not just the way that Macintosh sectors are 524 bytes
long. No, it's the way the Macintosh drive changes speed depending on which
track it's looking at, so that each track contains a different amount of data.
The reason for this is actually quite sensible: the tracks towards the centre
of the disk are obviously moving more slowly, so you can't pack the bits in
quite as closely (due to limitations in the magnetic media). You can use a
higher bitrate at the edge of the disk than in the middle. Many platforms, for
example the Commodore 64 1541 drive, changed bitrate this way.
But Macintosh disks used a constant bitrate and changed the speed that the disk
spun instead to achieve the same effect...
_Anyway_: FluxEngine will read them fine on conventional drives. Because it's
clever.
Macintosh computers never really used the twelve bytes of metadata and the
standard for disk images is to omit it. If you want them, specify that you want
524-byte sectors. The metadata will follow the 512 bytes of user data.
## Options
- Format variant:
- `400`: 400kB 80-track SSDD
- `800`: 800kB 80-track DSDD
- `metadata`: read/write 524 byte sectors
## References
- [MAME's ap_dsk35.cpp file](https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp),
without which I'd never have managed to do this
- [Crazy Disk Encoding
Schemes](https://www.bigmessowires.com/2011/10/02/crazy-disk-encoding-schemes/), which made
me realise just how nuts the format is
- [Les Disquettes et le drive Disk II](http://www.hackzapple.com/DISKII/DISKIITECH.HTM), an
epicly detailed writeup of the Apple II disk format (which is closely related)
- [The DiskCopy 4.2
format](https://www.discferret.com/wiki/Apple_DiskCopy_4.2), described on
the DiskFerret website.

View File

@@ -1,92 +0,0 @@
Disk: Macintosh
===============
Macintosh disks come in two varieties: the newer 1440kB ones, which are
perfectly ordinary PC disks you should use `fluxengine read ibm` to read, and
the older 800kB disks (and 400kB for the single sides ones). They have 80
tracks and up to 12 sectors per track.
They are also completely insane.
It's not just the weird, custom GCR encoding. It's not just the utterly
bizarre additional encoding/checksum built on top of that where [every byte
is mutated according to the previous bytes in the
sector](https://www.bigmessowires.com/2011/10/02/crazy-disk-encoding-schemes/).
It's not just the odd way in which disks think they have four sides, two on
one side and two on the other, so that the track byte stores only the bottom
6 bits of the track number. It's not just the way that Macintosh sectors are
524 bytes long. No, it's the way the Macintosh drive changes speed depending
on which track it's looking at, so that each track contains a different
amount of data.
The reason for this is actually quite sensible: the tracks towards the centre
of the disk are obviously moving more slowly, so you can't pack the bits in
quite as closely (due to limitations in the magnetic media). You can use a
higher bitrate at the edge of the disk than in the middle. Many platforms,
for example the Commodore 64 1541 drive, changed bitrate this way.
But Macintosh disks used a constant bitrate and changed the speed that the
disk spun instead to achieve the same effect...
_Anyway_: FluxEngine will read them fine on conventional drives.
Because it's clever.
Reading discs
-------------
Just do:
```
fluxengine read <format> -o mac.dsk
```
...where `<format>` can be `mac400` or `mac800`.
You should end up with a `mac.dsk` file containing a raw sector image
(equivalent to `.img`).
**Big warning!** Mac disk images are complicated due to the way the tracks are
different sizes and the odd sector size. What you get above is a triangular
disk image, which contains all the 512-byte user data areas concatenated
together in filesystem order. It does not contain the twelve bytes of metadata.
If you want these as well, specify that you want 524 byte sectors with
`--output.image.img.trackdata.sector_size=512`. The metadata will follow the
512 bytes of user data.
FluxEngine also supports DiskCopy 4.2 disk images, which may be a better option
if you're going to be using the image on a real Macintosh (or with other
tooling which supports it). To get one of these, use a filename like
`mac.diskcopy`. This will contain both user data and metadata.
Writing discs
-------------
Just do:
```
fluxengine write <format> -i mac.dsk
```
...where `<format>` can be `mac400` or `mac800`.
It'll read the image file and write it out.
The same warning as above applies --- you can use normal `.dsk` files but it's
problematic. Consider using DiskCopy 4.2 files instead.
Useful references
-----------------
- [MAME's ap_dsk35.cpp file](https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp),
without which I'd never have managed to do this
- [Crazy Disk Encoding
Schemes](https://www.bigmessowires.com/2011/10/02/crazy-disk-encoding-schemes/), which made
me realise just how nuts the format is
- [Les Disquettes et le drive Disk II](http://www.hackzapple.com/DISKII/DISKIITECH.HTM), an
epicly detailed writeup of the Apple II disk format (which is closely related)
- [The DiskCopy 4.2
format](https://www.discferret.com/wiki/Apple_DiskCopy_4.2), described on
the DiskFerret website.

View File

@@ -1,5 +1,5 @@
Disk: Micropolis
================
<!-- This file is automatically generated. Do not edit. -->
# 100tpi MetaFloppy disks
Micropolis MetaFloppy disks use MFM and hard sectors. Mod I was 48 TPI and
stored 143k per side. Mod II was 100 TPI and stored 315k per side. Each of the
@@ -17,43 +17,6 @@ of the drive and can't be changed in software. You'll need to get hold of a
100tpi Micropolis drive. Luckily these seem to use the same connector and
pinout as a 96tpi PC 5.25" drive. In use they should be identical.
Reading disks
-------------
Based on your floppy drive, just do one of:
```
fluxengine read micropolis143 # single-sided Mod I
fluxengine read micropolis287 # double-sided Mod I
fluxengine read micropolis315 # single-sided Mod II
fluxengine read micropolis630 # double-sided Mod II
```
You should end up with a `micropolis.img` of the corresponding size. The image
is written in CHS order, but HCS is generally used by CP/M tools so
double-sided disk images may need to be post-processed. Half-full double-sided
disks can be read as single-sided disks to work around the problem.
The [CP/M BIOS](https://www.seasip.info/Cpm/bios.html) defined SELDSK, SETTRK,
and SETSEC, but no function to select the head/side. Double-sided floppies
could be represented as having either twice the number of sectors, for CHS, or
twice the number of tracks, HCS; the second side's tracks in opposite order
logically followed the first side (e.g., tracks 77-153). Micropolis disks
tended to be the latter.
It's also possible to output to VGI, which retains OS-specific "user data" and
machine-specific ECC. Add `--vgi` to the command line after the chosen
Micropolis profile:
```
fluxengine read micropolis143 --vgi # single-sided Mod I
fluxengine read micropolis287 --vgi # double-sided Mod I
fluxengine read micropolis315 --vgi # single-sided Mod II
fluxengine read micropolis630 --vgi # double-sided Mod II
```
You should end up with a `micropolis.vgi` instead. The format is well-defined
for double-sided disks so post-processing is not necessary.
While most operating systems use the standard Micropolis checksum, Vector
Graphic MZOS uses a unique checksum. The decoder will automatically detect
the checksum type in use; however, a specific checksum type may be forced
@@ -65,26 +28,24 @@ using the `--decoder.micropolis.checksum_type=n` where the type is one of:
| 1 | Standard Micropolis (MDOS, CP/M, OASIS) |
| 2 | Vector Graphic MZOS |
The [CP/M BIOS](https://www.seasip.info/Cpm/bios.html) defined SELDSK, SETTRK,
and SETSEC, but no function to select the head/side. Double-sided floppies
could be represented as having either twice the number of sectors, for CHS, or
twice the number of tracks, HCS; the second side's tracks in opposite order
logically followed the first side (e.g., tracks 77-153). Micropolis disks
tended to be the latter. FluxEngine always emits CHS format disks, so you may
need to apply extra options to change the format if desired.
Writing disks
-------------
## Options
Just do one of:
- :
- `143`: 143kB 5.25" SSDD hard-sectored; Micropolis MetaFloppy Mod I
- `287`: 287kB 5.25" DSDD hard-sectored; Micropolis MetaFloppy Mod I
- `315`: 315kB 5.25" SSDD hard-sectored; Micropolis MetaFloppy Mod II
- `630`: 630kB 5.25" DSDD hard-sectored; Micropolis MetaFloppy Mod II
- `vgi`: Read/write VGI format images with 275 bytes per sector
```
fluxengine write micropolis143 # single-sided Mod I
fluxengine write micropolis287 # double-sided Mod I
fluxengine write micropolis315 # single-sided Mod II
fluxengine write micropolis630 # double-sided Mod II
fluxengine write micropolis143 --vgi # single-sided Mod I
fluxengine write micropolis287 --vgi # double-sided Mod I
fluxengine write micropolis315 --vgi # single-sided Mod II
fluxengine write micropolis630 --vgi # double-sided Mod II
```
Useful references
-----------------
## References
- [Micropolis 1040/1050 S-100 Floppy Disk Subsystems User's Manual][micropolis1040/1050].
Section 6, pages 261-266. Documents pre-ECC sector format. Note that the
@@ -99,3 +60,4 @@ Useful references
[micropolis1040/1050]: http://www.bitsavers.org/pdf/micropolis/metafloppy/1084-01_1040_1050_Users_Manual_Apr79.pdf
[vectordualmode]: http://bitsavers.org/pdf/vectorGraphic/hardware/7200-1200-02-1_Dual-Mode_Disk_Controller_Board_Engineering_Documentation_Feb81.pdf
[altairz80]: http://www.bitsavers.org/simh.trailing-edge.com_201206/pdf/altairz80_doc.pdf

View File

@@ -1,15 +1,15 @@
Disk: DVK MX
============
<!-- This file is automatically generated. Do not edit. -->
# Soviet-era PDP-11 clone
The DVK (in Russian, ДВК, Диалоговый вычислительный комплекс or Dialogue
The DVK (in Russian, 沾7d65
Computing Complex) was a late 1970s Soviet personal computer, a cut-down
version of the professional SM EVM (СМ ЭВМ, abbreviation of Система Малых ЭВМ
version of the professional SM EVM (ⵁ241c
--- literally System of Mini Computers), which _itself_ was an unlicensed
clone of the PDP-11. The MX board was an early floppy drive controller board
for it.
<div style="text-align: center">
<a href="http://www.leningrad.su/museum/show_big.php?n=1006"><img src="dvk3m.jpg" style="max-width: 60%" alt="A Durango F85, held precariously"></a>
<a href="http://www.leningrad.su/museum/show_big.php?n=1006"><img src="dvk3m.jpg" style="max-width: 60%" alt="A DVK computer"></a>
</div>
The MX format is interesting in that it has to be read a track at a time. The
@@ -38,23 +38,11 @@ A track is:
The checksum is just the unsigned integer sum of all the words in the sector.
Words are all stored little-endian.
Reading discs
-------------
## Options
```
fluxengine read mx440
```
(no options)
You should end up with an `mx.img` which will vary in length depending on the format. The default is double-sided 80-track. For the other formats, use:
* single-sided 40-track: `mx110`
* double-sided 40-track: `mx220_ds`
* single-sided 80-track: `mx220_ss`
* double-sided 80-track: `mx440`
Useful references
-----------------
## References
- [The Soviet Digital Electronics
Museum](http://www.leningrad.su/museum/main.php) (source of the image
@@ -63,3 +51,4 @@ Useful references
- [a random post on the HxC2001 support
forum](http://torlus.com/floppy/forum/viewtopic.php?t=1384) with lots of
information on the format

14
doc/disk-n88basic.md Normal file
View File

@@ -0,0 +1,14 @@
<!-- This file is automatically generated. Do not edit. -->
# PC8800/PC98 5.25"/3.5" 77-track 26-sector DSHD
The N88-BASIC disk format is the one used by the operating system of the same
name for the Japanese PC8800 and PC98 computers. It is another IBM scheme
variant, and is very similar to some mixed-format CP/M disk formats, where
track 0 side 0 uses 128-byte single density sectors and the rest of the disk
uses 512-byte double density sectors. (The reason for this is that the PC8800
boot ROM could only read single density data.)
## Options
(no options)

View File

@@ -1,5 +1,5 @@
Disk: Northstar
================
<!-- This file is automatically generated. Do not edit. -->
# 5.25" hard sectored
Northstar 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
@@ -18,51 +18,14 @@ writer are provided for double-sided disks. The .nsi image writer supports
both single- and double-sided disks; however single-sided .nsi images are
equivalent to .img images.
Reading disks
-------------
## Options
You must use a 48-TPI (40-track) 300RPM 5.25" floppy drive.
(no options)
To read a double-sided North Star floppy, run:
```
fluxengine read <format>
```
...where `<format>` is one of the formats listed below.
You should end up with a `northstar.nsi` with a file size dependent on the floppy
disk type.
Writing disks
-------------
You must use a 48-TPI (40-track) 300RPM 5.25" floppy drive and make
sure that the drive's spindle speed is adjusted to exactly 300RPM.
To write a double-sided North Star floppy, run:
```
fluxengine write <format> -i image_to_write.nsi
```
...where `<format>` is one of the formats listed below.
Available formats
-----------------
The following formats are supported:
| Format name | Disk Type | File Size (bytes) |
| -------------- | ----------------------------------- | ----------------- |
| `northstar87` | Single-Sided, Single-Density (SSSD) | 89,600 |
| `northstar175` | Single-Sided, Double-Density (SSDD) | 179,200 |
| `northstar350` | Double-Sided, Double-Density (DSDD) | 358,400 |
Useful references
-----------------
## References
- [MICRO-DISK SYSTEM MDS-A-D DOUBLE DENSITY Manual][northstar_mds].
Page 33 documents sector format for single- and double-density.
[northstar_mds]: http://bitsavers.org/pdf/northstar/boards/Northstar_MDS-A-D_1978.pdf

20
doc/disk-psos.md Normal file
View File

@@ -0,0 +1,20 @@
<!-- This file is automatically generated. Do not edit. -->
# 800kB DSDD with PHILE
pSOS was an influential real-time operating system from the 1980s, used mainly
on 68000-based machines, lasting up until about 2000 when it was bought (and
cancelled) by Wind River. It had its own floppy disk format and file system,
both of which are partially supported here.
The PHILE file system is almost completely undocumented and so many of the data
structures have had to be reverse engineered and are not well known. Please
[get in touch](https://github.com/davidgiven/fluxengine/issues/new) if you know
anything about it.
The floppy disk format itself is an IBM scheme variation with 1024-byte sectors
and, oddly, swapped sides.
## Options
(no options)

20
doc/disk-rolandd20.md Normal file
View File

@@ -0,0 +1,20 @@
<!-- This file is automatically generated. Do not edit. -->
# 3.5" electronic synthesiser disks
The Roland D20 is a classic electronic synthesiser with a built-in floppy
drive, used for saving MIDI sequences and samples.
Weirdly, it seems to use precisely the same format as the Brother word
processors: a thoroughly non-IBM-compatible custom GCR system.
FluxEngine pretends to support this, but it has had almost no testing, the only
disk image I have seen for it was mostly corrupt, and very little is known
about the format, so I have no idea whether it's correct or not.
Please [get in touch](https://github.com/davidgiven/fluxengine/issues/new) if
you know anything about it.
## Options
(no options)

11
doc/disk-rx50.md Normal file
View File

@@ -0,0 +1,11 @@
<!-- This file is automatically generated. Do not edit. -->
# 400kB 5.25" 80-track 10-sector SSDD
The Digital RX50 is one of the external floppy drive units used by Digital's
range of computers, especially the DEC Rainbow microcomputer. It is a fairly
vanilla single-sided IBM scheme variation.
## Options
(no options)

View File

@@ -0,0 +1,4 @@
<!-- This file is automatically generated. Do not edit. -->
# Adjust configuration for a Shugart drive
(This format has no documentation. Please file a bug.)

View File

@@ -1,5 +1,5 @@
Disk: Smaky 6
=============
<!-- This file is automatically generated. Do not edit. -->
# 308kB 5.25" 77-track 16-sector SSDD, hard sectored
The Smaky 6 is a Swiss computer from 1978 produced by Epsitec. It's based
around a Z80 processor and has one or two Micropolis 5.25" drives which use
@@ -12,32 +12,15 @@ FluxEngine supports these, although because the Micropolis drives use a 100tpi
track pitch, you can't read Smaky 6 disks with a normal PC 96tpi drive. You
will have to find a 100tpi drive from somewhere (they're rare).
Reading disks
-------------
You must use a 100-tpi 80-track 5.25" floppy drive.
To read a Smaky 6 floppy, do:
```
fluxengine read smaky6
```
You should end up with a `smaky6.img` file.
Filesystem access
-----------------
There is experimental read-only support for the Smaky 6 filesystem, allowing
the directory to be listed and files read from disks. It's not known whether
this is completely correct, so don't trust it! See the [Filesystem
Access](filesystem.md) page for more information.
this is completely correct, so don't trust it!
## Options
Useful references
-----------------
(no options)
## References
- [Smaky Info, 1978-2002 (in French)](https://www.smaky.ch/theme.php?id=sminfo)

View File

@@ -1,5 +1,5 @@
Disk: TI DS990 FD1000
=====================
<!-- This file is automatically generated. Do not edit. -->
# 1126kB 8" DSSD
The Texas Instruments DS990 was a multiuser modular computing system from 1998,
based around the TMS-9900 processor (as used by the TI-99). It had an 8" floppy
@@ -16,31 +16,12 @@ href="https://www.old-computers.com/museum/computer.asp?st=1&c=1025">old-compute
FluxEngine will read and write these (but only the DSDD MFM variant).
Reading discs
-------------
## Options
Just do:
(no options)
```
fluxengine read tids990
```
You should end up with an `tids990.img` which is 1153152 bytes long.
Writing discs
-------------
Just do:
```
fluxengine write tids990 -i tids990.img
```
Useful references
-----------------
## References
- [The FD1000 Depot Maintenance
Manual](http://www.bitsavers.org/pdf/ti/990/disk/2261885-9701_FD1000depotVo1_Jan81.pdf)
Manual](http://www.bitsavers.org/pdf/ti/990/disk/2261885-9701_FD1000depotVo1_Jan81.pdf)

12
doc/disk-tiki.md Normal file
View File

@@ -0,0 +1,12 @@
<!-- This file is automatically generated. Do not edit. -->
# CP/M
The Tiki 100 is a Z80-based Norwegian microcomputer from the mid 1980s intended
for eductional use. It mostly ran an unbranded CP/M clone, and uses fairly
normal CP/M disks --- IBM scheme and from 128 to 512 bytes per sector depending
on the precise format.
## Options
(no options)

View File

@@ -1,42 +0,0 @@
Disk: TRS-80
============
The TRS-80 models I, III and IV (but not the II, 100, 2000, Colour Computer
or Pocket Computer) was a popular line of Z80-based home computers made by
Tandy Corporation and sold by Radio Shack. There were some of the first
generation of domestic micromputers, with the Model I released in 1978.
There were a myriad of different floppy disk interfaces, some produced by
Tandy and some by third parties, using all the various combinations of 40-
and 80-track, FM, MFM, etc.
Luckily the encoding scheme was mostly compatible with the IBM scheme, with a
few minor variations: when using FM encoding, the TRS-80 wrote the sectors on
track 17 (where the directory was) with a non-standard DAM byte.
FluxEngine's IBM reader can handle TRS-80 disks natively.
Reading discs
-------------
Just do:
```
fluxengine read ibm -o trs80.jv3
```
You should end up with an `trs80.jv3` of the appropriate size. It's a simple
array of sectors in JV3 format.
If you've got a 40-track disk, use `--cylinders=0-79x2`.
If you've got a single density disk, use
`--decoder.ibm.trackdata.read_fm=true`. (Double density is the default.)
Useful references
-----------------
- [The JV3 file format](https://www.tim-mann.org/trs80/dskspec.html):
documents the most popular emulator disk image.

View File

@@ -1,5 +1,5 @@
Disk: Victor 9000
=================
<!-- This file is automatically generated. Do not edit. -->
# 1224kB 5.25" DSDD GCR
The Victor 9000 / Sirius One was a rather strange old 8086-based machine
which used a disk format very reminiscent of the Commodore format; not a
@@ -8,7 +8,7 @@ sector GCR disks, with a variable-speed drive and a varying number of sectors
per track --- from 19 to 12. Disks can be double-sided, meaning that they can
store 1224kB per disk, which was almost unheard of back then. Because the way
that the tracks on head 1 are offset from head 0 (this happens with all disks),
the speed zone allocation on head 1 differ from head 0...
the speed zone allocation on head 1 differs from head 0...
| Zone | Head 0 tracks | Head 1 tracks | Sectors | Original period (ms) |
|:----:|:-------------:|:-------------:|:-------:|:--------------------:|
@@ -34,39 +34,11 @@ and track 39 on Head 1.
FluxEngine can read and write both the single-sided and double-sided variants.
Reading discs
-------------
## Options
Just do:
(no options)
```
fluxengine read <format>
```
...where `<format>` can be `victor9k_ss` or `victor9k_ds`.
For `victor9k_ss` you should end up with an `victor9k.img` which is 627200 bytes long.
For `victor9k_ds` you should end up with an `victor9k.img` which is 1224192 bytes long.
**Big warning!** The image is triangular, where each track occupies a different
amount of space. Victor disk images are complicated due to the way the tracks
are different sizes and the odd sector size.
Writing discs
-------------
Just do:
```
fluxengine read victor9k_ss -i victor9k.img
```
**Big warning!** This uses the same triangular disk image that reading uses.
Useful references
-----------------
## References
- [The Victor 9000 technical reference manual](http://bitsavers.org/pdf/victor/victor9000/Victor9000TechRef_Jun82.pdf)

View File

@@ -1,5 +1,5 @@
Disk: Zilog MCZ
===============
<!-- This file is automatically generated. Do not edit. -->
# 320kB 8" 77-track SSSD hard-sectored
The Zilog MCZ is an extremely early Z80 development system, produced by
Zilog, which came out in 1976. It used twin 8-inch hard sectored floppy
@@ -24,22 +24,14 @@ I haven't been able to try this for real. If anyone has any of these disks,
an 8-inch drive, a FluxEngine and the appropriate adapter, please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new)...
Reading discs
-------------
## Options
Just do:
(no options)
```
fluxengine read zilogmcz
```
You should end up with an `zilogmcz.img` which is 315392 bytes long.
Useful references
-----------------
## References
* [About the Zilog MCZ](http://www.retrotechnology.com/restore/zilog.html),
containing lots of useful links
* [The hardware user's manual](https://amaus.org/static/S100/zilog/ZDS/Zilog%20ZDS%201-25%20Hardware%20Users%20Manual.pdf)

View File

@@ -155,6 +155,14 @@ more common tools.
encoding. You can specify a profile if you want to write a subset of the
disk.
- `fluxengine merge -s <fluxfile> -s <fluxfile...> -d <fluxfile`
Merges data from multiple flux files together. This is useful if you have
several reads from an unreliable disk where each read has a different set
of good sectors. By merging the flux files, you get to combine all the
data. Don't use this on reads of different disks, for obvious results! Note
that this works on flux files, not on flux sources.
- `fluxengine inspect -s <flux source> -c <cylinder> -h <head> -B`
Reads flux (possibly from a disk) and does various analyses of it to try

View File

@@ -1,6 +1,5 @@
#!/bin/sh
dir=`dirname "$0"`
cd "$dir"
export DYLD_FALLBACK_FRAMEWORK_PATH=../Resources
export DYLD_FALLBACK_LIBRARY_PATH=../Resources:/opt/local/lib
exec ./fluxengine-gui "$@"

BIN
extras/fluxfile.piko Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
extras/fluxfile.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

BIN
extras/hardware.piko Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
extras/hardware.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

BIN
extras/imagefile.piko Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
extras/imagefile.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

View File

@@ -9,6 +9,7 @@ LIBFLUXENGINE_SRCS = \
lib/decoders/fmmfm.cc \
lib/encoders/encoders.cc \
lib/environment.cc \
lib/fl2.cc \
lib/flags.cc \
lib/fluxmap.cc \
lib/fluxsink/a2rfluxsink.cc \
@@ -18,6 +19,7 @@ LIBFLUXENGINE_SRCS = \
lib/fluxsink/hardwarefluxsink.cc \
lib/fluxsink/scpfluxsink.cc \
lib/fluxsink/vcdfluxsink.cc \
lib/fluxsource/a2rfluxsource.cc \
lib/fluxsource/cwffluxsource.cc \
lib/fluxsource/erasefluxsource.cc \
lib/fluxsource/fl2fluxsource.cc \
@@ -69,14 +71,17 @@ LIBFLUXENGINE_SRCS = \
lib/utils.cc \
lib/vfs/acorndfs.cc \
lib/vfs/amigaffs.cc \
lib/vfs/appledos.cc \
lib/vfs/applesingle.cc \
lib/vfs/brother120fs.cc \
lib/vfs/cbmfs.cc \
lib/vfs/cpmfs.cc \
lib/vfs/fatfs.cc \
lib/vfs/lif.cc \
lib/vfs/machfs.cc \
lib/vfs/prodos.cc \
lib/vfs/smaky6fs.cc \
lib/vfs/philefs.cc \
lib/vfs/vfs.cc \
lib/vfs/fluxsectorinterface.cc \
lib/vfs/imagesectorinterface.cc \

View File

@@ -47,6 +47,12 @@ Bytes::Bytes(const std::string& s):
_high(s.size())
{}
Bytes::Bytes(const char* s):
_data(createVector((const uint8_t*)s, strlen(s))),
_low(0),
_high(strlen(s))
{}
Bytes::Bytes(std::initializer_list<uint8_t> data):
_data(createVector(data)),
_low(0),
@@ -215,6 +221,24 @@ Bytes Bytes::reverseBits() const
return output;
}
Bytes Bytes::operator + (const Bytes& other)
{
Bytes output;
ByteWriter bw(output);
bw += *this;
bw += other;
return output;
}
Bytes Bytes::operator * (size_t count)
{
Bytes output;
ByteWriter bw(output);
while (count--)
bw += *this;
return output;
}
uint8_t toByte(
std::vector<bool>::const_iterator start,
std::vector<bool>::const_iterator end)

View File

@@ -12,6 +12,7 @@ public:
Bytes();
Bytes(unsigned size);
Bytes(const uint8_t* ptr, size_t len);
Bytes(const char* data);
Bytes(const std::string& data);
Bytes(std::initializer_list<uint8_t> data);
Bytes(std::shared_ptr<std::vector<uint8_t>> data);
@@ -64,6 +65,9 @@ public:
std::vector<bool> toBits() const;
Bytes reverseBits() const;
Bytes operator + (const Bytes& other);
Bytes operator * (size_t count);
ByteReader reader() const;
ByteWriter writer();

View File

@@ -12,38 +12,64 @@ import "lib/drive.proto";
import "lib/common.proto";
import "lib/layout.proto";
// NEXT_TAG: 21
message ConfigProto {
optional string comment = 8;
optional bool is_extension = 13;
repeated string include = 19;
optional LayoutProto layout = 18;
optional ImageReaderProto image_reader = 12;
optional ImageWriterProto image_writer = 9;
optional FluxSourceProto flux_source = 10;
optional FluxSinkProto flux_sink = 11;
optional DriveProto drive = 15;
optional EncoderProto encoder = 3;
optional DecoderProto decoder = 4;
optional UsbProto usb = 5;
optional RangeProto tracks = 6;
optional RangeProto heads = 7;
optional int32 tpi = 16 [ (help) = "TPI of image; if 0, use TPI of drive" ];
optional FilesystemProto filesystem = 17;
repeated OptionProto option = 20;
enum SupportStatus
{
UNSUPPORTED = 0;
DINOSAUR = 1;
UNICORN = 2;
}
message OptionProto {
optional string name = 1 [(help) = "Option name" ];
optional string comment = 2 [(help) = "Help text for option" ];
optional string message = 3 [(help) = "Message to display when option is in use" ];
optional ConfigProto config = 4 [(help) = "Option data", (recurse) = false ];
// NEXT_TAG: 27
message ConfigProto
{
optional string shortname = 24;
optional string comment = 8;
optional bool is_extension = 13;
repeated string documentation = 23;
optional SupportStatus read_support_status = 25 [ default = UNSUPPORTED ];
optional SupportStatus write_support_status = 26 [ default = UNSUPPORTED ];
optional LayoutProto layout = 18;
optional ImageReaderProto image_reader = 12;
optional ImageWriterProto image_writer = 9;
optional FluxSourceProto flux_source = 10;
optional FluxSinkProto flux_sink = 11;
optional DriveProto drive = 15;
optional EncoderProto encoder = 3;
optional DecoderProto decoder = 4;
optional UsbProto usb = 5;
optional RangeProto tracks = 6;
optional RangeProto heads = 7;
optional float tpi = 16 [ (help) = "TPI of image; if 0, use TPI of drive" ];
optional FilesystemProto filesystem = 17;
repeated OptionProto option = 20;
repeated OptionGroupProto option_group = 22;
}
// NEXT_TAG: 7
message OptionProto
{
optional string name = 1 [ (help) = "option name" ];
optional string comment = 2 [ (help) = "help text for option" ];
optional string message = 3
[ (help) = "message to display when option is in use" ];
optional bool set_by_default = 6 [
(help) = "this option is applied by default",
default = false
];
optional ConfigProto config = 4
[ (help) = "option data", (recurse) = false ];
}
message OptionGroupProto
{
optional string comment = 1 [ (help) = "help text for option group" ];
repeated OptionProto option = 2;
}

View File

@@ -16,6 +16,7 @@
#include "arch/micropolis/micropolis.h"
#include "arch/mx/mx.h"
#include "arch/northstar/northstar.h"
#include "arch/rolandd20/rolandd20.h"
#include "arch/smaky6/smaky6.h"
#include "arch/tids990/tids990.h"
#include "arch/victor9k/victor9k.h"
@@ -49,6 +50,7 @@ std::unique_ptr<Decoder> Decoder::create(const DecoderProto& config)
{DecoderProto::kMicropolis, createMicropolisDecoder },
{DecoderProto::kMx, createMxDecoder },
{DecoderProto::kNorthstar, createNorthstarDecoder },
{DecoderProto::kRolandd20, createRolandD20Decoder },
{DecoderProto::kSmaky6, createSmaky6Decoder },
{DecoderProto::kTids990, createTids990Decoder },
{DecoderProto::kVictor9K, createVictor9kDecoder },

View File

@@ -13,6 +13,7 @@ import "arch/macintosh/macintosh.proto";
import "arch/micropolis/micropolis.proto";
import "arch/mx/mx.proto";
import "arch/northstar/northstar.proto";
import "arch/rolandd20/rolandd20.proto";
import "arch/smaky6/smaky6.proto";
import "arch/tids990/tids990.proto";
import "arch/victor9k/victor9k.proto";
@@ -20,7 +21,7 @@ import "arch/zilogmcz/zilogmcz.proto";
import "lib/fluxsink/fluxsink.proto";
import "lib/common.proto";
//NEXT: 31
//NEXT: 32
message DecoderProto {
optional double pulse_debounce_threshold = 1 [default = 0.30,
(help) = "ignore pulses with intervals shorter than this, in fractions of a clock"];
@@ -47,6 +48,7 @@ message DecoderProto {
MicropolisDecoderProto micropolis = 14;
MxDecoderProto mx = 15;
NorthstarDecoderProto northstar = 24;
RolandD20DecoderProto rolandd20 = 31;
Smaky6DecoderProto smaky6 = 30;
Tids990DecoderProto tids990 = 16;
Victor9kDecoderProto victor9k = 17;

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