Compare commits

...

247 Commits

Author SHA1 Message Date
David Given
8dbd2a72a7 Merge pull request #150 from davidgiven/sixbit
Fix the new sampler and sequencer
2020-04-03 23:54:29 +02:00
David Given
c29e131a3b Convert the IBM format back into a unicorn now I've fixed it. 2020-04-03 23:49:01 +02:00
David Given
a9e30c1e49 Fix an off-by-one error in the sequencer that should have it generating correct
sequences.
2020-04-03 22:58:51 +02:00
David Given
972c8c6b61 Fix off-by-one sampler error, so now the clock rates are right again. 2020-04-03 22:27:33 +02:00
David Given
2007ff7546 Fix merge wibble. 2020-04-03 21:49:26 +02:00
David Given
64694580cd Remember to bump the protocol number after the bytecode change. 2020-04-03 21:46:51 +02:00
David Given
deaab94494 Merge pull request #146 from davidgiven/sixbit
Switch to a simplified encoding with a six-bit timer.
2020-04-03 00:45:21 +02:00
David Given
1509e1f89d Document the new bytecode format. 2020-04-03 00:38:20 +02:00
David Given
29e1ddc2ff Bytecode upgrades always produce the latest version; we don't want to upgrade
1->2->3 (although that specific case is harmless, by accident).
2020-04-03 00:36:55 +02:00
David Given
1fe6434563 Something is wrong with the IBM PC writer, I don't know what. Mark it as a
dinosaur again.
2020-04-03 00:27:12 +02:00
David Given
0367b7e77d Merge from trunk. 2020-04-01 00:06:35 +02:00
David Given
e6da85bf64 Merge pull request #145 from davidgiven/writereport
Add a machine readable read report.
2020-04-01 00:04:54 +02:00
David Given
cd19fcdadd The CSV report now writes records for every sector in the output map, even the
missing ones.
2020-03-31 00:14:23 +02:00
David Given
1954f02cfb Don't reset the device on startup because it confuses Linux. 2020-03-30 22:23:18 +01:00
David Given
39b23200b0 Fix missing flag dependency. 2020-03-29 23:12:12 +02:00
David Given
0644d6d965 Remove some stray tracing (which was causing problems). Fix a potential problem
where sampleclock posedges could be lost in the sequencer.
2020-03-29 23:11:53 +02:00
David Given
a075694d8e Rewrite the sequencer to work with the new six bit bytecode. Fiddle with the
USB stuff a lot in an attempt to resolve the weird packet loss issue.
2020-03-29 15:10:35 +02:00
David Given
b1ea5a9a35 Rework the writer to use a fluxsink rather than just writing stuff directly. 2020-03-29 15:08:45 +02:00
David Given
00087cbb6b Fix a catastrophic DMA setup bug which was causing (probably) every other byte
of data to be mixed up with every other byte... but as every other byte was a
0x80 we never noticed up until now.
2020-03-20 00:06:58 +01:00
David Given
1b48ea20c4 Remove the cruncher. 2020-03-20 00:06:07 +01:00
David Given
3d0f019fc4 Replace the sampler with one using the new simplified bytecode. 2020-03-19 23:39:23 +01:00
David Given
a08bfc183f Display the tick value along with each interval in the histogram. 2020-03-19 22:16:07 +01:00
David Given
c5aef9b051 Annotate inspect to display USB bandwidth. 2020-03-15 13:47:17 +00:00
David Given
fc2655ecd6 Rework the bytecode format to use a much simplified setup: a six-bit timer with
the top two bits reserved for pulse and index state. This is actually smaller,
bandwidth-wise, than the old version, and may be smaller than the crunched
version.
2020-03-14 18:58:43 +00:00
David Given
a737c723d3 Make sure to update before installing packages. 2020-03-14 14:47:04 +00:00
David Given
37aa8b62b0 Add a --write-csv=X option to the reader to dump the sector status map as a
machine-readable file.
2020-03-14 14:35:19 +00:00
David Given
a401173f6d Teach the Amiga decoder how many sectors to expect on each track. 2020-03-09 12:54:29 +00:00
David Given
ce76dc4279 Merge pull request #140 from davidgiven/sectors
Fix a couple of annoying but minor issues
2020-02-28 00:08:11 +01:00
David Given
1025bd857b Don't crashloop if the USB's not connected, as it causes the drives to be
constantly reprobed (which runs the motor).
2020-02-27 22:32:27 +01:00
David Given
025802b2d0 Count required sectors correctly on mac disks. 2020-02-27 22:30:05 +01:00
David Given
adbcb2cd31 Merge pull request #139 from davidgiven/sectors
Add support for required sectors, drive autodetection and fix homing on 8" drives.
2020-02-24 21:55:27 +01:00
David Given
c47a563790 Don't seek to track -1 on homing (it appears to upset 8" drives). Detect which
drives are present, so that if only a single drive is attached then it's always
track 0, regardless of which connector it's on.
2020-02-24 21:47:40 +01:00
David Given
04c09d1a5b Hopefully, fix the problem where ^Cing a job gets the board in a confused
state.
2020-02-21 22:16:20 +01:00
David Given
323da8272a Hopefully add support for giving the reader a set of required sectors, so if
one is missing then we can tell and the track can be reread.
2020-02-21 22:14:44 +01:00
David Given
38700c79fc Merge pull request #137 from davidgiven/docs
Update documentation.
2020-02-20 00:32:19 +01:00
David Given
d504d1890a Remember to document that you need a FDD cable. 2020-02-20 00:30:17 +01:00
David Given
d53e757cfb Rework some of the Brother documentation. 2020-02-20 00:25:55 +01:00
David Given
4983239458 Rework the hardware and software documentation. 2020-02-20 00:17:16 +01:00
David Given
376985828a Add the ditaa pinout table. 2020-02-19 23:49:43 +01:00
David Given
dce0a26820 Merge pull request #136 from davidgiven/ibm
Finally add write support for IBM disks.
2020-02-18 23:20:57 +01:00
David Given
14e0a67e7d IBM write support gets the much vaunted unicorn. 2020-02-18 23:10:09 +01:00
David Given
1656947764 Disable write precompensation, as it actually seems to make things worse. 2020-02-18 22:49:12 +01:00
David Given
647862cdbd Update the firmware for the new cruncher. 2020-02-18 22:13:41 +01:00
David Given
4a8d83838c Add IBM write support documentation. 2020-02-18 22:12:56 +01:00
David Given
8acf8e181d Rework the crunch encoding to be, hopefully, smaller and use less bandwidth for
HD disks.
2020-02-18 21:37:32 +01:00
David Given
2df9920209 Rename test bulktransport to test bandwidth; avoid watchdog failures while the
bandwidth test is running.
2020-02-18 19:12:52 +01:00
David Given
1a6c6b5420 The bandwidth tester now tests bandwidth in both directions. It looks like my
default USB port only gets about 500kB/s write bandwidth. However, when plugged
into a port with 850kB/s, I still get underrun errors...
2020-02-17 23:58:40 +01:00
David Given
edc56d44d6 Non-functioning archive checkin: You can only have 120-odd DMA buffers, so my
last 'fix' was in fact non-functional.
2020-02-17 23:28:40 +01:00
David Given
ef4eff0195 So writing now works, but only if USB DMA is enabled. But that breaks reading.
I've worked round this in a simple but brute force manner and it now looks as
if reading *and* writing work, more or less. There does still seem to be the
odd bad sector when writing 1440kB disks.
2020-02-17 21:41:01 +01:00
David Given
df8d45bf66 Rework the output fifo to be a bit more correct about the sync signals, which
in turn allows the sequencer to lose less time --- this gets the effective
clock rate down to about 1.01us. However we still seem to lose the last sector
on 18-sector disks and there are some disk reads so something is still wrong.
2020-02-17 00:13:13 +01:00
David Given
89a27619ff Correctly propagate MFM encoding state. 2020-02-15 21:47:01 +01:00
David Given
387a86969a Some verilog optimisations which shave off a few p-terms. 2020-02-15 12:15:51 +01:00
David Given
acb5059d17 Rewrite the sampler *again* to, hopefully, be more stable and not lose ticks.
Luckily, we have just enough space in the FPGA to use an actual logic counter,
which simplifies things hugely.
2020-02-15 12:09:19 +01:00
David Given
a4002d2617 Fix a few encoder issues; but while 720kB disks work fine in a real machine,
1440kB disks don't.
2020-02-14 22:47:15 +01:00
David Given
a63a90bbd0 Only attempt to remove the old file on overwrite if it actually exists. 2020-02-14 22:30:46 +01:00
David Given
d25f96dd24 The IBM encoder now works well enough that my USB floppy drive thinks the disk
is formatted... but has trouble reading sectors above about 1000. Also remember
that encoding MFM needs to keep one bit of state at all times.
2020-02-14 00:06:51 +01:00
David Given
e8febe6508 Allow converting Bytes to bits, and slicing until the end of an object. 2020-02-14 00:04:31 +01:00
David Given
ad3a930c6a Do the boilerplate of the IBM encoder. 2020-02-09 23:56:20 +01:00
David Given
be41c1de76 You now need to explicitly specify whether flux files get merged or
overwritten, because the old behaviour was weird. Reader uses SqliteFluxSink to
write flux files, rather than raw database accesses.
2020-02-08 12:41:50 +01:00
David Given
d528978667 Merge pull request #135 from davidgiven/docs
Update docs.
2020-02-08 11:53:31 +01:00
David Given
827fcf69d2 Another typo fix. 2020-02-08 11:52:39 +01:00
David Given
711ff545e0 Typo fix. 2020-02-08 11:51:19 +01:00
David Given
5befa31050 Document the GreaseWeazle. 2020-02-08 11:50:09 +01:00
David Given
8e5c2d0ebb Merge from trunk. 2020-02-08 11:35:31 +01:00
David Given
f95fceeb3d Finally document IBM disks. 2020-02-08 11:34:57 +01:00
David Given
003b20dbf0 Merge pull request #133 from davidgiven/fixing
Display RPM values correctly
2020-01-29 00:52:04 +01:00
David Given
cd9bbaa4b6 Merge from trunk. 2020-01-29 00:42:06 +01:00
David Given
71e622bf72 Fix the fe-rpm RPM display, which didn't get updated when we switched to
nanoseconds internally.

Fixes #132
2020-01-29 00:41:58 +01:00
David Given
2a065a08df Remember to ship brother240tool with Windows. 2020-01-28 00:45:04 +01:00
David Given
6087228378 Merge pull request #131 from davidgiven/fixing
Debug builds are now debuggable.
2020-01-28 00:40:39 +01:00
David Given
efd74e0d7b Stop trying to make Github CI on Windows work. It just doesn't seem to be
running mingw via ninja.
2020-01-28 00:33:30 +01:00
David Given
b68a9dcc4f Looks like there is no pacman.exe in the image, so we have to go with the
additional download.
2020-01-28 00:30:48 +01:00
David Given
008855daa9 ...more... 2020-01-28 00:27:21 +01:00
David Given
7a9d36de2a ... 2020-01-28 00:23:59 +01:00
David Given
c56e982c9a ... 2020-01-28 00:22:18 +01:00
David Given
002cc171a2 More sigh. 2020-01-28 00:19:39 +01:00
David Given
32e721b47a Sigh. 2020-01-28 00:15:31 +01:00
David Given
1e82f697a9 Temporarily disable AppVeyor to prevent spurious builds. 2020-01-28 00:14:45 +01:00
David Given
fa09631e32 More exploration. 2020-01-28 00:13:38 +01:00
David Given
e06436ce1e Okay, that's not working. What's available? 2020-01-28 00:05:23 +01:00
David Given
b2f443e1ad Let's try this, but with the right path. 2020-01-27 23:57:48 +01:00
David Given
2e07be0cf7 Let's try this instead. 2020-01-27 23:56:55 +01:00
David Given
bf0b14d094 Okay, changing the path didn't help. 2020-01-27 23:51:22 +01:00
David Given
c9f5803194 The system is installing, but it's not finding mingw32. 2020-01-27 23:47:30 +01:00
David Given
5293560c02 Try enabling the windows CI build again. 2020-01-27 23:41:03 +01:00
David Given
c49823aa9d Now the optimised binaries are optimised and the debug binaries aren't. 2020-01-27 23:36:33 +01:00
David Given
c4ef4882ae Merge pull request #130 from davidgiven/fixing
Fix a nasty firmware memory corruption bug
2020-01-27 23:15:20 +01:00
David Given
a8eca06cf0 Don't hang if we hit the end of stream while waiting for a data record. 2020-01-27 23:09:29 +01:00
David Given
065257b5aa Remove stray tracing. 2020-01-27 23:09:07 +01:00
David Given
29bdfc043a Allow fractional revolutions and non-synced reading. Find more things which
need fixing in the firmware sampler.
2020-01-27 22:52:25 +01:00
David Given
933ffe7ab4 Find and attempt to fix a memory corruption error when sampling --- if the next
fragment arrives from the sampler before usbbuffer has finished being
transmitted via USB, it'll get overwritten. I've disabled DMA USB to make the
code easier to understand and made sure that we flush things more rigorously.
This may help the weird pipe errors, too.
2020-01-27 21:40:27 +01:00
David Given
e517f28563 Merge pull request #129 from davidgiven/fixing
Make decoding more robust
2020-01-27 01:21:40 +01:00
David Given
91ffcf59c3 When reading data records, retry if we get an UNKNOWN record to work the
occasional false positive (which happens now and again with MFM).
2020-01-27 01:15:14 +01:00
David Given
51c618f325 Merge from trunk. 2020-01-26 23:53:29 +01:00
David Given
9dc1067032 Add --dump-sectors. Sector positions are recorded correctly. 2020-01-26 18:30:35 +01:00
David Given
9e75dc3af1 Merge pull request #127 from davidgiven/fixing
Fix an issue where HD IBM disks can't be read
2020-01-26 18:06:15 +01:00
David Given
efa4c933b3 Made the MFM marker byte detection a lot more robust --- prevents false
positives; it looks like the new sampler is producing a little bit of wobble
which randomly spoofs a marker byte now and again. This prevents this from
happening (so far).
2020-01-26 17:58:08 +01:00
David Given
6af80d1e5e Improve some messaging. A data record is always pushed, even if it's empty (to
help debugging).
2020-01-26 17:49:12 +01:00
David Given
0c48897814 Add a minimum clock threshold. 2020-01-26 17:48:33 +01:00
David Given
60e5e35947 Merge pull request #124 from davidgiven/tweaks
Several index pulse fixes
2020-01-12 01:42:39 +01:00
David Given
86c4e959ca Mac error fix. 2020-01-12 01:38:19 +01:00
David Given
b0c675c589 Improved error messages when using fe-rpm and it doesn't work. 2020-01-12 01:34:12 +01:00
David Given
d77841c3b7 Add the ability to fake the index pulse source, allowing old drives to be used
with FluxEngine.
2020-01-12 01:23:47 +01:00
David Given
4ed1fb6bac Document the extra index pulses. 2020-01-10 21:23:33 +01:00
David Given
bcc9e9d9a5 Bump the protocol number (I forgot last time I changed the protocol). 2020-01-10 21:04:33 +01:00
David Given
ec327e25a4 Merge pull request #121 from davidgiven/amiga
Add support for writing Amiga disks.
2019-12-14 21:55:00 +01:00
David Given
d0ed5b32f7 Add support for 528-byte sectors. Adjust the post-index gap to (try?) and fit
all the data in one revolution. I think my write clock is a bit slow.
2019-12-14 21:49:31 +01:00
David Given
7c66e1b0d4 Don't recalibrate after drive errors --- it's really annoying and I don't think it helps. 2019-12-14 21:43:48 +01:00
David Given
4475e9f085 Increase the default bit-error-threshold to 0.4, because that's the value I
almost always end up using.
2019-12-14 21:33:39 +01:00
David Given
5c9639ec5a Document the Amiga write support. 2019-12-14 21:15:47 +01:00
David Given
792cc88192 The Amiga writer now generates valid flux files --- but it looks like the
writer's broken (both the Amiga and the Brother have failed).
2019-12-14 20:44:48 +01:00
David Given
21fe586724 Update OSX build instructions. 2019-12-14 11:37:43 +01:00
David Given
5a0fb2761a Update OSX build instructions. 2019-12-14 11:37:43 +01:00
David Given
ef4581ed39 Merge from trunk. 2019-12-13 23:59:03 +01:00
David Given
73419704c2 Merge pull request #118 from davidgiven/fixing
Fix a few minor build tweaks.
2019-12-13 23:56:12 +01:00
David Given
a8b92d4780 This works; final tweaking. 2019-12-13 23:53:42 +01:00
David Given
98140b0646 More testing. 2019-12-13 23:50:33 +01:00
David Given
4429ce1f84 More build tweaks. 2019-12-13 23:48:17 +01:00
David Given
1f50941a2c Merge from trunk. 2019-12-13 23:45:17 +01:00
David Given
a7de04848c Merge pull request #117 from davidgiven/githubci
Convert to Github CI instead of Travis.
2019-12-13 23:43:03 +01:00
David Given
c264fec6e9 The Windows stuff doesn't work, so let's shelve it for now. 2019-12-13 23:38:49 +01:00
David Given
4488b2542f Update MSYS build environment. 2019-12-13 23:24:23 +01:00
David Given
2f1a5189d6 Oh, yeah, let's actually check out our project! 2019-12-13 23:21:58 +01:00
David Given
effaeff51e Something's not right --- test. 2019-12-13 23:19:48 +01:00
David Given
1210549f59 Try Windows builds on github. 2019-12-13 23:12:29 +01:00
David Given
7200de9702 Add more Homebrew packages. 2019-12-13 23:02:13 +01:00
David Given
5dd5c8516a Don't do builds on push_request, because these always coincide with pushes. 2019-12-13 23:01:08 +01:00
David Given
f7fb2a844b Github's OSX doesn't install pkg-config by default? 2019-12-13 23:00:10 +01:00
David Given
20b1b2a4a8 More YAML syntax fiddling? 2019-12-13 22:58:28 +01:00
David Given
f8b8bc2295 Fiddle with YAML syntax. 2019-12-13 22:57:29 +01:00
David Given
2d4d56d09f Try Github CI for OSX builds instead of Travis. 2019-12-13 22:55:37 +01:00
David Given
39599b76c8 Stop building the Ubuntu version with Travis. 2019-12-13 22:52:30 +01:00
David Given
c2c40ccfbb Also remember to install ninja-build. 2019-12-13 22:48:48 +01:00
David Given
ab42eb23f4 Remember to run apt as sudo. 2019-12-13 22:47:20 +01:00
David Given
05eff0e528 Try to build using github CI. 2019-12-13 22:44:58 +01:00
David Given
23311b4b68 Start looking at Github CI. 2019-12-13 22:41:19 +01:00
David Given
5e97df8d15 Better diagnostics when a package can't be found. 2019-12-13 22:39:58 +01:00
David Given
898e8c551c Produce a build-time error if the pkg-config packages aren't available. 2019-12-13 22:34:24 +01:00
David Given
ad69c6bd27 Fix stupid (but thankfully harmless) typo. 2019-12-13 22:33:39 +01:00
David Given
661399cc83 Update document with the new flippy mod doc; explain how Mac disks are broken. 2019-12-12 21:04:42 +01:00
David Given
edbb4b1daa Merge pull request #115 from davidgiven/reading
Rewrite the entire sampler.
2019-12-12 20:46:12 +01:00
David Given
6389e8a756 Update pin number (which was wrong). 2019-12-12 20:35:20 +01:00
David Given
c187b79d80 Add a 300RPM clock on 3[0] and a 360RPM clock on 3[1], for use with faking
index pulses to the drive.
2019-12-12 20:34:44 +01:00
David Given
edbe624c5a Hopefully, finally, fix the hang-on-read issue. 2019-12-12 20:09:49 +01:00
David Given
44e2334815 Typo fix. Make sure that both drives get deselected when the motor stops (to
make the LEDs go out).
2019-12-12 00:17:59 +01:00
David Given
b448ab7917 Finally squeeze everything in to the Verilog sampler. It does seem to work
better... the the USB hangup problem persists. Mac disks are still
nigh-unreadable.
2019-12-12 00:12:20 +01:00
David Given
072a097003 Archival (non-functioning) checkin of Verilog-based sampler code. Sadly, we've
run into size limits for the device, and I need to slim down.
2019-12-11 22:51:27 +01:00
David Given
a66e704bab Start ripping out the awful UDB-based sampler code, replacing it with a Verilog
one and a standalone FIFO. This gets the FIFO working.
2019-12-11 21:13:57 +01:00
David Given
ed0d578b18 Merge pull request #114 from davidgiven/fixing1
Add support for measuring signal line voltages.
2019-12-11 00:28:13 +01:00
David Given
32bb956710 Detect voltage levels *correctly*. 2019-12-11 00:05:34 +01:00
David Given
f436d6b582 Add a feature where we can measure the FDD bus signal voltages using the PSoC's
ADCs. Increase the track step pulse width to 6us, because.
2019-12-10 22:36:18 +01:00
David Given
d2f8c27cb6 Add checksum routine. 2019-12-01 09:11:36 +01:00
David Given
eaa3c57425 Non-functional boilerplate of Amiga write support. 2019-12-01 09:07:43 +01:00
David Given
549f12a2ab Merge from master. 2019-12-01 08:36:44 +01:00
David Given
aea254fbe7 Made the AES Lanier reader work again. 2019-12-01 08:35:25 +01:00
David Given
8ee6eed4dc Merge pull request #112 from davidgiven/fixing
Rework the sequencer completely, because bizarrely writing disks just stopped working
2019-11-29 22:02:44 +01:00
David Given
3094c5c919 Add missing files. 2019-11-29 18:49:22 +01:00
David Given
1e012699af Clean up the Verilog a bit. 2019-11-25 20:54:13 +01:00
David Given
91d6e9aeb9 Rewrite the sequencer engine with a separate fifo component and a pure verilog
sequencer --- much easier to understand. We can write disks again!
2019-11-25 20:52:13 +01:00
David Given
a40b26ff46 Archival checkin for trying to figure out why writes no longer work. 2019-11-24 15:14:32 +01:00
David Given
ebcb9c4bb0 Switch the output lines to open-drain drive low. 2019-11-24 02:06:45 +01:00
David Given
2520834b18 Add the brother240tool program. 2019-11-24 01:46:02 +01:00
David Given
a1f3087046 Add an 8" drive datasheet. 2019-11-24 01:45:43 +01:00
David Given
e9670e205e Don't erroneously crash out if the t and s parameters are omitted from the rpm
command.
2019-10-14 22:25:43 +02:00
David Given
658e2b7295 Properly initialise flags.
Fixes: #109
2019-09-22 21:55:37 +02:00
David Given
7b4a8d6de2 Merge pull request #108 from davidgiven/scp
Add very beta support for scp import and export
2019-09-21 22:32:52 +02:00
David Given
e8f7b51aef Another documentation tweak. 2019-09-21 22:15:44 +02:00
David Given
9d6bc57a5f Update documentation. 2019-08-31 12:32:52 +02:00
David Given
73766f92b4 Fix the flux to scp converter. 2019-08-31 12:31:33 +02:00
David Given
80badf3b54 Remember to check in the build file changes... 2019-08-31 12:09:11 +02:00
David Given
116529f85a Archival, non-working checkin of the flux to scp converter (it appears to
produce garbage).
2019-08-31 01:41:11 +02:00
David Given
5a2b2bc07a Allow support for command lines with non-argument filenames. 2019-08-31 01:40:44 +02:00
David Given
41070395c0 Merge pull request #105 from davidgiven/scp
Add conversion support for Supercard Pro scp files.
2019-08-28 23:52:27 +02:00
David Given
4304d1eede Add the Supercard Pro decoder. 2019-08-28 23:32:09 +02:00
David Given
46f1b0aef4 Merge pull request #104 from davidgiven/sampler
Fix a nasty sampler bug leading to corrupted data
2019-08-28 00:23:21 +02:00
David Given
9923d67a7c Merge pull request #103 from davidgiven/visualiser
Add a simple disk visualiser.
2019-08-28 00:20:49 +02:00
David Given
99335a84fd Add documentation for the visualiser. 2019-08-28 00:19:18 +02:00
David Given
c266779433 Fix a bug where index pulses where being turned into flux pulses on read,
leading to completely broken data whenever an index pulse happened.
2019-08-27 23:58:07 +02:00
David Given
bdcc12cd53 Correctly import M_PI. 2019-08-27 23:08:13 +02:00
David Given
7988d0fe24 Don't replace bad sectors with more bad sectors. This means that if a sector is
permanently bad, the one which wins and goes into the output SectorSet is the
first one, not the last one. Frequently the last sector is truncated by the end
of read and so it isn't useful.
2019-08-27 22:39:24 +02:00
David Given
27f5c294b1 The visualiser period can now be specified in a flag. 2019-08-27 01:21:49 +02:00
David Given
b9a53e0d1c First draft of the visualiser. 2019-08-27 01:07:57 +02:00
David Given
f8b6d5e6fb Merge. 2019-08-25 00:24:34 +02:00
David Given
04ff31c348 Add a flag to the IBM decoder to tell it to ignore the logical sector IDs (some
formats don't use these).
2019-08-25 00:24:02 +02:00
David Given
77b4aebd1b Fix crashing bug when reading Kryoflux streams. 2019-08-24 23:53:22 +02:00
David Given
4056364300 Merge pull request #99 from davidgiven/extensions
Validate image extensions before reading, not after.
2019-08-22 22:09:42 +02:00
David Given
60bfe050d3 Refactor the way image extensions are handled to be generally cleaner. Add
support for validating ImageSpecs before we actually want to read/write an
image, so as to allow us to check the extension *before* wasting time reading a
disk. Make .d81 an alias of .img.
2019-08-21 00:45:10 +02:00
David Given
28d0ce765e Merge pull request #93 from davidgiven/hex
Add precompiled firmware
2019-08-15 22:26:50 +02:00
David Given
4954d33307 Add documentation for using the precompiled firmware. 2019-08-15 22:19:30 +02:00
David Given
55f3354287 Add precompiled hex for the firmware. 2019-08-15 21:52:11 +02:00
David Given
d6ae373fa8 Merge pull request #92 from davidgiven/d64
Add write-only support for D64 disk images.
2019-08-15 20:46:02 +02:00
David Given
a626d5f9a0 Add write-only support for D64 disk images. 2019-08-15 20:30:07 +02:00
David Given
29db67528d Merge pull request #90 from davidgiven/write
Add support for writing LDBS image files.
2019-08-10 21:40:56 +02:00
David Given
31d7477c6a Add LDBS documentation. 2019-08-10 21:35:21 +02:00
David Given
56af9eaf18 The LDBS ST1 field is now populated correctly on error. 2019-08-10 21:25:07 +02:00
David Given
5de0636fe7 First attempt at writing LDBS files. Not quite right. 2019-08-09 23:21:55 +02:00
David Given
f9117b8d11 Added a simple library for reading and writing LDBS files. 2019-08-09 22:41:07 +02:00
David Given
10d385375f Merge pull request #87 from davidgiven/write
Add support for pluggable input and output formats.
2019-08-09 21:22:03 +02:00
David Given
2f72c3f8f0 Mac images now put the metadata second. 2019-08-09 21:13:29 +02:00
David Given
54edff9b94 Amiga disks can now optionally emit the metadata. 2019-08-09 21:04:48 +02:00
David Given
112377f885 Add pluggable image readers, plus some documentation. 2019-08-09 20:56:06 +02:00
David Given
87e29fc386 Merge from write branch (because we want the new image code). 2019-08-08 23:27:11 +02:00
David Given
b1db5c48b1 Ignore more temporary files. 2019-08-08 23:26:42 +02:00
David Given
38fab7edcb Refactor all the image writing stuff into a ImageWriter subclass hierarchy with
a factory based on extension.
2019-08-08 00:27:41 +02:00
David Given
d8172154c3 Output images now get geometry specs to indicate what kind of file to write. 2019-08-07 23:23:07 +02:00
David Given
eb924780ab Refactor dataspecs to allow them to be used for other things too. 2019-08-06 23:50:02 +02:00
David Given
28e0ef0463 Merge pull request #84 from davidgiven/cleanup
Mechanical refactor to rearrange the source files into a more pleasing order.
2019-08-06 22:33:05 +02:00
David Given
4b07c38782 Mechanical refactor to rearrange the source files into a more pleasing order. 2019-08-06 22:25:11 +02:00
David Given
e0256adf77 Merge pull request #83 from davidgiven/write
Write code refactoring.
2019-08-06 22:24:40 +02:00
David Given
5748f017dd Refactor the write code to make it easier to add new encoders. 2019-08-06 22:17:58 +02:00
David Given
973f4c2c2d Skeleton work to factor out the encoder logic. 2019-08-03 22:30:30 +02:00
David Given
8e1774c69f Show the clock rate in kHz as well, because that's more useful. 2019-07-12 23:09:50 +02:00
David Given
56a36072f7 Sampler state machine cleanup; more debugging tools for the logic analyser. 2019-07-12 21:09:53 +02:00
David Given
8755d108ed Sanity check Mac sectors --- reject anything with an out-of-bounds sector ID. 2019-07-11 23:16:02 +02:00
David Given
ea40cd73d1 Merge pull request #78 from davidgiven/mx
Bugfixing and documentation for MX disks.
2019-07-11 22:02:24 +02:00
David Given
0e28899b72 Add the page on the MX disk format. Mac disks are now unicorns. 2019-07-11 21:34:01 +02:00
David Given
eee30db981 Honour the logical track numbering in MX disks (allows reading 40-track disks). 2019-07-11 21:03:54 +02:00
David Given
6959d18017 Merge pull request #77 from davidgiven/build
Build cleanups
2019-07-11 12:18:59 +02:00
David Given
9f92ce0ef7 Typo fix. 2019-07-11 12:10:36 +02:00
David Given
7658c1d774 Move the fluxtoau tool into the main client. 2019-07-11 12:00:25 +02:00
David Given
31dc3504e6 Move the fluxtovcd file into the main client. 2019-07-11 11:52:38 +02:00
David Given
af0c9d4261 Move cwftoflux into the main client. 2019-07-11 11:45:15 +02:00
David Given
155b9daef6 Clean build scripts a bit. 2019-07-11 11:25:29 +02:00
David Given
a2fdbc5c73 Merge pull request #76 from davidgiven/sampling
Minor fixes and debugging tooling
2019-07-11 00:10:18 +02:00
David Given
1e3581c5f3 Turns out I was using the wrong error threshold flags for Mac disks. ND disks
work fine now.
2019-07-10 23:55:16 +02:00
David Given
a1d345856e Add a tool for converting flux files directly to mono or stereo au files,
suitable for comparison in Audacity.
2019-07-10 22:56:39 +02:00
David Given
7a775afaea Make sure that CounterClock is actually 12MHz, and not 12-ish MHz. Doesn't
help.
2019-07-10 20:26:17 +02:00
David Given
c27c4fe312 Synchronise input pins and set LVTTL levels. More correct, but doesn't help the
read issues.
2019-07-10 19:48:03 +02:00
David Given
ad295c683c The sampler pulse conversion now uses BUS_CLK as the sample clock. 2019-07-09 22:43:46 +02:00
David Given
3f8fdaa27a Fix missing flag initialisation. 2019-07-09 22:38:35 +02:00
David Given
9f5d01787f Fix missing flag initialisation. 2019-07-09 22:38:35 +02:00
David Given
3c4487c42e Add the tool to export flux files as VCD, so that they can be read into
pulseview.
2019-07-09 00:48:35 +02:00
David Given
e5c2168a35 Merge. 2019-07-08 23:32:53 +02:00
David Given
a06d8ff05a Merge pull request #74 from davidgiven/tools
Build brother120tool and cwftoflux.
2019-07-08 23:32:20 +02:00
David Given
c91ca1b730 Try to stop OSX from running brew cleanup every time. 2019-07-08 23:27:54 +02:00
David Given
3b6ea24df5 Rename fnmatchemu to just emu, because I'm sure we'll want to put more stuff
there.
2019-07-08 23:21:49 +02:00
David Given
b0e905fdd0 Typo fix. 2019-07-08 23:16:46 +02:00
David Given
34a858346e Add our own copy of fnmatch(), which mingw doesn't have --- we need it for the
Brother tool and I'm sure it'll come in useful elsewhere.
2019-07-08 23:12:58 +02:00
David Given
499cb4f720 Typo fix. 2019-07-08 22:46:16 +02:00
David Given
3960b1e4d7 Build brother120tool and cwftoflux. 2019-07-08 22:44:38 +02:00
David Given
98ea5e9600 Connect up pins 2.3, 2.4 and 2.5 for debugging with a logic analyser. 2019-07-08 01:08:05 +02:00
David Given
ce6077fa22 Apply Denis Kushch's timing fixes to the schematic. No more warnings on builds! 2019-07-08 00:52:59 +02:00
David Given
adb1e9ba00 Document the precompiled releases. 2019-07-07 01:50:31 +02:00
David Given
7752fd9f2c Remember that our build artifact is now a zipfile... 2019-07-07 01:45:11 +02:00
David Given
5db81e681f Appveyor really doesn't have zip? 2019-07-07 01:44:08 +02:00
David Given
6ef969fd7e Upload zipfiles rather than uncompressed exes. 2019-07-07 01:42:05 +02:00
David Given
98dece78c6 Let's see if we can avoid the infinite build loops this time... 2019-07-07 01:37:58 +02:00
175 changed files with 13291 additions and 3721 deletions

View File

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

41
.github/workflows/ccpp.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: C/C++ CI
on: [push]
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: apt
run: sudo apt update && sudo apt install libusb-1.0-0-dev libsqlite3-dev ninja-build
- name: make
run: make
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: brew
run: brew install sqlite pkg-config libusb ninja
- name: make
run: make
# build-windows:
# runs-on: windows-latest
# steps:
# - uses: numworks/setup-msys2@v1
# with:
# path-type: inherit
# - uses: actions/checkout@v1
# - name: pacman
# run: |
# msys2do pacman -S --noconfirm --needed make ninja mingw-w64-i686-libusb mingw-w64-i686-sqlite3 mingw-w64-i686-zlib mingw-w64-i686-gcc zip
# - name: build
# run: |
# msys2do make

View File

@@ -3,6 +3,10 @@ streams
.*\.flux
.*\.img
.*\.raw
.*\.orig
.vscode
remote
FluxEngine.cydsn/CortexM3
FluxEngine.cydsn/Generated_Source
FluxEngine.cydsn/codegentemp

View File

@@ -1,37 +0,0 @@
language: shell
git:
depth: 1
matrix:
include:
-
os: linux
sudo: false
dist: xenial
compiler: gcc
env: CXX=g++-8
script:
- make
-
os: osx
osx_image: xcode10.2
compiler: clang
addons:
apt:
sources:
- llvm-toolchain-precise-3.8
- ubuntu-toolchain-r-test
packages:
- ninja-build
- libusb-1.0-0-dev
- libsqlite3-dev
- g++-8
homebrew:
packages:
- ninja
script:
- make

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
#include "cyfitter_cfg.h"
#include "cydevice_trm.h"
#include "cyfitter.h"
#include "`$INSTANCE_NAME`_h.h"
void `$INSTANCE_NAME`_Start()
{
`$INSTANCE_NAME`_Init();
}
void `$INSTANCE_NAME`_Stop()
{
`$INSTANCE_NAME`_Disable();
}
void `$INSTANCE_NAME`_Init()
{
`$INSTANCE_NAME`_Enable();
}
void `$INSTANCE_NAME`_Enable()
{
}
void `$INSTANCE_NAME`_Disable()
{
}

View File

@@ -0,0 +1,50 @@
#if !defined(`$INSTANCE_NAME`_H)
#define `$INSTANCE_NAME`_H
#include "cytypes.h"
#include "cyfitter.h"
#include "CyLib.h"
#define `$INSTANCE_NAME`_FIFO_PTR ((reg8 *) `$INSTANCE_NAME`_dp__F0_REG)
/* Macros to clear DP FIFOs.*/
#define `$INSTANCE_NAME`_CLEAR do { \
CY_SET_XTND_REG8(\
((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x01u | \
CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)));\
CY_SET_XTND_REG8(\
((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfeu & \
CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)));\
} while(0)
/* Macros to set FIFO level mode. See the TRM for details */
#define `$INSTANCE_NAME`_SET_LEVEL_NORMAL \
CY_SET_XTND_REG8(\
((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfbu & \
CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)))
#define `$INSTANCE_NAME`_SET_LEVEL_MID \
CY_SET_XTND_REG8(\
((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x04u | \
CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)))
/* Macros to set FIFO to single-buffer mode. */
#define `$INSTANCE_NAME`_SINGLE_BUFFER_SET \
CY_SET_XTND_REG8(\
((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x01u | \
CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)))
/* Macros to return the FIFO to normal mode. */
#define `$INSTANCE_NAME`_SINGLE_BUFFER_UNSET \
CY_SET_XTND_REG8(\
((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfeu & \
CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)))
void `$INSTANCE_NAME`_Enable();
void `$INSTANCE_NAME`_Disable();
void `$INSTANCE_NAME`_Start();
void `$INSTANCE_NAME`_Stop();
void `$INSTANCE_NAME`_Init();
#endif
/* [] END OF FILE */

