Compare commits

...

120 Commits

Author SHA1 Message Date
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
119 changed files with 10171 additions and 2579 deletions

View File

@@ -15,7 +15,7 @@ install:
build_script:
- make
- zip -9 fluxengine.zip fluxengine.exe brother120tool.exe
- zip -9 fluxengine.zip fluxengine.exe brother120tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
artifacts:
- path: fluxengine.zip

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

@@ -0,0 +1,34 @@
name: C/C++ CI
on: [push]
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: apt
run: 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
- 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:
# msystem: MSYS
# - 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: make
# 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,39 +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
env:
- HOMEBREW_NO_INSTALL_CLEANUP=1
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,168 @@
//`#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_WAIT = 1'b0;
localparam STATE_READ = 1'b1;
reg state;
reg oldreq;
assign ack = (state != STATE_READ);
always @(posedge clk)
begin
case (state)
STATE_WAIT:
begin
if (!empty)
begin
if (req && !oldreq)
begin
state <= STATE_READ;
end
oldreq <= req;
end
end
STATE_READ:
begin
state <= STATE_WAIT;
end
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: STATE_WAITFORREQ*/
`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: STATE_LOAD*/
`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, state}),
/* 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,6 +28,54 @@
<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" />
@@ -147,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" />
@@ -170,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" />
@@ -241,6 +360,53 @@
<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" />
@@ -288,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" />
@@ -312,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" />
@@ -609,18 +823,27 @@
<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>
@@ -638,16 +861,18 @@
<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="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" />
@@ -3859,6 +4084,32 @@
<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" />
@@ -3894,7 +4145,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">
@@ -3912,9 +4163,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,5" />
<Data key="Port Format" value="12,7" />
</Group>
</Group>
<Group key="fede1767-f3fd-4021-b3d7-8f9d88f36f9b">

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,134 @@
//`#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_RESET = 0;
localparam STATE_WAITING = 1;
localparam STATE_INTERVAL = 2;
localparam STATE_DISPATCH = 3;
localparam STATE_OPCODE = 4;
localparam STATE_COUNTING = 5;
reg [2:0] state;
wire [6:0] counter;
wire countnow;
assign countnow = (state == STATE_COUNTING);
wire counterreset;
assign counterreset = (state == STATE_INTERVAL) || (state == STATE_OPCODE);
SuperCounter #(.Delta(1), .ResetValue(0)) Counter
(
/* input */ .clk(clock),
/* input */ .reset(counterreset),
/* input */ .count(countnow),
/* output */ .d(counter)
);
reg oldsampleclock;
wire sampleclocked;
assign sampleclocked = !oldsampleclock && sampleclock;
reg oldindex;
wire indexed;
assign indexed = !oldindex && index;
wire rdataed;
reg oldrdata;
assign rdataed = !oldrdata && rdata;
assign req = (state == STATE_INTERVAL) || (state == STATE_OPCODE);
always @(posedge clock)
begin
if (reset)
begin
state <= STATE_RESET;
opcode <= 0;
oldsampleclock <= 0;
oldindex <= 0;
oldrdata <= 0;
end
else
case (state)
STATE_RESET:
state <= STATE_WAITING;
STATE_WAITING:
begin
if (rdataed || indexed)
begin
opcode <= {0, counter};
state <= STATE_INTERVAL;
end
else if (sampleclocked)
begin
oldsampleclock <= 1;
if (counter == 7'h7f)
begin
opcode <= {0, counter};
state <= STATE_OPCODE;
end
else
state <= STATE_COUNTING;
end
if (oldrdata && !rdata)
oldrdata <= 0;
if (oldindex && !index)
oldindex <= 0;
if (oldsampleclock && !sampleclock)
oldsampleclock <= 0;
end
STATE_INTERVAL: /* interval byte sent here; counter reset */
state <= STATE_DISPATCH;
STATE_DISPATCH: /* relax after interval byte, dispatch for opcode */
begin
if (rdataed)
begin
oldrdata <= 1;
opcode <= 8'h80;
state <= STATE_OPCODE;
end
else if (indexed)
begin
oldindex <= 1;
opcode <= 8'h81;
state <= STATE_OPCODE;
end
else
state <= STATE_WAITING;
end
STATE_OPCODE: /* opcode byte sent here */
state <= STATE_WAITING;
STATE_COUNTING: /* counter changes here */
state <= STATE_WAITING;
endcase
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,103 @@
//`#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_IDLE = 0;
localparam STATE_LOAD = 1;
localparam STATE_WAITING = 2;
localparam STATE_PULSING = 3;
localparam STATE_INDEXING = 4;
localparam OPCODE_PULSE = 8'h80;
localparam OPCODE_INDEX = 8'h81;
reg [2:0] state;
reg [6:0] countdown;
assign req = (state == STATE_LOAD);
assign wdata = (state == STATE_PULSING);
assign debug_state = state;
reg olddataclock;
wire dataclocked;
always @(posedge clock) olddataclock <= dataclock;
assign dataclocked = !olddataclock && dataclock;
reg oldsampleclock;
wire sampleclocked;
always @(posedge clock) oldsampleclock <= sampleclock;
assign sampleclocked = !oldsampleclock && sampleclock;
reg oldindex;
wire indexed;
always @(posedge clock) oldindex <= index;
assign indexed = !oldindex && index;
always @(posedge clock)
begin
if (reset)
begin
state <= STATE_IDLE;
countdown <= 0;
end
else
case (state)
STATE_IDLE:
state <= STATE_LOAD;
STATE_LOAD:
if (dataclocked)
case (opcode)
OPCODE_PULSE:
state <= STATE_PULSING;
OPCODE_INDEX:
state <= STATE_INDEXING;
default:
begin
countdown <= opcode[6:0];
state <= STATE_WAITING;
end
endcase
STATE_WAITING:
if (sampleclocked)
begin
if (countdown == 0)
state <= STATE_LOAD;
else
countdown <= countdown - 1;
end
STATE_PULSING:
state <= STATE_LOAD;
STATE_INDEXING:
if (indexed)
state <= STATE_LOAD;
else
state <= STATE_INDEXING;
endcase
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

@@ -17,14 +17,14 @@
#define STEP_TOWARDS0 1
#define STEP_AWAYFROM0 0
static volatile uint32_t clock = 0;
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_SIZE 64
@@ -45,6 +45,21 @@ 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 +100,22 @@ 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)
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,6 +126,16 @@ 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)
@@ -138,25 +175,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);
}
static void 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())
break;
step(STEP_TOWARDS0);
}
/* Step to -1, which should be a nop, to reset the disk on disk change. */
step(STEP_TOWARDS0);
}
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 +215,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,17 +252,29 @@ 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);
}
@@ -249,7 +305,7 @@ static void deinit_dma(void)
static void init_capture_dma(void)
{
dma_channel = CAPTURE_DMA_DmaInitialize(
dma_channel = SAMPLER_DMA_DmaInitialize(
2 /* bytes */,
true /* request per burst */,
HI16(CYDEV_PERIPH_BASE),
@@ -264,8 +320,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,26 +332,26 @@ 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_MID;
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;
if (f->synced)
{
index_irq = false;
while (!index_irq)
;
index_irq = false;
}
crunch_state_t cs = {};
cs.outputptr = usb_buffer;
@@ -305,8 +361,6 @@ static void cmd_read(struct read_frame* f)
dma_reading_from_td = -1;
dma_underrun = false;
int count = 0;
SAMPLER_CONTROL_Write(0); /* !reset */
CAPTURE_CONTROL_Write(1);
CyDmaChSetInitialTd(dma_channel, td[dma_writing_to_td]);
CyDmaClearPendingDrq(dma_channel);
CyDmaChEnable(dma_channel, 1);
@@ -314,32 +368,27 @@ 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;
/* Start transferring. */
int revolutions = f->revolutions;
uint32_t start_time = clock;
while (!dma_underrun)
{
CyWdtClear();
/* Have we reached the index pulse? */
if (index_irq)
{
index_irq = false;
revolutions--;
if (revolutions == 0)
break;
}
/* Wait for the next block to be read. */
while (dma_reading_from_td == dma_writing_to_td)
{
/* On an underrun, give up immediately. */
if (dma_underrun)
goto abort;
/* Also finish if the sample session is over. */
if ((clock - start_time) >= f->milliseconds)
goto abort;
}
uint8_t dma_buffer_usage = 0;
@@ -350,15 +399,14 @@ static void cmd_read(struct read_frame* f)
crunch(&cs);
dma_buffer_usage += BUFFER_SIZE - cs.inputlen;
count++;
/* If there is no available space in the output buffer, flush the buffer via
* USB and go again. */
if (cs.outputlen == 0)
{
while (USBFS_GetEPState(FLUXENGINE_DATA_IN_EP_NUM) != USBFS_IN_BUFFER_EMPTY)
{
if (index_irq || dma_underrun)
goto abort;
}
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE);
cs.outputptr = usb_buffer;
cs.outputlen = BUFFER_SIZE;
}
@@ -366,22 +414,33 @@ static void cmd_read(struct read_frame* f)
dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td);
}
abort:;
CAPTURE_CONTROL_Write(0);
bool saved_dma_underrun = dma_underrun;
CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN);
while (CyDmaChGetRequest(dma_channel))
;
donecrunch(&cs);
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
unsigned zz = cs.outputlen;
/* If there's a complete packet waiting, send it. */
if (cs.outputlen != BUFFER_SIZE)
{
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE);
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
}
if ((cs.outputlen != 0) && (cs.outputlen != BUFFER_SIZE))
{
/* If there's a partial packet waiting, send it; this will also terminate the transfer. */
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE-cs.outputlen);
if ((cs.outputlen == BUFFER_SIZE) || (cs.outputlen == 0))
}
else
{
/* Otherwise just terminate the transfer. */
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);
@@ -391,7 +450,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)
@@ -412,25 +471,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_NORMAL;
REPLAY_FIFO_CLEAR;
REPLAY_FIFO_SINGLE_BUFFER_UNSET;
CyExitCriticalSection(i);
}
seek_to(current_track);
@@ -452,6 +514,8 @@ static void cmd_write(struct write_frame* f)
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)
@@ -555,7 +619,7 @@ 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)
@@ -573,6 +637,7 @@ abort:
}
deinit_dma();
print("write finished");
if (dma_underrun)
{
@@ -607,17 +672,101 @@ 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;
}
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];
@@ -662,6 +811,10 @@ 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);
@@ -674,9 +827,11 @@ int main(void)
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);
@@ -692,10 +847,7 @@ 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())
@@ -709,6 +861,7 @@ int main(void)
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,8 +1,8 @@
PACKAGES = zlib sqlite3 libusb-1.0
export CFLAGS = -O3 -g --std=c++14 \
export CFLAGS = -Os -g --std=c++14 \
-ffunction-sections -fdata-sections
export LDFLAGS = -O3
export LDFLAGS = -Os
ifeq ($(OS), Windows_NT)
export CXX = /mingw32/bin/g++
@@ -13,6 +13,13 @@ 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

View File

@@ -89,7 +89,7 @@ people who've had it work).
| [Brother 120kB](doc/disk-brother.md) | 🦄 | | |
| [Brother 240kB](doc/disk-brother.md) | 🦄 | 🦄 | |
| [Brother FB-100](doc/disk-fb100.md) | 🦖 | | Tandy Model 100, Husky Hunter, knitting machines |
| [Macintosh 800kB](doc/disk-macintosh.md) | 🦄 | | and probably the 400kB too |
| [Macintosh 800kB](doc/disk-macintosh.md) | 🦖 | | and probably the 400kB too |
| [TRS-80](doc/disk-trs80.md) | 🦖 | | a minor variation of the IBM scheme |
{: .datatable }

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

View File

@@ -1,12 +1,17 @@
#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
{
@@ -17,4 +22,20 @@ public:
void decodeSectorRecord();
};
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

View File

@@ -21,47 +21,6 @@
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);
@@ -78,20 +37,22 @@ void AmigaDecoder::decodeSectorRecord()
const uint8_t* ptr = bytes.begin() + 3;
Bytes header = deinterleave(ptr, 4);
Bytes recoveryinfo = deinterleave(ptr, 16);
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 = deinterleave(ptr, 4).reader().read_be32();
uint32_t gotheaderchecksum = checksum(rawbytes.slice(6, 40));
uint32_t wantedheaderchecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
uint32_t gotheaderchecksum = amigaChecksum(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);
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;
}

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

@@ -0,0 +1,126 @@
#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 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);
}
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 });
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

@@ -154,7 +154,7 @@ std::unique_ptr<Fluxmap> BrotherEncoder::encode(
write_sector_data(bits, cursor, sectorData->data);
}
if (cursor > bits.size())
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });

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()

View File

@@ -31,8 +31,9 @@ struct IbmIdam
class IbmDecoder : public AbstractDecoder
{
public:
IbmDecoder(unsigned sectorBase):
_sectorBase(sectorBase)
IbmDecoder(unsigned sectorBase, bool ignoreSideByte=false):
_sectorBase(sectorBase),
_ignoreSideByte(ignoreSideByte)
{}
RecordType advanceToNextRecord();
@@ -41,6 +42,7 @@ public:
private:
unsigned _sectorBase;
bool _ignoreSideByte;
unsigned _currentSectorSize;
unsigned _currentHeaderLength;
};

View File

@@ -180,5 +180,7 @@ 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));
}

View File

Binary file not shown.

View File