View File

Binary file not shown.

View File

@@ -0,0 +1,128 @@
//`#start header` -- edit after this line, do not edit this line
`include "cypress.v"
//`#end` -- edit above this line, do not edit this line
/* Ultra-simple FIFO in component: a byte is shifted in every clock when req
* is high. */
module FIFOin (drq, clk, d, req);
output drq;
input clk;
input [7:0] d;
input req;
//`#start body` -- edit after this line, do not edit this line
wire [7:0] pi;
assign pi = d;
wire load;
assign load = req;
cy_psoc3_dp #(.cy_dpconfig(
{
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM0: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM1: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM2: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM3: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM4: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM5: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM6: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM7: */
8'hFF, 8'h00, /*CFG9: */
8'hFF, 8'hFF, /*CFG11-10: */
`SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH,
`SC_CI_A_ARITH, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL,
`SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI,
`SC_SI_A_DEFSI, /*CFG13-12: */
`SC_A0_SRC_PIN, `SC_SHIFT_SL, 1'h0,
1'h0, `SC_FIFO1_BUS, `SC_FIFO0_ALU,
`SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN,
`SC_FB_NOCHN, `SC_CMP1_NOCHN,
`SC_CMP0_NOCHN, /*CFG15-14: */
10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX,
`SC_FIFO_LEVEL,`SC_FIFO__SYNC,`SC_EXTCRC_DSBL,
`SC_WRK16CAT_DSBL /*CFG17-16: */
}
)) dp(
/* input */ .clk(clk),
/* input [02:00] */ .cs_addr(3'b0), // Program counter
/* input */ .route_si(1'b0), // Shift in
/* input */ .route_ci(1'b0), // Carry in
/* input */ .f0_load(load), // Load FIFO 0
/* input */ .f1_load(1'b0), // Load FIFO 1
/* input */ .d0_load(1'b0), // Load Data Register 0
/* input */ .d1_load(1'b0), // Load Data Register 1
/* output */ .ce0(), // Accumulator 0 = Data register 0
/* output */ .cl0(), // Accumulator 0 < Data register 0
/* output */ .z0(), // Accumulator 0 = 0
/* output */ .ff0(), // Accumulator 0 = FF
/* output */ .ce1(), // Accumulator [0|1] = Data register 1
/* output */ .cl1(), // Accumulator [0|1] < Data register 1
/* output */ .z1(), // Accumulator 1 = 0
/* output */ .ff1(), // Accumulator 1 = FF
/* output */ .ov_msb(), // Operation over flow
/* output */ .co_msb(), // Carry out
/* output */ .cmsb(), // Carry out
/* output */ .so(), // Shift out
/* output */ .f0_bus_stat(drq), // not empty
/* output */ .f0_blk_stat(full),// full
/* output */ .f1_bus_stat(), // FIFO 1 status to uP
/* output */ .f1_blk_stat(), // FIFO 1 status to DP
/* input */ .ci(1'b0), // Carry in from previous stage
/* output */ .co(), // Carry out to next stage
/* input */ .sir(1'b0), // Shift in from right side
/* output */ .sor(), // Shift out to right side
/* input */ .sil(1'b0), // Shift in from left side
/* output */ .sol(), // Shift out to left side
/* input */ .msbi(1'b0), // MSB chain in
/* output */ .msbo(), // MSB chain out
/* input [01:00] */ .cei(2'b0), // Compare equal in from prev stage
/* output [01:00] */ .ceo(), // Compare equal out to next stage
/* input [01:00] */ .cli(2'b0), // Compare less than in from prv stage
/* output [01:00] */ .clo(), // Compare less than out to next stage
/* input [01:00] */ .zi(2'b0), // Zero detect in from previous stage
/* output [01:00] */ .zo(), // Zero detect out to next stage
/* input [01:00] */ .fi(2'b0), // 0xFF detect in from previous stage
/* output [01:00] */ .fo(), // 0xFF detect out to next stage
/* input [01:00] */ .capi(2'b0), // Capture in from previous stage
/* output [01:00] */ .capo(), // Capture out to next stage
/* input */ .cfbi(1'b0), // CRC Feedback in from previous stage
/* output */ .cfbo(), // CRC Feedback out to next stage
/* input [07:00] */ .pi(pi), // Parallel data port
/* output [07:00] */ .po() // Parallel data port
);
//`#end` -- edit above this line, do not edit this line
endmodule
//`#start footer` -- edit after this line, do not edit this line
//`#end` -- edit above this line, do not edit this line

View File

@@ -0,0 +1,27 @@
#include "cyfitter_cfg.h"
#include "cydevice_trm.h"
#include "cyfitter.h"
#include "`$INSTANCE_NAME`_h.h"
void `$INSTANCE_NAME`_Start()
{
`$INSTANCE_NAME`_Init();
}
void `$INSTANCE_NAME`_Stop()
{
`$INSTANCE_NAME`_Disable();
}
void `$INSTANCE_NAME`_Init()
{
`$INSTANCE_NAME`_Enable();
}
void `$INSTANCE_NAME`_Enable()
{
}
void `$INSTANCE_NAME`_Disable()
{
}

View File

@@ -0,0 +1,50 @@
#if !defined(`$INSTANCE_NAME`_H)
#define `$INSTANCE_NAME`_H
#include "cytypes.h"
#include "cyfitter.h"
#include "CyLib.h"
#define `$INSTANCE_NAME`_FIFO_PTR ((reg8 *) `$INSTANCE_NAME`_dp__F0_REG)
/* Macros to clear DP FIFOs.*/
#define `$INSTANCE_NAME`_CLEAR do { \
CY_SET_XTND_REG8(\
((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x01u | \
CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)));\
CY_SET_XTND_REG8(\
((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfeu & \
CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)));\
} while(0)
/* Macros to set FIFO level mode. See the TRM for details */
#define `$INSTANCE_NAME`_SET_LEVEL_NORMAL \
CY_SET_XTND_REG8(\
((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfbu & \
CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)))
#define `$INSTANCE_NAME`_SET_LEVEL_MID \
CY_SET_XTND_REG8(\
((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x04u | \
CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)))
/* Macros to set FIFO to single-buffer mode. */
#define `$INSTANCE_NAME`_SINGLE_BUFFER_SET \
CY_SET_XTND_REG8(\
((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0x01u | \
CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)))
/* Macros to return the FIFO to normal mode. */
#define `$INSTANCE_NAME`_SINGLE_BUFFER_UNSET \
CY_SET_XTND_REG8(\
((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG), 0xfeu & \
CY_GET_XTND_REG8(((reg8 *) `$INSTANCE_NAME`_dp__DP_AUX_CTL_REG)))
void `$INSTANCE_NAME`_Enable();
void `$INSTANCE_NAME`_Disable();
void `$INSTANCE_NAME`_Start();
void `$INSTANCE_NAME`_Stop();
void `$INSTANCE_NAME`_Init();
#endif
/* [] END OF FILE */

View File

Binary file not shown.

View File

@@ -0,0 +1,169 @@
//`#start header` -- edit after this line, do not edit this line
`include "cypress.v"
//`#end` -- edit above this line, do not edit this line
// Generated on 11/16/2017 at 15:44
// Component: FIFOout
module FIFOout (
input req,
input clk,
output [7:0] d,
output drq,
output empty,
output ack
);
//`#start body` -- edit after this line, do not edit this line
/* Reads from the FIFO are done based on the FIFO being not empty. */
wire [7:0] po;
assign d = po;
localparam STATE_WAITFORREQ = 0;
localparam STATE_READFROMFIFO = 1;
localparam STATE_WAITFORNREQ = 2;
reg [1:0] state;
wire readfromfifo;
assign ack = (state == STATE_WAITFORNREQ);
assign readfromfifo = (state == STATE_READFROMFIFO);
always @(posedge clk)
begin
case (state)
/* opcode is not valid; req is low; wait for req to go high. */
STATE_WAITFORREQ:
begin
if (!empty && req)
state <= STATE_READFROMFIFO;
end
/* Fetch a single value from the FIFO. */
STATE_READFROMFIFO:
state <= STATE_WAITFORNREQ;
/* opcode is valid; ack is high. Wait for req to go low. */
STATE_WAITFORNREQ:
if (!req)
state <= STATE_WAITFORREQ;
endcase
end
cy_psoc3_dp #(.cy_dpconfig(
{
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM0: idle */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC___F0, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM1: read from fifo */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM2: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM3: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM4: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM5: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM6: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM7: */
8'hFF, 8'h00, /*CFG9: */
8'hFF, 8'hFF, /*CFG11-10: */
`SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH,
`SC_CI_A_ARITH, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL,
`SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI,
`SC_SI_A_DEFSI, /*CFG13-12: */
`SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0,
1'h0, `SC_FIFO1_BUS, `SC_FIFO0_BUS,
`SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN,
`SC_FB_NOCHN, `SC_CMP1_NOCHN,
`SC_CMP0_NOCHN, /*CFG15-14: */
10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX,
`SC_FIFO_LEVEL,`SC_FIFO_ASYNC,`SC_EXTCRC_DSBL,
`SC_WRK16CAT_DSBL /*CFG17-16: */
}
)) dp(
/* input */ .reset(1'b0),
/* input */ .clk(clk),
/* input [02:00] */ .cs_addr({2'b0, readfromfifo}),
/* input */ .route_si(1'b0),
/* input */ .route_ci(1'b0),
/* input */ .f0_load(1'b0),
/* input */ .f1_load(1'b0),
/* input */ .d0_load(1'b0),
/* input */ .d1_load(1'b0),
/* output */ .ce0(),
/* output */ .cl0(),
/* output */ .z0(),
/* output */ .ff0(),
/* output */ .ce1(),
/* output */ .cl1(),
/* output */ .z1(),
/* output */ .ff1(),
/* output */ .ov_msb(),
/* output */ .co_msb(),
/* output */ .cmsb(),
/* output */ .so(),
/* output */ .f0_bus_stat(drq), // not full
/* output */ .f0_blk_stat(empty), // empty
/* output */ .f1_bus_stat(),
/* output */ .f1_blk_stat(),
/* input */ .ci(1'b0), // Carry in from previous stage
/* output */ .co(),// Carry out to next stage
/* input */ .sir(1'b0), // Shift in from right side
/* output */ .sor(), // Shift out to right side
/* input */ .sil(1'b0), // Shift in from left side
/* output */ .sol(), // Shift out to left side
/* input */ .msbi(1'b0), // MSB chain in
/* output */ .msbo(), // MSB chain out
/* input [01:00] */ .cei(2'b0), // Compare equal in from prev stage
/* output [01:00] */ .ceo(), // Compare equal out to next stage
/* input [01:00] */ .cli(2'b0), // Compare less than in from prv stage
/* output [01:00] */ .clo(), // Compare less than out to next stage
/* input [01:00] */ .zi(2'b0), // Zero detect in from previous stage
/* output [01:00] */ .zo(), // Zero detect out to next stage
/* input [01:00] */ .fi(2'b0), // 0xFF detect in from previous stage
/* output [01:00] */ .fo(), // 0xFF detect out to next stage
/* input [01:00] */ .capi(2'b0), // Software capture from previous stage
/* output [01:00] */ .capo(), // Software capture to next stage
/* input */ .cfbi(1'b0), // CRC Feedback in from previous stage
/* output */ .cfbo(), // CRC Feedback out to next stage
/* input [07:00] */ .pi(8'b0), // Parallel data port
/* output [07:00] */ .po(po) // Parallel data port
);
//`#end` -- edit above this line, do not edit this line
endmodule
//`#start footer` -- edit after this line, do not edit this line
//`#end` -- edit above this line, do not edit this line

View File

@@ -28,13 +28,85 @@
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="1a7e8637-3b6b-4e84-839c-0dfc18fdaf5b">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_DIVIDER" />
<Data key="desired_freq" value="0" />
<Data key="desired_unit" value="15" />
<Data key="divider" value="0" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="Clock_5" />
<Data key="named_src_direct_connect" value="True" />
<Data key="netlist_name" value="Clock_5" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" />
<Data key="src_clk_name" value="BUS_CLK" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="3f3708ae-fb62-4012-919b-9a3b9a1dfbc2">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_DIVIDER" />
<Data key="desired_freq" value="0" />
<Data key="desired_unit" value="15" />
<Data key="divider" value="0" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="Clock_8" />
<Data key="named_src_direct_connect" value="True" />
<Data key="netlist_name" value="Clock_8" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" />
<Data key="src_clk_name" value="BUS_CLK" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="4eef02b9-8ad1-43c4-85f1-b3335faa5fc4">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_DIVIDER" />
<Data key="desired_freq" value="0" />
<Data key="desired_unit" value="15" />
<Data key="divider" value="0" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="Clock_3" />
<Data key="named_src_direct_connect" value="True" />
<Data key="netlist_name" value="Clock_3" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" />
<Data key="src_clk_name" value="BUS_CLK" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="06c4d5d4-f15f-4b29-a1d0-c24b2e38b1ec">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_FREQ" />
<Data key="desired_freq" value="24" />
<Data key="desired_freq" value="12" />
<Data key="desired_unit" value="6" />
<Data key="divider" value="1" />
<Data key="divider" value="2" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
@@ -50,7 +122,7 @@
<Data key="src_clk_name" value="IMO" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
<Data key="user_set_domain" value="True" />
</Group>
<Group key="24cd38f7-f472-4403-837f-86807c8f5333">
<Data key="check_tolerance" value="True" />
@@ -123,6 +195,54 @@
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="71bc291d-84a7-40a8-b7b2-1c8a34326a31">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_FREQ" />
<Data key="desired_freq" value="300" />
<Data key="desired_unit" value="0" />
<Data key="divider" value="65536" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="CLOCK300" />
<Data key="named_src_direct_connect" value="False" />
<Data key="netlist_name" value="CLOCK300" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="CEF43CFB-0213-49b9-B980-2FFAB81C5B47" />
<Data key="src_clk_name" value="IMO" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="90ce0c72-9f10-44ef-a049-f0f525d59bea">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_FREQ" />
<Data key="desired_freq" value="128" />
<Data key="desired_unit" value="0" />
<Data key="divider" value="65536" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="CLOCK8" />
<Data key="named_src_direct_connect" value="False" />
<Data key="netlist_name" value="CLOCK8" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="CEF43CFB-0213-49b9-B980-2FFAB81C5B47" />
<Data key="src_clk_name" value="IMO" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="349ffa20-8576-4ac3-9a6f-34ef606de6cf">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
@@ -146,6 +266,29 @@
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="4033c29d-f4bc-4e94-ac95-aa587e869f88/696a0979-21fc-4185-bf38-6c79febcde7a">
<Data key="check_tolerance" value="False" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="AUTO" />
<Data key="desired_freq" value="1600000" />
<Data key="desired_unit" value="0" />
<Data key="divider" value="40" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="OUTPUT_VOLTAGE_ADC_theACLK" />
<Data key="netlist_name" value="\OUTPUT_VOLTAGE_ADC:theACLK\" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="61737EF6-3B74-48f9-8B91-F7473A442AE7" />
<Data key="src_clk_name" value="MASTER_CLK" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="6616e828-6611-4893-a674-66c861d79d6c">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
@@ -193,6 +336,101 @@
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="75187c05-9501-4450-b306-6ccdd3bb77db">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_DIVIDER" />
<Data key="desired_freq" value="0" />
<Data key="desired_unit" value="15" />
<Data key="divider" value="0" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="Clock_5" />
<Data key="named_src_direct_connect" value="True" />
<Data key="netlist_name" value="Clock_5" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" />
<Data key="src_clk_name" value="BUS_CLK" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="09974428-e912-491f-8d2f-361ba50e7599">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_DIVIDER" />
<Data key="desired_freq" value="0" />
<Data key="desired_unit" value="15" />
<Data key="divider" value="0" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="Clock_6" />
<Data key="named_src_direct_connect" value="True" />
<Data key="netlist_name" value="Clock_6" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" />
<Data key="src_clk_name" value="BUS_CLK" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="a5825a94-fa18-4e4f-a843-bc687cacbd56/696a0979-21fc-4185-bf38-6c79febcde7a">
<Data key="check_tolerance" value="False" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="AUTO" />
<Data key="desired_freq" value="1600000" />
<Data key="desired_unit" value="0" />
<Data key="divider" value="40" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="INPUT_VOLTAGE_ADC_theACLK" />
<Data key="netlist_name" value="\INPUT_VOLTAGE_ADC:theACLK\" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="61737EF6-3B74-48f9-8B91-F7473A442AE7" />
<Data key="src_clk_name" value="MASTER_CLK" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="b762c287-7f87-4b21-982e-84be01dc5115">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_DIVIDER" />
<Data key="desired_freq" value="0" />
<Data key="desired_unit" value="15" />
<Data key="divider" value="0" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="Clock_2" />
<Data key="named_src_direct_connect" value="True" />
<Data key="netlist_name" value="Clock_2" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" />
<Data key="src_clk_name" value="BUS_CLK" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="b0162966-0060-4af5-82d1-fcb491ad7619/be0a0e37-ad17-42ca-b5a1-1a654d736358">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
@@ -216,6 +454,30 @@
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="b722443b-8f81-46dc-bf9b-c95eb62bc181">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_DIVIDER" />
<Data key="desired_freq" value="0" />
<Data key="desired_unit" value="15" />
<Data key="divider" value="0" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="Clock_1" />
<Data key="named_src_direct_connect" value="True" />
<Data key="netlist_name" value="Clock_1" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" />
<Data key="src_clk_name" value="BUS_CLK" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="cb7e877c-9fb4-4fc1-a708-f1e48eb5a68c">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
@@ -240,6 +502,30 @@
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="d3075dc6-05c8-4dc9-9959-cf7014c0e66f">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_DIVIDER" />
<Data key="desired_freq" value="0" />
<Data key="desired_unit" value="15" />
<Data key="divider" value="0" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="Clock_7" />
<Data key="named_src_direct_connect" value="True" />
<Data key="netlist_name" value="Clock_7" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" />
<Data key="src_clk_name" value="BUS_CLK" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="e4a53a4c-40e1-4747-a72a-10193ffdf31c">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
@@ -537,41 +823,60 @@
<Group key="DWRInstGuidMapping">
<Group key="Clock">
<Data key="0b2f9bbb-00ce-4115-a788-ffb9d046a9e5" value="Clock_4" />
<Data key="1a7e8637-3b6b-4e84-839c-0dfc18fdaf5b" value="Clock_5" />
<Data key="3f3708ae-fb62-4012-919b-9a3b9a1dfbc2" value="Clock_8" />
<Data key="4eef02b9-8ad1-43c4-85f1-b3335faa5fc4" value="Clock_3" />
<Data key="06c4d5d4-f15f-4b29-a1d0-c24b2e38b1ec" value="CounterClock" />
<Data key="24cd38f7-f472-4403-837f-86807c8f5333" value="PULSE_CLOCK" />
<Data key="63ed4137-0b09-4256-8a27-35c9a2653f1a" value="Clock_2" />
<Data key="66f14071-bddd-4b4d-a9aa-a129cceaa7b6" value="Clock_3" />
<Data key="71bc291d-84a7-40a8-b7b2-1c8a34326a31" value="CLOCK300" />
<Data key="90ce0c72-9f10-44ef-a049-f0f525d59bea" value="CLOCK8" />
<Data key="349ffa20-8576-4ac3-9a6f-34ef606de6cf" value="Clock_1" />
<Data key="4033c29d-f4bc-4e94-ac95-aa587e869f88/696a0979-21fc-4185-bf38-6c79febcde7a" value="OUTPUT_VOLTAGE_ADC_theACLK" />
<Data key="6616e828-6611-4893-a674-66c861d79d6c" value="SignalSamplingClock" />
<Data key="12664fc6-9d70-44b1-8a49-887a292e1b7f" value="Clock_3" />
<Data key="75187c05-9501-4450-b306-6ccdd3bb77db" value="Clock_5" />
<Data key="09974428-e912-491f-8d2f-361ba50e7599" value="Clock_6" />
<Data key="a5825a94-fa18-4e4f-a843-bc687cacbd56/696a0979-21fc-4185-bf38-6c79febcde7a" value="INPUT_VOLTAGE_ADC_theACLK" />
<Data key="b762c287-7f87-4b21-982e-84be01dc5115" value="Clock_2" />
<Data key="b0162966-0060-4af5-82d1-fcb491ad7619/be0a0e37-ad17-42ca-b5a1-1a654d736358" value="UART_IntClock" />
<Data key="b722443b-8f81-46dc-bf9b-c95eb62bc181" value="Clock_1" />
<Data key="cb7e877c-9fb4-4fc1-a708-f1e48eb5a68c" value="CounterClock" />
<Data key="d3075dc6-05c8-4dc9-9959-cf7014c0e66f" value="Clock_7" />
<Data key="e4a53a4c-40e1-4747-a72a-10193ffdf31c" value="Clock_1" />
<Data key="efd5f185-0c32-4824-ba72-3ceb5356f5a7" value="Clock_1" />
</Group>
<Group key="Pin">
<Data key="3e1862bb-be82-47b0-9549-7ebfe76b6f7b" value="Pin_2" />
<Data key="4a398466-709f-4228-9500-96178658e13e" value="RDATA" />
<Data key="5a3407c1-b434-4438-a7b4-b9dfd2280495" value="MOTEA" />
<Data key="8d318d8b-cf7b-4b6b-b02c-ab1c5c49d0ba" value="SW1" />
<Data key="8fc20a4f-e4d1-44b3-a5d4-546e8628d61e" value="LED" />
<Data key="12e00eac-69b5-4717-85c8-25ef6b224d4c" value="DEBUG_PINS" />
<Data key="41e2d8ed-5494-4d8c-8ff7-f4f789cece51" value="REDWC" />
<Data key="264be2d3-9481-494b-8d9c-c1905a45e9cc" value="FDD" />
<Data key="472f8fdb-f772-44fb-8897-cc690694237b" value="WDATA" />
<Data key="736cb12b-c863-43d4-a8f0-42f06023f8b5" value="SIDE1" />
<Data key="4249c923-fcff-453b-8629-bec6fddd00c1" value="STEP" />
<Data key="27315b0e-6a8c-4b7f-be77-73ab434fa803" value="Pin_1" />
<Data key="1425177d-0d0e-4468-8bcc-e638e5509a9b" value="UartRx" />
<Data key="a5d987c6-e45b-45b9-ad93-656fab06d720" value="TRK00" />
<Data key="a93ef5b3-00f4-42c0-8fad-0e275a7e2537" value="MOTEB" />
<Data key="b8380fb7-fdb8-449f-bd8d-c4ca96cdf55a" value="DEBUG_PINS" />
<Data key="bc2e8987-db82-469c-bf6f-22fd3464cc70" value="DEBUG_PINS" />
<Data key="bc5d52a1-1b25-4aa0-9ba9-3f81d122772f" value="DEBUG_PINS" />
<Data key="beca5e2d-f70f-4900-a4db-7eca1ed3126e/8b77a6c4-10a0-4390-971c-672353e2a49c" value="USBFS_Dm" />
<Data key="beca5e2d-f70f-4900-a4db-7eca1ed3126e/618a72fc-5ddd-4df5-958f-a3d55102db42" value="USBFS_Dp" />
<Data key="c5367cde-21d5-4866-9a32-d16abfea0c61" value="WPT" />
<Data key="d19368c5-6855-41bb-a9ff-808938abef00" value="INDEX" />
<Data key="e9f14b5a-b2bf-49b8-98f3-d7b5a43ace8d" value="DRVSB" />
<Data key="e851a3b9-efb8-48be-bbb8-b303b216c393" value="LED" />
<Data key="e851a3b9-efb8-48be-bbb8-b303b216c393" value="INDEX300" />
<Data key="e51063a9-4fad-40c7-a06b-7cc4b137dc18" value="DSKCHG" />
<Data key="ea7ee228-8b3f-426c-8bb8-cd7a81937769" value="DIR" />
<Data key="ed092b9b-d398-4703-be89-cebf998501f6" value="UartTx" />
<Data key="fbd1f839-40f9-498e-a48b-5f3048ea5c3d/52f31aa9-2f0a-497d-9a1f-1424095e13e6" value="SW_Tx_UART_1_tx" />
<Data key="f9a7371a-8a7d-4144-8b08-69e3d2a3a663" value="INDEX360" />
<Data key="fbd1f839-40f9-498e-a48b-5f3048ea5c3d/52f31aa9-2f0a-497d-9a1f-1424095e13e6" value="UART_tx" />
<Data key="fede1767-f3fd-4021-b3d7-8f9d88f36f9b" value="DRVSA" />
<Data key="fff78075-035e-43d7-8577-bc5be4d21926" value="WGATE" />
</Group>
@@ -3660,6 +3965,11 @@
</Group>
</Group>
<Group key="Pin2">
<Group key="3e1862bb-be82-47b0-9549-7ebfe76b6f7b">
<Group key="0">
<Data key="Port Format" value="0,6" />
</Group>
</Group>
<Group key="4a398466-709f-4228-9500-96178658e13e">
<Group key="0">
<Data key="Port Format" value="1,5" />
@@ -3675,6 +3985,25 @@
<Data key="Port Format" value="2,2" />
</Group>
</Group>
<Group key="8fc20a4f-e4d1-44b3-a5d4-546e8628d61e">
<Group key="0">
<Data key="Port Format" value="2,1" />
</Group>
</Group>
<Group key="12e00eac-69b5-4717-85c8-25ef6b224d4c">
<Group key="0">
<Data key="Port Format" value="2,2" />
</Group>
<Group key="1">
<Data key="Port Format" value="2,3" />
</Group>
<Group key="2">
<Data key="Port Format" value="2,4" />
</Group>
<Group key="3">
<Data key="Port Format" value="2,0" />
</Group>
</Group>
<Group key="41e2d8ed-5494-4d8c-8ff7-f4f789cece51">
<Group key="0">
<Data key="Port Format" value="2,7" />
@@ -3742,6 +4071,11 @@
<Data key="Port Format" value="1,0" />
</Group>
</Group>
<Group key="27315b0e-6a8c-4b7f-be77-73ab434fa803">
<Group key="0">
<Data key="Port Format" value="0,7" />
</Group>
</Group>
<Group key="1425177d-0d0e-4468-8bcc-e638e5509a9b">
<Group key="0">
<Data key="Port Format" value="12,6" />
@@ -3757,6 +4091,43 @@
<Data key="Port Format" value="12,1" />
</Group>
</Group>
<Group key="b8380fb7-fdb8-449f-bd8d-c4ca96cdf55a">
<Group key="0">
<Data key="Port Format" value="2,5" />
</Group>
<Group key="1">
<Data key="Port Format" value="2,4" />
</Group>
<Group key="2">
<Data key="Port Format" value="2,3" />
</Group>
</Group>
<Group key="bc2e8987-db82-469c-bf6f-22fd3464cc70">
<Group key="0">
<Data key="Port Format" value="0,0" />
</Group>
<Group key="1">
<Data key="Port Format" value="0,1" />
</Group>
<Group key="2">
<Data key="Port Format" value="0,2" />
</Group>
<Group key="3">
<Data key="Port Format" value="0,3" />
</Group>
<Group key="4">
<Data key="Port Format" value="0,4" />
</Group>
<Group key="5">
<Data key="Port Format" value="0,5" />
</Group>
<Group key="6">
<Data key="Port Format" value="0,6" />
</Group>
<Group key="7">
<Data key="Port Format" value="0,7" />
</Group>
</Group>
<Group key="bc5d52a1-1b25-4aa0-9ba9-3f81d122772f">
<Group key="0">
<Data key="Port Format" value="0,5" />
@@ -3792,7 +4163,7 @@
</Group>
<Group key="e851a3b9-efb8-48be-bbb8-b303b216c393">
<Group key="0">
<Data key="Port Format" value="2,1" />
<Data key="Port Format" value="3,0" />
</Group>
</Group>
<Group key="e51063a9-4fad-40c7-a06b-7cc4b137dc18">
@@ -3810,9 +4181,14 @@
<Data key="Port Format" value="12,7" />
</Group>
</Group>
<Group key="f9a7371a-8a7d-4144-8b08-69e3d2a3a663">
<Group key="0">
<Data key="Port Format" value="3,1" />
</Group>
</Group>
<Group key="fbd1f839-40f9-498e-a48b-5f3048ea5c3d/52f31aa9-2f0a-497d-9a1f-1424095e13e6">
<Group key="0">
<Data key="Port Format" value="2,0" />
<Data key="Port Format" value="12,7" />
</Group>
</Group>
<Group key="fede1767-f3fd-4021-b3d7-8f9d88f36f9b">
@@ -3840,7 +4216,7 @@
<Data key="CYDEV_ECC_ENABLE" value="False" />
<Data key="CYDEV_HEAP_SIZE" value="0x80" />
<Data key="CYDEV_INSTRUCT_CACHE_ENABLED" value="True" />
<Data key="CYDEV_PROTECTION_ENABLE" value="False" />
<Data key="CYDEV_PROTECTION_ENABLE" value="True" />
<Data key="CYDEV_STACK_SIZE" value="0x0800" />
<Data key="CYDEV_TEMPERATURE" value="0C - 85/125C" />
<Data key="CYDEV_TRACE_ENABLED" value="False" />

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,99 @@
//`#start header` -- edit after this line, do not edit this line
`include "cypress.v"
`include "../SuperCounter/SuperCounter.v"
//`#end` -- edit above this line, do not edit this line
// Generated on 12/11/2019 at 21:18
// Component: Sampler
module Sampler (
output [2:0] debug_state,
output reg [7:0] opcode,
output req,
input clock,
input index,
input rdata,
input reset,
input sampleclock
);
//`#start body` -- edit after this line, do not edit this line
localparam STATE_WAITING = 0;
localparam STATE_OPCODE = 1;
reg [0:0] state;
reg [5:0] counter;
reg oldsampleclock;
reg oldindex;
reg oldrdata;
reg sampleclocked;
reg indexed;
reg rdataed;
assign req = (state == STATE_OPCODE);
always @(posedge clock)
begin
if (reset)
begin
state <= STATE_WAITING;
opcode <= 0;
sampleclocked <= 0;
indexed <= 0;
rdataed <= 0;
oldsampleclock <= 0;
oldindex <= 0;
oldrdata <= 0;
counter <= 0;
end
else
begin
/* Remember positive egdes for sampleclock, index and rdata. */
if (sampleclock && !oldsampleclock)
sampleclocked <= 1;
oldsampleclock <= sampleclock;
if (index && !oldindex)
indexed <= 1;
oldindex <= index;
if (rdata && !oldrdata)
rdataed <= 1;
oldrdata <= rdata;
case (state)
STATE_WAITING:
begin
if (sampleclocked)
begin
if (rdataed || indexed || (counter == 6'h3f))
begin
opcode <= {rdataed, indexed, counter};
rdataed <= 0;
indexed <= 0;
counter <= 1; /* remember to count this tick */
state <= STATE_OPCODE;
end
else
counter <= counter + 1;
sampleclocked <= 0;
end
end
STATE_OPCODE: /* opcode sent here */
begin
state <= STATE_WAITING;
end
endcase
end
end
//`#end` -- edit above this line, do not edit this line
endmodule
//`#start footer` -- edit after this line, do not edit this line
//`#end` -- edit above this line, do not edit this line

View File

@@ -0,0 +1,91 @@
//`#start header` -- edit after this line, do not edit this line
`include "cypress.v"
//`#end` -- edit above this line, do not edit this line
// Generated on 11/24/2019 at 17:25
// Component: Sequencer
module Sequencer (
output req, /* request new data on leading edge */
output wdata,
output [2:0] debug_state,
input clock,
input dataclock, /* incoming data on leading edge */
input [7:0] opcode,
input index,
input sampleclock,
input reset
);
//`#start body` -- edit after this line, do not edit this line
localparam STATE_LOAD = 0;
localparam STATE_WRITING = 1;
reg state;
reg [5:0] countdown;
reg pulsepending;
assign req = (!reset && (state == STATE_LOAD));
assign wdata = (!reset && (state == STATE_WRITING) && (countdown == 0) && pulsepending);
assign debug_state = 0;
reg olddataclock;
wire dataclocked;
always @(posedge clock) olddataclock <= dataclock;
assign dataclocked = !olddataclock && dataclock;
reg oldsampleclock;
reg sampleclocked;
reg oldindex;
wire indexed;
always @(posedge clock) oldindex <= index;
assign indexed = !oldindex && index;
always @(posedge clock)
begin
if (reset)
begin
state <= STATE_LOAD;
countdown <= 0;
pulsepending <= 0;
oldsampleclock <= 0;
end
else
begin
if (!oldsampleclock && sampleclock)
sampleclocked <= 1;
oldsampleclock <= sampleclock;
case (state)
STATE_LOAD:
begin
/* A posedge on dataclocked indicates that another opcode has
* arrived. */
if (dataclocked)
begin
pulsepending <= opcode[7];
countdown <= opcode[5:0] - 1; /* compensate for delay in last tick */
state <= STATE_WRITING;
end
end
STATE_WRITING:
begin
if (sampleclocked)
begin
if (countdown == 0)
state <= STATE_LOAD;
else
countdown <= countdown - 1;
sampleclocked <= 0;
end
end
endcase
end
end
//`#end` -- edit above this line, do not edit this line
endmodule
//`#start footer` -- edit after this line, do not edit this line
//`#end` -- edit above this line, do not edit this line

View File

Binary file not shown.

View File

@@ -0,0 +1,156 @@
//`#start header` -- edit after this line, do not edit this line
`include "cypress.v"
//`#end` -- edit above this line, do not edit this line
// Generated on 11/16/2017 at 15:44
// Component: FIFOout
module SuperCounter (
input clk,
input reset,
input count,
output [7:0] d,
output drq,
output empty,
output ack
);
//`#start body` -- edit after this line, do not edit this line
parameter ResetValue = 0;
parameter Delta = 1;
wire [7:0] po;
assign d = po;
localparam STATE_RESET = 0;
localparam STATE_WAIT = 1;
localparam STATE_ADD = 2;
reg oldcount;
wire counted;
assign counted = count && !oldcount;
always @(posedge clk) oldcount <= count;
wire [2:0] cs;
assign cs = reset ? STATE_RESET : (counted ? STATE_ADD : STATE_WAIT);
cy_psoc3_dp #(.d0_init(ResetValue), .d1_init(Delta),
.cy_dpconfig(
{
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC___D0, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM0: STATE_RESET*/
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM1: STATE_WAIT*/
`CS_ALU_OP__ADD, `CS_SRCA_A0, `CS_SRCB_D1,
`CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM2: STATE_ADD*/
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM3: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM4: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM5: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM6: */
`CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
`CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
`CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
`CS_CMP_SEL_CFGA, /*CFGRAM7: */
8'hFF, 8'h00, /*CFG9: */
8'hFF, 8'hFF, /*CFG11-10: */
`SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH,
`SC_CI_A_ARITH, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL,
`SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI,
`SC_SI_A_DEFSI, /*CFG13-12: */
`SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0,
1'h0, `SC_FIFO1_BUS, `SC_FIFO0_BUS,
`SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN,
`SC_FB_NOCHN, `SC_CMP1_NOCHN,
`SC_CMP0_NOCHN, /*CFG15-14: */
10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX,
`SC_FIFO_LEVEL,`SC_FIFO_ASYNC,`SC_EXTCRC_DSBL,
`SC_WRK16CAT_DSBL /*CFG17-16: */
}
)) dp(
/* input */ .reset(1'b0),
/* input */ .clk(clk),
/* input [02:00] */ .cs_addr(cs),
/* input */ .route_si(1'b0),
/* input */ .route_ci(1'b0),
/* input */ .f0_load(1'b0),
/* input */ .f1_load(1'b0),
/* input */ .d0_load(1'b0),
/* input */ .d1_load(1'b0),
/* output */ .ce0(),
/* output */ .cl0(),
/* output */ .z0(),
/* output */ .ff0(),
/* output */ .ce1(),
/* output */ .cl1(),
/* output */ .z1(),
/* output */ .ff1(),
/* output */ .ov_msb(),
/* output */ .co_msb(),
/* output */ .cmsb(),
/* output */ .so(),
/* output */ .f0_bus_stat(),
/* output */ .f0_blk_stat(),
/* output */ .f1_bus_stat(),
/* output */ .f1_blk_stat(),
/* input */ .ci(1'b0), // Carry in from previous stage
/* output */ .co(),// Carry out to next stage
/* input */ .sir(1'b0), // Shift in from right side
/* output */ .sor(), // Shift out to right side
/* input */ .sil(1'b0), // Shift in from left side
/* output */ .sol(), // Shift out to left side
/* input */ .msbi(1'b0), // MSB chain in
/* output */ .msbo(), // MSB chain out
/* input [01:00] */ .cei(2'b0), // Compare equal in from prev stage
/* output [01:00] */ .ceo(), // Compare equal out to next stage
/* input [01:00] */ .cli(2'b0), // Compare less than in from prv stage
/* output [01:00] */ .clo(), // Compare less than out to next stage
/* input [01:00] */ .zi(2'b0), // Zero detect in from previous stage
/* output [01:00] */ .zo(), // Zero detect out to next stage
/* input [01:00] */ .fi(2'b0), // 0xFF detect in from previous stage
/* output [01:00] */ .fo(), // 0xFF detect out to next stage
/* input [01:00] */ .capi(2'b0), // Software capture from previous stage
/* output [01:00] */ .capo(), // Software capture to next stage
/* input */ .cfbi(1'b0), // CRC Feedback in from previous stage
/* output */ .cfbo(), // CRC Feedback out to next stage
/* input [07:00] */ .pi(8'b0), // Parallel data port
/* output [07:00] */ .po(po) // Parallel data port
);
//`#end` -- edit above this line, do not edit this line
endmodule
//`#start footer` -- edit after this line, do not edit this line
//`#end` -- edit above this line, do not edit this line

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -5,7 +5,6 @@
#include <setjmp.h>
#include "project.h"
#include "../protocol.h"
#include "../lib/common/crunch.h"
#define MOTOR_ON_TIME 5000 /* milliseconds */
#define STEP_INTERVAL_TIME 6 /* ms */
@@ -17,16 +16,19 @@
#define STEP_TOWARDS0 1
#define STEP_AWAYFROM0 0
static volatile uint32_t clock = 0;
static bool drive0_present;
static bool drive1_present;
static volatile uint32_t clock = 0; /* ms */
static volatile bool index_irq = false;
static bool motor_on = false;
static uint32_t motor_on_time = 0;
static bool homed = false;
static int current_track = 0;
static uint8_t current_drive_flags = 0;
static struct set_drive_frame current_drive_flags;
#define BUFFER_COUNT 16
#define BUFFER_COUNT 64 /* the maximum */
#define BUFFER_SIZE 64
static uint8_t td[BUFFER_COUNT];
static uint8_t dma_buffer[BUFFER_COUNT][BUFFER_SIZE] __attribute__((aligned()));
@@ -41,10 +43,27 @@ static volatile bool dma_underrun = false;
#define DECLARE_REPLY_FRAME(STRUCT, TYPE) \
STRUCT r = {.f = { .type = TYPE, .size = sizeof(STRUCT) }}
static void stop_motor(void);
static void system_timer_cb(void)
{
CyGlobalIntDisable;
clock++;
static int counter300rpm = 0;
counter300rpm++;
if (counter300rpm == 200)
counter300rpm = 0;
static int counter360rpm = 0;
counter360rpm++;
if (counter360rpm == 167)
counter360rpm = 0;
FAKE_INDEX_GENERATOR_REG_Write(
((counter300rpm == 0) ? 1 : 0)
| ((counter360rpm == 0) ? 2 : 0));
CyGlobalIntEnable;
}
@@ -85,10 +104,25 @@ static void print(const char* msg, ...)
UART_PutCRLF();
}
static void set_drive_flags(struct set_drive_frame* flags)
{
if (current_drive_flags.drive != flags->drive)
{
stop_motor();
homed = false;
}
current_drive_flags = *flags;
DRIVESELECT_REG_Write(flags->drive ? 2 : 1); /* select drive 1 or 0 */
DENSITY_REG_Write(flags->high_density); /* density bit */
INDEX_REG_Write(flags->index_mode);
}
static void start_motor(void)
{
if (!motor_on)
{
set_drive_flags(&current_drive_flags);
MOTOR_REG_Write(1);
CyDelay(1000);
homed = false;
@@ -99,12 +133,28 @@ static void start_motor(void)
CyWdtClear();
}
static void stop_motor(void)
{
if (motor_on)
{
MOTOR_REG_Write(0);
DRIVESELECT_REG_Write(0); /* deselect all drives */
motor_on = false;
}
}
static void wait_until_writeable(int ep)
{
while (USBFS_GetEPState(ep) != USBFS_IN_BUFFER_EMPTY)
;
}
static void wait_until_readable(int ep)
{
while (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_FULL)
;
}
static void send_reply(struct any_frame* f)
{
print("reply 0x%02x", f->f.type);
@@ -122,9 +172,15 @@ static void send_error(int code)
/* buffer must be big enough for a frame */
static int usb_read(int ep, uint8_t buffer[FRAME_SIZE])
{
if (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_FULL)
{
USBFS_EnableOutEP(ep);
wait_until_readable(ep);
}
int length = USBFS_GetEPCount(ep);
USBFS_ReadOutEP(ep, buffer, length);
while (USBFS_GetEPState(ep) == USBFS_OUT_BUFFER_FULL)
while (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_EMPTY)
;
return length;
}
@@ -138,25 +194,36 @@ static void cmd_get_version(struct any_frame* f)
static void step(int dir)
{
STEP_REG_Write(dir);
CyDelayUs(1);
STEP_REG_Write(dir | 2);
CyDelayUs(1);
STEP_REG_Write(dir);
STEP_REG_Write(dir); /* step high */
CyDelayUs(6);
STEP_REG_Write(dir | 2); /* step low */
CyDelayUs(6);
STEP_REG_Write(dir); /* step high again, drive moves now */
CyDelay(STEP_INTERVAL_TIME);
}
/* returns true if it looks like a drive is attached */
static bool home(void)
{
for (int i=0; i<100; i++)
{
/* Don't keep stepping forever, because if a drive's
* not connected bad things happen. */
if (TRACK0_REG_Read())
return true;
step(STEP_TOWARDS0);
}
return false;
}
static void seek_to(int track)
{
start_motor();
if (!homed)
if (!homed || (track == 0))
{
print("homing");
while (!TRACK0_REG_Read())
step(STEP_TOWARDS0);
/* Step to -1, which should be a nop, to reset the disk on disk change. */
step(STEP_TOWARDS0);
home();
homed = true;
current_track = 0;
@@ -167,11 +234,7 @@ static void seek_to(int track)
while (track != current_track)
{
if (TRACK0_REG_Read())
{
if (current_track != 0)
print("unexpectedly detected track 0");
current_track = 0;
}
if (track > current_track)
{
@@ -208,26 +271,40 @@ static void cmd_measure_speed(struct any_frame* f)
{
start_motor();
index_irq = false;
while (!index_irq)
;
index_irq = false;
int start_clock = clock;
int elapsed = 0;
while (!index_irq)
;
int end_clock = clock;
{
elapsed = clock - start_clock;
if (elapsed > 1000)
{
elapsed = 0;
break;
}
}
if (elapsed != 0)
{
index_irq = false;
start_clock = clock;
while (!index_irq)
elapsed = clock - start_clock;
}
DECLARE_REPLY_FRAME(struct speed_frame, F_FRAME_MEASURE_SPEED_REPLY);
r.period_ms = end_clock - start_clock;
r.period_ms = elapsed;
send_reply((struct any_frame*) &r);
}
static void cmd_bulk_test(struct any_frame* f)
static void cmd_bulk_write_test(struct any_frame* f)
{
uint8_t buffer[64];
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
for (int x=0; x<64; x++)
{
CyWdtClear();
for (int y=0; y<256; y++)
{
for (unsigned z=0; z<sizeof(buffer); z++)
@@ -236,11 +313,44 @@ static void cmd_bulk_test(struct any_frame* f)
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, buffer, sizeof(buffer));
}
}
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_TEST_REPLY);
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_WRITE_TEST_REPLY);
send_reply(&r);
}
static void cmd_bulk_read_test(struct any_frame* f)
{
uint8_t buffer[64];
bool passed = true;
for (int x=0; x<64; x++)
{
CyWdtClear();
for (int y=0; y<256; y++)
{
usb_read(FLUXENGINE_DATA_OUT_EP_NUM, buffer);
for (unsigned z=0; z<sizeof(buffer); z++)
{
if (buffer[z] != (uint8)(x+y+z))
{
print("fail %d+%d+%d == %d, not %d", x, y, z, buffer[z], (uint8)(x+y+z));
passed = false;
}
}
}
}
print("passed=%d", passed);
if (passed)
{
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_BULK_READ_TEST_REPLY);
send_reply(&r);
}
else
send_error(F_ERROR_INVALID_VALUE);
}
static void deinit_dma(void)
{
for (int i=0; i<BUFFER_COUNT; i++)
@@ -249,8 +359,8 @@ static void deinit_dma(void)
static void init_capture_dma(void)
{
dma_channel = CAPTURE_DMA_DmaInitialize(
2 /* bytes */,
dma_channel = SAMPLER_DMA_DmaInitialize(
1 /* bytes */,
true /* request per burst */,
HI16(CYDEV_PERIPH_BASE),
HI16(CYDEV_SRAM_BASE));
@@ -264,8 +374,8 @@ static void init_capture_dma(void)
nexti = 0;
CyDmaTdSetConfiguration(td[i], BUFFER_SIZE, td[nexti],
CY_DMA_TD_INC_DST_ADR | CAPTURE_DMA__TD_TERMOUT_EN);
CyDmaTdSetAddress(td[i], LO16((uint32)&SAMPLER_DATAPATH_F0_REG), LO16((uint32)&dma_buffer[i]));
CY_DMA_TD_INC_DST_ADR | SAMPLER_DMA__TD_TERMOUT_EN);
CyDmaTdSetAddress(td[i], LO16((uint32)SAMPLER_FIFO_FIFO_PTR), LO16((uint32)&dma_buffer[i]));
}
}
@@ -276,36 +386,31 @@ static void cmd_read(struct read_frame* f)
/* Do slow setup *before* we go into the real-time bit. */
SAMPLER_CONTROL_Write(1); /* reset */
{
uint8_t i = CyEnterCriticalSection();
SAMPLER_DATAPATH_F0_SET_LEVEL_MID;
SAMPLER_DATAPATH_F0_CLEAR;
SAMPLER_DATAPATH_F0_SINGLE_BUFFER_UNSET;
SAMPLER_FIFO_SET_LEVEL_NORMAL;
SAMPLER_FIFO_CLEAR;
SAMPLER_FIFO_SINGLE_BUFFER_UNSET;
CyExitCriticalSection(i);
}
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
init_capture_dma();
/* Wait for the beginning of a rotation. */
/* Wait for the beginning of a rotation, if requested. */
print("wait");
index_irq = false;
while (!index_irq)
;
index_irq = false;
crunch_state_t cs = {};
cs.outputptr = usb_buffer;
cs.outputlen = BUFFER_SIZE;
if (f->synced)
{
index_irq = false;
while (!index_irq)
;
index_irq = false;
}
dma_writing_to_td = 0;
dma_reading_from_td = -1;
dma_underrun = false;
int count = 0;
SAMPLER_CONTROL_Write(0); /* !reset */
CyDmaChSetInitialTd(dma_channel, td[dma_writing_to_td]);
CyDmaClearPendingDrq(dma_channel);
CyDmaChEnable(dma_channel, 1);
@@ -313,73 +418,67 @@ static void cmd_read(struct read_frame* f)
/* Wait for the first DMA transfer to complete, after which we can start the
* USB transfer. */
while ((dma_writing_to_td == 0) && !index_irq)
while (dma_writing_to_td == 0)
;
dma_reading_from_td = 0;
bool dma_running = true;
/* Start transferring. */
int revolutions = f->revolutions;
while (!dma_underrun)
uint32_t start_time = clock;
for (;;)
{
CyWdtClear();
/* Have we reached the index pulse? */
if (index_irq)
{
index_irq = false;
revolutions--;
if (revolutions == 0)
break;
}
/* If the sample session is over, stop reading but continue processing until
* the DMA chain is empty. */
/* Wait for the next block to be read. */
while (dma_reading_from_td == dma_writing_to_td)
if ((clock - start_time) >= f->milliseconds)
{
/* On an underrun, give up immediately. */
if (dma_underrun)
goto abort;
}
uint8_t dma_buffer_usage = 0;
while (dma_buffer_usage < BUFFER_SIZE)
{
cs.inputptr = dma_buffer[dma_reading_from_td] + dma_buffer_usage;
cs.inputlen = BUFFER_SIZE - dma_buffer_usage;
crunch(&cs);
dma_buffer_usage += BUFFER_SIZE - cs.inputlen;
count++;
if (cs.outputlen == 0)
if (dma_running)
{
while (USBFS_GetEPState(FLUXENGINE_DATA_IN_EP_NUM) != USBFS_IN_BUFFER_EMPTY)
{
if (index_irq || dma_underrun)
goto abort;
}
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE);
cs.outputptr = usb_buffer;
cs.outputlen = BUFFER_SIZE;
CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN);
while (CyDmaChGetRequest(dma_channel))
;
dma_running = false;
dma_underrun = false;
}
}
dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td);
/* If there's an underrun event, stop immediately. */
if (dma_underrun)
goto abort;
/* If there are no more blocks to be read, check to see if we've finished. */
if (dma_reading_from_td == dma_writing_to_td)
{
/* Also if we've run out of blocks to send. */
if (!dma_running)
goto abort;
}
else
{
/* Otherwise, there's a block waiting, so attempt to send it. */
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, dma_buffer[dma_reading_from_td], BUFFER_SIZE);
count++;
dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td);
}
}
abort:
CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN);
while (CyDmaChGetRequest(dma_channel))
;
abort:;
bool saved_dma_underrun = dma_underrun;
donecrunch(&cs);
/* Terminate the transfer (all transfers are an exact number of fragments). */
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
unsigned zz = cs.outputlen;
if (cs.outputlen != BUFFER_SIZE)
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE-cs.outputlen);
if ((cs.outputlen == BUFFER_SIZE) || (cs.outputlen == 0))
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, NULL, 0);
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, NULL, 0);
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
deinit_dma();
if (dma_underrun)
if (saved_dma_underrun)
{
print("underrun after %d packets");
send_error(F_ERROR_UNDERRUN);
@@ -389,7 +488,7 @@ abort:
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_READ_REPLY);
send_reply(&r);
}
print("count=%d i=%d d=%d zz=%d", count, index_irq, dma_underrun, zz);
print("count=%d i=%d d=%d", count, index_irq, dma_underrun);
}
static void init_replay_dma(void)
@@ -410,25 +509,28 @@ static void init_replay_dma(void)
CyDmaTdSetConfiguration(td[i], BUFFER_SIZE, td[nexti],
CY_DMA_TD_INC_SRC_ADR | SEQUENCER_DMA__TD_TERMOUT_EN);
CyDmaTdSetAddress(td[i], LO16((uint32)&dma_buffer[i]), LO16((uint32)&SEQUENCER_DATAPATH_F0_REG));
CyDmaTdSetAddress(td[i], LO16((uint32)&dma_buffer[i]), LO16((uint32)REPLAY_FIFO_FIFO_PTR));
}
}
static void cmd_write(struct write_frame* f)
{
print("cmd_write");
if (f->bytes_to_write % FRAME_SIZE)
{
send_error(F_ERROR_INVALID_VALUE);
return;
}
SEQUENCER_CONTROL_Write(1); /* put the sequencer into reset */
SIDE_REG_Write(f->side);
SEQUENCER_CONTROL_Write(1); /* reset */
{
uint8_t i = CyEnterCriticalSection();
SEQUENCER_DATAPATH_F0_SET_LEVEL_NORMAL;
SEQUENCER_DATAPATH_F0_CLEAR;
SEQUENCER_DATAPATH_F0_SINGLE_BUFFER_UNSET;
uint8_t i = CyEnterCriticalSection();
REPLAY_FIFO_SET_LEVEL_MID;
REPLAY_FIFO_CLEAR;
REPLAY_FIFO_SINGLE_BUFFER_UNSET;
CyExitCriticalSection(i);
}
seek_to(current_track);
@@ -443,13 +545,11 @@ static void cmd_write(struct write_frame* f)
dma_reading_from_td = -1;
dma_underrun = false;
crunch_state_t cs = {};
cs.outputlen = BUFFER_SIZE;
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
int old_reading_from_td = -1;
for (;;)
{
//CyWdtClear();
/* Read data from USB into the buffers. */
if (NEXT_BUFFER(dma_writing_to_td) != dma_reading_from_td)
@@ -457,54 +557,25 @@ static void cmd_write(struct write_frame* f)
if (writing && (dma_underrun || index_irq))
goto abort;
/* Read crunched data, if necessary. */
if (cs.inputlen == 0)
uint8_t* buffer = dma_buffer[dma_writing_to_td];
if (finished)
{
if (finished)
{
/* There's no more data to read, so fake some. */
for (int i=0; i<BUFFER_SIZE; i++)
usb_buffer[i+0] = 0x7f;
cs.inputptr = usb_buffer;
cs.inputlen = BUFFER_SIZE;
}
else
{
while (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) != USBFS_OUT_BUFFER_FULL)
{
if (writing && (dma_underrun || index_irq))
goto abort;
}
int length = usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer);
cs.inputptr = usb_buffer;
cs.inputlen = length;
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
count_read++;
if ((length < FRAME_SIZE) || (count_read == packets))
finished = true;
}
/* There's no more data to read, so fake some. */
memset(buffer, 0x3f, BUFFER_SIZE);
}
/* If there *is* data waiting in the buffer, uncrunch it. */
if (cs.inputlen != 0)
else
{
cs.outputptr = dma_buffer[dma_writing_to_td] + BUFFER_SIZE - cs.outputlen;
uncrunch(&cs);
if (cs.outputlen == 0)
{
/* Completed a DMA buffer; queue it for writing. */
dma_writing_to_td = NEXT_BUFFER(dma_writing_to_td);
cs.outputlen = BUFFER_SIZE;
}
(void) usb_read(FLUXENGINE_DATA_OUT_EP_NUM, buffer);
count_read++;
if (count_read == packets)
finished = true;
}
dma_writing_to_td = NEXT_BUFFER(dma_writing_to_td);
/* Once all the buffers are full, start writing. */
/* If we have a full buffer, start writing. */
if ((dma_reading_from_td == -1) && (dma_writing_to_td == BUFFER_COUNT-1))
{
dma_reading_from_td = old_reading_from_td = 0;
@@ -519,7 +590,7 @@ static void cmd_write(struct write_frame* f)
/* Wait for the index marker. While this happens, the DMA engine
* will prime the FIFO. */
index_irq = false;
while (!index_irq)
;
@@ -530,7 +601,7 @@ static void cmd_write(struct write_frame* f)
SEQUENCER_CONTROL_Write(0); /* start writing! */
}
}
if (writing && (dma_underrun || index_irq))
goto abort;
@@ -553,31 +624,28 @@ abort:
CyDmaChDisable(dma_channel);
}
//debug("p=%d cr=%d cw=%d f=%d l=%d w=%d index=%d underrun=%d", packets, count_read, count_written, finished, listening, writing, index_irq, dma_underrun);
print("p=%d cr=%d cw=%d f=%d w=%d index=%d underrun=%d", packets, count_read, count_written, finished, writing, index_irq, dma_underrun);
if (!finished)
{
while (count_read < packets)
/* There's still some data to read, so just read and blackhole it ---
* easier than trying to terminate the connection. */
while (count_read != packets)
{
if (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL)
{
int length = usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer);
if (length < FRAME_SIZE)
break;
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
count_read++;
}
(void) usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer);
count_read++;
}
USBFS_DisableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
}
deinit_dma();
if (dma_underrun)
{
print("underrun!");
send_error(F_ERROR_UNDERRUN);
return;
}
print("success");
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_WRITE_REPLY);
send_reply((struct any_frame*) &r);
}
@@ -605,17 +673,105 @@ static void cmd_erase(struct erase_frame* f)
static void cmd_set_drive(struct set_drive_frame* f)
{
if (current_drive_flags != f->drive_flags)
{
current_drive_flags = f->drive_flags;
DRIVE_REG_Write(current_drive_flags);
homed = false;
}
if (drive0_present && !drive1_present)
f->drive = 0;
if (drive1_present && !drive0_present)
f->drive = 1;
set_drive_flags(f);
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_SET_DRIVE_REPLY);
send_reply((struct any_frame*) &r);
}
static uint16_t read_output_voltage_mv(void)
{
OUTPUT_VOLTAGE_ADC_StartConvert();
OUTPUT_VOLTAGE_ADC_IsEndConversion(OUTPUT_VOLTAGE_ADC_WAIT_FOR_RESULT);
uint16_t samples = OUTPUT_VOLTAGE_ADC_GetResult16();
return OUTPUT_VOLTAGE_ADC_CountsTo_mVolts(samples);
}
static void read_output_voltages(struct voltages* v)
{
SIDE_REG_Write(1); /* set DIR to low (remember this is inverted) */
CyDelay(100);
v->logic0_mv = read_output_voltage_mv();
SIDE_REG_Write(0);
CyDelay(100);
v->logic1_mv = read_output_voltage_mv();
}
static uint16_t read_input_voltage_mv(void)
{
INPUT_VOLTAGE_ADC_StartConvert();
INPUT_VOLTAGE_ADC_IsEndConversion(INPUT_VOLTAGE_ADC_WAIT_FOR_RESULT);
uint16_t samples = INPUT_VOLTAGE_ADC_GetResult16();
return INPUT_VOLTAGE_ADC_CountsTo_mVolts(samples);
}
static void read_input_voltages(struct voltages* v)
{
home();
CyDelay(50);
v->logic0_mv = read_input_voltage_mv();
step(STEP_AWAYFROM0);
CyDelay(50);
v->logic1_mv = read_input_voltage_mv();
}
static void cmd_measure_voltages(void)
{
stop_motor();
INPUT_VOLTAGE_ADC_Start();
INPUT_VOLTAGE_ADC_SetPower(INPUT_VOLTAGE_ADC__HIGHPOWER);
OUTPUT_VOLTAGE_ADC_Start();
OUTPUT_VOLTAGE_ADC_SetPower(OUTPUT_VOLTAGE_ADC__HIGHPOWER);
DECLARE_REPLY_FRAME(struct voltages_frame, F_FRAME_MEASURE_VOLTAGES_REPLY);
CyWdtClear();
MOTOR_REG_Write(0); /* should be ignored anyway */
DRIVESELECT_REG_Write(0); /* deselect both drives */
CyDelay(200); /* wait for things to settle */
read_output_voltages(&r.output_both_off);
read_input_voltages(&r.input_both_off);
CyWdtClear();
DRIVESELECT_REG_Write(1); /* select drive 0 */
CyDelay(50);
read_output_voltages(&r.output_drive_0_selected);
read_input_voltages(&r.input_drive_0_selected);
MOTOR_REG_Write(1);
CyDelay(300);
CyWdtClear();
read_output_voltages(&r.output_drive_0_running);
read_input_voltages(&r.input_drive_0_running);
MOTOR_REG_Write(0);
CyDelay(300);
CyWdtClear();
DRIVESELECT_REG_Write(2); /* select drive 1 */
CyDelay(50);
read_output_voltages(&r.output_drive_1_selected);
read_input_voltages(&r.input_drive_1_selected);
MOTOR_REG_Write(1);
CyDelay(300);
CyWdtClear();
read_output_voltages(&r.output_drive_1_running);
read_input_voltages(&r.input_drive_1_running);
MOTOR_REG_Write(0);
CyDelay(300);
CyWdtClear();
DRIVESELECT_REG_Write(0);
homed = false;
INPUT_VOLTAGE_ADC_Stop();
OUTPUT_VOLTAGE_ADC_Stop();
send_reply((struct any_frame*) &r);
}
static void handle_command(void)
{
static uint8_t input_buffer[FRAME_SIZE];
@@ -637,8 +793,12 @@ static void handle_command(void)
cmd_measure_speed(f);
break;
case F_FRAME_BULK_TEST_CMD:
cmd_bulk_test(f);
case F_FRAME_BULK_WRITE_TEST_CMD:
cmd_bulk_write_test(f);
break;
case F_FRAME_BULK_READ_TEST_CMD:
cmd_bulk_read_test(f);
break;
case F_FRAME_READ_CMD:
@@ -660,28 +820,49 @@ static void handle_command(void)
case F_FRAME_SET_DRIVE_CMD:
cmd_set_drive((struct set_drive_frame*) f);
break;
case F_FRAME_MEASURE_VOLTAGES_CMD:
cmd_measure_voltages();
break;
default:
send_error(F_ERROR_BAD_COMMAND);
}
}
static void detect_drives(void)
{
current_drive_flags.drive = 0;
start_motor();
drive0_present = home();
stop_motor();
current_drive_flags.drive = 1;
start_motor();
drive1_present = home();
stop_motor();
print("drive 0: %s drive 1: %s", drive0_present ? "yes" : "no", drive1_present ? "yes" : "no");
}
int main(void)
{
CyGlobalIntEnable;
CySysTickStart();
CySysTickSetCallback(4, system_timer_cb);
INDEX_IRQ_StartEx(&index_irq_cb);
CAPTURE_DMA_FINISHED_IRQ_StartEx(&capture_dma_finished_irq_cb);
SAMPLER_DMA_FINISHED_IRQ_StartEx(&capture_dma_finished_irq_cb);
SEQUENCER_DMA_FINISHED_IRQ_StartEx(&replay_dma_finished_irq_cb);
DRIVE_REG_Write(0);
INPUT_VOLTAGE_ADC_Stop();
OUTPUT_VOLTAGE_ADC_Stop();
DRIVESELECT_REG_Write(0);
UART_Start();
USBFS_Start(0, USBFS_DWR_VDDD_OPERATION);
USBFS_DisableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
detect_drives();
CyWdtStart(CYWDT_1024_TICKS, CYWDT_LPMODE_DISABLED);
/* UART_PutString("GO\r"); */
for (;;)
{
CyWdtClear();
@@ -690,23 +871,21 @@ int main(void)
{
uint32_t time_on = clock - motor_on_time;
if (time_on > MOTOR_ON_TIME)
{
MOTOR_REG_Write(0);
motor_on = false;
}
stop_motor();
}
if (!USBFS_GetConfiguration() || USBFS_IsConfigurationChanged())
{
print("Waiting for USB...");
while (!USBFS_GetConfiguration())
;
CyWdtClear();
print("USB ready");
USBFS_EnableOutEP(FLUXENGINE_CMD_OUT_EP_NUM);
}
if (USBFS_GetEPState(FLUXENGINE_CMD_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL)
{
set_drive_flags(&current_drive_flags);
handle_command();
USBFS_EnableOutEP(FLUXENGINE_CMD_OUT_EP_NUM);
print("idle");

View File

@@ -1,24 +1,40 @@
PACKAGES = zlib sqlite3 libusb-1.0
export CFLAGS = --std=c++14 -ffunction-sections -fdata-sections
export LDFLAGS =
export COPTFLAGS = -Os
export LDOPTFLAGS = -Os -s
export CDBGFLAGS = -O0 -g
export LDDBGFLAGS = -O0 -g
ifeq ($(OS), Windows_NT)
export CXX = /mingw32/bin/g++
export AR = /mingw32/bin/ar rcs
export STRIP = /mingw32/bin/strip
export CFLAGS = -O3 -g --std=c++14 -I/mingw32/include/libusb-1.0
export LDFLAGS = -O3
export CFLAGS += -I/mingw32/include/libusb-1.0
export LDFLAGS +=
export LIBS = -static -lz -lsqlite3 -lusb-1.0
export EXTENSION = .exe
else
packages-exist = $(shell pkg-config --exists $(PACKAGES) && echo yes)
ifneq ($(packages-exist),yes)
$(warning These pkg-config packages are installed: $(shell pkg-config --list-all | sort | awk '{print $$1}'))
$(error You must have these pkg-config packages installed: $(PACKAGES))
endif
export CXX = g++
export AR = ar rcs
export STRIP = strip
export CFLAGS = -Og -g --std=c++14 $(shell pkg-config --cflags $(PACKAGES))
export LDFLAGS = -Og
export CFLAGS += $(shell pkg-config --cflags $(PACKAGES))
export LDFLAGS +=
export LIBS = $(shell pkg-config --libs $(PACKAGES))
export EXTENSION =
endif
CFLAGS += -Ilib -Idep/fmt
CFLAGS += -Ilib -Idep/fmt -Iarch
export OBJDIR = .obj

View File

@@ -24,17 +24,10 @@ Don't believe me? Watch the demo reel!
<iframe width="373" height="210" src="https://www.youtube.com/embed/m_s1iw8eW7o" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
**Important note.** On 2019-02-09 I did a hardware redesign and moved the pins on
the board. Sorry for the inconvenience, but it means you don't have to modify
the board any more to make it work. If you built the hardware prior to then,
you'll need to adjust it.
**Another important note.** On 2019-07-03 I've revamped the build process and
the (command line) user interface. It should be much nicer now, not least in
that there's a single client binary with all the functionality in it. The
interface is a little different, but not much. The build process is now
better (simpler). See [the building](doc/building.md) and
[using](doc/using.md) pages for more details.
**Important note.** On 2020-04-02 I changed the bytecode format (and firmware).
Flux files will need to be upgraded with `fluxengine upgradefluxfile`. The new
format should be more reliable and use way, way less bandwidth. Sorry for the
inconvenience.
Where?
------
@@ -79,10 +72,10 @@ people who've had it work).
| Format | Read? | Write? | Notes |
|:-----------------------------------------|:-----:|:------:|-------|
| IBM PC compatible | 🦄 | | and compatibles (like the Atari ST) |
| [Acorn ADFS](doc/disk-acornadfs.md) | 🦄 | | single- and double- sided |
| [Acorn DFS](doc/disk-acorndfs.md) | 🦄 | | |
| [Ampro Little Board](doc/disk-ampro.md) | 🦖 | | |
| [IBM PC compatible](doc/disk-ibm.md) | 🦄 | 🦄 | and compatibles (like the Atari ST) |
| [Acorn ADFS](doc/disk-acornadfs.md) | 🦄 | 🦖* | single- and double- sided |
| [Acorn DFS](doc/disk-acorndfs.md) | 🦄 | 🦖* | |
| [Ampro Little Board](doc/disk-ampro.md) | 🦖 | 🦖* | |
| [Apple II DOS 3.3](doc/disk-apple2.md) | 🦄 | | doesn't do logical sector remapping |
| [Amiga](doc/disk-amiga.md) | 🦄 | | |
| [Commodore 64 1541](doc/disk-c64.md) | 🦖 | | and probably the other GCR formats |
@@ -90,9 +83,13 @@ people who've had it work).
| [Brother 240kB](doc/disk-brother.md) | 🦄 | 🦄 | |
| [Brother FB-100](doc/disk-fb100.md) | 🦖 | | Tandy Model 100, Husky Hunter, knitting machines |
| [Macintosh 800kB](doc/disk-macintosh.md) | 🦖 | | and probably the 400kB too |
| [TRS-80](doc/disk-trs80.md) | 🦖 | | a minor variation of the IBM scheme |
| [TRS-80](doc/disk-trs80.md) | 🦖 | 🦖* | a minor variation of the IBM scheme |
{: .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
@@ -105,6 +102,7 @@ at least, check the CRC so what data's there is probably good.
|:-----------------------------------------|:-----:|:------:|-------|
| [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 |
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8-inch |
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8-inch _and_ hard sectors |
{: .datatable }
@@ -188,3 +186,8 @@ maintained by Victor Zverovich (`vitaut <https://github.com/vitaut>`) and
Jonathan Müller (`foonathan <https://github.com/foonathan>`) with
contributions from many other people. It is licensed under the terms of the
BSD license. Please see the contents of the directory for the full text.
As an exception, `dep/emu` contains parts of the OpenBSD C library
code, Todd Miller and William A. Rowe (and probably others). It is licensed
under the terms of the 3-clause BSD license. Please see the contents of the
directory for the full text. It's been lightly modified by me.

101
arch/amiga/amiga.cc Normal file
View File

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

43
arch/amiga/amiga.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef AMIGA_H
#define AMIGA_H
#include "encoders/encoders.h"
#define AMIGA_SECTOR_RECORD 0xaaaa44894489LL
#define AMIGA_TRACKS_PER_DISK 80
#define AMIGA_SECTORS_PER_TRACK 11
#define AMIGA_RECORD_SIZE 0x21f
class Sector;
class Fluxmap;
class SectorSet;
class AmigaDecoder : public AbstractDecoder
{
public:
virtual ~AmigaDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
std::set<unsigned> requiredSectors(Track& track) const;
};
class AmigaEncoder : public AbstractEncoder
{
public:
virtual ~AmigaEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
};
extern FlagGroup amigaEncoderFlags;
extern uint32_t amigaChecksum(const Bytes& bytes);
extern Bytes amigaInterleave(const Bytes& input);
extern Bytes amigaDeinterleave(const uint8_t*& input, size_t len);
extern Bytes amigaDeinterleave(const Bytes& input);
#endif

65
arch/amiga/decoder.cc Normal file
View File

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

129
arch/amiga/encoder.cc Normal file
View File

@@ -0,0 +1,129 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "amiga.h"
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
FlagGroup amigaEncoderFlags;
static DoubleFlag clockRateUs(
{ "--clock-rate" },
"Encoded data clock rate (microseconds).",
2.00);
static DoubleFlag postIndexGapMs(
{ "--post-index-gap" },
"Post-index gap before first sector header (milliseconds).",
0.5);
static bool lastBit;
static int charToInt(char c)
{
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
{
for (bool bit : src)
{
if (cursor < bits.size())
bits[cursor++] = bit;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
{
cursor += width;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
{
assert(!(bytes.size() & 3));
Bytes interleaved = amigaInterleave(bytes);
encodeMfm(bits, cursor, interleaved, lastBit);
}
static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, uint32_t data)
{
Bytes b(4);
ByteWriter bw(b);
bw.write_be32(data);
write_interleaved_bytes(bits, cursor, b);
}
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector)
{
if ((sector->data.size() != 512) && (sector->data.size() != 528))
Error() << "unsupported sector size --- you must pick 512 or 528";
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6*8);
std::vector<bool> headerBits(20*16);
unsigned headerCursor = 0;
Bytes header =
{
0xff, /* Amiga 1.0 format byte */
(uint8_t) ((sector->logicalTrack<<1) | sector->logicalSide),
(uint8_t) sector->logicalSector,
(uint8_t) (AMIGA_SECTORS_PER_TRACK - sector->logicalSector)
};
write_interleaved_bytes(headerBits, headerCursor, header);
Bytes recoveryInfo(16);
if (sector->data.size() == 528)
recoveryInfo = sector->data.slice(512, 16);
write_interleaved_bytes(headerBits, headerCursor, recoveryInfo);
std::vector<bool> dataBits(512*16);
unsigned dataCursor = 0;
write_interleaved_bytes(dataBits, dataCursor, sector->data);
write_bits(bits, cursor, headerBits);
uint32_t headerChecksum = amigaChecksum(toBytes(headerBits));
write_interleaved_bytes(bits, cursor, headerChecksum);
uint32_t dataChecksum = amigaChecksum(toBytes(dataBits));
write_interleaved_bytes(bits, cursor, dataChecksum);
write_bits(bits, cursor, dataBits);
}
std::unique_ptr<Fluxmap> AmigaEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, postIndexGapMs * 1000 / clockRateUs, { true, false });
lastBit = false;
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
}
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
}

View File

@@ -9,6 +9,9 @@
#define BROTHER_DATA_RECORD_CHECKSUM 3
#define BROTHER_DATA_RECORD_ENCODED_SIZE 415
#define BROTHER_TRACKS_PER_DISK 78
#define BROTHER_SECTORS_PER_TRACK 12
class Sector;
class Fluxmap;
@@ -22,9 +25,15 @@ public:
void decodeDataRecord();
};
extern void writeBrotherSectorHeader(std::vector<bool>& bits, unsigned& cursor,
int track, int sector);
extern void writeBrotherSectorData(std::vector<bool>& bits, unsigned& cursor,
const Bytes& data);
class BrotherEncoder : public AbstractEncoder
{
public:
virtual ~BrotherEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
};
extern FlagGroup brotherEncoderFlags;
#endif

View File

@@ -3,6 +3,7 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "record.h"
#include "brother.h"
#include "sector.h"

167
arch/brother/encoder.cc Normal file
View File

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

View File

View File

View File

View File

View File

View File

View File

View File

@@ -73,8 +73,10 @@ const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56c);
* encoding (you can't do 10 00). So this can't be spoofed by user data.
*
* shifted: 10 00 10 01 00 01 00 1
*
* It's repeated three times.
*/
const FluxPattern MFM_PATTERN(16, 0x4489);
const FluxPattern MFM_PATTERN(48, 0x448944894489LL);
const FluxMatchers ANY_RECORD_PATTERN(
{
@@ -100,7 +102,8 @@ AbstractDecoder::RecordType IbmDecoder::advanceToNextRecord()
if (_currentHeaderLength > 0)
readRawBits(_currentHeaderLength*16);
auto idbits = readRawBits(16);
uint8_t id = decodeFmMfm(idbits).slice(0, 1)[0];
const Bytes idbytes = decodeFmMfm(idbits);
uint8_t id = idbytes.slice(0, 1)[0];
seek(here);
switch (id)
@@ -134,6 +137,9 @@ void IbmDecoder::decodeSectorRecord()
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, _currentHeaderLength + 5));
if (wantCrc == gotCrc)
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
if (_ignoreSideByte)
_sector->logicalSide = _sector->physicalSide;
}
void IbmDecoder::decodeDataRecord()

235
arch/ibm/encoder.cc Normal file
View File

@@ -0,0 +1,235 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "ibm.h"
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
#include "fmt/format.h"
#include <ctype.h>
/* IAM record separator:
* 0xC2 is:
* data: 1 1 0 0 0 0 1 0 = 0xc2
* mfm: 01 01 00 10 10 10 01 00 = 0x5254
* special: 01 01 00 10 00 10 01 00 = 0x5224
*/
#define MFM_IAM_SEPARATOR 0x5224
/* FM IAM record:
* flux: XXXX-XXX-XXXX-X- = 0xf77a
* clock: X X - X - X X X = 0xd7
* data: X X X X X X - - = 0xfc
*/
#define FM_IAM_RECORD 0xf77a
/* MFM IAM record:
* data: 1 1 1 1 1 1 0 0 = 0xfc
* flux: 01 01 01 01 01 01 00 10 = 0x5552
*/
#define MFM_IAM_RECORD 0x5552
/* MFM record separator:
* 0xA1 is:
* data: 1 0 1 0 0 0 0 1 = 0xa1
* mfm: 01 00 01 00 10 10 10 01 = 0x44a9
* special: 01 00 01 00 10 00 10 01 = 0x4489
* ^^^^^
* When shifted out of phase, the special 0xa1 byte becomes an illegal
* encoding (you can't do 10 00). So this can't be spoofed by user data.
*
* shifted: 10 00 10 01 00 01 00 1
*
* It's repeated three times.
*/
#define MFM_RECORD_SEPARATOR 0x4489
#define MFM_RECORD_SEPARATOR_BYTE 0xa1
/* MFM IDAM byte:
* data: 1 1 1 1 1 1 1 0 = 0xfe
* mfm: 01 01 01 01 01 01 01 00 = 0x5554
*/
/* MFM DAM byte:
* data: 1 1 1 1 1 0 1 1 = 0xfb
* mfm: 01 01 01 01 01 00 01 01 = 0x5545
*/
static int charToInt(char c)
{
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
}
void IbmEncoder::writeRawBits(uint32_t data, int width)
{
_cursor += width;
_lastBit = data & 1;
for (int i=0; i<width; i++)
{
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
void IbmEncoder::writeBytes(const Bytes& bytes)
{
if (_parameters.useFm)
encodeFm(_bits, _cursor, bytes);
else
encodeMfm(_bits, _cursor, bytes, _lastBit);
}
void IbmEncoder::writeBytes(int count, uint8_t byte)
{
Bytes bytes = { byte };
for (int i=0; i<count; i++)
writeBytes(bytes);
}
static uint8_t decodeUint16(uint16_t raw)
{
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
}
std::unique_ptr<Fluxmap> IbmEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
double clockRateUs = 1e3 / _parameters.clockRateKhz;
if (!_parameters.useFm)
clockRateUs /= 2.0;
int bitsPerRevolution = (_parameters.trackLengthMs * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
uint8_t idamUnencoded = decodeUint16(_parameters.idamByte);
uint8_t damUnencoded = decodeUint16(_parameters.damByte);
uint8_t sectorSize = 0;
{
int s = _parameters.sectorSize >> 7;
while (s > 1)
{
s >>= 1;
sectorSize += 1;
}
}
uint8_t gapFill = _parameters.useFm ? 0x00 : 0x4e;
writeBytes(_parameters.gap0, gapFill);
if (_parameters.emitIam)
{
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
if (!_parameters.useFm)
{
for (int i=0; i<3; i++)
writeRawBits(MFM_IAM_SEPARATOR, 16);
}
writeRawBits(_parameters.useFm ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
writeBytes(_parameters.gap1, gapFill);
}
bool first = true;
for (char sectorChar : _parameters.sectorSkew)
{
int sectorId = charToInt(sectorChar);
if (!first)
writeBytes(_parameters.gap3, gapFill);
first = false;
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
if (!sectorData)
Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId);
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
{
Bytes header;
ByteWriter bw(header);
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
if (!_parameters.useFm)
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(idamUnencoded);
bw.write_8(sectorData->logicalTrack);
bw.write_8(sectorData->logicalSide);
bw.write_8(sectorData->logicalSector + _parameters.startSectorId);
bw.write_8(sectorSize);
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!_parameters.useFm)
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(_parameters.idamByte, 16);
conventionalHeaderStart += 1;
writeBytes(header.slice(conventionalHeaderStart));
}
writeBytes(_parameters.gap2, gapFill);
{
Bytes data;
ByteWriter bw(data);
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
if (!_parameters.useFm)
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(damUnencoded);
Bytes truncatedData = sectorData->data.slice(0, _parameters.sectorSize);
bw += truncatedData;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!_parameters.useFm)
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(_parameters.damByte, 16);
conventionalHeaderStart += 1;
writeBytes(data.slice(conventionalHeaderStart));
}
}
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeBytes(1, gapFill);
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);
return fluxmap;
}

View File

@@ -2,6 +2,7 @@
#define IBM_H
#include "decoders/decoders.h"
#include "encoders/encoders.h"
/* IBM format (i.e. ordinary PC floppies). */
@@ -31,66 +32,68 @@ struct IbmIdam
class IbmDecoder : public AbstractDecoder
{
public:
IbmDecoder(unsigned sectorBase):
_sectorBase(sectorBase)
IbmDecoder(unsigned sectorBase, bool ignoreSideByte=false,
const std::set<unsigned> requiredSectors=std::set<unsigned>()):
_sectorBase(sectorBase),
_ignoreSideByte(ignoreSideByte),
_requiredSectors(requiredSectors)
{}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
std::set<unsigned> requiredSectors(Track& track) const
{ return _requiredSectors; }
private:
unsigned _sectorBase;
bool _ignoreSideByte;
std::set<unsigned> _requiredSectors;
unsigned _currentSectorSize;
unsigned _currentHeaderLength;
};
#if 0
class AbstractIbmDecoder : public AbstractSoftSectorDecoder
struct IbmParameters
{
int trackLengthMs;
int sectorSize;
bool emitIam;
int startSectorId;
int clockRateKhz;
bool useFm;
uint16_t idamByte;
uint16_t damByte;
int gap0;
int gap1;
int gap2;
int gap3;
std::string sectorSkew;
};
class IbmEncoder : public AbstractEncoder
{
public:
AbstractIbmDecoder(unsigned sectorIdBase):
_sectorIdBase(sectorIdBase)
{}
virtual ~AbstractIbmDecoder() {}
IbmEncoder(const IbmParameters& parameters):
_parameters(parameters)
{}
SectorVector decodeToSectors(const RawRecordVector& rawRecords, unsigned physicalTrack, unsigned physicalSide);
virtual ~IbmEncoder() {}
protected:
virtual int skipHeaderBytes() const = 0;
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
private:
unsigned _sectorIdBase;
void writeRawBits(uint32_t data, int width);
void writeBytes(const Bytes& bytes);
void writeBytes(int count, uint8_t value);
void writeSync();
private:
IbmParameters _parameters;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
class IbmFmDecoder : public AbstractIbmDecoder
{
public:
IbmFmDecoder(unsigned sectorIdBase):
AbstractIbmDecoder(sectorIdBase)
{}
int recordMatcher(uint64_t fifo) const;
protected:
int skipHeaderBytes() const
{ return 0; }
};
class IbmMfmDecoder : public AbstractIbmDecoder
{
public:
IbmMfmDecoder(unsigned sectorIdBase):
AbstractIbmDecoder(sectorIdBase)
{}
nanoseconds_t guessClock(Fluxmap& fluxmap) const;
int recordMatcher(uint64_t fifo) const;
protected:
int skipHeaderBytes() const
{ return 3; }
};
#endif
#endif

View File

@@ -153,6 +153,9 @@ void MacintoshDecoder::decodeSectorRecord()
uint8_t formatByte = decode_data_gcr(header[3]);
uint8_t wantedsum = decode_data_gcr(header[4]);
if (encodedSector > 11)
return;
_sector->logicalTrack = _track->physicalTrack;
_sector->logicalSide = decode_side(encodedSide);
_sector->logicalSector = encodedSector;
@@ -177,5 +180,29 @@ void MacintoshDecoder::decodeDataRecord()
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
_sector->status = Sector::BAD_CHECKSUM;
_sector->data = decode_crazy_data(inputbuffer, _sector->status);
Bytes userData = decode_crazy_data(inputbuffer, _sector->status);
_sector->data.clear();
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
}
std::set<unsigned> MacintoshDecoder::requiredSectors(Track& track) const
{
int count;
if (track.physicalTrack < 16)
count = 12;
else if (track.physicalTrack < 32)
count = 11;
else if (track.physicalTrack < 48)
count = 10;
else if (track.physicalTrack < 64)
count = 9;
else
count = 8;
std::set<unsigned> sectors;
while (count--)
sectors.insert(count);
return sectors;
}

View File

@@ -18,6 +18,8 @@ public:
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
std::set<unsigned> requiredSectors(Track& track) const;
};
#endif

View File

@@ -37,7 +37,7 @@ AbstractDecoder::RecordType MxDecoder::advanceToNextRecord()
const FluxMatcher* matcher = nullptr;
_sector->clock = _clock = _fmr->seekToPattern(ID_PATTERN, matcher);
readRawBits(32); /* skip the ID mark */
readRawBits(32); /* skip the track number */
_logicalTrack = decodeFmMfm(readRawBits(32)).reader().read_be16();
}
else if (_currentSector == 10)
{
@@ -67,7 +67,7 @@ void MxDecoder::decodeSectorRecord()
gotChecksum += br.read_le16();
uint16_t wantChecksum = br.read_le16();
_sector->logicalTrack = _track->physicalTrack;
_sector->logicalTrack = _logicalTrack;
_sector->logicalSide = _track->physicalSide;
_sector->logicalSector = _currentSector;
_sector->data = bytes.slice(0, SECTOR_SIZE);

View File

@@ -15,6 +15,7 @@ public:
private:
nanoseconds_t _clock;
int _currentSector;
int _logicalTrack;
};
#endif