@@ -103,7 +103,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 +154,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.
@@ -139,11 +175,6 @@ the port and proceed normally.
## Building the client
**Before you read this:** If you're on Windows, good news! You can download a
*precompiled version of the FluxEngine client [from the GitHub releases
*page](https://github.com/davidgiven/fluxengine/releases/latest). Simply unzip
*it somewhere and run it from a `cmd` window (or other shell).
The client software is where the intelligence, such as it is, is. It's pretty
generic libusb stuff and should build and run on Windows, Linux and OSX as
well, although on Windows it'll need MSYS2 and mingw32. You'll need to
@@ -151,7 +182,7 @@ 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`.
- 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`.

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

@@ -129,8 +129,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

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

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

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

@@ -123,8 +123,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

@@ -51,7 +51,7 @@ In order to do anything useful, you have to plug it in to a floppy disk drive (o
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
7. Do `fluxengine test bulktransport` 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.
@@ -64,6 +64,16 @@ In order to do anything useful, you have to plug it in to a floppy disk drive (o
9. Profit!
## Bonus hardware features
For advanced users, the board has a few extra signals which are useful for special purposes.
- Pin 3[0] produces short pulses every 200ms. This is useful for spoofing
index signals to 300 RPM drives; for example, to read flippy disks.
- Pin 3[1] is the same, but produces the pulses every 166ms; this works with
360 RPM drives.
## The programs
I'm sorry to say that the client program is very badly documented --- it's
@@ -112,6 +122,51 @@ sensible for the command you're using.
**Important note:** FluxEngine _always_ uses zero-based units (even if the
*disk format says otherwise).
### Input and output specifiers
These use a very similar syntax to the source and destination specifiers
(because they're based on the same microformat library!) but are used for
input and output _images_: i.e. nicely lined up arrays of sectors which you
can actually do something with.
Use `--input` (`-i`) or `--output` (`-o`) as appropriate to tell FluxEngine
where you want to read from or write to. The actual format is autodetected
based on 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
High density disks use a different magnetic medium to low and double density
@@ -131,6 +186,23 @@ 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. Many formats
require `--revolutions=2` (which should happen automatically); or you can
increase the number to sample more data.
- `--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.
@@ -144,12 +216,13 @@ directory.
- `fluxengine inspect`: dumps the raw pulsetrain / bitstream to stdout.
Mainly useful for debugging.
- `fluxengine read*`: reads various formats of disk. See the per-format
- `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.
specified file. There are various `--dump` options for showing raw data
during the decode process.
- `fluxengine write*`: writes various formats of disk. Again, see the
- `fluxengine write *`: writes various formats of disk. Again, see the
per-format documentation above.
- `fluxengine writeflux`: writes raw flux files. This is much less useful
@@ -168,18 +241,50 @@ directory.
- `fluxengine seek`: moves the head. Mainly useful for finding out whether
your drive can seek to track 82. (Mine can't.)
- `fluxengine testbulktransport`: measures your USB throughput. You need
- `fluxengine test bulktransport`: 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 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
@@ -187,25 +292,24 @@ 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.
## 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 --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:
also copy the flux to brother.flux 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

@@ -2,6 +2,7 @@
#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)
@@ -280,6 +281,16 @@ Bytes Bytes::uncrunch() const
return output;
}
void Bytes::writeToFile(const std::string& filename) const
{
std::ofstream f(filename, std::ios::out | std::ios::binary);
if (!f.is_open())
Error() << fmt::format("cannot open output file '{}'", filename);
f.write((const char*) cbegin(), size());
f.close();
}
ByteReader Bytes::reader() const
{
return ByteReader(*this);

View File

@@ -56,6 +56,8 @@ public:
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

@@ -5,6 +5,11 @@
#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)
{
@@ -74,31 +79,108 @@ void DataSpec::set(const std::string& spec)
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,57 @@
#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 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 +66,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)

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();
@@ -41,21 +41,32 @@ void AbstractDecoder::decodeToSectors(Track& track)
/* 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.readNextMatchingOpcode(F_OP_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);

View File

@@ -20,6 +20,7 @@ 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);
static inline Bytes decodeFmMfm(const std::vector<bool> bits)
{ return decodeFmMfm(bits.begin(), bits.end()); }

View File

@@ -18,13 +18,18 @@ 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);
static DoubleFlag minimumClockUs(
{ "--minimum-clock-us" },
"Refuse to detect clocks shorter than this, to avoid false positives.",
0.75);
int FluxmapReader::readOpcode(unsigned& ticks)
{
ticks = 0;
@@ -222,7 +227,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++)

View File

@@ -51,3 +51,25 @@ Bytes decodeFmMfm(
return bytes;
}
void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input)
{
bool lastBit = false;
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

@@ -29,7 +29,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 +66,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 +77,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 +157,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);
}

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;

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;

View File

@@ -1,8 +1,12 @@
#ifndef FLUXSINK_H
#define FLUXSINK_H
#include "flags.h"
extern FlagGroup hardwareFluxSinkFlags;
class Fluxmap;
class DataSpec;
class FluxSpec;
class FluxSink
{
@@ -14,7 +18,7 @@ private:
static std::unique_ptr<FluxSink> createHardwareFluxSink(unsigned drive);
public:
static std::unique_ptr<FluxSink> create(const DataSpec& spec);
static std::unique_ptr<FluxSink> create(const FluxSpec& spec);
public:
virtual void writeFlux(int track, int side, Fluxmap& fluxmap) = 0;

View File

@@ -4,8 +4,15 @@
#include "usb.h"
#include "fluxsink/fluxsink.h"
FlagGroup hardwareFluxSinkFlags;
static bool high_density = false;
static IntFlag indexMode(
{ "--write-index-mode" },
"index pulse source (0=drive, 1=300 RPM fake source, 2=360 RPM fake source",
0);
void setHardwareFluxSinkDensity(bool high_density)
{
::high_density = high_density;
@@ -26,7 +33,7 @@ public:
public:
void writeFlux(int track, int side, Fluxmap& fluxmap)
{
usbSetDrive(_drive, high_density);
usbSetDrive(_drive, high_density, indexMode);
usbSeek(track);
Bytes crunched = fluxmap.rawBytes().crunch();

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<FluxSource> FluxSource::create(const DataSpec& spec)
std::unique_ptr<FluxSource> FluxSource::create(const FluxSpec& spec)
{
const auto& filename = spec.filename;

View File

@@ -6,7 +6,7 @@
extern FlagGroup hardwareFluxSourceFlags;
class Fluxmap;
class DataSpec;
class FluxSpec;
class FluxSource
{
@@ -19,7 +19,7 @@ private:
static std::unique_ptr<FluxSource> createStreamFluxSource(const std::string& path);
public:
static std::unique_ptr<FluxSource> create(const DataSpec& spec);
static std::unique_ptr<FluxSource> create(const FluxSpec& spec);
public:
virtual std::unique_ptr<Fluxmap> readFlux(int track, int side) = 0;
@@ -27,8 +27,9 @@ public:
virtual bool retryable() { return false; }
};
extern void setHardwareFluxSourceRevolutions(int revolutions);
extern void setHardwareFluxSourceRevolutions(double revolutions);
extern void setHardwareFluxSourceDensity(bool high_density);
extern void setHardwareFluxSourceSynced(bool synced);
#endif

View File

@@ -3,13 +3,24 @@
#include "fluxmap.h"
#include "usb.h"
#include "fluxsource/fluxsource.h"
#include "fmt/format.h"
FlagGroup hardwareFluxSourceFlags;
static IntFlag revolutions(
static DoubleFlag revolutions(
{ "--revolutions" },
"read this many revolutions of the disk",
1);
1.25);
static BoolFlag synced(
{ "--sync-with-index" },
"whether to wait for an index pulse before started to read",
false);
static IntFlag indexMode(
{ "--index-mode" },
"index pulse source (0=drive, 1=300 RPM fake source, 2=360 RPM fake source",
0);
static bool high_density = false;
@@ -24,6 +35,10 @@ public:
HardwareFluxSource(unsigned drive):
_drive(drive)
{
usbSetDrive(_drive, high_density, indexMode);
std::cerr << "Measuring rotational speed... " << std::flush;
_oneRevolution = usbGetRotationalPeriod();
std::cerr << fmt::format("{}ms\n", _oneRevolution / 1e6);
}
~HardwareFluxSource()
@@ -33,9 +48,9 @@ public:
public:
std::unique_ptr<Fluxmap> readFlux(int track, int side)
{
usbSetDrive(_drive, high_density);
usbSetDrive(_drive, high_density, indexMode);
usbSeek(track);
Bytes crunched = usbRead(side, revolutions);
Bytes crunched = usbRead(side, synced, revolutions * _oneRevolution);
auto fluxmap = std::make_unique<Fluxmap>();
fluxmap->appendBytes(crunched.uncrunch());
return fluxmap;
@@ -54,13 +69,19 @@ public:
private:
unsigned _drive;
unsigned _revolutions;
nanoseconds_t _oneRevolution;
};
void setHardwareFluxSourceRevolutions(int revolutions)
void setHardwareFluxSourceRevolutions(double revolutions)
{
::revolutions.setDefaultValue(revolutions);
}
void setHardwareFluxSourceSynced(bool synced)
{
::synced.setDefaultValue(synced);
}
std::unique_ptr<FluxSource> FluxSource::createHardwareFluxSource(unsigned drive)
{
return std::unique_ptr<FluxSource>(new HardwareFluxSource(drive));

View File

@@ -24,7 +24,7 @@ public:
void recalibrate() {}
private:
const std::string& _path;
const std::string _path;
};
std::unique_ptr<FluxSource> FluxSource::createStreamFluxSource(const std::string& path)

View File

@@ -22,7 +22,7 @@ extern void hexdumpForSrp16(std::ostream& stream, const Bytes& bytes);
class Error
{
public:
~Error()
[[ noreturn ]] ~Error()
{
std::cerr << "Error: " << _stream.str() << std::endl;
exit(1);

View File

@@ -1,150 +1,25 @@
#include "globals.h"
#include "image.h"
#include "flags.h"
#include "dataspec.h"
#include "sector.h"
#include "sectorset.h"
#include "imagereader/imagereader.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
Geometry guessGeometry(const SectorSet& sectors)
SectorSet readSectorsFromFile(const ImageSpec& spec)
{
Geometry g;
sectors.calculateSize(g.tracks, g.heads, g.sectors, g.sectorSize);
return g;
return ImageReader::create(spec)->readImage();
}
void readSectorsFromFile(SectorSet& sectors, const Geometry& geometry,
const std::string& filename)
void writeSectorsToFile(const SectorSet& sectors, const ImageSpec& spec)
{
std::ifstream inputFile(filename, std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
size_t headSize = geometry.sectors * geometry.sectorSize;
size_t trackSize = headSize * geometry.heads;
std::cout << fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total",
geometry.tracks, geometry.heads,
geometry.sectors, geometry.sectorSize,
geometry.tracks * trackSize / 1024)
<< std::endl;
for (int track = 0; track < geometry.tracks; track++)
{
for (int head = 0; head < geometry.heads; head++)
{
for (int sectorId = 0; sectorId < geometry.sectors; sectorId++)
{
inputFile.seekg(track*trackSize + head*headSize + sectorId*geometry.sectorSize, std::ios::beg);
Bytes data(geometry.sectorSize);
inputFile.read((char*) data.begin(), geometry.sectorSize);
std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId);
sector.reset(new Sector);
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalTrack = track;
sector->logicalSide = sector->physicalSide = head;
sector->logicalSector = sectorId;
sector->data = data;
}
}
}
}
void writeSectorsToFile(const SectorSet& sectors, const Geometry& geometry,
const std::string& filename)
{
/* Emit the map. */
int badSectors = 0;
int missingSectors = 0;
int totalSectors = 0;
std::cout << "H.SS Tracks --->" << std::endl;
for (int head = 0; head < geometry.heads; head++)
{
for (int sectorId = 0; sectorId < geometry.sectors; sectorId++)
{
std::cout << fmt::format("{}.{:2} ", head, sectorId);
for (int track = 0; track < geometry.tracks; track++)
{
Sector* sector = sectors.get(track, head, sectorId);
if (!sector)
{
std::cout << 'X';
missingSectors++;
}
else
{
switch (sector->status)
{
case Sector::OK:
std::cout << '.';
break;
case Sector::BAD_CHECKSUM:
std::cout << 'B';
badSectors++;
break;
case Sector::CONFLICT:
std::cout << 'C';
badSectors++;
break;
default:
std::cout << '?';
break;
}
}
totalSectors++;
}
std::cout << std::endl;
}
}
int goodSectors = totalSectors - missingSectors - badSectors;
if (totalSectors == 0)
std::cout << "No sectors in output; skipping analysis" << std::endl;
else
{
std::cout << "Good sectors: " << goodSectors << "/" << totalSectors
<< " (" << (100*goodSectors/totalSectors) << "%)"
<< std::endl;
std::cout << "Missing sectors: " << missingSectors << "/" << totalSectors
<< " (" << (100*missingSectors/totalSectors) << "%)"
<< std::endl;
std::cout << "Bad sectors: " << badSectors << "/" << totalSectors
<< " (" << (100*badSectors/totalSectors) << "%)"
<< std::endl;
}
size_t headSize = geometry.sectors * geometry.sectorSize;
size_t trackSize = headSize * geometry.heads;
std::cout << fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total",
geometry.tracks, geometry.heads,
geometry.sectors, geometry.sectorSize,
geometry.tracks * trackSize / 1024)
<< std::endl;
std::ofstream outputFile(filename, std::ios::out | std::ios::binary);
if (!outputFile.is_open())
Error() << "cannot open output file";
for (int track = 0; track < geometry.tracks; track++)
{
for (int head = 0; head < geometry.heads; head++)
{
for (int sectorId = 0; sectorId < geometry.sectors; sectorId++)
{
auto sector = sectors.get(track, head, sectorId);
if (sector)
{
outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*geometry.sectorSize, std::ios::beg);
outputFile.write((const char*) sector->data.cbegin(), sector->data.size());
}
}
}
}
std::unique_ptr<ImageWriter> writer(ImageWriter::create(sectors, spec));
writer->adjustGeometry();
writer->printMap();
writer->writeImage();
}

View File

@@ -2,26 +2,13 @@
#define IMAGE_H
class SectorSet;
class ImageSpec;
class Geometry
{
public:
int tracks;
int heads;
int sectors;
int sectorSize;
};
extern Geometry guessGeometry(const SectorSet& sectors);
extern void readSectorsFromFile(
SectorSet& sectors,
const Geometry& geometry,
const std::string& filename);
extern SectorSet readSectorsFromFile(
const ImageSpec& filename);
extern void writeSectorsToFile(
const SectorSet& sectors,
const Geometry& geometry,
const std::string& filename);
const ImageSpec& filename);
#endif

View File

@@ -0,0 +1,52 @@
#include "globals.h"
#include "image.h"
#include "flags.h"
#include "dataspec.h"
#include "sector.h"
#include "sectorset.h"
#include "imagereader/imagereader.h"
#include "fmt/format.h"
std::map<std::string, ImageReader::Constructor> ImageReader::formats =
{
{".adf", ImageReader::createImgImageReader},
{".d81", ImageReader::createImgImageReader},
{".img", ImageReader::createImgImageReader},
};
static bool ends_with(const std::string& value, const std::string& ending)
{
if (ending.size() > value.size())
return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
ImageReader::Constructor ImageReader::findConstructor(const ImageSpec& spec)
{
const auto& filename = spec.filename;
for (const auto& e : formats)
{
if (ends_with(filename, e.first))
return e.second;
}
return NULL;
}
std::unique_ptr<ImageReader> ImageReader::create(const ImageSpec& spec)
{
verifyImageSpec(spec);
return findConstructor(spec)(spec);
}
void ImageReader::verifyImageSpec(const ImageSpec& spec)
{
if (!findConstructor(spec))
Error() << "unrecognised image filename extension";
}
ImageReader::ImageReader(const ImageSpec& spec):
spec(spec)
{}

View File

@@ -0,0 +1,38 @@
#ifndef IMAGEREADER_H
#define IMAGEREADER_H
class SectorSet;
class ImageSpec;
class ImageReader
{
public:
ImageReader(const ImageSpec& spec);
virtual ~ImageReader() {};
public:
static std::unique_ptr<ImageReader> create(const ImageSpec& spec);
static void verifyImageSpec(const ImageSpec& spec);
private:
typedef
std::function<
std::unique_ptr<ImageReader>(const ImageSpec& spec)
>
Constructor;
static std::map<std::string, Constructor> formats;
static std::unique_ptr<ImageReader> createImgImageReader(const ImageSpec& spec);
static Constructor findConstructor(const ImageSpec& spec);
public:
virtual SectorSet readImage() = 0;
protected:
ImageSpec spec;
};
#endif

View File

@@ -0,0 +1,66 @@
#include "globals.h"
#include "image.h"
#include "flags.h"
#include "dataspec.h"
#include "sector.h"
#include "sectorset.h"
#include "imagereader/imagereader.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
class ImgImageReader : public ImageReader
{
public:
ImgImageReader(const ImageSpec& spec):
ImageReader(spec)
{}
SectorSet readImage()
{
std::ifstream inputFile(spec.filename, std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
size_t headSize = spec.sectors * spec.bytes;
size_t trackSize = headSize * spec.heads;
std::cout << fmt::format("reading {} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total",
spec.cylinders, spec.heads,
spec.sectors, spec.bytes,
spec.cylinders * trackSize / 1024)
<< std::endl;
SectorSet sectors;
for (int track = 0; track < spec.cylinders; track++)
{
for (int head = 0; head < spec.heads; head++)
{
for (int sectorId = 0; sectorId < spec.sectors; sectorId++)
{
inputFile.seekg(track*trackSize + head*headSize + sectorId*spec.bytes, std::ios::beg);
Bytes data(spec.bytes);
inputFile.read((char*) data.begin(), spec.bytes);
std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId);
sector.reset(new Sector);
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalTrack = track;
sector->logicalSide = sector->physicalSide = head;
sector->logicalSector = sectorId;
sector->data = data;
}
}
}
return sectors;
}
};
std::unique_ptr<ImageReader> ImageReader::createImgImageReader(
const ImageSpec& spec)
{
return std::unique_ptr<ImageReader>(new ImgImageReader(spec));
}

View File

@@ -0,0 +1,64 @@
#include "globals.h"
#include "image.h"
#include "flags.h"
#include "dataspec.h"
#include "sector.h"
#include "sectorset.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
#include "ldbs.h"
#include <algorithm>
#include <iostream>
#include <fstream>
static int sectors_per_track(int track)
{
if (track < 17)
return 21;
if (track < 24)
return 19;
if (track < 30)
return 18;
return 17;
}
class D64ImageWriter : public ImageWriter
{
public:
D64ImageWriter(const SectorSet& sectors, const ImageSpec& spec):
ImageWriter(sectors, spec)
{}
void writeImage()
{
std::cout << "writing D64 triangular image\n";
std::ofstream outputFile(spec.filename, std::ios::out | std::ios::binary);
if (!outputFile.is_open())
Error() << "cannot open output file";
uint32_t offset = 0;
for (int track = 0; track < 40; track++)
{
int sectorCount = sectors_per_track(track);
for (int sectorId = 0; sectorId < sectorCount; sectorId++)
{
const auto& sector = sectors.get(track, 0, sectorId);
if (sector)
{
outputFile.seekp(offset);
outputFile.write((const char*) sector->data.cbegin(), 256);
}
offset += 256;
}
}
}
};
std::unique_ptr<ImageWriter> ImageWriter::createD64ImageWriter(
const SectorSet& sectors, const ImageSpec& spec)
{
return std::unique_ptr<ImageWriter>(new D64ImageWriter(sectors, spec));
}

View File

@@ -0,0 +1,128 @@
#include "globals.h"
#include "image.h"
#include "flags.h"
#include "dataspec.h"
#include "sector.h"
#include "sectorset.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
std::map<std::string, ImageWriter::Constructor> ImageWriter::formats =
{
{".adf", ImageWriter::createImgImageWriter},
{".d64", ImageWriter::createD64ImageWriter},
{".d81", ImageWriter::createImgImageWriter},
{".img", ImageWriter::createImgImageWriter},
{".ldbs", ImageWriter::createLDBSImageWriter},
};
static bool ends_with(const std::string& value, const std::string& ending)
{
if (ending.size() > value.size())
return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
ImageWriter::Constructor ImageWriter::findConstructor(const ImageSpec& spec)
{
const auto& filename = spec.filename;
for (const auto& e : formats)
{
if (ends_with(filename, e.first))
return e.second;
}
return NULL;
}
std::unique_ptr<ImageWriter> ImageWriter::create(const SectorSet& sectors, const ImageSpec& spec)
{
verifyImageSpec(spec);
return findConstructor(spec)(sectors, spec);
}
void ImageWriter::verifyImageSpec(const ImageSpec& spec)
{
if (!findConstructor(spec))
Error() << "unrecognised image filename extension";
}
ImageWriter::ImageWriter(const SectorSet& sectors, const ImageSpec& spec):
sectors(sectors),
spec(spec)
{}
void ImageWriter::adjustGeometry()
{
if (!spec.initialised)
{
sectors.calculateSize(spec.cylinders, spec.heads, spec.sectors, spec.bytes);
spec.initialised = true;
std::cout << "Autodetecting output geometry\n";
}
}
void ImageWriter::printMap()
{
int badSectors = 0;
int missingSectors = 0;
int totalSectors = 0;
std::cout << "H.SS Tracks --->" << std::endl;
for (int head = 0; head < spec.heads; head++)
{
for (int sectorId = 0; sectorId < spec.sectors; sectorId++)
{
std::cout << fmt::format("{}.{:2} ", head, sectorId);
for (int track = 0; track < spec.cylinders; track++)
{
const auto& sector = sectors.get(track, head, sectorId);
if (!sector)
{
std::cout << 'X';
missingSectors++;
}
else
{
switch (sector->status)
{
case Sector::OK:
std::cout << '.';
break;
case Sector::BAD_CHECKSUM:
std::cout << 'B';
badSectors++;
break;
case Sector::CONFLICT:
std::cout << 'C';
badSectors++;
break;
default:
std::cout << '?';
break;
}
}
totalSectors++;
}
std::cout << std::endl;
}
}
int goodSectors = totalSectors - missingSectors - badSectors;
if (totalSectors == 0)
std::cout << "No sectors in output; skipping analysis" << std::endl;
else
{
std::cout << "Good sectors: " << goodSectors << "/" << totalSectors
<< " (" << (100*goodSectors/totalSectors) << "%)"
<< std::endl;
std::cout << "Missing sectors: " << missingSectors << "/" << totalSectors
<< " (" << (100*missingSectors/totalSectors) << "%)"
<< std::endl;
std::cout << "Bad sectors: " << badSectors << "/" << totalSectors
<< " (" << (100*badSectors/totalSectors) << "%)"
<< std::endl;
}
}

View File

@@ -0,0 +1,45 @@
#ifndef IMAGEWRITER_H
#define IMAGEWRITER_H
class SectorSet;
class ImageSpec;
class ImageWriter
{
public:
ImageWriter(const SectorSet& sectors, const ImageSpec& spec);
virtual ~ImageWriter() {};
public:
static std::unique_ptr<ImageWriter> create(const SectorSet& sectors, const ImageSpec& spec);
static void verifyImageSpec(const ImageSpec& filename);
private:
typedef
std::function<
std::unique_ptr<ImageWriter>(const SectorSet& sectors, const ImageSpec& spec)
>
Constructor;
static std::map<std::string, Constructor> formats;
static std::unique_ptr<ImageWriter> createImgImageWriter(
const SectorSet& sectors, const ImageSpec& spec);
static std::unique_ptr<ImageWriter> createLDBSImageWriter(
const SectorSet& sectors, const ImageSpec& spec);
static std::unique_ptr<ImageWriter> createD64ImageWriter(
const SectorSet& sectors, const ImageSpec& spec);
static Constructor findConstructor(const ImageSpec& spec);
public:
virtual void adjustGeometry();
void printMap();
virtual void writeImage() = 0;
protected:
const SectorSet& sectors;
ImageSpec spec;
};
#endif

View File

@@ -0,0 +1,62 @@
#include "globals.h"
#include "image.h"
#include "flags.h"
#include "dataspec.h"
#include "sector.h"
#include "sectorset.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
class ImgImageWriter : public ImageWriter
{
public:
ImgImageWriter(const SectorSet& sectors, const ImageSpec& spec):
ImageWriter(sectors, spec)
{}
void writeImage()
{
unsigned numCylinders = spec.cylinders;
unsigned numHeads = spec.heads;
unsigned numSectors = spec.sectors;
unsigned numBytes = spec.bytes;
size_t headSize = numSectors * numBytes;
size_t trackSize = headSize * numHeads;
std::cout << fmt::format("writing {} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total",
numCylinders, numHeads,
numSectors, numBytes,
numCylinders * trackSize / 1024)
<< std::endl;
std::ofstream outputFile(spec.filename, std::ios::out | std::ios::binary);
if (!outputFile.is_open())
Error() << "cannot open output file";
for (int track = 0; track < numCylinders; track++)
{
for (int head = 0; head < numHeads; head++)
{
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
const auto& sector = sectors.get(track, head, sectorId);
if (sector)
{
outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*numBytes, std::ios::beg);
outputFile.write((const char*) sector->data.cbegin(), sector->data.size());
}
}
}
}
}
};
std::unique_ptr<ImageWriter> ImageWriter::createImgImageWriter(
const SectorSet& sectors, const ImageSpec& spec)
{
return std::unique_ptr<ImageWriter>(new ImgImageWriter(sectors, spec));
}

View File

@@ -0,0 +1,107 @@
#include "globals.h"
#include "image.h"
#include "flags.h"
#include "dataspec.h"
#include "sector.h"
#include "sectorset.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
#include "ldbs.h"
#include <algorithm>
#include <iostream>
#include <fstream>
class LDBSImageWriter : public ImageWriter
{
public:
LDBSImageWriter(const SectorSet& sectors, const ImageSpec& spec):
ImageWriter(sectors, spec)
{}
void writeImage()
{
LDBS ldbs;
unsigned numCylinders = spec.cylinders;
unsigned numHeads = spec.heads;
unsigned numSectors = spec.sectors;
unsigned numBytes = spec.bytes;
std::cout << fmt::format("writing {} tracks, {} heads, {} sectors, {} bytes per sector",
numCylinders, numHeads,
numSectors, numBytes)
<< std::endl;
Bytes trackDirectory;
ByteWriter trackDirectoryWriter(trackDirectory);
int trackDirectorySize = 0;
trackDirectoryWriter.write_le16(0);
for (int track = 0; track < numCylinders; track++)
{
for (int head = 0; head < numHeads; head++)
{
Bytes trackHeader;
ByteWriter trackHeaderWriter(trackHeader);
int actualSectors = 0;
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
const auto& sector = sectors.get(track, head, sectorId);
if (sector)
actualSectors++;
}
trackHeaderWriter.write_le16(0x000C); /* offset of sector headers */
trackHeaderWriter.write_le16(0x0012); /* length of each sector descriptor */
trackHeaderWriter.write_le16(actualSectors);
trackHeaderWriter.write_8(0); /* data rate unknown */
trackHeaderWriter.write_8(0); /* recording mode unknown */
trackHeaderWriter.write_8(0); /* format gap length */
trackHeaderWriter.write_8(0); /* filler byte */
trackHeaderWriter.write_le16(0); /* approximate track length */
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
const auto& sector = sectors.get(track, head, sectorId);
if (sector)
{
uint32_t sectorLabel = (('S') << 24) | ((track & 0xff) << 16) | (head << 8) | sectorId;
uint32_t sectorAddress = ldbs.put(sector->data, sectorLabel);
trackHeaderWriter.write_8(track);
trackHeaderWriter.write_8(head);
trackHeaderWriter.write_8(sectorId);
trackHeaderWriter.write_8(0); /* power-of-two size */
trackHeaderWriter.write_8((sector->status == Sector::OK) ? 0x00 : 0x20); /* 8272 status 1 */
trackHeaderWriter.write_8(0); /* 8272 status 2 */
trackHeaderWriter.write_8(1); /* number of copies */
trackHeaderWriter.write_8(0); /* filler byte */
trackHeaderWriter.write_le32(sectorAddress);
trackHeaderWriter.write_le16(0); /* trailing bytes */
trackHeaderWriter.write_le16(0); /* approximate offset */
trackHeaderWriter.write_le16(sector->data.size());
}
}
uint32_t trackLabel = (('T') << 24) | ((track & 0xff) << 16) | ((track >> 8) << 8) | head;
uint32_t trackHeaderAddress = ldbs.put(trackHeader, trackLabel);
trackDirectoryWriter.write_be32(trackLabel);
trackDirectoryWriter.write_le32(trackHeaderAddress);
trackDirectorySize++;
}
}
trackDirectoryWriter.seek(0);
trackDirectoryWriter.write_le16(trackDirectorySize);
uint32_t trackDirectoryAddress = ldbs.put(trackDirectory, LDBS_TRACK_BLOCK);
Bytes data = ldbs.write(trackDirectoryAddress);
data.writeToFile(spec.filename);
}
};
std::unique_ptr<ImageWriter> ImageWriter::createLDBSImageWriter(
const SectorSet& sectors, const ImageSpec& spec)
{
return std::unique_ptr<ImageWriter>(new LDBSImageWriter(sectors, spec));
}

81
lib/ldbs.cc Normal file
View File

@@ -0,0 +1,81 @@
#include "globals.h"
#include <string.h>
#include "bytes.h"
#include "ldbs.h"
#include "fmt/format.h"
LDBS::LDBS()
{}
uint32_t LDBS::put(const Bytes& data, uint32_t type)
{
uint32_t address = top;
Block& block = blocks[address];
block.type = type;
block.data = data;
top += data.size() + 20;
return address;
}
uint32_t LDBS::read(const Bytes& data)
{
ByteReader br(data);
br.seek(0);
if ((br.read_be32() != LDBS_FILE_MAGIC)
|| (br.read_be32() != LDBS_FILE_TYPE))
Error() << "not a valid LDBS file";
uint32_t address = br.read_le32();
br.skip(4);
uint32_t trackDirectory = br.read_le32();
while (address)
{
br.seek(address);
if (br.read_be32() != LDBS_BLOCK_MAGIC)
Error() << fmt::format("invalid block at address 0x{:x}", address);
Block& block = blocks[address];
block.type = br.read_be32();
uint32_t size = br.read_le32();
br.skip(4);
address = br.read_le32();
block.data.writer().append(br.read(size));
}
top = data.size();
return trackDirectory;
}
const Bytes LDBS::write(uint32_t trackDirectory)
{
Bytes data(top);
ByteWriter bw(data);
uint32_t previous = 0;
for (const auto& e : blocks)
{
bw.seek(e.first);
bw.write_be32(LDBS_BLOCK_MAGIC);
bw.write_be32(e.second.type);
bw.write_le32(e.second.data.size());
bw.write_le32(e.second.data.size());
bw.write_le32(previous);
bw.append(e.second.data);
previous = e.first;
}
bw.seek(0);
bw.write_be32(LDBS_FILE_MAGIC);
bw.write_be32(LDBS_FILE_TYPE);
bw.write_le32(previous);
bw.write_le32(0);
bw.write_le32(trackDirectory);
return data;
}