29
dep/emu/charclass.h Normal file
View File

@@ -0,0 +1,29 @@
/*
* Public domain, 2008, Todd C. Miller <Todd.Miller@courtesan.com>
*
* $OpenBSD: charclass.h,v 1.1 2008/10/01 23:04:13 millert Exp $
*/
/*
* POSIX character class support for fnmatch() and glob().
*/
static struct cclass {
const char *name;
int (*isctype)(int);
} cclasses[] = {
{ "alnum", isalnum },
{ "alpha", isalpha },
{ "blank", isblank },
{ "cntrl", iscntrl },
{ "digit", isdigit },
{ "graph", isgraph },
{ "lower", islower },
{ "print", isprint },
{ "punct", ispunct },
{ "space", isspace },
{ "upper", isupper },
{ "xdigit", isxdigit },
{ NULL, NULL }
};
#define NCCLASSES (sizeof(cclasses) / sizeof(cclasses[0]) - 1)

481
dep/emu/fnmatch.c Normal file
View File

@@ -0,0 +1,481 @@
/*
* This has been lightly tweaked by me, David Given, to change the exported name
* so that it doesn't conflict with the real fnmatch (if any).
*/
/* $OpenBSD: fnmatch.c,v 1.16 2011/12/06 11:47:46 stsp Exp $ */
/* Copyright (c) 2011, VMware, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the VMware, Inc. nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 2008 Todd C. Miller <millert@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* Authored by William A. Rowe Jr. <wrowe; apache.org, vmware.com>, April 2011
*
* Derived from The Open Group Base Specifications Issue 7, IEEE Std 1003.1-2008
* as described in;
* http://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html
*
* Filename pattern matches defined in section 2.13, "Pattern Matching Notation"
* from chapter 2. "Shell Command Language"
* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13
* where; 1. A bracket expression starting with an unquoted <circumflex> '^'
* character CONTINUES to specify a non-matching list; 2. an explicit <period> '.'
* in a bracket expression matching list, e.g. "[.abc]" does NOT match a leading
* <period> in a filename; 3. a <left-square-bracket> '[' which does not introduce
* a valid bracket expression is treated as an ordinary character; 4. a differing
* number of consecutive slashes within pattern and string will NOT match;
* 5. a trailing '\' in FNM_ESCAPE mode is treated as an ordinary '\' character.
*
* Bracket expansion defined in section 9.3.5, "RE Bracket Expression",
* from chapter 9, "Regular Expressions"
* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_03_05
* with no support for collating symbols, equivalence class expressions or
* character class expressions. A partial range expression with a leading
* hyphen following a valid range expression will match only the ordinary
* <hyphen> and the ending character (e.g. "[a-m-z]" will match characters
* 'a' through 'm', a <hyphen> '-', or a 'z').
*
* Supports BSD extensions FNM_LEADING_DIR to match pattern to the end of one
* path segment of string, and FNM_CASEFOLD to ignore alpha case.
*
* NOTE: Only POSIX/C single byte locales are correctly supported at this time.
* Notably, non-POSIX locales with FNM_CASEFOLD produce undefined results,
* particularly in ranges of mixed case (e.g. "[A-z]") or spanning alpha and
* nonalpha characters within a range.
*
* XXX comments below indicate porting required for multi-byte character sets
* and non-POSIX locale collation orders; requires mbr* APIs to track shift
* state of pattern and string (rewinding pattern and string repeatedly).
*
* Certain parts of the code assume 0x00-0x3F are unique with any MBCS (e.g.
* UTF-8, SHIFT-JIS, etc). Any implementation allowing '\' as an alternate
* path delimiter must be aware that 0x5C is NOT unique within SHIFT-JIS.
*/
#include "fnmatch.h"
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include "charclass.h"
#define RANGE_MATCH 1
#define RANGE_NOMATCH 0
#define RANGE_ERROR (-1)
static int
classmatch(const char *pattern, char test, int foldcase, const char **ep)
{
struct cclass *cc;
const char *colon;
size_t len;
int rval = RANGE_NOMATCH;
const char * const mismatch = pattern;
if (*pattern != '[' || pattern[1] != ':') {
*ep = mismatch;
return(RANGE_ERROR);
}
pattern += 2;
if ((colon = strchr(pattern, ':')) == NULL || colon[1] != ']') {
*ep = mismatch;
return(RANGE_ERROR);
}
*ep = colon + 2;
len = (size_t)(colon - pattern);
if (foldcase && strncmp(pattern, "upper:]", 7) == 0)
pattern = "lower:]";
for (cc = cclasses; cc->name != NULL; cc++) {
if (!strncmp(pattern, cc->name, len) && cc->name[len] == '\0') {
if (cc->isctype((unsigned char)test))
rval = RANGE_MATCH;
break;
}
}
if (cc->name == NULL) {
/* invalid character class, treat as normal text */
*ep = mismatch;
rval = RANGE_ERROR;
}
return(rval);
}
/* Most MBCS/collation/case issues handled here. Wildcard '*' is not handled.
* EOS '\0' and the FNM_PATHNAME '/' delimiters are not advanced over,
* however the "\/" sequence is advanced to '/'.
*
* Both pattern and string are **char to support pointer increment of arbitrary
* multibyte characters for the given locale, in a later iteration of this code
*/
static int fnmatch_ch(const char **pattern, const char **string, int flags)
{
const char * const mismatch = *pattern;
const int nocase = !!(flags & FNM_CASEFOLD);
const int escape = !(flags & FNM_NOESCAPE);
const int slash = !!(flags & FNM_PATHNAME);
int result = FNM_NOMATCH;
const char *startch;
int negate;
if (**pattern == '[')
{
++*pattern;
/* Handle negation, either leading ! or ^ operators (never both) */
negate = ((**pattern == '!') || (**pattern == '^'));
if (negate)
++*pattern;
/* ']' is an ordinary character at the start of the range pattern */
if (**pattern == ']')
goto leadingclosebrace;
while (**pattern)
{
if (**pattern == ']') {
++*pattern;
/* XXX: Fix for MBCS character width */
++*string;
return (result ^ negate);
}
if (escape && (**pattern == '\\')) {
++*pattern;
/* Patterns must be terminated with ']', not EOS */
if (!**pattern)
break;
}
/* Patterns must be terminated with ']' not '/' */
if (slash && (**pattern == '/'))
break;
/* Match character classes. */
if (classmatch(*pattern, **string, nocase, pattern)
== RANGE_MATCH) {
result = 0;
continue;
}
leadingclosebrace:
/* Look at only well-formed range patterns;
* "x-]" is not allowed unless escaped ("x-\]")
* XXX: Fix for locale/MBCS character width
*/
if (((*pattern)[1] == '-') && ((*pattern)[2] != ']'))
{
startch = *pattern;
*pattern += (escape && ((*pattern)[2] == '\\')) ? 3 : 2;
/* NOT a properly balanced [expr] pattern, EOS terminated
* or ranges containing a slash in FNM_PATHNAME mode pattern
* fall out to to the rewind and test '[' literal code path
*/
if (!**pattern || (slash && (**pattern == '/')))
break;
/* XXX: handle locale/MBCS comparison, advance by MBCS char width */
if ((**string >= *startch) && (**string <= **pattern))
result = 0;
else if (nocase && (isupper(**string) || isupper(*startch)
|| isupper(**pattern))
&& (tolower(**string) >= tolower(*startch))
&& (tolower(**string) <= tolower(**pattern)))
result = 0;
++*pattern;
continue;
}
/* XXX: handle locale/MBCS comparison, advance by MBCS char width */
if ((**string == **pattern))
result = 0;
else if (nocase && (isupper(**string) || isupper(**pattern))
&& (tolower(**string) == tolower(**pattern)))
result = 0;
++*pattern;
}
/* NOT a properly balanced [expr] pattern; Rewind
* and reset result to test '[' literal
*/
*pattern = mismatch;
result = FNM_NOMATCH;
}
else if (**pattern == '?') {
/* Optimize '?' match before unescaping **pattern */
if (!**string || (slash && (**string == '/')))
return FNM_NOMATCH;
result = 0;
goto fnmatch_ch_success;
}
else if (escape && (**pattern == '\\') && (*pattern)[1]) {
++*pattern;
}
/* XXX: handle locale/MBCS comparison, advance by the MBCS char width */
if (**string == **pattern)
result = 0;
else if (nocase && (isupper(**string) || isupper(**pattern))
&& (tolower(**string) == tolower(**pattern)))
result = 0;
/* Refuse to advance over trailing slash or nulls
*/
if (!**string || !**pattern || (slash && ((**string == '/') || (**pattern == '/'))))
return result;
fnmatch_ch_success:
++*pattern;
++*string;
return result;
}
int fnmatch(const char *pattern, const char *string, int flags)
{
static const char dummystring[2] = {' ', 0};
const int escape = !(flags & FNM_NOESCAPE);
const int slash = !!(flags & FNM_PATHNAME);
const char *strendseg;
const char *dummyptr;
const char *matchptr;
int wild;
/* For '*' wild processing only; surpress 'used before initialization'
* warnings with dummy initialization values;
*/
const char *strstartseg = NULL;
const char *mismatch = NULL;
int matchlen = 0;
if (strnlen(pattern, PATH_MAX) == PATH_MAX ||
strnlen(string, PATH_MAX) == PATH_MAX)
return (FNM_NOMATCH);
if (*pattern == '*')
goto firstsegment;
while (*pattern && *string)
{
/* Pre-decode "\/" which has no special significance, and
* match balanced slashes, starting a new segment pattern
*/
if (slash && escape && (*pattern == '\\') && (pattern[1] == '/'))
++pattern;
if (slash && (*pattern == '/') && (*string == '/')) {
++pattern;
++string;
}
firstsegment:
/* At the beginning of each segment, validate leading period behavior.
*/
if ((flags & FNM_PERIOD) && (*string == '.'))
{
if (*pattern == '.')
++pattern;
else if (escape && (*pattern == '\\') && (pattern[1] == '.'))
pattern += 2;
else
return FNM_NOMATCH;
++string;
}
/* Determine the end of string segment
*
* Presumes '/' character is unique, not composite in any MBCS encoding
*/
if (slash) {
strendseg = strchr(string, '/');
if (!strendseg)
strendseg = strchr(string, '\0');
}
else {
strendseg = strchr(string, '\0');
}
/* Allow pattern '*' to be consumed even with no remaining string to match
*/
while (*pattern)
{
if ((string > strendseg)
|| ((string == strendseg) && (*pattern != '*')))
break;
if (slash && ((*pattern == '/')
|| (escape && (*pattern == '\\')
&& (pattern[1] == '/'))))
break;
/* Reduce groups of '*' and '?' to n '?' matches
* followed by one '*' test for simplicity
*/
for (wild = 0; ((*pattern == '*') || (*pattern == '?')); ++pattern)
{
if (*pattern == '*') {
wild = 1;
}
else if (string < strendseg) { /* && (*pattern == '?') */
/* XXX: Advance 1 char for MBCS locale */
++string;
}
else { /* (string >= strendseg) && (*pattern == '?') */
return FNM_NOMATCH;
}
}
if (wild)
{
strstartseg = string;
mismatch = pattern;
/* Count fixed (non '*') char matches remaining in pattern
* excluding '/' (or "\/") and '*'
*/
for (matchptr = pattern, matchlen = 0; 1; ++matchlen)
{
if ((*matchptr == '\0')
|| (slash && ((*matchptr == '/')
|| (escape && (*matchptr == '\\')
&& (matchptr[1] == '/')))))
{
/* Compare precisely this many trailing string chars,
* the resulting match needs no wildcard loop
*/
/* XXX: Adjust for MBCS */
if (string + matchlen > strendseg)
return FNM_NOMATCH;
string = strendseg - matchlen;
wild = 0;
break;
}
if (*matchptr == '*')
{
/* Ensure at least this many trailing string chars remain
* for the first comparison
*/
/* XXX: Adjust for MBCS */
if (string + matchlen > strendseg)
return FNM_NOMATCH;
/* Begin first wild comparison at the current position */
break;
}
/* Skip forward in pattern by a single character match
* Use a dummy fnmatch_ch() test to count one "[range]" escape
*/
/* XXX: Adjust for MBCS */
if (escape && (*matchptr == '\\') && matchptr[1]) {
matchptr += 2;
}
else if (*matchptr == '[') {
dummyptr = dummystring;
fnmatch_ch(&matchptr, &dummyptr, flags);
}
else {
++matchptr;
}
}
}
/* Incrementally match string against the pattern
*/
while (*pattern && (string < strendseg))
{
/* Success; begin a new wild pattern search
*/
if (*pattern == '*')
break;
if (slash && ((*string == '/')
|| (*pattern == '/')
|| (escape && (*pattern == '\\')
&& (pattern[1] == '/'))))
break;
/* Compare ch's (the pattern is advanced over "\/" to the '/',
* but slashes will mismatch, and are not consumed)
*/
if (!fnmatch_ch(&pattern, &string, flags))
continue;
/* Failed to match, loop against next char offset of string segment
* until not enough string chars remain to match the fixed pattern
*/
if (wild) {
/* XXX: Advance 1 char for MBCS locale */
string = ++strstartseg;
if (string + matchlen > strendseg)
return FNM_NOMATCH;
pattern = mismatch;
continue;
}
else
return FNM_NOMATCH;
}
}
if (*string && !(slash && (*string == '/')))
return FNM_NOMATCH;
if (*pattern && !(slash && ((*pattern == '/')
|| (escape && (*pattern == '\\')
&& (pattern[1] == '/')))))
return FNM_NOMATCH;
}
/* Where both pattern and string are at EOS, declare success
*/
if (!*string && !*pattern)
return 0;
/* pattern didn't match to the end of string */
return FNM_NOMATCH;
}

21
dep/emu/fnmatch.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef FNMATCH_H
#define FNMATCH_H
#ifdef __cplusplus
extern "C" {
#endif
#define FNM_NOMATCH 1 /* Match failed. */
#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
#define FNM_PERIOD 0x04 /* Period must be matched by period. */
#define FNM_CASEFOLD 0x08 /* Fold cases */
extern int fnmatch(const char *pattern, const char *string, int flags);
#ifdef __cplusplus
}
#endif
#endif

View File

Binary file not shown.

View File

@@ -25,6 +25,8 @@ This is the physical stuff you'll need.
connector](https://eu.mouser.com/ProductDetail/Amphenol-FCI/86130342114345E1LF?qs=%2Fha2pyFadug%252BpMTyxmFhglPPVKuWXYuFpPNgq%252BsrzhDnXxo8B28k7UCGc7F%2FXjsi)
(or one of the other myriad compatible connectors; there's a billion).
- A floppy drive cable, preferably one with two connectors and a twist.
- A suitable power supply. 3.5" floppy drives use 5V at about an amp
(usually less) --- sadly, too much to power from USB. 5.25" floppy drives
also require 12V. An old but decent quality PC power supply is ideal, as
@@ -48,7 +50,7 @@ All you need to do is attach your chosen connector to the board. You'll need
to make sure that pin 2 on the cable is connected to pin 2.7 on the board,
and pin 34 to pin 1.7 on the board (and of course all the ones in between).
Apart from grounding the board (see below), this is literally all there is to
it.
it. The actual pinout is described in detail below.
The pads are small, but soldering them isn't too bad with a needle-nosed
soldering iron tip.
@@ -103,7 +105,43 @@ the unconnected pins and solder a short piece of wire to a GND pin on the
board. Alternatively you'll need to splice it into your drive's power supply
cable somehow. (The black one.)
## Building the firmware
## Programming the board
You've got two options here. You can either use the precompiled firmware
supplied with the source, or else install the Cypress SDK and build it
yourself. If you want to hack the firmware source you need the latter, but
if you trust me to do it for you use the precompiled firmware. In either
case you'll need Windows and have to install some Cypress stuff.
**Before you read this:** If you're on Windows, good news! You can download a
precompiled version of the FluxEngine client and precompiled firmware [from
the GitHub releases
page](https://github.com/davidgiven/fluxengine/releases/latest). Simply unzip
it somewhere and run the `.exe` files from a `cmd` window (or other shell).
Follow the instructions below to program the board with the firmware.
### Using the precompiled firmware
On your Windows machine, [install the PSoC
Programmer](https://www.cypress.com/products/psoc-programming-solutions).
**Note:** _not_ the Cypress Programmer, which is for a different board!
Cypress will make you register.
Once done, run it. Plug the blunt end of the FluxEngine board into a USB
port (the end which is a USB connector). The programmer should detect it
and report it as a KitProg. You may be prompted to upgrade the programmer
hardware; if so, follow the instructions and do it.
Now go to File -> File Load and open
`FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex` in the
project. If you're on Windows, the precompiled zipfile also contains a copy
of this file. Press the Program button (the one in the toolbar marked with a
down arrow). Stuff will happen and you should be left with three green boxes
in the status bar and 'Programming Succeeded' at the top of the log window.
You're done. You can unplug the board and close the programmer.
### Building the firmware yourself
On your Windows machine, [install the Cypress SDK and CY8CKIT-059
BSP](http://www.cypress.com/documentation/development-kitsboards/cy8ckit-059-psoc-5lp-prototyping-kit-onboard-programmer-and).
@@ -118,7 +156,7 @@ tutorial and making the LED on your board flash. It'll tell you where all the
controls are and how to program the board. Remember that the big end of the
board plugs into your computer for programming.
When you're ready, open the `FluxEngine.cydsn/FluxEngine.cywrk` workspace,
When you're ready, open the `FluxEngine.cydsn/FluxEngine.cyprj` project,
pick 'Program' from the menu, and the firmware should compile and be
programmed onto your board.
@@ -137,6 +175,7 @@ pattern. Press and hold the little button near the light for five seconds
until the light stays solidly on. Now you should be able to acquire
the port and proceed normally.
## Building the client
The client software is where the intelligence, such as it is, is. It's pretty
@@ -145,12 +184,12 @@ well, although on Windows it'll need MSYS2 and mingw32. You'll need to
install some support packages.
- For Linux (this is Ubuntu, but this should apply to Debian too):
`ninja-build`, `libusb-1.0-0-dev`, `libsqlite3-dev`.
- For OSX with Homebrew: `ninja`.
`ninja-build`, `libusb-1.0-0-dev`, `libsqlite3-dev`.
- For OSX with Homebrew: `ninja`, `libusb`, `pkg-config`, `sqlite`.
- For Windows with MSYS2: `make`, `ninja`, `mingw-w64-i686-libusb`,
`mingw-w64-i686-sqlite3`, `mingw-w64-i686-zlib`, `mingw-w64-i686-gcc`.
`mingw-w64-i686-sqlite3`, `mingw-w64-i686-zlib`, `mingw-w64-i686-gcc`.
These lists are not necessarily exhaustive --- plaese [get in
These lists are not necessarily exhaustive --- please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new) if I've missed
anything.
@@ -161,11 +200,176 @@ dependencies and you should be able to put it anywhere.
If it doesn't build, please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new).
## Connecting it up
You should now have a working board, so it's time to test it.
1. Plug the motherboard end of your floppy disk cable into the FluxEngine.
The **red stripe goes on the right**. The **lower set of
holes connect to the board**. See the pinout below.
If you're using header pins, the upper row of holes in the connector
should overhang the edge of the board. If you're using a floppy drive
motherboard connector, you're golden, of course (unless you have one of
those annoying unkeyed cables, or have accidentally soldered the
connector on in the wrong place --- don't laugh, I've done it.)
2. Plug the drive end of your floppy disk cable into the drive (or drives).
Floppy disk cables typically have [two pairs of floppy disk drive
connectors with a twist between
them](http://www.nullmodem.com/Floppy.htm). (Each pair has one connector
for a 3.5" drive and a different one for a 5.25" drive.) (Some cables
are cheap and just have the 3.5" connectors. Some are _very_ cheap and
have a single 3.5" connector, after the twist.)
If you have **two** drives, plug them into both connectors. FluxEngine,
sadly, non-standard disk numbering (there are reasons). Drive 0 is the
one nearest the motherboard; that is, before the twist. Drive 1 is the
one at the end of the cable; that is, after the twist. Drive 0 is the
default. You can tell the client to select drive 1 by using `-s :d=1`.
If you have **one** drive, you may plug it into _either_ connector.
FluxEngine will autodetect it and treat it as drive 0. However, you'll
get the most reliable electrical signal if you plug it in at the end of
the cable.
**A note on termination:** some 5.25" drives require jumper configuration
to tell them whether they're at the end of the cable or in the middle of
the cable. 3.5" drives don't, and my 5.25" drives don't, so I can't
advise there. Consult your drive datasheet for details.
3. **Important.** Make sure that no disk you care about is in the drive.
(Because if your wiring is wrong and a disk is inserted, you'll corrupt
it.)
4. Connect the floppy drive to power. Nothing should happen. If you've
connected something in backwards, you'll see the drive light up, the motor
start, and if you didn't take the disk out, one track has just been wiped.
If this happens, check your wiring.
5. Strip off the little piece of protective plastic on the USB socket on the
board --- the little socket at the end, not the big programmer plug.
6. Connect the FluxEngine to your PC via USB.
7. Insert a scratch disk and do `fluxengine rpm` from the shell. The motor
should work and it'll tell you that the disk is spinning at about 300
rpm for a 3.5" disk, or 360 rpm for a 5.25" disk. If it doesn't, please
[get in touch](https://github.com/davidgiven/fluxengine/issues/new).
8. Do `fluxengine test bandwidth` from the shell. It'll measure your USB
bandwidth. Ideally you should be getting above 800kB/s in both directions.
FluxEngine needs about 300kB/s for a DD disk and about 600kB/s for a HD
disk, so if you're getting less than this, try a different USB port.
9. Insert a standard PC formatted floppy disk into the drive (probably a good
idea to remove the old disk first). Then do `fluxengine read ibm`. It
should read the disk, emitting copious diagnostics, and spit out an
`ibm.img` file containing the decoded disk image (either 1440kB or 720kB
depending).
10. Profit!
## Technical details
The board pinout and the way it's connected to the floppy bus is described
below.
```ditaa
:-E -s 0.75
+-----+
|||||||
+----+-----+----+
+cAAA +
+ Debug board +
+----+-----+----+
+ GND|cDDD | VDD+
+----+ +----+
INDEX300 ---+ 3.0| | GND+--------------------------+
+----+ +----+ +--+--+ |
INDEX360 ---+ 3.1| | 1.7+------ DISKCHG --+34+33+--+
+----+ +----+ +--+--+
+ 3.2| | 1.6+------- SIDE1 ---+32+31+
+----+ +----+ +--+--+
+ 3.3| | 1.5+------- RDATA ---+30+29+
+----+ +----+ +--+--+
+ 3.4| | 1.4+-------- WPT ----+28+27+
+----+ +----+ +--+--+
+ 3.5| | 1.3+------- TRK00 ---+26+25+
+----+ +----+ +--+--+
+ 3.6| | 1.2+------- WGATE ---+24+23+
+----+ +----+ +--+--+
+ 3.7| | 1.1+------- WDATA ---+22+21+
+----+ +----+ +--+--+
+15.0| | 1.0+------- STEP ----+20+19+
+----+ +----+ +--+--+
+15.1| |12.0+-------- DIR ----+18+17+
+----+ +----+ +--+--+
+15.2| |12.1+------- MOTEB ---+16+15+
+----+ +----+ +--+--+
+15.3| |12.2+------- DRVSA ---+14+13+
+----+ +----+ +--+--+
+15.4| |12.3+------- DRVSB ---+12+11+
+----+ +----+ +--+--+
+15.5| |12.4+------- MOTEA ---+10+9 +
+----+ +----+ +--+--+
+ 0.0| |12.5+------- INDEX ---+8 +7 +
+----+ +----+ +--+--+
+ 0.1| |12.6+-------- n/c ----+6 +5 +
+----+ +----+ +--+--+
+ 0.2| |12.7+- TX --- n/c ----+4 +3 +
+----+ +----+ +--+--+
+ 0.3| | 2.7+------- REDWC ---+2 +1 +
+----+ +----+ +--+--+
+ 0.4| | 2.6+
+----+ +----+ FDD socket
+ 0.5| | 2.5+
+----+ +----+
+ 0.6| | 2.4+ TX: debug UART from board
+----+ +----+
+ 0.7| | 2.3+
+----+ +----+
+ RST| | 2.2+
+----+ +----+
+ GND| | 2.1+
+----+ USB +----+
+ VDD+-----+ 2.0+
+----+-----+----+
PSoC5 board
```
Notes:
- `TX` is the debug UART port. It's on pin 12.7 because the board routes it
to the USB serial port on the programmer, so you can get debug information
from the FluxEngine by just plugging the programming end into a USB port
and using a serial terminal at 115200 baud. If you solder a floppy drive
connector on, then it'll end up connected to pin 4 of the floppy drive bus,
which is usually not connected. It's possible that some floppy drives do,
in fact, use this pin. You may wish to remove pin 4 from the floppy drive
socket before attaching it to the FluxEngine to make sure that this pin is
not connected; however, so far I have not found any drives for which this
is necessary. If you do find one, _please_ [get in
touch](https://github.com/davidgiven/fluxengine/issues/new) so I can
document it.
- The `GND` pin only really needs to be connected to one of the floppy bus
ground pins; pin 33 is the closest. For extra safety, you can bridge all
the odd numbered pins together and ground them all if you like.
- `INDEX300` and `INDEX360` are optional output pins which generate fake
timing pulses for 300 and 360 RPM drives. These are useful for certain
rather exotic things. See the section on flippy disks [in the FAQ](faq.md)
for more details; you can normally ignore these.
## Next steps
The board's now assembled and programmed. Plug it into your drive, strip the
plastic off the little USB connector and plug that into your computer, and
you're ready to start using it.
You should now be ready to go. You'll want to read [the client
documentation](using.md) for information about how to actually do interesting
things.
I _do_ make updates to the firmware whenever necessary, so you may need to
reprogram it at intervals; you may want to take this into account if you

View File

@@ -10,7 +10,7 @@ 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 discs
Reading disks
-------------
Just do:
@@ -23,6 +23,39 @@ 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:b=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 rake 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:b=528
```
Useful references
-----------------

View File

@@ -14,10 +14,12 @@ Apparently about 20% of Brother word processors have alignment issues which
means that the disks can't be read by FluxEngine (because the tracks on the
disk don't line up with the position of the head in a PC drive). The word
processors themselves solved this by microstepping until they found where the
real track is, but normal PC drives aren't capable of doing this.
Particularly with the 120kB disks, you might want to fiddle with the start
track (e.g. `:t=0-79x2`) to get a clean read. Keep an eye on the bad sector
map that's dumped at the end of a read.
real track is, but normal PC drives aren't capable of doing this. Particularly
with the 120kB disks, you might want to fiddle with the start track (e.g.
`:t=0-79x2`) to get a clean read. Keep an eye on the bad sector map that's
dumped at the end of a read. My word processor likes to put logical track 0 on
physical track 3, which means that logical track 77 is on physical track 80;
luckily my PC drive can access track 80.
Using FluxEngine to *write* disks isn't a problem, so the
simplest solution is to use FluxEngine to create a new disk, with the tracks
@@ -30,7 +32,7 @@ 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 discs
Reading disks
-------------
Just do:
@@ -41,7 +43,7 @@ fluxengine read brother
You should end up with a `brother.img` which is 239616 bytes long.
Writing discs
Writing disks
-------------
Just do:
@@ -53,6 +55,27 @@ fluxengine write brother
...and it'll write a `brother.img` file which is 239616 bytes long to the
disk. (Use `-i` to specify a different input filename.)
Dealing with misaligned disks
-----------------------------
While FluxEngine can't read misaligned disks directly, Brother word processors
_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.
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.
3. Use FluxEngine to read the data off the correctly aligned disk.
I realise this is rather unsatisfactory, as the Brother hardware is becoming
rarer and they cope rather badly with damaged disks, but this is a limitation
of the hardware of normal PC drives. (It _is_ possible to deliberately misalign
a drive to make it match up with a bad disk, but this is for experts only --- I
wouldn't dare.)
Low level format
----------------
@@ -60,14 +83,6 @@ The drive is a single-sided 3.5" drive spinning at not 300 rpm (I don't know
the precise speed yet but FluxEngine doesn't care). The 240kB disks have 78
tracks and the 120kB disks have 39.
The Brother drive alignment is kinda variable; when you put the disk in the
drive it seeks all the way to physical track 0 and then starts searching for
something which looks like data. My machine likes to put logical track 0 on
physical track 3. FluxEngine puts logical track 0 on physical track 0 for
simplicity, which works fine (at least on my machine). If this doesn't work
for you, [get in touch](https://github.com/davidgiven/fluxengine/issues/new);
there are potential workarounds.
Each track has 12 256-byte sectors. The drive ignores the index hole so they're
lined up all anyhow. As FluxEngine can only read from index to index, it
actually reads two complete revolutions and reassembles the sectors from that.
@@ -129,8 +144,8 @@ 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. Once
done, this will work:
offsets 0x015 and 0x100 from 0x58 to 0xf0 before mtools will touch it. The
supplied `brother240tool` will do this. Once done, this will work:
```
mdir -i brother.img
@@ -138,7 +153,8 @@ 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.
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

View File

@@ -23,7 +23,7 @@ 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 20 sectors, each 256 bytes long.
A standard 1541 disk has 35 tracks of 17 to 21 sectors, each 256 bytes long.
Reading discs
-------------
@@ -34,15 +34,14 @@ Just do:
fluxengine read c64
```
You should end up with an `c64.img` which is 187136 bytes long (for a normal
1541 disk).
You should end up with an `c64.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/).
**Big warning!** The image may not work in an emulator. Commodore 64 disk images are
**Big warning!** Commodore 64 disk images are
complicated due to the way the tracks are different sizes and the odd sector
size. FluxEngine chooses to store them in a simple 256 x 20 x 35 layout,
with holes where missing sectors should be. This was easiest. If anyone can
suggest a better way, please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new).
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.
Useful references
-----------------

152
doc/disk-ibm.md Normal file
View File

@@ -0,0 +1,152 @@
Disk: Generic IBM
=================
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
forms, but they're all fundamentally the same: either FM or MFM, either single
or double sided, with distinct sector header and data records and no sector
metadata. Systems which use IBM scheme disks include but are not limited to:
- IBM PCs (naturally)
- Atari ST
- late era Apple machines
- Acorn machines
- the TRS-80
- late era Commodore machines (the 1571 and so on)
- most CP/M machines
- etc
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.
The generic decoder is mostly self-configuring, and will detect the format of
your disk for you.
Reading disks
-------------
Just do:
fluxengine read ibm
...and you'll end up with an `ibm.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.
Configuration options you'll want include:
- `--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.
- `--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.
- `--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.
The syntax is:
fluxengine write ibm -i input.img <options>
The format of `input.img` will vary depending on the kind of disk you're
writing, which is configured by the options. There are some presets, which
you will almost certainly want to use if possible:
- `--ibm-preset-720`: a standard 720kB DS DD 3.5" disk, with 80 cylinders,
2 sides, and 9 sectors per track.
- `--ibm-preset-1440`: a standard 1440kB DS HD 3.5" disk, with 80
cylinders, 2 sides, and 18 sectors per track.
These options simply preset the following, lower-level options. Note that
options are processed left to right, so it's possible to use a preset and
then change some settings. To see the values for a preset, simply append
`--help`.
- `--ibm-track-length-ms=N`: one disk rotation, in milliseconds. This is used
to determine whether all the data will fit on a track or not. `fluxengine
rpm` will tell you this; it'll be 200 for a normal 3.5" drive and 166 for a
normal 5.25" drive.
- `--ibm-sector-size=N`: the size of a sector, in bytes. Must be a power of
two.
- `--ibm-emit-iam=true|false`: whether to emit the IAM record at the top of
the track. The standard format requires it, but it's ignored by absolutely
everyone and you can fit a bit more data on the disk without it.
- `--ibm-start-sector-id=N`: the sector ID of the first sector. Normally 1,
except for non-standard formats like Acorn's, which use 0.
- `--ibm-use-fm=true|false`: uses FM rather than MFM.
- `--ibm-idam-byte=N`: the sixteen-bit raw bit pattern used for the IDAM ID
byte. Big-endian, clock bit first.
- `--ibm-dam-byte-N`: the sixteen-bit raw bit pattern used for the DAM ID
byte. Big-endian, clock bit first.
- `--ibm-gap0-bytes=N`: the size of gap 0 in bytes (between the start of
the track and the IAM record).
- `--ibm-gap1-bytes=N`: the size of gap 1 in bytes (between the IAM record
and the first sector record).
- `--ibm-gap2-bytes=N`: the size of gap 2 in bytes (between each sector
record and the data record).
- `--ibm-gap3-bytes=N`: the size of gap 3 in bytes (between the data record
and the next sector record).
- `--ibm-sector-skew=0123...`: a string representing the order in which to
write sectors: each character represents on sector, with `0` being the
first (always, regardless of `--ibm-start-sector-id` above). Sectors 10 and
above are represented as latters from `A` up.
Mixed-format disks
------------------
Some disks, usually those belonging to early CP/M machines, have more than one
format on the disk at once. Typically, the first few tracks will be low-density
FM encoded and will be read by the machine's ROM; those tracks contain new
floppy drive handling code capable of coping with MFM data, and so the rest of
the disk will use that, allowing them to store more data.
FluxEngine copes with these fine, but the disk images are a bit weird. If track
0 is FM and contains five sectors, but track 1 is MFM with nine sectors (MFM is
more efficient and the sectors are physically smaller, allowing you to get more
on), then the resulting image will have nine sectors per track... but track 0
will only contain data in the first five.
This is typically what you want as it makes locating the sectors in the image
easier, but emulators will typically require a different format. Please [get
in touch](https://github.com/davidgiven/fluxengine/issues/new) if you have
specific requirements (nothing's come up yet). Alternatively, you can tell
FluxEngine to write a [`.ldbs`
file](http://www.seasip.info/Unix/LibDsk/ldbs.html) and then use
[libdsk](http://www.seasip.info/Unix/LibDsk/) to convert it to something
useful.
One easy option when reading these is to simply read the two sections of the
disk into two different image files.
FluxEngine can write these too, but in two different passes with different
options. It's possible to assemble a flux file by judicious use of `-D
something.flux --merge`, which can then be written in a single pass with
`fluxengine writeflux`, but it's usually not worth the bother: just write the
boot tracks, then write the data tracks, possibly with a script for automation.

View File

@@ -28,8 +28,16 @@ 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 a conventional drive. Because
it's clever.
_Anyway_: FluxEngine will read them fine on conventional drives.
Because it's clever.
**Big note.** Apparently --- and I'm still getting to the bottom of this ---
some drives work and some don't. My drives produce about 90% good reads of
known good disks. One rumour I've heard is that drives sometimes include
filters which damage the signals at very particular intervals which Mac disks
use, but frankly this seems unlikely; it could be a software issue at my end
and I'm investigating. If you have any insight, please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new).
Reading discs
-------------
@@ -50,6 +58,10 @@ with holes where missing sectors should be. This was easiest. If anyone can
suggest a better way, please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new).
The 12 bytes of metadata _follow_ the 512 bytes of user payload in the sector
image. If you don't want it, specify a geometry in the output file with a
512-byte sectore size like `-o mac.img:c=80:h=1:s=12:b=512`.
Useful references
-----------------

65
doc/disk-mx.md Normal file
View File

@@ -0,0 +1,65 @@
Disk: DVK MX
============
The DVK (in Russian, ДВК, Диалоговый вычислительный комплекс or Dialogue
Computing Complex) was a late 1970s Soviet personal computer, a cut-down
version of the professional SM EVM (СМ ЭВМ, abbreviation of Система Малых ЭВМ
--- 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>
</div>
The MX format is interesting in that it has to be read a track at a time. The
format contains the usual ID prologue at the beginning of the track, then
eleven data blocks and checksums, then the epilogue, then it stops. The
actual encoding is normal FM. There were four different disk variants, in all
combinations of single- and double-sided and 40- and 80-tracked; but every
track contained eleven 256-byte sectors.
The format varies subtly depending on whether you're using the 'new' driver
or the 'old' driver. FluxEngine should read both.
A track is:
* 8 x 0x0000 words (FM encoded as 01010101...)
* 1 x 0x00F3 --- start of track
* 1 x 0xnnnn --- track number
* 11 of:
* 128 words (256 bytes) of data
* 16 bit checksum
* **if 'new' format:**
* 3 x 0x83nn --- `n = (track_number<<1) + side_number`
* **if 'old' format:**
* 3 x 0x8301
The checksum is just the unsigned integer sum of all the words in the sector.
Words are all stored little-endian.
Reading discs
-------------
```
fluxengine read mx
```
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: `-s :s=0:t=0-79x2`
* double-sided 40-track: `-s :s=0-1:t=0-79x2`
* single-sided 40-track: `-s :s=0:t=0-79`
* double-sided 40-track: `-s :s=0-1:t=0-79`
Useful references
-----------------
- [The Soviet Digital Electronics
Museum](http://www.leningrad.su/museum/main.php) (source of the image
above)
- [a random post on the HxC2001 support
forum](http://torlus.com/floppy/forum/viewtopic.php?t=1384) with lots of
information on the format

BIN
doc/dvk3m.jpg Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

View File

@@ -48,7 +48,7 @@ haven't had the chance to try it end-for-end. I really need a hard-sectored
**Q.** Does it work with flippy disks?
Uhhh... probably not.
Uhhh... maybe?
So the problem with flippy disks (5.25" single-sided disks which could be
inserted upside down to read the second side) is the index hole. Trouble is,
@@ -79,16 +79,26 @@ the other. But a flippy disk has both sets of tracks in the same place,
because they're both accessed using the side 0 head...
The only real way round this is to modify a 5.25" drive. That's _seriously_
not in FluxEngine's remit. Sorry.
not in FluxEngine's remit, but I've had some [excellent documentation
contributed](Index_sensor_mod_FDD_1.1.pdf) on how to do this. I've never done
it myself; if you try this and it works/doesn't work, as always, [get in
touch](https://github.com/davidgiven/fluxengine/issues/new).
**Q.** Is this like KryoFlux / Catweasel / DiskFerret? Do you support KryoFlux
Another option is to fake the index signal to the drive completely. The
FluxEngine emits suitable pulses for a 300RPM drive on pin 3[0] and the
equivalent pulses for a 360RPM drive on pin 3[1]. Disclaimer: I have never used
these.
**Q.** Is this like Supercard Pro / KryoFlux / Catweasel / DiskFerret? Do you
*support KryoFlux
stream files?
**A.** It's very like all of these; the idea's old, and lots of people have
tried it (you can get away with any sufficiently fast microcontroller and
enough RAM). FluxEngine can read from KryoFlux stream files natively, and
there's a tool which will let you convert at least one kind of Catweasel file
to FluxEngine's native flux file format.
there's a tool which will let you convert at least one kind of Catweasel
files and Supercard Pro files to and from FluxEngine's native flux file
format.
**Q.** Can I use this to make exact copies of disks?

View File

@@ -22,19 +22,17 @@ FluxEngine, where a different datapath state machine thingy (the PSoC5LP has
24, all independently programmable) to interpret the bytecodes, generate a
stream of pulses to the disk.
The bytecode format represents an interval between pulses as a byte, a pulse
as a byte, and the index hole as a byte. Timer overflows are handled by
sending multiple intervals in a row. However, the USB transport applies a
simple compression system to this in order to get the USB bandwidth down to
something manageable.
The bytecode format is very simple with a six-bit interval since the previous
event in the lower six bits and the top two bits are set of a pulse or an index
hole (or both, or neither).
An HD floppy has a nominal pulse frequency of 500kHz, and we use a sample
clock of 12MHz (every 83ns). This means that our 500kHz pulses will have an
average interval of 24. This gives us more than enough resolution. At this
speed, in the 200ms that a 3.5" disk takes to rotate, we will see about
100,000 pulses. Each one is encoded as two bytes, one for the interval and
one to generate the pulse; so that revolution generates 200kB of data.
(Extremely approximately. The actual figure is less.)
100,000 pulses. Each one is encoded as a single byte; so that revolution
generates 100kB of data. (Extremely approximately. The actual figure varies
depending on what data is stored on the disk.)
(The clock needs to be absolutely rock solid or we get jitter which makes the
data difficult to analyse, so 12 was chosen to be derivable from the
@@ -59,53 +57,42 @@ Some useful and/or interesting numbers:
## Why don't I use an Arduino / STM32 / ESP32 / Raspberry Pi / etc?
I've got a _lot_ of questions on this, and multiple Github issues of people
-I've got a _lot_ of questions on this, and multiple Github issues of people
debating it. It's complicated, but it's essentially a tradeoff between speed
and complexity.
and complexity.-
FluxEngine's read process involves generating a lot of data using a fairly
brute force sampling approach --- about 150kB per disk revolution, and
sometimes it needs to record multiple revolutions. Most microcontrollers
don't have enough RAM to buffer this, so instead I have to stream it over USB
back to the host PC in real time. The disk won't wait, so I need to stream data faster
than the disk is producing it: the total is about 800kB/s.
**Update as of 2020-01-08:**
Handling USB is pretty CPU-hungry, so my candidate microntroller has to be
able to cope with the ruinously strict real-time requirements of the
sampler's 12MHz clock as well as keeping up with 13,000 USB interrupts a
second (one for each 64-byte frame) in order to transfer the data.
Right. Well.
The Atmels and STM32s I found were perfectly capable of doing the real-time
sampling, using hand-tool assembly, but I very much doubt whether they could
do the USB streaming as well (although I want to move away from the Cypress
onto something less proprietary and easier to source, so I'd like to be
proven wrong here).
This section used to have a long explanation as to why these other platforms
were unsuitable --- essentially, they're generally missing out on either the
realtimeness to sample the data correctly (Raspberry Pi) or enough CPU to
stream the data over USB while also sampling it (Arduino).
The Raspberry Pi easily has enough processing power and memory, but it's also
got terrible GPIO pin read performance --- [about
1kHz](https://raspberrypi.stackexchange.com/questions/9646/how-fast-is-gpiodma-multi-i2s-input/10197#10197).
That's a long way from the 12MHz I need.
This is correct, but it turns out that the STM32 has some built-in features
which support the FluxEngine's use case almost exactly: you can configure the
DMA engine to sample the interval between pulses and write them directly into
memory, and you can configure the PWM engine the read samples from memory and
use them to time pulses to the output. There's a bit less functionality, so you
can't do things like measure the signal voltages, and they're less convenient
as you need an adapter cable or board, but this will allow you to replicate the
FluxEngine hardware on a $2 Blue Pill.
The PSoC5LP part I'm using has enough CPU to handle the USB side of things,
and it _also_ has a whole set of FPGA-like soft programmable features,
including 24 mini-ALU systems that are ideally suited to exactly this kind of
sampling. I can read the disk and generate the byte stream describing the
flux pattern entirely in 'hardware', without involving the main CPU at all.
This is then DMAed directly into a set of ring buffers read for the USB
system to pick up and relay back to the PC. It's incredibly simple and works
well. (The same applies to writing flux back onto the disk.)
I am _not_ planning on replacing the PSoC5 with a Blue Pill, because someone
already has: [the GreaseWeazle](https://github.com/keirf/Greaseweazle/wiki) is
a completely open source firmware package which will read and write Supercard
Pro files via a standard Blue Pill. The GreaseWeazle's USB protocol is
different from the FluxEngine's so they're not directly interchangeable. You
can, however, read a Supercard Pro file with a GreaseWeazle and then use the
FluxEngine client to decode it. It should work the other way around, too, but
FluxEngine's SCP export [is curently
broken](https://github.com/davidgiven/fluxengine/issues/134).
The development board I'm using, the
[CY8CKIT-059](https://www.cypress.com/documentation/development-kitsboards/cy8ckit-059-psoc-5lp-prototyping-kit-onboard-programmer-and),
also has another big advantage: it's the right shape. It's got 17 holes in a
row connected to GPIO pins, and it's a native 5V part, which means I can just
connect a floppy drive connector directly to the board without needing to
build any hardware. No adapter board, no level shifting, no special cable,
nothing. This makes the FluxEngine hardware incredibly easy to assemble,
which therefore means cheap.
I _am_ considering adding direct support for the GreaseWeazle to the FluxEngine
client, which will let you just plug one in and make it go as a direct
replacement to the FluxEngine hardware.
Speaking of which, the CY8CKIT-059 is $10. (Before shipping, which is
admittedly expensive.)
### Some useful links
@@ -123,8 +110,12 @@ admittedly expensive.)
sheet](http://www.bitsavers.org/pdf/mitsubishi/floppy/M4851/TJ2-G30211A_M4851_DSHH_48TPI_OEM_Manual_Nov83.pdf):
the equivalent data sheet for a representative 5.25" drive.
- [The DRG Business Machines YD-174 manual](https://electrickery.hosting.philpem.me.uk/comp/divcomp/doc/YE_Data_YD-174_8inchFloppyDriveTechnicalManual.pdf):
the equivalent manual (data sheets hadn't been invented then) for a
representative 8" drive.
- [KryoFlux stream file
documentation](https://www.kryoflux.com/download/kryoflux_stream_protocol_rev1.1.pdf):
the format of KryoFlux stream files (partially supported by FluxEngine)

View File

@@ -1,68 +1,8 @@
Using a FluxEngine
==================
So you've [built the hardware](building.md)! What now?
## Connecting it up
In order to do anything useful, you have to plug it in to a floppy disk drive (or two).
1. Plug the motherboard end of your floppy disk cable into the FluxEngine.
The **red stripe goes on the right**. The **lower set of
holes connect to the board**. (Pin 2 of the connector needs to connect
to pin 2.7 on the board.)
If you're using header pins, the upper row of holes in the connector
should overhang the edge of the board. If you're using a floppy drive
motherboard connector, you're golden, of course (unless you have one of
those annoying unkeyed cables, or have accidentally soldered the
connector on in the wrong place --- don't laugh, I've done it.)
2. Plug the drive end of your floppy disk cable into the drive (or drives).
Floppy disk cables typically have [two pairs of floppy disk drive
connectors with a twist between
them](http://www.nullmodem.com/Floppy.htm). (Each pair has one connector
for a 3.5" drive and a different one for a 5.25" drive.) (Some cables
are cheap and just have the 3.5" connectors. Some are _very_ cheap and
have a single 3.5" connector, after the twist.)
FluxEngine uses, sadly, non-standard disk numbering (there are reasons).
Drive 0 is the one nearest the motherboard; that is, before the twist.
Drive 1 is the one at the end of the cable; that is, after the twist.
Drive 0 is the default. If you only have one drive, remember to plug the
drive into the connector _before_ the twist. (Or use `-s :d=1` to select
drive 1 when working with disks.)
3. **Important.** Make sure that no disk you care about is in the drive.
(Because if your wiring is wrong and a disk is inserted, you'll corrupt it.)
4. Connect the floppy drive to power. Nothing should happen. If you've
connected something in backwards, you'll see the drive light up, the
motor start, and if you didn't take the disk out, one track has just
been wiped. If this happens, check your wiring.
5. Connect the FluxEngine to your PC via USB --- using the little socket on
the board, not the big programmer plug.
6. Insert a scratch disk and do `fluxengine rpm` from the shell. The motor
should work and it'll tell you that the disk is spinning at about 300
rpm for a 3.5" disk, or 360 rpm for a 5.25" disk. If it doesn't, please
[get in touch](https://github.com/davidgiven/fluxengine/issues/new).
7. Do `fluxengine testbulktransport` from the shell. It'll measure your USB
bandwidth. Ideally you should be getting above 900kB/s. FluxEngine needs
about 850kB/s, so if you're getting less than this, try a different USB
port.
8. Insert a standard PC formatted floppy disk into the drive (probably a good
idea to remove the old disk first). Then do `fluxengine read ibm`. It
should read the disk, emitting copious diagnostics, and spit out an
`ibm.img` file containing the decoded disk image (either 1440kB or 720kB
depending).
9. Profit!
So you've [built the hardware](building.md), programmed and tested it! What
now?
## The programs
@@ -71,10 +11,33 @@ moving too quickly for the documentation to keep up. It does respond to
`--help` or `help` depending on context. There are some common properties,
described below.
### Core concepts
FluxEngine fundamentally takes file system images and puts them on disk; or
reads the disk and produces a file system image.
A file system image typically has the extension `.img`. It contains a
sector-by-sector record of the _decoded_ data on the disk. For example, on a
disk with 512 byte sectors, one sector will occupy 512 bytes. These are
typically what you want in everyday life.
FluxEngine can also record the raw magnetic data on the disk into a file, which
we call a _flux file_. This contains all the low-level data which the drive
produced as the disk rotated. These are continuous streams of samples from the
disk and are completely useless in day-to-day life. FluxEngine uses its own
format for this, `.flux`, although it's capable of limited interchange with
Kryoflux, Supercard Pro and Catweasel files. A flux file will typically contain
from 80 to 150 kilobytes of data per track.
In general, FluxEngine can use either a real disk or a flux file
interchangeably: you can specify either at (very nearly) any time. A very
common workflow is to read a disk to a flux file, and then reread from the flux
file while changing the decoder options, to save disk wear. It's also much faster.
### Source and destination specifiers
When reading from or writing to _a disk_ (or a file pretending to be a disk),
use the `--source` (`-s`) and `--dest` (`-d`) options to tell FluxEngine
When reading from or writing _flux_ (either from or to a real disk, or a flux
file), use the `--source` (`-s`) and `--dest` (`-d`) options to tell FluxEngine
which bits of the disk you want to access. These use a common syntax:
```
@@ -84,7 +47,7 @@ fluxengine read ibm -s fakedisk.flux:t=0-79:s=0
- To access a real disk, leave out the filename (so `:t=0-79:s=0`).
- To access only some tracks, use the `t=` modifier. To access only some
sides, use the `s=` modifier. To change drives, use `d=`.
sides, use the `s=` modifier.
- Inside a modifier, you can use a comma separated list of ranges. So
`:t=0-3` and `:t=0,1,2,3` are equivalent.
@@ -110,7 +73,49 @@ If you _don't_ specify a modifier, you'll get the default, which should be
sensible for the command you're using.
**Important note:** FluxEngine _always_ uses zero-based units (even if the
*disk format says otherwise).
disk format says otherwise).
### Input and output specifiers
When reading or writing _file system images_, use the `--input` (`-i`) and
`--output` (`-o`) options to specify the file and file format. These use a very
similar syntax to the source and destination specifiers (because they're based
on the same microformat library!) but with different specifiers. Also, the
exact format varies according to the extension:
- `.img` or `.adf`: raw sector images in CHS order. Append
`:c=80:h=2:s=9:b=512` to set the geometry; that specifies 80 cylinders, 2
heads, 9 sectors, 512 bytes per sector. For output files (`--output`) the
geometry will be autodetected if left unspecified. For input files you
normally have to specify it.
- `.ldbs`: John Elliott's [LDBS disk image
format](http://www.seasip.info/Unix/LibDsk/ldbs.html), which is
consumable by the [libdsk](http://www.seasip.info/Unix/LibDsk/) suite of
tools. This allows things like variable numbers of sectors per track
(e.g. Macintosh or Commodore 64) and also provides information about
whether sectors were read correctly. You can use libdsk to convert this
to other formats, using a command like this:
```
$ dsktrans out.ldbs -otype tele out.td0
```
...to convert to TeleDisk format. (Note you have to use dsktrans rather
than dskconv due to a minor bug in the geometry hadnling.)
FluxEngine's LDBS support is currently limited to write only, and
it doesn't store a lot of the more esoteric LDBS features like format
types, timings, and data rates.
- `.d64`: the venerable Commodore 64 disk image format as used by the 1540,
1541, etc. This is a special-purpose format due to the weird layout of
1540 disks and while you can use this for non-Commodore disks the result
will be gibberish. Use this to image Commodore 64 disks and load the
result into an emulator.
FluxEngine's D64 support is currently limited to write only. It will work
with up to 40 logical tracks.
### High density disks
@@ -131,6 +136,29 @@ case, and reading the disk label is much more reliable.
[Lots more information on high density vs double density disks can be found
here.](http://www.retrotechnology.com/herbs_stuff/guzis.html)
### Other important flags
These flags apply to many operations and are useful for modifying the overall
behaviour.
- `--revolutions=X`: when reading, spin the disk X times. X can be a floating
point number. The default is usually 1.25. Some formats default to 1.
Increasing the number will sample more data, and can be useful on dubious
disks to try and get a better read.
- `--sync-with-index=true|false`: wait for an index pulse before starting to
read the disk. (Ignored for write operations.) By default FluxEngine
doesn't, as it makes reads faster, but when diagnosing disk problems it's
helpful to have all your data start at the same place each time.
- `--index-source=X`, `--write-index-source=X`: set the source of index
pulses when reading or writing respectively. This is for use with drives
which don't produce index pulse data. Use 0 to get index pulses from the
drive, 1 to fake 300RPM pulses, or 2 to fake 360RPM pulses. Note this has
no effect on the _drive_, so it doesn't help with flippy disks, but is
useful for using very old drives with FluxEngine itself. If you use this
option, then any index marks in the sampled flux are, of course, garbage.
### The commands
The FluxEngine client software is a largely undocumented set of small tools.
@@ -139,73 +167,109 @@ installed anywhere and after building you'll find them in the `.obj`
directory.
- `fluxengine erase`: wipes (all or part of) a disk --- erases it without
writing a pulsetrain.
writing a pulsetrain.
- `fluxengine inspect`: dumps the raw pulsetrain / bitstream to stdout.
Mainly useful for debugging.
Mainly useful for debugging.
- `fluxengine read*`: reads various formats of disk. See the per-format
documentation linked from the table above. These all take an optional
`--write-flux` option which will cause the raw flux to be written to the
specified file.
- `fluxengine read *`: reads various formats of disk. See the per-format
documentation linked from the table [in the index page](../README.md).
These all take an optional `--write-flux` option which will cause the raw
flux to be written to the specified file as well as the normal decode.
There are various `--dump` options for showing raw data during the decode
process, and `--write-csv` will write a copious CSV report of the state of
every sector in the file in machine-readable format.
- `fluxengine write*`: writes various formats of disk. Again, see the
per-format documentation above.
- `fluxengine write *`: writes various formats of disk. Again, see the
per-format documentation [in the index page](../README.md).
- `fluxengine writeflux`: writes raw flux files. This is much less useful
than you might think: you can't write flux files read from a disk to
another disk. (See the [FAQ](faq.md) for more information.) It's mainly
useful for flux files synthesised by the other `fluxengine write` commands.
than you might think: you can't reliably write flux files read from a disk
to another disk. (See the [FAQ](faq.md) for more information.) It's mainly
useful for flux files synthesised by the other `fluxengine write` commands.
- `fluxengine writetestpattern`: writes regular pulses (at a configurable
interval) to the disk. Useful for testing drive jitter, erasing disks in a
more secure fashion, or simply debugging. Goes well with `fluxengine
inspect`.
interval) to the disk. Useful for testing drive jitter, erasing disks in a
more secure fashion, or simply debugging. Goes well with `fluxengine
inspect`.
- `fluxengine rpm`: measures the RPM of the drive (requires a disk in the
drive). Mainly useful for testing.
drive). Mainly useful for testing.
- `fluxengine seek`: moves the head. Mainly useful for finding out whether
your drive can seek to track 82. (Mine can't.)
your drive can seek to track 82. (Mine can't.)
- `fluxengine testbulktransport`: measures your USB throughput. You need
about 600kB/s for FluxEngine to work. You don't need a disk in the drive
for this one.
- `fluxengine test bandwidth`: measures your USB throughput. You don't need
a disk in the drive for this one.
- `fluxengine upgradefluxfile`: occasionally I need to upgrade the flux
file format in a non-backwards-compatible way; this tool will upgrade flux
files to the new format.
- `fluxengine test voltages`: measures your FDD bus signal voltages, which is
useful for testing for termination issues.
- `fluxengine upgradefluxfile`: occasionally I need to upgrade the flux file
format in a non-backwards-compatible way; this tool will upgrade flux files
to the new format.
- `fluxengine convert`: converts flux files from various formats to various
other formats. You can use this to convert Catweasel flux files to
FluxEngine's native format, FluxEngine flux files to various other formats
useful for debugging (including VCD which can be loaded into
[sigrok](http://sigrok.org)), and bidirectional conversion to and from
Supercard Pro `.scp` format.
**Important SCP note:** import (`fluxengine convert scptoflux`) should be
fairly robust, but export (`fluxengine convert fluxtoscp`) should only be
done with great caution as FluxEngine files contain features which can't be
represented very well in `.scp` format and they're probably pretty dubious.
As ever, please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new) with any
reports.
Commands which normally take `--source` or `--dest` get a sensible default if
left unspecified. `fluxengine read ibm` on its own will read drive 0 and
write an `ibm.img` file.
## Visualisation
When doing a read (either from a real disk or from a flux file) you can use
`--write-svg=output.svg` to write out a graphical visualisation of where the
sectors are on the disk. Here's a IBM PC 1232kB disk:
![A disk visualisation](./visualiser.svg)
Blue represents data, light blue a header, and red is a bad sector. Side zero
is on the left and side one is on the right.
The visualiser is extremely primitive and you have to explicitly tell it how
big your disk is, in milliseconds. The default is 200ms (for a normal 3.5"
disk). For a 5.25" disk, use `--visualiser-period=166`.
## Extra programs
Supplied with FluxEngine, but not part of FluxEngine, are some little tools I
wrote to do useful things. These are built alongside FluxEngine.
- `brother120tool`: extracts files from a 120kB Brother filesystem image.
- `cwftoflux`: converts (one flavour of) CatWeasel flux file into a
FluxEngine flux file.
- `brother120tool`, `brother240tool`: does things to Brother word processor
disks. These are [documented on the Brother disk format
page](disk-brother.md).
## The recommended workflow
So you've just received, say, a huge pile of old Brother word processor disks containing valuable historical data, and you want to read them.
So you've just received, say, a huge pile of old Brother word processor disks
containing valuable historical data, and you want to read them.
Typically I do this:
```
$ fluxengine read brother -s :d=0 -o brother.img --write-flux=brother.flux
$ fluxengine read brother -s :d=0 -o brother.img --write-flux=brother.flux --overwrite --write-svg=brother.svg
```
This will read the disk in drive 0 and write out a filesystem image. It'll
also copy the flux to brother.flux. If I then need to tweak the settings, I
can rerun the decode without having to physically touch the disk like this:
This will read the disk in drive 0 and write out a filesystem image. It'll also
copy the flux to `brother.flux` (replacing any old one) and write out an SVG
visualisation. If I then need to tweak the settings, I can rerun the decode
without having to physically touch the disk like this:
```
$ fluxengine read brother -s brother.flux -o brother.img
$ fluxengine read brother -s brother.flux -o brother.img --write-svg=brother.svg
```
Apart from being drastically faster, this avoids touching the (potentially

1
doc/visualiser.svg Normal file
View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 394 KiB

View File

@@ -1,20 +0,0 @@
#ifndef AMIGA_H
#define AMIGA_H
#define AMIGA_SECTOR_RECORD 0xaaaa44894489LL
#define AMIGA_RECORD_SIZE 0x21f
class Sector;
class Fluxmap;
class AmigaDecoder : public AbstractDecoder
{
public:
virtual ~AmigaDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
#endif

View File

@@ -1,97 +0,0 @@
#include "globals.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "amiga.h"
#include "bytes.h"
#include "fmt/format.h"
#include <string.h>
#include <algorithm>
/*
* Amiga disks use MFM but it's not quite the same as IBM MFM. They only use
* a single type of record with a different marker byte.
*
* See the big comment in the IBM MFM decoder for the gruesome details of how
* MFM works.
*/
static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD);
static Bytes deinterleave(const uint8_t*& input, size_t len)
{
assert(!(len & 1));
const uint8_t* odds = &input[0];
const uint8_t* evens = &input[len/2];
Bytes output;
ByteWriter bw(output);
for (size_t i=0; i<len/2; i++)
{
uint8_t o = *odds++;
uint8_t e = *evens++;
/* This is the 'Interleave bits with 64-bit multiply' technique from
* http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
*/
uint16_t result =
(((e * 0x0101010101010101ULL & 0x8040201008040201ULL)
* 0x0102040810204081ULL >> 49) & 0x5555) |
(((o * 0x0101010101010101ULL & 0x8040201008040201ULL)
* 0x0102040810204081ULL >> 48) & 0xAAAA);
bw.write_be16(result);
}
input += len;
return output;
}
static uint32_t checksum(const Bytes& bytes)
{
ByteReader br(bytes);
uint32_t checksum = 0;
assert((bytes.size() & 3) == 0);
while (!br.eof())
checksum ^= br.read_be32();
return checksum & 0x55555555;
}
AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord()
{
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
if (_fmr->eof() || !_sector->clock)
return UNKNOWN_RECORD;
return SECTOR_RECORD;
}
void AmigaDecoder::decodeSectorRecord()
{
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
const auto& rawbytes = toBytes(rawbits).slice(0, AMIGA_RECORD_SIZE*2);
const auto& bytes = decodeFmMfm(rawbits).slice(0, AMIGA_RECORD_SIZE);
const uint8_t* ptr = bytes.begin() + 3;
Bytes header = deinterleave(ptr, 4);
Bytes recoveryinfo = deinterleave(ptr, 16);
_sector->logicalTrack = header[1] >> 1;
_sector->logicalSide = header[1] & 1;
_sector->logicalSector = header[2];
uint32_t wantedheaderchecksum = deinterleave(ptr, 4).reader().read_be32();
uint32_t gotheaderchecksum = checksum(rawbytes.slice(6, 40));
if (gotheaderchecksum != wantedheaderchecksum)
return;
uint32_t wanteddatachecksum = deinterleave(ptr, 4).reader().read_be32();
uint32_t gotdatachecksum = checksum(rawbytes.slice(62, 1024));
_sector->data = deinterleave(ptr, 512);
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}

View File

@@ -1,92 +0,0 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "brother.h"
#include "crc.h"
static int encode_header_gcr(uint16_t word)
{
switch (word)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "header_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
static int encode_data_gcr(uint8_t data)
{
switch (data)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width)
{
cursor += width;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
void writeBrotherSectorHeader(std::vector<bool>& bits, unsigned& cursor,
int track, int sector)
{
write_bits(bits, cursor, 0xffffffff, 31);
write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32);
write_bits(bits, cursor, encode_header_gcr(track), 16);
write_bits(bits, cursor, encode_header_gcr(sector), 16);
write_bits(bits, cursor, encode_header_gcr(0x2f), 16);
}
void writeBrotherSectorData(std::vector<bool>& bits, unsigned& cursor, const Bytes& data)
{
write_bits(bits, cursor, 0xffffffff, 32);
write_bits(bits, cursor, BROTHER_DATA_RECORD, 32);
uint16_t fifo = 0;
int width = 0;
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
Error() << "unsupported sector size";
auto write_byte = [&](uint8_t byte)
{
fifo |= (byte << (8 - width));
width += 8;
while (width >= 5)
{
uint8_t quintet = fifo >> 11;
fifo <<= 5;
width -= 5;
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
}
};
for (uint8_t byte : data)
write_byte(byte);
uint32_t realCrc = crcbrother(data);
write_byte(realCrc>>16);
write_byte(realCrc>>8);
write_byte(realCrc);
write_byte(0x58); /* magic */
write_byte(0xd4);
while (width != 0)
write_byte(0);
}

View File

@@ -1,7 +1,7 @@
#include "globals.h"
#include "bytes.h"
#include "fmt/format.h"
#include "common/crunch.h"
#include <fstream>
#include <zlib.h>
static std::shared_ptr<std::vector<uint8_t>> createVector(unsigned size)
@@ -146,6 +146,28 @@ Bytes Bytes::slice(unsigned start, unsigned len) const
}
}
Bytes Bytes::slice(unsigned start) const
{
int len = 0;
if (start < size())
len = size() - start;
return slice(start, len);
}
std::vector<bool> Bytes::toBits() const
{
std::vector<bool> bits;
for (uint8_t byte : *this)
{
for (int i=0; i<8; i++)
{
bits.push_back(byte & 0x80);
byte <<= 1;
}
}
return bits;
}
uint8_t toByte(
std::vector<bool>::const_iterator start,
std::vector<bool>::const_iterator end)
@@ -226,58 +248,14 @@ Bytes Bytes::decompress() const
return output;
}
Bytes Bytes::crunch() const
void Bytes::writeToFile(const std::string& filename) const
{
Bytes output;
ByteWriter bw(output);
Bytes outputBuffer(1024*1024);
std::ofstream f(filename, std::ios::out | std::ios::binary);
if (!f.is_open())
Error() << fmt::format("cannot open output file '{}'", filename);
crunch_state_t cs = {};
cs.inputptr = begin();
cs.inputlen = size();
do
{
cs.outputptr = outputBuffer.begin();
cs.outputlen = outputBuffer.size();
::crunch(&cs);
bw += outputBuffer.slice(0, outputBuffer.size() - cs.outputlen);
}
while (cs.inputlen != 0);
cs.outputptr = outputBuffer.begin();
cs.outputlen = outputBuffer.size();
donecrunch(&cs);
bw += outputBuffer.slice(0, outputBuffer.size() - cs.outputlen);
return output;
}
Bytes Bytes::uncrunch() const
{
Bytes output;
ByteWriter bw(output);
Bytes outputBuffer(1024*1024);
crunch_state_t cs = {};
cs.inputptr = begin();
cs.inputlen = size();
do
{
cs.outputptr = outputBuffer.begin();
cs.outputlen = outputBuffer.size();
::uncrunch(&cs);
bw += outputBuffer.slice(0, outputBuffer.size() - cs.outputlen);
}
while (cs.inputlen != 0);
cs.outputptr = outputBuffer.begin();
cs.outputlen = outputBuffer.size();
doneuncrunch(&cs);
bw += outputBuffer.slice(0, outputBuffer.size() - cs.outputlen);
return output;
f.write((const char*) cbegin(), size());
f.close();
}
ByteReader Bytes::reader() const

View File

@@ -47,15 +47,17 @@ public:
{ resize(0); return *this; }
Bytes slice(unsigned start, unsigned len) const;
Bytes slice(unsigned start) const;
Bytes swab() const;
Bytes compress() const;
Bytes decompress() const;
Bytes crunch() const;
Bytes uncrunch() const;
std::vector<bool> toBits() const;
ByteReader reader() const;
ByteWriter writer();
void writeToFile(const std::string& filename) const;
private:
std::shared_ptr<std::vector<uint8_t>> _data;
unsigned _low;

View File

@@ -1,86 +0,0 @@
#include <stdint.h>
#include <stdbool.h>
#include "crunch.h"
void crunch(crunch_state_t* state)
{
while (state->inputlen && state->outputlen)
{
uint8_t data = *state->inputptr++;
state->inputlen--;
if (data & 0x80)
{
state->fifo = (state->fifo << 2) | 2 | (data & 1);
state->fifolen += 2;
}
else
{
state->fifo = (state->fifo << 8) | data;
state->fifolen += 8;
}
if (state->fifolen >= 8)
{
data = state->fifo >> (state->fifolen - 8);
*state->outputptr++ = data;
state->outputlen--;
state->fifolen -= 8;
}
}
}
void donecrunch(crunch_state_t* state)
{
if (state->fifolen > 0)
{
uint8_t b = 0;
state->inputptr = &b;
state->inputlen = 1;
crunch(state);
}
}
void uncrunch(crunch_state_t* state)
{
while (state->inputlen && state->outputlen)
{
if (state->fifolen < 8)
{
if (state->inputlen)
{
state->fifo = (state->fifo << 8) | *state->inputptr++;
state->inputlen--;
state->fifolen += 8;
}
else
state->fifo <<= 8;
}
uint8_t data = state->fifo >> (state->fifolen - 8);
if (data & 0x80)
{
data = ((data >> 6) & 0x01) | 0x80;
state->fifolen -= 2;
}
else
state->fifolen -= 8;
if (data)
{
*state->outputptr++ = data;
state->outputlen--;
}
}
}
void doneuncrunch(crunch_state_t* state)
{
if (state->fifolen > 0)
{
uint8_t b = 0;
state->inputptr = &b;
state->inputlen = 1;
uncrunch(state);
}
}

View File

@@ -1,45 +0,0 @@
#ifndef CRUNCH_H
#define CRUNCH_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/* To save bandwidth, we compress the byte stream from the sampler when
* sending it over USB. The encoding used is:
*
* 0nnn.nnnn: value 0x00..0x7f
* 1n : value 0x80|n
*
* The end of the buffer is terminated with zeroes, which are ignored
* (not written to the output).
*
* This saves ~40%, which gets us in under the bandwidth cap.
*/
typedef struct crunch_state_t
{
const uint8_t* inputptr;
uint32_t inputlen;
uint8_t* outputptr;
uint32_t outputlen;
uint16_t fifo;
uint8_t fifolen;
}
crunch_state_t;
/* Crunches as much as possible and then stops. */
extern void crunch(crunch_state_t* state);
extern void donecrunch(crunch_state_t* state);
/* Uncrunches as much as possible and then stops. */
extern void uncrunch(crunch_state_t* state);
extern void doneuncrunch(crunch_state_t* state);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -5,39 +5,41 @@
#include <regex>
#include <sstream>
MissingModifierException::MissingModifierException(const std::string& mod):
mod(mod),
std::runtime_error(fmt::format("missing mandatory modifier '{}'", mod))
{}
std::vector<std::string> DataSpec::split(
const std::string& s, const std::string& delimiter)
{
std::vector<std::string> ret;
size_t start = 0;
size_t end = 0;
size_t len = 0;
do
{
end = s.find(delimiter,start);
len = end - start;
std::string token = s.substr(start, len);
ret.emplace_back( token );
start += len + delimiter.length();
}
while (end != std::string::npos);
if (!s.empty())
{
size_t start = 0;
size_t end = 0;
size_t len = 0;
do
{
end = s.find(delimiter,start);
len = end - start;
std::string token = s.substr(start, len);
ret.emplace_back( token );
start += len + delimiter.length();
}
while (end != std::string::npos);
}
return ret;
}
DataSpec::Modifier DataSpec::parseMod(const std::string& spec)
std::set<unsigned> DataSpec::parseRange(const std::string& data)
{
static const std::regex MOD_REGEX("([a-z]*)=([-x+0-9,]*)");
static const std::regex DATA_REGEX("([0-9]+)(?:(?:-([0-9]+))|(?:\\+([0-9]+)))?(?:x([0-9]+))?");
static const std::regex DATA_REGEX("([0-9]+)(?:(?:-([0-9]+))|(?:\\+([0-9]+)))?(?:x([0-9]+))?");
std::smatch match;
if (!std::regex_match(spec, match, MOD_REGEX))
Error() << "invalid data modifier syntax '" << spec << "'";
Modifier m;
m.name = match[1];
m.source = spec;
for (auto& data : split(match[2], ","))
std::set<unsigned> result;
for (auto& data : split(data, ","))
{
int start = 0;
int count = 1;
@@ -59,9 +61,24 @@ DataSpec::Modifier DataSpec::parseMod(const std::string& spec)
Error() << "mod '" << data << "' specifies an illegal quantity";
for (int i = start; i < (start+count); i += step)
m.data.insert(i);
result.insert(i);
}
return result;
}
DataSpec::Modifier DataSpec::parseMod(const std::string& spec)
{
static const std::regex MOD_REGEX("([a-z]*)=([-x+0-9,]*)");
std::smatch match;
if (!std::regex_match(spec, match, MOD_REGEX))
Error() << "invalid data modifier syntax '" << spec << "'";
Modifier m;
m.name = match[1];
m.source = spec;
m.data = parseRange(match[2]);
return m;
}
@@ -69,36 +86,113 @@ void DataSpec::set(const std::string& spec)
{
std::vector<std::string> words = split(spec, ":");
if (words.size() == 0)
Error() << "empty data specification (you have to specify *something*)";
return;
filename = words[0];
if (words.size() > 1)
{
locations.clear();
for (size_t i = 1; i < words.size(); i++)
{
auto mod = parseMod(words[i]);
if ((mod.name != "t") && (mod.name != "s") && (mod.name != "d"))
Error() << fmt::format("unknown data modifier '{}'", mod.name);
modifiers[mod.name] = mod;
}
}
}
const auto& drives = modifiers["d"].data;
const DataSpec::Modifier& DataSpec::at(const std::string& mod) const
{
try
{
return modifiers.at(mod);
}
catch (const std::out_of_range& e)
{
throw MissingModifierException(mod);
}
}
bool DataSpec::has(const std::string& mod) const
{
return modifiers.find(mod) != modifiers.end();
}
FluxSpec::FluxSpec(const DataSpec& spec)
{
try
{
filename = spec.filename;
locations.clear();
const auto& drives = spec.at("d").data;
if (drives.size() != 1)
Error() << "you must specify exactly one drive";
drive = *drives.begin();
const auto& tracks = modifiers["t"].data;
const auto& sides = modifiers["s"].data;
const auto& tracks = spec.at("t").data;
const auto& sides = spec.at("s").data;
for (auto track : tracks)
{
for (auto side : sides)
locations.push_back({ drive, track, side });
}
for (const auto& e : spec.modifiers)
{
const auto name = e.second.name;
if ((name != "t") && (name != "s") && (name != "d"))
Error() << fmt::format("unknown fluxspec modifier '{}'", name);
}
}
catch (const MissingModifierException& e)
{
Error() << e.what() << " in fluxspec '" << spec << "'";
}
}
ImageSpec::ImageSpec(const DataSpec& spec)
{
try
{
filename = spec.filename;
if (!spec.has("c") && !spec.has("h") && !spec.has("s") && !spec.has("b"))
{
cylinders = heads = sectors = bytes = 0;
initialised = false;
}
else
{
cylinders = spec.at("c").only();
heads = spec.at("h").only();
sectors = spec.at("s").only();
bytes = spec.at("b").only();
initialised = true;
}
}
catch (const MissingModifierException& e)
{
Error() << e.what() << " in imagespec '" << spec << "'";
}
for (const auto& e : spec.modifiers)
{
const auto name = e.second.name;
if ((name != "c") && (name != "h") && (name != "s") && (name != "b"))
Error() << fmt::format("unknown fluxspec modifier '{}'", name);
}
}
ImageSpec::ImageSpec(const std::string filename,
unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes):
filename(filename),
cylinders(cylinders),
heads(heads),
sectors(sectors),
bytes(bytes),
initialised(true)
{}
DataSpec::operator std::string(void) const
{
std::stringstream ss;

View File

@@ -1,8 +1,59 @@
#ifndef DATASPEC_H
#define DATASPEC_H
class MissingModifierException : public std::runtime_error
{
public:
MissingModifierException(const std::string& mod);
const std::string mod;
};
class DataSpec
{
public:
struct Modifier
{
std::string name;
std::set<unsigned> data;
std::string source;
bool operator == (const Modifier& other) const
{ return (name == other.name) && (data == other.data); }
bool operator != (const Modifier& other) const
{ return (name != other.name) || (data != other.data); }
unsigned only() const
{
if (data.size() != 1)
Error() << "modifier " << name << " can only have one value";
return *(data.begin());
}
};
public:
static std::vector<std::string> split(
const std::string& s, const std::string& delimiter);
static std::set<unsigned> parseRange(const std::string& spec);
static Modifier parseMod(const std::string& spec);
public:
DataSpec(const std::string& spec)
{ set(spec); }
void set(const std::string& spec);
operator std::string () const;
const Modifier& at(const std::string& mod) const;
bool has(const std::string& mod) const;
std::string filename;
std::map<std::string, Modifier> modifiers;
};
class FluxSpec
{
public:
struct Location
{
@@ -17,36 +68,29 @@ public:
{ return (drive != other.drive) || (track != other.track) || (side != other.side); }
};
struct Modifier
{
std::string name;
std::set<unsigned> data;
std::string source;
bool operator == (const Modifier& other) const
{ return (name == other.name) && (data == other.data); }
bool operator != (const Modifier& other) const
{ return (name != other.name) || (data != other.data); }
};
public:
FluxSpec(const DataSpec& dataspec);
public:
static std::vector<std::string> split(
const std::string& s, const std::string& delimiter);
static Modifier parseMod(const std::string& spec);
public:
DataSpec(const std::string& spec)
{ set(spec); }
void set(const std::string& spec);
operator std::string () const;
std::string filename;
std::map<std::string, Modifier> modifiers;
std::vector<Location> locations;
unsigned drive;
unsigned revolutions;
};
class ImageSpec
{
public:
ImageSpec(const DataSpec& dataspec);
ImageSpec(const std::string filename,
unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes);
public:
std::string filename;
unsigned cylinders;
unsigned heads;
unsigned sectors;
unsigned bytes;
bool initialised : 1;
};
static inline std::ostream& operator << (std::ostream& os, const DataSpec& dataSpec)
@@ -75,4 +119,34 @@ private:
DataSpec _value;
};
class RangeFlag : public Flag
{
public:
RangeFlag(const std::vector<std::string>& names, const std::string helptext,
const std::string& defaultValue):
Flag(names, helptext),
_stringValue(defaultValue),
_value(DataSpec::parseRange(defaultValue))
{}
const std::set<unsigned>& get() const
{ checkInitialised(); return _value; }
operator const std::set<unsigned>& () const
{ return get(); }
bool hasArgument() const { return true; }
const std::string defaultValueAsString() const { return _stringValue; }
void set(const std::string& value)
{
_stringValue = value;
_value = DataSpec::parseRange(value);
}
private:
std::string _stringValue;
std::set<unsigned> _value;
};
#endif

View File

@@ -25,7 +25,7 @@ void AbstractDecoder::decodeToSectors(Track& track)
beginTrack();
for (;;)
{
Fluxmap::Position recordStart = sector.position = fmr.tell();
Fluxmap::Position recordStart = fmr.tell();
sector.clock = 0;
sector.status = Sector::MISSING;
sector.data.clear();
@@ -35,27 +35,38 @@ void AbstractDecoder::decodeToSectors(Track& track)
return;
if ((r == UNKNOWN_RECORD) || (r == DATA_RECORD))
{
fmr.readNextMatchingOpcode(F_OP_PULSE);
fmr.findEvent(F_BIT_PULSE);
continue;
}
/* Read the sector record. */
recordStart = fmr.tell();
sector.position = recordStart = fmr.tell();
decodeSectorRecord();
pushRecord(recordStart, fmr.tell());
Fluxmap::Position recordEnd = fmr.tell();
pushRecord(recordStart, recordEnd);
if (sector.status == Sector::DATA_MISSING)
{
/* The data is in a separate record. */
r = advanceToNextRecord();
sector.headerStartTime = recordStart.ns();
sector.headerEndTime = recordEnd.ns();
for (;;)
{
r = advanceToNextRecord();
if (r != UNKNOWN_RECORD)
break;
if (fmr.findEvent(F_BIT_PULSE) == 0)
break;
}
recordStart = fmr.tell();
if (r == DATA_RECORD)
{
recordStart = fmr.tell();
decodeDataRecord();
pushRecord(recordStart, fmr.tell());
}
recordEnd = fmr.tell();
pushRecord(recordStart, recordEnd);
}
sector.dataStartTime = recordStart.ns();
sector.dataEndTime = recordEnd.ns();
if (sector.status != Sector::MISSING)
track.sectors.push_back(sector);
@@ -77,3 +88,10 @@ void AbstractDecoder::pushRecord(const Fluxmap::Position& start, const Fluxmap::
_track->rawrecords.push_back(record);
_fmr->seek(here);
}
std::set<unsigned> AbstractDecoder::requiredSectors(Track& track) const
{
static std::set<unsigned> empty;
return empty;
}

View File

@@ -20,6 +20,8 @@ extern void setDecoderManualClockRate(double clockrate_us);
extern Bytes decodeFmMfm(std::vector<bool>::const_iterator start,
std::vector<bool>::const_iterator end);
extern void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input, bool& lastBit);
extern void encodeFm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input);
static inline Bytes decodeFmMfm(const std::vector<bool> bits)
{ return decodeFmMfm(bits.begin(), bits.end()); }
@@ -50,6 +52,11 @@ public:
void seek(const Fluxmap::Position& pos)
{ return _fmr->seek(pos); }
/* Returns a set of sectors required to exist on this track. If the reader
* sees any missing, it will consider this to be an error and will retry
* the read. */
virtual std::set<unsigned> requiredSectors(Track& track) const;
protected:
virtual void beginTrack() {};
virtual RecordType advanceToNextRecord() = 0;

View File

@@ -18,23 +18,27 @@ DoubleFlag pulseDebounceThreshold(
static DoubleFlag clockDecodeThreshold(
{ "--bit-error-threshold" },
"Amount of error to tolerate in pulse timing, in fractions of a clock.",
0.20);
0.40);
static DoubleFlag clockIntervalBias(
{ "--clock-interval-bias" },
"Adjust intervals between pulses by this many clocks before decoding.",
-0.02);
int FluxmapReader::readOpcode(unsigned& ticks)
static DoubleFlag minimumClockUs(
{ "--minimum-clock-us" },
"Refuse to detect clocks shorter than this, to avoid false positives.",
0.75);
uint8_t FluxmapReader::getNextEvent(unsigned& ticks)
{
ticks = 0;
while (!eof())
{
uint8_t b = _bytes[_pos.bytes++];
if (b < 0x80)
ticks += b;
else
ticks += b & 0x3f;
if (b & (F_BIT_PULSE|F_BIT_INDEX))
{
_pos.ticks += ticks;
return b;
@@ -42,21 +46,21 @@ int FluxmapReader::readOpcode(unsigned& ticks)
}
_pos.ticks += ticks;
return -1;
return 0;
}
unsigned FluxmapReader::readNextMatchingOpcode(uint8_t opcode)
unsigned FluxmapReader::findEvent(uint8_t target)
{
unsigned ticks = 0;
for (;;)
{
unsigned thisTicks;
int op = readOpcode(thisTicks);
uint8_t bits = getNextEvent(thisTicks);
ticks += thisTicks;
if (op == -1)
if (eof())
return 0;
if (op == opcode)
if (bits & target)
return ticks;
}
}
@@ -68,7 +72,7 @@ unsigned FluxmapReader::readInterval(nanoseconds_t clock)
while (ticks < thresholdTicks)
{
unsigned thisTicks = readNextMatchingOpcode(F_OP_PULSE);
unsigned thisTicks = findEvent(F_BIT_PULSE);
if (!thisTicks)
break;
ticks += thisTicks;
@@ -191,7 +195,7 @@ void FluxmapReader::seek(nanoseconds_t ns)
while (!eof() && (_pos.ticks < ticks))
{
unsigned t;
readOpcode(t);
getNextEvent(t);
}
_pos.zeroes = 0;
}
@@ -222,7 +226,9 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu
seek(positions[intervalCount-match.intervals]);
_pos.zeroes = match.zeroes;
matching = match.matcher;
return match.clock * NS_PER_TICK;
nanoseconds_t detectedClock = match.clock * NS_PER_TICK;
if (detectedClock > (minimumClockUs*1000))
return match.clock * NS_PER_TICK;
}
for (unsigned i=0; i<intervalCount; i++)
@@ -230,7 +236,7 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu
positions[i] = positions[i+1];
candidates[i] = candidates[i+1];
}
candidates[intervalCount] = readNextMatchingOpcode(F_OP_PULSE);
candidates[intervalCount] = findEvent(F_BIT_PULSE);
positions[intervalCount] = tell();
}
@@ -241,7 +247,7 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu
void FluxmapReader::seekToIndexMark()
{
readNextMatchingOpcode(F_OP_INDEX);
findEvent(F_BIT_INDEX);
_pos.zeroes = 0;
}

View File

@@ -96,8 +96,8 @@ public:
_pos = pos;
}
int readOpcode(unsigned& ticks);
unsigned readNextMatchingOpcode(uint8_t opcode);
uint8_t getNextEvent(unsigned& ticks);
unsigned findEvent(uint8_t bits);
unsigned readInterval(nanoseconds_t clock); /* with debounce support */
/* Important! You can only reliably seek to 1 bits. */

View File

@@ -51,3 +51,48 @@ Bytes decodeFmMfm(
return bytes;
}
void encodeFm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input)
{
if (bits.size() == 0)
return;
unsigned len = bits.size()-1;
for (uint8_t b : input)
{
for (int i=0; i<8; i++)
{
bool bit = b & 0x80;
b <<= 1;
if (cursor >= len)
return;
bits[cursor++] = true;
bits[cursor++] = bit;
}
}
}
void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input, bool& lastBit)
{
if (bits.size() == 0)
return;
unsigned len = bits.size()-1;
for (uint8_t b : input)
{
for (int i=0; i<8; i++)
{
bool bit = b & 0x80;
b <<= 1;
if (cursor >= len)
return;
bits[cursor++] = !lastBit && !bit;
bits[cursor++] = bit;
lastBit = bit;
}
}
}