41
lib/ldbs.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef LDBS_H
#define LDBS_H
class Bytes;
/* A very simple interface to John Elliott's LDBS image format:
* http://www.seasip.info/Unix/LibDsk/ldbs.html
*/
#define LDBS_FILE_MAGIC 0x4C425301 /* "LBS\01" */
#define LDBS_FILE_TYPE 0x44534B02 /* "DSK\02" */
#define LDBS_BLOCK_MAGIC 0x4C444201 /* "LDB\01" */
#define LDBS_TRACK_BLOCK 0x44495201 /* "DIR\01" */
class LDBS
{
public:
LDBS();
public:
const Bytes& get(uint32_t address) const
{ return blocks.at(address).data; }
uint32_t put(const Bytes& data, uint32_t type);
public:
const Bytes write(uint32_t trackDirectory);
uint32_t read(const Bytes& bytes);
private:
struct Block
{
uint32_t type;
Bytes data;
};
std::map<uint32_t, Block> blocks;
unsigned top = 20;
};
#endif

View File

@@ -9,32 +9,48 @@
#include "decoders/decoders.h"
#include "sector.h"
#include "sectorset.h"
#include "visualiser.h"
#include "record.h"
#include "image.h"
#include "bytes.h"
#include "decoders/rawbits.h"
#include "track.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
FlagGroup readerFlags { &hardwareFluxSourceFlags, &fluxmapReaderFlags };
FlagGroup readerFlags { &hardwareFluxSourceFlags, &fluxmapReaderFlags, &visualiserFlags };
static DataSpecFlag source(
{ "--source", "-s" },
"source for data",
":t=0-79:s=0-1:d=0");
static DataSpecFlag output(
{ "--output", "-o" },
"output image file to write to",
"");
static StringFlag destination(
{ "--write-flux", "-f" },
"write the raw magnetic flux to this file",
"");
static StringFlag visualise(
{ "--write-svg" },
"write a visualisation of the disk to this file",
"");
static SettableFlag justRead(
{ "--just-read" },
"just read the disk and do no further processing");
static SettableFlag dumpRecords(
{ "--dump-records" },
"Dump the parsed records.");
"Dump the parsed but undecoded records.");
static SettableFlag dumpSectors(
{ "--dump-sectors" },
"Dump the decoded sectors.");
static IntFlag retries(
{ "--retries" },
@@ -52,6 +68,11 @@ void setReaderDefaultSource(const std::string& source)
::source.set(source);
}
void setReaderDefaultOutput(const std::string& output)
{
::output.set(output);
}
void setReaderRevolutions(int revolutions)
{
setHardwareFluxSourceRevolutions(revolutions);
@@ -71,9 +92,9 @@ void Track::readFluxmap()
std::vector<std::unique_ptr<Track>> readTracks()
{
const DataSpec& dataSpec = source;
const FluxSpec spec(source);
std::cout << "Reading from: " << dataSpec << std::endl;
std::cout << "Reading from: " << source << std::endl;
setHardwareFluxSourceDensity(highDensityFlag);
@@ -92,10 +113,10 @@ std::vector<std::unique_ptr<Track>> readTracks()
);
}
std::shared_ptr<FluxSource> fluxSource = FluxSource::create(dataSpec);
std::shared_ptr<FluxSource> fluxSource = FluxSource::create(spec);
std::vector<std::unique_ptr<Track>> tracks;
for (const auto& location : dataSpec.locations)
for (const auto& location : spec.locations)
{
auto track = std::make_unique<Track>(location.track, location.side);
track->fluxsource = fluxSource;
@@ -132,7 +153,7 @@ static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replaceme
return;
}
}
if (!replacing || (replacing->status != Sector::OK))
if (!replacing || ((replacing->status != Sector::OK) && (replacement.status == Sector::OK)))
{
if (!replacing)
replacing.reset(new Sector);
@@ -140,8 +161,11 @@ static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replaceme
}
}
void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename)
void readDiskCommand(AbstractDecoder& decoder)
{
const ImageSpec outputSpec(output);
ImageWriter::verifyImageSpec(outputSpec);
bool failures = false;
SectorSet allSectors;
auto tracks = readTracks();
@@ -199,10 +223,7 @@ void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename
std::cout << "giving up" << std::endl
<< " ";
else
{
std::cout << retry << " retries remaining" << std::endl;
track->fluxsource->recalibrate();
}
}
if (dumpRecords)
@@ -210,13 +231,27 @@ void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename
std::cout << "\nRaw (undecoded) records follow:\n\n";
for (auto& record : track->rawrecords)
{
std::cout << fmt::format("I+{:.2f}us", record.position.ns() / 1000.0)
<< std::endl;
std::cout << fmt::format("I+{:.2f}us with {:.2f}us clock\n",
record.position.ns() / 1000.0, record.clock / 1000.0);
hexdump(std::cout, record.data);
std::cout << std::endl;
}
}
if (dumpSectors)
{
std::cout << "\nDecoded sectors follow:\n\n";
for (auto& i : readSectors)
{
auto& sector = i.second;
std::cout << fmt::format("{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock\n",
sector->logicalTrack, sector->logicalSide, sector->logicalSector,
sector->position.ns() / 1000.0, sector->clock / 1000.0);
hexdump(std::cout, sector->data);
std::cout << std::endl;
}
}
int size = 0;
bool printedTrack = false;
for (auto& i : readSectors)
@@ -239,8 +274,10 @@ void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename
std::cout << size << " bytes decoded." << std::endl;
}
Geometry geometry = guessGeometry(allSectors);
writeSectorsToFile(allSectors, geometry, outputFilename);
if (!visualise.get().empty())
visualiseSectorsToFile(allSectors, visualise.get());
writeSectorsToFile(allSectors, outputSpec);
if (failures)
std::cerr << "Warning: some sectors could not be decoded." << std::endl;
}

View File

@@ -11,10 +11,11 @@ class Track;
extern FlagGroup readerFlags;
extern void setReaderDefaultSource(const std::string& source);
extern void setReaderDefaultOutput(const std::string& output);
extern void setReaderRevolutions(int revolutions);
extern std::vector<std::unique_ptr<Track>> readTracks();
extern void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename);
extern void readDiskCommand(AbstractDecoder& decoder);
#endif

View File

@@ -27,6 +27,10 @@ public:
Status status = Status::INTERNAL_ERROR;
Fluxmap::Position position;
nanoseconds_t clock = 0;
nanoseconds_t headerStartTime = 0;
nanoseconds_t headerEndTime = 0;
nanoseconds_t dataStartTime = 0;
nanoseconds_t dataEndTime = 0;
int physicalTrack = 0;
int physicalSide = 0;
int logicalTrack = 0;

View File

@@ -18,8 +18,9 @@ Sector* SectorSet::get(int track, int head, int sector) const
return i->second.get();
}
void SectorSet::calculateSize(int& numTracks, int& numHeads, int& numSectors,
int& sectorSize) const
void SectorSet::calculateSize(
unsigned& numTracks, unsigned& numHeads,
unsigned& numSectors, unsigned& sectorSize) const
{
numTracks = numHeads = numSectors = sectorSize = 0;
@@ -28,10 +29,10 @@ void SectorSet::calculateSize(int& numTracks, int& numHeads, int& numSectors,
auto& sector = i.second;
if (sector)
{
numTracks = std::max(numTracks, sector->logicalTrack+1);
numHeads = std::max(numHeads, sector->logicalSide+1);
numSectors = std::max(numSectors, sector->logicalSector+1);
sectorSize = std::max(sectorSize, (int)sector->data.size());
numTracks = std::max(numTracks, (unsigned)sector->logicalTrack+1);
numHeads = std::max(numHeads, (unsigned)sector->logicalSide+1);
numSectors = std::max(numSectors, (unsigned)sector->logicalSector+1);
sectorSize = std::max(sectorSize, (unsigned)sector->data.size());
}
}
}

View File

@@ -17,8 +17,12 @@ public:
std::unique_ptr<Sector>& get(int track, int head, int sector);
Sector* get(int track, int head, int sector) const;
void calculateSize(int& numTracks, int& numHeads, int& numSectors,
int& sectorSize) const;
const std::map<const key_t, std::unique_ptr<Sector>>& get() const
{ return _data; }
void calculateSize(
unsigned& numTracks, unsigned& numHeads, unsigned& numSectors,
unsigned& sectorSize) const;
private:
std::map<const key_t, std::unique_ptr<Sector>> _data;

View File

@@ -149,7 +149,7 @@ nanoseconds_t usbGetRotationalPeriod(void)
usb_cmd_send(&f, f.f.size);
auto r = await_reply<struct speed_frame>(F_FRAME_MEASURE_SPEED_REPLY);
return r->period_ms * 1000;
return r->period_ms * 1000000;
}
static int large_bulk_transfer(int ep, Bytes& bytes)
@@ -202,13 +202,16 @@ void usbTestBulkTransport()
await_reply<struct any_frame>(F_FRAME_BULK_TEST_REPLY);
}
Bytes usbRead(int side, int revolutions)
Bytes usbRead(int side, bool synced, nanoseconds_t readTime)
{
struct read_frame f = {
.f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
.revolutions = (uint8_t) revolutions
.synced = (uint8_t) synced
};
uint16_t milliseconds = readTime / 1e6;
((uint8_t*)&f.milliseconds)[0] = milliseconds;
((uint8_t*)&f.milliseconds)[1] = milliseconds >> 8;
usb_cmd_send(&f, f.f.size);
auto fluxmap = std::unique_ptr<Fluxmap>(new Fluxmap);
@@ -253,15 +256,51 @@ void usbErase(int side)
await_reply<struct any_frame>(F_FRAME_ERASE_REPLY);
}
void usbSetDrive(int drive, bool high_density)
void usbSetDrive(int drive, bool high_density, int index_mode)
{
usb_init();
struct set_drive_frame f = {
{ .type = F_FRAME_SET_DRIVE_CMD, .size = sizeof(f) },
.drive_flags = (uint8_t)((drive ? DRIVE_1 : DRIVE_0) | (high_density ? DRIVE_HD : DRIVE_DD)),
.drive = (uint8_t) drive,
.high_density = high_density,
.index_mode = (uint8_t) index_mode
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_SET_DRIVE_REPLY);
}
/* Hacky: the board always operates in little-endian mode. */
static uint16_t read_short_from_usb(uint16_t usb)
{
uint8_t* p = (uint8_t*)&usb;
return p[0] | (p[1] << 8);
}
static void convert_voltages_from_usb(const struct voltages& vin, struct voltages& vout)
{
vout.logic0_mv = read_short_from_usb(vin.logic0_mv);
vout.logic1_mv = read_short_from_usb(vin.logic1_mv);
}
void usbMeasureVoltages(struct voltages_frame* voltages)
{
usb_init();
struct any_frame f = {
{ .type = F_FRAME_MEASURE_VOLTAGES_CMD, .size = sizeof(f) },
};
usb_cmd_send(&f, f.f.size);
struct voltages_frame* r = await_reply<struct voltages_frame>(F_FRAME_MEASURE_VOLTAGES_REPLY);
convert_voltages_from_usb(r->input_both_off, voltages->input_both_off);
convert_voltages_from_usb(r->input_drive_0_selected, voltages->input_drive_0_selected);
convert_voltages_from_usb(r->input_drive_1_selected, voltages->input_drive_1_selected);
convert_voltages_from_usb(r->input_drive_0_running, voltages->input_drive_0_running);
convert_voltages_from_usb(r->input_drive_1_running, voltages->input_drive_1_running);
convert_voltages_from_usb(r->output_both_off, voltages->output_both_off);
convert_voltages_from_usb(r->output_drive_0_selected, voltages->output_drive_0_selected);
convert_voltages_from_usb(r->output_drive_1_selected, voltages->output_drive_1_selected);
convert_voltages_from_usb(r->output_drive_0_running, voltages->output_drive_0_running);
convert_voltages_from_usb(r->output_drive_1_running, voltages->output_drive_1_running);
}

View File

@@ -9,9 +9,10 @@ extern void usbRecalibrate();
extern void usbSeek(int track);
extern nanoseconds_t usbGetRotationalPeriod();
extern void usbTestBulkTransport();
extern Bytes usbRead(int side, int revolutions);
extern Bytes usbRead(int side, bool synced, nanoseconds_t readTime);
extern void usbWrite(int side, const Bytes& bytes);
extern void usbErase(int side);
extern void usbSetDrive(int drive, bool high_density);
extern void usbSetDrive(int drive, bool high_density, int index_mode);
extern void usbMeasureVoltages(struct voltages_frame* voltages);
#endif

95
lib/visualiser.cc Normal file
View File

@@ -0,0 +1,95 @@
#define _USE_MATH_DEFINES
#include "globals.h"
#include "image.h"
#include "sector.h"
#include "sectorset.h"
#include "visualiser.h"
#include "fmt/format.h"
#include "flags.h"
#include <iostream>
#include <fstream>
#include <math.h>
FlagGroup visualiserFlags;
static IntFlag period(
{ "--visualiser-period" },
"rotational period for use by the visualiser (milliseconds)",
200);
static const int SIZE = 480;
static const int BORDER = 10;
static const int RADIUS = (SIZE/2) - (BORDER/2);
static const int CORE = 50;
static const int TRACKS = 83;
static const double TRACK_SPACING = double(RADIUS-CORE) / TRACKS;
void visualiseSectorsToFile(const SectorSet& sectors, const std::string& filename)
{
std::cout << "writing visualisation\n";
std::ofstream f(filename, std::ios::out);
if (!f.is_open())
Error() << "cannot open visualisation file";
f << fmt::format("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"{0} {1} {2} {3}\">",
0, 0, SIZE*2, SIZE);
const double radians_per_ns = 2.0*M_PI / (period*1e6);
auto drawSide = [&](int side)
{
f << fmt::format("<g transform='matrix(1 0 0 -1 {} {})'>", SIZE/2 + (side*SIZE), SIZE/2);
f << fmt::format("<circle cx='0' cy='0' r='{}' stroke='none' fill='#ccc'/>", RADIUS);
for (int physicalTrack = 0; physicalTrack < TRACKS; physicalTrack++)
{
double radius = CORE + physicalTrack*TRACK_SPACING;
f << fmt::format("<circle cx='0' cy='0' r='{}' stroke='#888' stroke-width='0.5' fill='none'/>", radius);
auto drawArc = [&](const std::unique_ptr<Sector>& sector, nanoseconds_t start, nanoseconds_t end, const std::string& colour)
{
start %= period*1000000;
end %= period*1000000;
if (end < start)
end += period*1000000;
double theta1 = start * radians_per_ns;
double theta2 = end * radians_per_ns;
int large = (theta2 - theta1) >= M_PI;
f << fmt::format("\n<!-- {} {} = {} {} -->", start, end, theta1, theta2);
f << fmt::format("<path fill='none' stroke='{}' stroke-width='1.5' d='", colour);
f << fmt::format("M {} {} ", cos(theta1)*radius, sin(theta1)*radius);
f << fmt::format("A {0} {0} 0 {3} 1 {1} {2}", radius, cos(theta2)*radius, sin(theta2)*radius, large);
f << fmt::format("'><title>Track {} Head {} Sector {}; {}ms to {}ms</title></path>",
sector->logicalTrack, sector->logicalSide, sector->logicalSector,
start/1e6, end/1e6);
};
/* Sadly, SectorSets aren't indexable by physical track. */
for (const auto& e : sectors.get())
{
const auto& sector = e.second;
if ((sector->physicalSide == side) && (sector->physicalTrack == physicalTrack))
{
const char* colour = "#f00";
if (sector->status == Sector::OK)
colour = "#00f";
if (sector->headerStartTime && sector->headerEndTime)
drawArc(sector, sector->headerStartTime, sector->headerEndTime, "#0ff");
if (sector->dataStartTime && sector->dataEndTime)
drawArc(sector, sector->dataStartTime, sector->dataEndTime, colour);
}
}
}
f << "</g>";
};
f << fmt::format("<rect x='0' y='0' width='{}' height='{}' stroke='none' fill='#fff'/>", SIZE*2, SIZE);
drawSide(0);
drawSide(1);
f << "</svg>";
}