View File

18
lib/encoders/encoders.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef ENCODERS_H
#define ENCODERS_H
class Fluxmap;
class SectorSet;
class AbstractEncoder
{
public:
virtual ~AbstractEncoder() {}
public:
virtual std::unique_ptr<Fluxmap> encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors) = 0;
};
#endif

View File

@@ -1,5 +1,6 @@
#include "globals.h"
#include "flags.h"
#include "fmt/format.h"
static FlagGroup* currentFlagGroup;
static std::vector<Flag*> all_flags;
@@ -29,7 +30,7 @@ void FlagGroup::addFlag(Flag* flag)
_flags.push_back(flag);
}
void FlagGroup::parseFlags(int argc, const char* argv[])
std::vector<std::string> FlagGroup::parseFlagsWithFilenames(int argc, const char* argv[])
{
if (_initialised)
throw std::runtime_error("called parseFlags() twice");
@@ -66,6 +67,7 @@ void FlagGroup::parseFlags(int argc, const char* argv[])
/* Now actually parse them. */
std::vector<std::string> filenames;
int index = 1;
while (index < argc)
{
@@ -76,52 +78,73 @@ void FlagGroup::parseFlags(int argc, const char* argv[])
std::string value;
bool usesthat = false;
if ((thisarg.size() == 0) || (thisarg[0] != '-'))
Error() << "non-option parameter " << thisarg << " seen (try --help)";
if ((thisarg.size() > 1) && (thisarg[1] == '-'))
if (thisarg.size() == 0)
{
/* Long option. */
auto equals = thisarg.rfind('=');
if (equals != std::string::npos)
{
key = thisarg.substr(0, equals);
value = thisarg.substr(equals+1);
}
else
{
key = thisarg;
value = thatarg;
usesthat = true;
}
/* Ignore this argument. */
}
else if (thisarg[0] != '-')
{
/* This is a filename. */
filenames.push_back(thisarg);
}
else
{
/* Short option. */
/* This is a flag. */
if (thisarg.size() > 2)
if ((thisarg.size() > 1) && (thisarg[1] == '-'))
{
key = thisarg.substr(0, 2);
value = thisarg.substr(2);
/* Long option. */
auto equals = thisarg.rfind('=');
if (equals != std::string::npos)
{
key = thisarg.substr(0, equals);
value = thisarg.substr(equals+1);
}
else
{
key = thisarg;
value = thatarg;
usesthat = true;
}
}
else
{
key = thisarg;
value = thatarg;
usesthat = true;
/* Short option. */
if (thisarg.size() > 2)
{
key = thisarg.substr(0, 2);
value = thisarg.substr(2);
}
else
{
key = thisarg;
value = thatarg;
usesthat = true;
}
}
auto flag = flags_by_name.find(key);
if (flag == flags_by_name.end())
Error() << "unknown flag '" << key << "'; try --help";
flag->second->set(value);
if (usesthat && flag->second->hasArgument())
index++;
}
auto flag = flags_by_name.find(key);
if (flag == flags_by_name.end())
Error() << "unknown flag '" << key << "'; try --help";
flag->second->set(value);
index++;
if (usesthat && flag->second->hasArgument())
index++;
}
return filenames;
}
void FlagGroup::parseFlags(int argc, const char* argv[])
{
auto filenames = parseFlagsWithFilenames(argc, argv);
if (!filenames.empty())
Error() << "non-option parameter " << *filenames.begin() << " seen (try --help)";
}
void FlagGroup::checkInitialised() const
@@ -135,6 +158,8 @@ Flag::Flag(const std::vector<std::string>& names, const std::string helptext):
_names(names),
_helptext(helptext)
{
if (!currentFlagGroup)
Error() << "no flag group defined for " << *names.begin();
_group.addFlag(this);
}
@@ -148,6 +173,11 @@ void BoolFlag::set(const std::string& value)
Error() << "can't parse '" << value << "'; try 'true' or 'false'";
}
const std::string HexIntFlag::defaultValueAsString() const
{
return fmt::format("0x{:x}", _defaultValue);
}
static void doHelp()
{
std::cout << "FluxEngine options:" << std::endl;

View File

@@ -14,6 +14,7 @@ public:
public:
void parseFlags(int argc, const char* argv[]);
std::vector<std::string> parseFlagsWithFilenames(int argc, const char* argv[]);
void addFlag(Flag* flag);
void checkInitialised() const;
@@ -134,6 +135,17 @@ public:
void set(const std::string& value) { _value = std::stoi(value); }
};
class HexIntFlag : public IntFlag
{
public:
HexIntFlag(const std::vector<std::string>& names, const std::string helptext,
int defaultValue = 0):
IntFlag(names, helptext, defaultValue)
{}
const std::string defaultValueAsString() const;
};
class DoubleFlag : public ValueFlag<double>
{
public:
@@ -146,7 +158,7 @@ public:
void set(const std::string& value) { _value = std::stod(value); }
};
class BoolFlag : public ValueFlag<double>
class BoolFlag : public ValueFlag<bool>
{
public:
BoolFlag(const std::vector<std::string>& names, const std::string helptext,

View File

@@ -15,8 +15,7 @@ Fluxmap& Fluxmap::appendBytes(const uint8_t* ptr, size_t len)
while (len--)
{
uint8_t byte = *ptr++;
if (byte < 0x80)
_ticks += byte;
_ticks += byte & 0x3f;
bw.write_8(byte);
}
@@ -24,12 +23,19 @@ Fluxmap& Fluxmap::appendBytes(const uint8_t* ptr, size_t len)
return *this;
}
uint8_t& Fluxmap::findLastByte()
{
if (_bytes.empty())
appendByte(0x00);
return *(_bytes.end() - 1);
}
Fluxmap& Fluxmap::appendInterval(uint32_t ticks)
{
while (ticks >= 0x7f)
while (ticks >= 0x3f)
{
appendByte(0x7f);
ticks -= 0x7f;
appendByte(0x3f);
ticks -= 0x3f;
}
appendByte((uint8_t)ticks);
return *this;
@@ -37,13 +43,13 @@ Fluxmap& Fluxmap::appendInterval(uint32_t ticks)
Fluxmap& Fluxmap::appendPulse()
{
appendByte(0x80);
findLastByte() |= 0x80;
return *this;
}
Fluxmap& Fluxmap::appendIndex()
{
appendByte(0x81);
findLastByte() |= 0x40;
return *this;
}
@@ -54,25 +60,26 @@ void Fluxmap::precompensate(int threshold_ticks, int amount_ticks)
for (unsigned i=0; i<_bytes.size(); i++)
{
uint8_t& prev = (i == 0) ? junk : _bytes[i-1];
uint8_t curr = _bytes[i];
uint8_t prevticks = prev & 0x3f;
uint8_t currticks = _bytes[i] & 0x3f;
if (curr < (3*threshold_ticks))
if (currticks < (3*threshold_ticks))
{
if ((prev <= threshold_ticks) && (curr > threshold_ticks))
if ((prevticks <= threshold_ticks) && (currticks > threshold_ticks))
{
/* 01001; move the previous bit backwards. */
if (prev >= (1+amount_ticks))
if (prevticks >= (1+amount_ticks))
prev -= amount_ticks;
if (curr <= (0x7f-amount_ticks))
curr += amount_ticks;
if (currticks <= (0x7f-amount_ticks))
currticks += amount_ticks;
}
else if ((prev > threshold_ticks) && (curr <= threshold_ticks))
else if ((prevticks > threshold_ticks) && (currticks <= threshold_ticks))
{
/* 00101; move the current bit forwards. */
if (prev <= (0x7f-amount_ticks))
if (prevticks <= (0x7f-amount_ticks))
prev += amount_ticks;
if (curr >= (1+amount_ticks))
curr -= amount_ticks;
if (currticks >= (1+amount_ticks))
currticks -= amount_ticks;
}
}
}

View File

@@ -21,6 +21,7 @@ public:
public:
nanoseconds_t duration() const { return _duration; }
unsigned ticks() const { return _ticks; }
size_t bytes() const { return _bytes.size(); }
const Bytes& rawBytes() const { return _bytes; }
@@ -47,6 +48,9 @@ public:
void precompensate(int threshold_ticks, int amount_ticks);
private:
uint8_t& findLastByte();
private:
nanoseconds_t _duration = 0;
int _ticks = 0;

View File

@@ -10,7 +10,7 @@ static bool ends_with(const std::string& value, const std::string& ending)
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
std::unique_ptr<FluxSink> FluxSink::create(const DataSpec& spec)
std::unique_ptr<FluxSink> FluxSink::create(const FluxSpec& spec)
{
const auto& filename = spec.filename;

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