12
lib/visualiser.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef VISUALISER_H
#define VISUALISER_H
#include "flags.h"
class SectorSet;
extern FlagGroup visualiserFlags;
extern void visualiseSectorsToFile(const SectorSet& sectors, const std::string& filename);
#endif

View File

@@ -15,13 +15,18 @@
#include "sector.h"
#include "sectorset.h"
FlagGroup writerFlags { &hardwareFluxSourceFlags };
FlagGroup writerFlags { &hardwareFluxSourceFlags, &hardwareFluxSinkFlags };
static DataSpecFlag dest(
{ "--dest", "-d" },
"destination for data",
":d=0:t=0-79:s=0-1");
static DataSpecFlag input(
{ "--input", "-i" },
"input image file to read from",
"");
static SettableFlag highDensityFlag(
{ "--high-density", "-H" },
"set the drive to high density mode");
@@ -33,12 +38,17 @@ void setWriterDefaultDest(const std::string& dest)
::dest.set(dest);
}
void setWriterDefaultInput(const std::string& input)
{
::input.set(input);
}
void writeTracks(
const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer)
{
const DataSpec& spec = dest;
const FluxSpec spec(dest);
std::cout << "Writing to: " << spec << std::endl;
std::cout << "Writing to: " << dest << std::endl;
setHardwareFluxSourceDensity(highDensityFlag);
setHardwareFluxSinkDensity(highDensityFlag);
@@ -103,12 +113,10 @@ void fillBitmapTo(std::vector<bool>& bitmap,
}
}
void writeDiskCommand(
AbstractEncoder& encoder, const Geometry& geometry, const std::string& inputFilename)
void writeDiskCommand(AbstractEncoder& encoder)
{
SectorSet allSectors;
readSectorsFromFile(allSectors, geometry, inputFilename);
const ImageSpec spec(input);
SectorSet allSectors = readSectorsFromFile(spec);
writeTracks(
[&](int track, int side) -> std::unique_ptr<Fluxmap>
{

View File

@@ -10,6 +10,7 @@ class AbstractEncoder;
class Geometry;
extern void setWriterDefaultDest(const std::string& dest);
extern void setWriterDefaultInput(const std::string& input);
extern void writeTracks(const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer);
@@ -17,7 +18,6 @@ extern void fillBitmapTo(std::vector<bool>& bitmap,
unsigned& cursor, unsigned terminateAt,
const std::vector<bool>& pattern);
extern void writeDiskCommand(
AbstractEncoder& encoder, const Geometry& geometry, const std::string& inputFilename);
extern void writeDiskCommand(AbstractEncoder& encoder);
#endif

View File

@@ -137,8 +137,16 @@ buildlibrary libfmt.a \
dep/fmt/posix.cc \
buildlibrary libbackend.a \
lib/imagereader/imagereader.cc \
lib/imagereader/imgimagereader.cc \
lib/imagewriter/d64imagewriter.cc \
lib/imagewriter/imagewriter.cc \
lib/imagewriter/imgimagewriter.cc \
lib/imagewriter/ldbsimagewriter.cc \
arch/aeslanier/decoder.cc \
arch/amiga/decoder.cc \
arch/amiga/encoder.cc \
arch/amiga/amiga.cc \
arch/apple2/decoder.cc \
arch/brother/decoder.cc \
arch/brother/encoder.cc \
@@ -171,17 +179,20 @@ buildlibrary libbackend.a \
lib/globals.cc \
lib/hexdump.cc \
lib/image.cc \
lib/ldbs.cc \
lib/reader.cc \
lib/sector.cc \
lib/sectorset.cc \
lib/sql.cc \
lib/usb.cc \
lib/visualiser.cc \
lib/writer.cc \
buildlibrary libfrontend.a \
src/fe-cwftoflux.cc \
src/fe-erase.cc \
src/fe-fluxtoau.cc \
src/fe-fluxtoscp.cc \
src/fe-fluxtovcd.cc \
src/fe-inspect.cc \
src/fe-readadfs.cc \
@@ -200,9 +211,12 @@ buildlibrary libfrontend.a \
src/fe-readvictor9k.cc \
src/fe-readzilogmcz.cc \
src/fe-rpm.cc \
src/fe-scptoflux.cc \
src/fe-seek.cc \
src/fe-testbulktransport.cc \
src/fe-testvoltages.cc \
src/fe-upgradefluxfile.cc \
src/fe-writeamiga.cc \
src/fe-writebrother.cc \
src/fe-writeflux.cc \
src/fe-writetestpattern.cc \
@@ -223,12 +237,21 @@ buildsimpleprogram brother120tool \
libemu.a \
libfmt.a \
buildsimpleprogram brother240tool \
-Idep/emu \
tools/brother240tool.cc \
libbackend.a \
libemu.a \
libfmt.a \
runtest bitaccumulator-test tests/bitaccumulator.cc
runtest bytes-test tests/bytes.cc
runtest compression-test tests/compression.cc
runtest crunch-test tests/crunch.cc
runtest dataspec-test tests/dataspec.cc
runtest flags-test tests/flags.cc
runtest fmmfm-test tests/fmmfm.cc
runtest bitaccumulator-test tests/bitaccumulator.cc
runtest kryoflux-test tests/kryoflux.cc
runtest compression-test tests/compression.cc
runtest bytes-test tests/bytes.cc
runtest crunch-test tests/crunch.cc
runtest fluxpattern-test tests/fluxpattern.cc
runtest fmmfm-test tests/fmmfm.cc
runtest kryoflux-test tests/kryoflux.cc
runtest ldbs-test tests/ldbs.cc
runtest amiga-test tests/amiga.cc

View File

@@ -3,7 +3,7 @@
enum
{
FLUXENGINE_VERSION = 8,
FLUXENGINE_VERSION = 11,
FLUXENGINE_VID = 0x1209,
FLUXENGINE_PID = 0x6e00,
@@ -62,6 +62,8 @@ enum
F_FRAME_RECALIBRATE_REPLY, /* any_frame */
F_FRAME_SET_DRIVE_CMD, /* setdrive_frame */
F_FRAME_SET_DRIVE_REPLY, /* any_frame */
F_FRAME_MEASURE_VOLTAGES_CMD, /* any_frame */
F_FRAME_MEASURE_VOLTAGES_REPLY, /* voltages_frame */
};
enum
@@ -73,6 +75,13 @@ enum
F_ERROR_INTERNAL,
};
enum
{
F_INDEX_REAL,
F_INDEX_300,
F_INDEX_360
};
enum
{
F_OP_PULSE = 0x80,
@@ -124,7 +133,8 @@ struct read_frame
{
struct frame_header f;
uint8_t side;
uint8_t revolutions;
uint8_t synced;
uint16_t milliseconds;
};
struct write_frame
@@ -143,7 +153,30 @@ struct erase_frame
struct set_drive_frame
{
struct frame_header f;
uint8_t drive_flags;
uint8_t drive;
uint8_t high_density;
uint8_t index_mode;
};
struct voltages
{
uint16_t logic0_mv;
uint16_t logic1_mv;
};
struct voltages_frame
{
struct frame_header f;
struct voltages output_both_off;
struct voltages output_drive_0_selected;
struct voltages output_drive_1_selected;
struct voltages output_drive_0_running;
struct voltages output_drive_1_running;
struct voltages input_both_off;
struct voltages input_drive_0_selected;
struct voltages input_drive_1_selected;
struct voltages input_drive_0_running;
struct voltages input_drive_1_running;
};
#endif

View File

@@ -3,7 +3,7 @@
#include "fluxmap.h"
#include "writer.h"
static FlagGroup flags;
static FlagGroup flags { &writerFlags };
int mainErase(int argc, const char* argv[])
{

View File

@@ -34,14 +34,15 @@ int mainConvertFluxToAu(int argc, const char* argv[])
{
flags.parseFlags(argc, argv);
const auto& locations = source.get().locations;
FluxSpec spec(source);
const auto& locations = spec.locations;
if (locations.size() != 1)
Error() << "the source dataspec must contain exactly one track (two sides count as two tracks)";
const auto& location = *(locations.begin());
std::cerr << "Reading source flux...\n";
setHardwareFluxSourceDensity(highDensityFlag);
std::shared_ptr<FluxSource> fluxsource = FluxSource::create(source);
std::shared_ptr<FluxSource> fluxsource = FluxSource::create(spec);
const auto& fluxmap = fluxsource->readFlux(location.track, location.side);
unsigned totalTicks = fluxmap->ticks() + 2;
unsigned channels = withIndex ? 2 : 1;

188
src/fe-fluxtoscp.cc Normal file
View File

@@ -0,0 +1,188 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "sql.h"
#include "bytes.h"
#include "protocol.h"
#include "dataspec.h"
#include "fmt/format.h"
#include "decoders/fluxmapreader.h"
#include "scp.h"
#include <fstream>
#include <algorithm>
static FlagGroup flags { };
static SettableFlag fortyTrackMode(
{ "--48", "-4" },
"set 48 tpi mode; only every other physical track is emitted"
);
static SettableFlag singleSided(
{ "--single-sided", "-s" },
"only emit side 0"
);
static IntFlag diskType(
{ "--disk-type" },
"sets the SCP disk type byte",
0xff
);
static sqlite3* inputDb;
static void syntax()
{
std::cout << "Syntax: fluxengine convert fluxtoscp <fluxfile> <scpfile>\n";
exit(0);
}
static void write_le32(uint8_t dest[4], uint32_t v)
{
dest[0] = v;
dest[1] = v >> 8;
dest[2] = v >> 16;
dest[3] = v >> 24;
}
static int strackno(int track, int side)
{
if (fortyTrackMode)
track /= 2;
if (singleSided)
return track;
else
return (track << 1) | side;
}
int mainConvertFluxToScp(int argc, const char* argv[])
{
auto filenames = flags.parseFlagsWithFilenames(argc, argv);
if (filenames.size() != 2)
syntax();
inputDb = sqlOpen(filenames[0], SQLITE_OPEN_READONLY);
auto tracks = sqlFindFlux(inputDb);
int maxTrack = 0;
int maxSide = 0;
for (auto p : tracks)
{
if (singleSided && (p.second == 1))
continue;
maxTrack = std::max(maxTrack, (int)p.first);
maxSide = std::max(maxSide, (int)p.second);
}
int maxStrack = strackno(maxTrack, maxSide);
std::cout << fmt::format("Writing {} {} SCP file containing {} SCP tracks\n",
fortyTrackMode ? "48 tpi" : "96 tpi",
singleSided ? "single sided" : "double sided",
maxStrack + 1
);
ScpHeader fileheader = {0};
fileheader.file_id[0] = 'S';
fileheader.file_id[1] = 'C';
fileheader.file_id[2] = 'P';
fileheader.version = 0x18; /* Version 1.8 of the spec */
fileheader.type = diskType;
fileheader.revolutions = 5;
fileheader.start_track = 0;
fileheader.end_track = maxStrack;
fileheader.flags = SCP_FLAG_INDEXED | (fortyTrackMode ? 0 : SCP_FLAG_96TPI);
fileheader.cell_width = 0;
fileheader.heads = singleSided ? 1 : 0;
Bytes trackdata;
ByteWriter trackdataWriter(trackdata);
int trackstep = 1 + fortyTrackMode;
int maxside = singleSided ? 0 : 1;
for (int track = 0; track <= maxTrack; track += trackstep)
{
for (int side = 0; side <= maxside; side++)
{
int strack = strackno(track, side);
std::cout << fmt::format("FE track {}.{}, SCP track {}: ", track, side, strack) << std::flush;
auto fluxmap = sqlReadFlux(inputDb, track, side);
ScpTrack trackheader = {0};
trackheader.track_id[0] = 'T';
trackheader.track_id[1] = 'R';
trackheader.track_id[2] = 'K';
trackheader.strack = strack;
FluxmapReader fmr(*fluxmap);
Bytes fluxdata;
ByteWriter fluxdataWriter(fluxdata);
int revolution = 0;
unsigned revTicks = 0;
unsigned totalTicks = 0;
unsigned ticksSinceLastPulse = 0;
uint32_t startOffset = 0;
while (revolution < 5)
{
unsigned ticks;
int opcode = fmr.readOpcode(ticks);
if (ticks)
{
ticksSinceLastPulse += ticks;
totalTicks += ticks;
revTicks += ticks;
}
switch (opcode)
{
case -1: /* end of flux, treat like an index marker */
case F_OP_INDEX:
{
auto* revheader = &trackheader.revolution[revolution];
write_le32(revheader->offset, startOffset + sizeof(ScpTrack));
write_le32(revheader->length, (fluxdataWriter.pos - startOffset) / 2);
write_le32(revheader->index, revTicks * NS_PER_TICK / 25);
revolution++;
revheader++;
revTicks = 0;
startOffset = fluxdataWriter.pos;
break;
}
case F_OP_PULSE:
{
unsigned t = ticksSinceLastPulse * NS_PER_TICK / 25;
while (t >= 0x10000)
{
fluxdataWriter.write_be16(0);
t -= 0x10000;
}
fluxdataWriter.write_be16(t);
ticksSinceLastPulse = 0;
break;
}
}
}
write_le32(fileheader.track[strack], trackdataWriter.pos + sizeof(ScpHeader));
trackdataWriter += Bytes((uint8_t*)&trackheader, sizeof(trackheader));
trackdataWriter += fluxdata;
std::cout << fmt::format("{} ms in {} bytes\n",
totalTicks * MS_PER_TICK,
fluxdata.size());
}
}
sqlClose(inputDb);
std::cout << "Writing output file...\n";
std::ofstream of(filenames[1], std::ios::out | std::ios::binary);
if (!of.is_open())
Error() << "cannot open output file";
of.write((const char*) &fileheader, sizeof(fileheader));
of.write((const char*) trackdata.begin(), trackdata.size());
of.close();
return 0;
}

View File

@@ -30,14 +30,15 @@ int mainConvertFluxToVcd(int argc, const char* argv[])
{
flags.parseFlags(argc, argv);
const auto& locations = source.get().locations;
const FluxSpec spec(source);
const auto& locations = spec.locations;
if (locations.size() != 1)
Error() << "the source dataspec must contain exactly one track (two sides count as two tracks)";
const auto& location = *(locations.begin());
std::cerr << "Reading source flux...\n";
setHardwareFluxSourceDensity(highDensityFlag);
std::shared_ptr<FluxSource> fluxsource = FluxSource::create(source);
std::shared_ptr<FluxSource> fluxsource = FluxSource::create(spec);
const auto& fluxmap = fluxsource->readFlux(location.track, location.side);
std::cerr << "Writing destination VCD...\n";

View File

@@ -12,11 +12,6 @@
static FlagGroup flags { &readerFlags };
static StringFlag outputFilename(
{ "--output", "-o" },
"The output image file to write to.",
"adfs.img");
static IntFlag sectorIdBase(
{ "--sector-id-base" },
"Sector ID of the first sector.",
@@ -25,10 +20,11 @@ static IntFlag sectorIdBase(
int mainReadADFS(int argc, const char* argv[])
{
setReaderDefaultSource(":t=0-79:s=0-1");
setReaderDefaultOutput("adfs.img");
flags.parseFlags(argc, argv);
IbmDecoder decoder(sectorIdBase);
readDiskCommand(decoder, outputFilename);
readDiskCommand(decoder);
return 0;
}

View File

@@ -12,19 +12,15 @@
static FlagGroup flags { &readerFlags };
static StringFlag outputFilename(
{ "--output", "-o" },
"The output image file to write to.",
"aeslanier.img");
int mainReadAESLanier(int argc, const char* argv[])
{
setReaderDefaultSource(":t=0-79:s=0");
setReaderDefaultOutput("aeslanier.img");
setReaderRevolutions(2);
flags.parseFlags(argc, argv);
AesLanierDecoder decoder;
readDiskCommand(decoder, outputFilename);
readDiskCommand(decoder);
return 0;
}

View File

@@ -13,19 +13,15 @@
static FlagGroup flags { &readerFlags };
static StringFlag outputFilename(
{ "--output", "-o" },
"The output image file to write to.",
"amiga.adf");
int mainReadAmiga(int argc, const char* argv[])
{
setReaderDefaultSource(":t=0-79:s=0-1");
setReaderDefaultOutput("amiga.adf:c=80:h=2:s=11:b=512");
setReaderRevolutions(2);
flags.parseFlags(argc, argv);
AmigaDecoder decoder;
readDiskCommand(decoder, outputFilename);
readDiskCommand(decoder);
return 0;
}

View File

@@ -12,11 +12,6 @@
static FlagGroup flags { &readerFlags };
static StringFlag outputFilename(
{ "--output", "-o" },
"The output image file to write to.",
"ampro.img");
static IntFlag sectorIdBase(
{ "--sector-id-base" },
"Sector ID of the first sector.",
@@ -25,11 +20,12 @@ static IntFlag sectorIdBase(
int mainReadAmpro(int argc, const char* argv[])
{
setReaderDefaultSource(":t=0-79:s=0");
setReaderDefaultOutput("ampro.adf");
setReaderRevolutions(2);
flags.parseFlags(argc, argv);
IbmDecoder decoder(sectorIdBase);
readDiskCommand(decoder, outputFilename);
readDiskCommand(decoder);
return 0;
}

View File

@@ -13,19 +13,15 @@
static FlagGroup flags { &readerFlags };
static StringFlag outputFilename(
{ "--output", "-o" },
"The output image file to write to.",
"apple2.img");
int mainReadApple2(int argc, const char* argv[])
{
setReaderDefaultSource(":t=0-79:s=0");
setReaderDefaultOutput("apple2.adf");
setReaderRevolutions(2);
flags.parseFlags(argc, argv);
Apple2Decoder decoder;
readDiskCommand(decoder, outputFilename);
readDiskCommand(decoder);
return 0;
}

View File

@@ -14,19 +14,15 @@
static FlagGroup flags { &readerFlags };
static StringFlag outputFilename(
{ "--output", "-o" },
"The output image file to write to.",
"brother.img");
int mainReadBrother(int argc, const char* argv[])
{
setReaderDefaultSource(":t=0-81:s=0");
setReaderDefaultOutput("brother.img");
setReaderRevolutions(2);
flags.parseFlags(argc, argv);
BrotherDecoder decoder;
readDiskCommand(decoder, outputFilename);
readDiskCommand(decoder);
return 0;
}

View File

@@ -13,19 +13,15 @@
static FlagGroup flags { &readerFlags };
static StringFlag outputFilename(
{ "--output", "-o" },
"The output image file to write to.",
"c64.img");
int mainReadC64(int argc, const char* argv[])
{
setReaderDefaultSource(":t=0-79x2:s=0");
setReaderDefaultOutput("c64.d64");
setReaderRevolutions(2);
flags.parseFlags(argc, argv);
Commodore64Decoder decoder;
readDiskCommand(decoder, outputFilename);
readDiskCommand(decoder);
return 0;
}

View File

@@ -12,11 +12,6 @@
static FlagGroup flags { &readerFlags };
static StringFlag outputFilename(
{ "--output", "-o" },
"The output image file to write to.",
"dfs.img");
static IntFlag sectorIdBase(
{ "--sector-id-base" },
"Sector ID of the first sector.",
@@ -25,11 +20,12 @@ static IntFlag sectorIdBase(
int mainReadDFS(int argc, const char* argv[])
{
setReaderDefaultSource(":t=0-79:s=0");
setReaderDefaultOutput("dfs.img");
setReaderRevolutions(2);
flags.parseFlags(argc, argv);
IbmDecoder decoder(sectorIdBase);
readDiskCommand(decoder, outputFilename);
readDiskCommand(decoder);
return 0;
}

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