diff --git a/FluxEngine.cydsn/FluxEngine.cyprj b/FluxEngine.cydsn/FluxEngine.cyprj
index 4566365c7..1cb49e91d 100644
--- a/FluxEngine.cydsn/FluxEngine.cyprj
+++ b/FluxEngine.cydsn/FluxEngine.cyprj
@@ -1058,27 +1058,27 @@
-
+
-
+
-
+
-
+
@@ -3282,6 +3282,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FluxEngine.cydsn/TopDesign/TopDesign.cysch b/FluxEngine.cydsn/TopDesign/TopDesign.cysch
index ffe2bf131..f96255765 100644
Binary files a/FluxEngine.cydsn/TopDesign/TopDesign.cysch and b/FluxEngine.cydsn/TopDesign/TopDesign.cysch differ
diff --git a/FluxEngine.cydsn/main.c b/FluxEngine.cydsn/main.c
index 9b956ba5e..f6da2a936 100644
--- a/FluxEngine.cydsn/main.c
+++ b/FluxEngine.cydsn/main.c
@@ -12,7 +12,7 @@
#define STEP_SETTLING_TIME 50 /* ms */
#define DISKSTATUS_WPT 1
-#define DISKSTATUS_DSKCHG 2
+#define DISKSTATUS_READY 2 /* Only used on QuickDisk drives */
#define STEP_TOWARDS0 1
#define STEP_AWAYFROM0 0
@@ -37,6 +37,7 @@ static uint8_t dma_channel;
static volatile int dma_writing_to_td = 0;
static volatile int dma_reading_from_td = 0;
static volatile bool dma_underrun = false;
+static crunch_state_t cs = {};
#define DECLARE_REPLY_FRAME(STRUCT, TYPE) \
STRUCT r = {.f = { .type = TYPE, .size = sizeof(STRUCT) }}
@@ -269,13 +270,8 @@ static void init_capture_dma(void)
}
}
-static void cmd_read(struct read_frame* f)
+static void init_capture(void)
{
- SIDE_REG_Write(f->side);
- seek_to(current_track);
-
- /* Do slow setup *before* we go into the real-time bit. */
-
SAMPLER_CONTROL_Write(1); /* reset */
{
@@ -288,84 +284,69 @@ static void cmd_read(struct read_frame* f)
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
init_capture_dma();
+}
- /* Wait for the beginning of a rotation. */
-
- print("wait");
- index_irq = false;
- while (!index_irq)
- ;
- index_irq = false;
-
- crunch_state_t cs = {};
+static void start_capture(void)
+{
+ memset(&cs, 0, sizeof(crunch_state_t));
cs.outputptr = usb_buffer;
cs.outputlen = BUFFER_SIZE;
dma_writing_to_td = 0;
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);
- /* Wait for the first DMA transfer to complete, after which we can start the
- * USB transfer. */
+ /* 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;
- while (!dma_underrun)
+/* returns true if capture is aborted */
+static bool do_capture_chunk(void)
+{
+ /* Wait for the next block to be read. */
+ while (dma_reading_from_td == dma_writing_to_td)
{
- 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;
- }
+ /* On an underrun, give up immediately. */
+ if (dma_underrun)
+ return true;
+ }
- uint8_t dma_buffer_usage = 0;
- while (dma_buffer_usage < BUFFER_SIZE)
+ uint8_t dma_buffer_usage = 0;
+ while (dma_buffer_usage < BUFFER_SIZE)
+ {
+ cs.inputptr = dma_buffer[dma_reading_from_td] + dma_buffer_usage;
+ cs.inputlen = BUFFER_SIZE - dma_buffer_usage;
+ crunch(&cs);
+ dma_buffer_usage += BUFFER_SIZE - cs.inputlen;
+ if (cs.outputlen == 0)
{
- cs.inputptr = dma_buffer[dma_reading_from_td] + dma_buffer_usage;
- cs.inputlen = BUFFER_SIZE - dma_buffer_usage;
- crunch(&cs);
- dma_buffer_usage += BUFFER_SIZE - cs.inputlen;
- count++;
- if (cs.outputlen == 0)
+ while (USBFS_GetEPState(FLUXENGINE_DATA_IN_EP_NUM) != USBFS_IN_BUFFER_EMPTY)
{
- while (USBFS_GetEPState(FLUXENGINE_DATA_IN_EP_NUM) != USBFS_IN_BUFFER_EMPTY)
- {
- if (index_irq || dma_underrun)
- goto abort;
- }
-
- USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE);
- cs.outputptr = usb_buffer;
- cs.outputlen = BUFFER_SIZE;
+ if (dma_underrun)
+ return true;
}
+
+ USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE);
+ cs.outputptr = usb_buffer;
+ cs.outputlen = BUFFER_SIZE;
}
- dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td);
}
-abort:;
+ dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td);
+
+ return false;
+}
+
+static void stop_capture(void)
+{
CAPTURE_CONTROL_Write(0);
CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN);
while (CyDmaChGetRequest(dma_channel))
@@ -373,25 +354,117 @@ abort:;
donecrunch(&cs);
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
- unsigned zz = cs.outputlen;
if (cs.outputlen != BUFFER_SIZE)
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE-cs.outputlen);
if ((cs.outputlen == BUFFER_SIZE) || (cs.outputlen == 0))
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, NULL, 0);
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
deinit_dma();
+}
- if (dma_underrun)
+static void cmd_read(struct read_frame* f)
+{
+ SIDE_REG_Write(f->side);
+ seek_to(current_track);
+
+ /* Do slow setup *before* we go into the real-time bit. */
+
+ init_capture();
+
+ /* Wait for the beginning of a rotation. */
+
+ index_irq = false;
+ while (!index_irq)
+ ;
+ index_irq = false;
+
+ /* Start transferring. */
+
+ start_capture();
+ int revolutions = f->revolutions;
+ while (!dma_underrun)
{
- print("underrun after %d packets");
- send_error(F_ERROR_UNDERRUN);
+ CyWdtClear();
+
+ /* Have we reached the index pulse? */
+ if (index_irq)
+ {
+ index_irq = false;
+ revolutions--;
+ if (revolutions == 0)
+ break;
+ }
+
+ if (do_capture_chunk())
+ goto abort;
}
+abort:;
+ stop_capture();
+
+ if (dma_underrun)
+ send_error(F_ERROR_UNDERRUN);
else
{
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);
+}
+
+static void cmd_read_qd(struct read_frame* f)
+{
+ SIDE_REG_Write(f->side);
+
+ /* Do slow setup *before* we go into the real-time bit. */
+
+ init_capture();
+
+ /* Reset the drive. */
+
+ STEP_REG_Write(2);
+ CyDelay(10); /* ms */
+ STEP_REG_Write(0);
+
+ /* Motor on, and wait for ready. */
+
+ MOTOR_REG_Write(1);
+ while (!(DISKSTATUS_REG_Read() & DISKSTATUS_READY))
+ ;
+
+ /* Turning the motor off has no effect until the head hits the stop,
+ * at which point it'll stop automatically. */
+
+ MOTOR_REG_Write(0);
+
+ /* Start transferring. */
+
+ start_capture();
+ while (!dma_underrun)
+ {
+ CyWdtClear();
+
+ /* Have we reached the end? */
+ if (!(DISKSTATUS_REG_Read() & DISKSTATUS_READY))
+ break;
+
+ if (do_capture_chunk())
+ goto abort;
+ }
+abort:;
+ stop_capture();
+
+ /* Reset the drive again to ensure the motor stops. */
+
+ STEP_REG_Write(2);
+ CyDelay(10); /* ms */
+ STEP_REG_Write(0);
+
+ if (dma_underrun)
+ send_error(F_ERROR_UNDERRUN);
+ else
+ {
+ DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_READ_QD_REPLY);
+ send_reply(&r);
+ }
}
static void init_replay_dma(void)
@@ -647,6 +720,10 @@ static void handle_command(void)
cmd_read((struct read_frame*) f);
break;
+ case F_FRAME_READ_QD_CMD:
+ cmd_read_qd((struct read_frame*) f);
+ break;
+
case F_FRAME_WRITE_CMD:
cmd_write((struct write_frame*) f);
break;
diff --git a/lib/dataspec.cc b/lib/dataspec.cc
index cbc04f3e5..b59cec6fb 100644
--- a/lib/dataspec.cc
+++ b/lib/dataspec.cc
@@ -112,6 +112,7 @@ FluxSpec::FluxSpec(const DataSpec& spec)
locations.clear();
+ quickdisk = spec.has("qd") && spec.at("qd").only();
const auto& drives = spec.at("d").data;
if (drives.size() != 1)
Error() << "you must specify exactly one drive";
@@ -128,7 +129,7 @@ FluxSpec::FluxSpec(const DataSpec& spec)
for (const auto& e : spec.modifiers)
{
const auto name = e.second.name;
- if ((name != "t") && (name != "s") && (name != "d"))
+ if ((name != "t") && (name != "s") && (name != "d") && (name != "qd"))
Error() << fmt::format("unknown fluxspec modifier '{}'", name);
}
}
diff --git a/lib/dataspec.h b/lib/dataspec.h
index ff3aac348..1d3ccd34b 100644
--- a/lib/dataspec.h
+++ b/lib/dataspec.h
@@ -73,6 +73,7 @@ class FluxSpec
std::string filename;
std::vector locations;
unsigned drive;
+ bool quickdisk : 1;
};
class ImageSpec
diff --git a/lib/fluxsource/fluxsource.cc b/lib/fluxsource/fluxsource.cc
index f8551fe47..6a354c4c8 100644
--- a/lib/fluxsource/fluxsource.cc
+++ b/lib/fluxsource/fluxsource.cc
@@ -15,7 +15,12 @@ std::unique_ptr FluxSource::create(const FluxSpec& spec)
const auto& filename = spec.filename;
if (filename.empty())
- return createHardwareFluxSource(spec.drive);
+ {
+ if (spec.quickdisk)
+ return createQuickdiskFluxSource(spec.drive);
+ else
+ return createHardwareFluxSource(spec.drive);
+ }
else if (ends_with(filename, ".flux"))
return createSqliteFluxSource(filename);
else if (ends_with(filename, "/"))
diff --git a/lib/fluxsource/fluxsource.h b/lib/fluxsource/fluxsource.h
index ac3c38650..6db05397c 100644
--- a/lib/fluxsource/fluxsource.h
+++ b/lib/fluxsource/fluxsource.h
@@ -4,6 +4,7 @@
#include "flags.h"
extern FlagGroup hardwareFluxSourceFlags;
+extern FlagGroup quickdiskFluxSourceFlags;
class Fluxmap;
class FluxSpec;
@@ -16,6 +17,7 @@ class FluxSource
private:
static std::unique_ptr createSqliteFluxSource(const std::string& filename);
static std::unique_ptr createHardwareFluxSource(unsigned drive);
+ static std::unique_ptr createQuickdiskFluxSource(unsigned drive);
static std::unique_ptr createStreamFluxSource(const std::string& path);
public:
diff --git a/lib/fluxsource/quickdiskfluxsource.cc b/lib/fluxsource/quickdiskfluxsource.cc
new file mode 100644
index 000000000..ff85b3bcc
--- /dev/null
+++ b/lib/fluxsource/quickdiskfluxsource.cc
@@ -0,0 +1,50 @@
+#include "globals.h"
+#include "flags.h"
+#include "fluxmap.h"
+#include "usb.h"
+#include "fluxsource/fluxsource.h"
+
+FlagGroup quickdiskFluxSourceFlags;
+
+class QuickdiskFluxSource : public FluxSource
+{
+public:
+ QuickdiskFluxSource(unsigned drive):
+ _drive(drive)
+ {
+ }
+
+ ~QuickdiskFluxSource()
+ {
+ }
+
+public:
+ std::unique_ptr readFlux(int track, int side)
+ {
+ usbSetDrive(_drive, false);
+ Bytes crunched = usbReadQD(side);
+ auto fluxmap = std::make_unique();
+ fluxmap->appendBytes(crunched.uncrunch());
+ return fluxmap;
+ }
+
+ void recalibrate()
+ {
+ }
+
+ bool retryable()
+ {
+ return true;
+ }
+
+private:
+ unsigned _drive;
+};
+
+std::unique_ptr FluxSource::createQuickdiskFluxSource(unsigned drive)
+{
+ return std::unique_ptr(new QuickdiskFluxSource(drive));
+}
+
+
+
diff --git a/lib/reader.cc b/lib/reader.cc
index 86bccba64..a48b719e4 100644
--- a/lib/reader.cc
+++ b/lib/reader.cc
@@ -16,7 +16,7 @@
#include "track.h"
#include "fmt/format.h"
-FlagGroup readerFlags { &hardwareFluxSourceFlags, &fluxmapReaderFlags };
+FlagGroup readerFlags { &hardwareFluxSourceFlags, &quickdiskFluxSourceFlags, &fluxmapReaderFlags };
static DataSpecFlag source(
{ "--source", "-s" },
@@ -85,8 +85,6 @@ std::vector> readTracks()
std::cout << "Reading from: " << source << std::endl;
- setHardwareFluxSourceDensity(highDensityFlag);
-
if (!destination.get().empty())
{
outdb = sqlOpen(destination, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
@@ -102,8 +100,9 @@ std::vector> readTracks()
);
}
- std::shared_ptr fluxSource = FluxSource::create(spec);
+ setHardwareFluxSourceDensity(highDensityFlag);
+ std::shared_ptr fluxSource = FluxSource::create(spec);
std::vector> tracks;
for (const auto& location : spec.locations)
{
diff --git a/lib/usb.cc b/lib/usb.cc
index 57043a87e..566016098 100644
--- a/lib/usb.cc
+++ b/lib/usb.cc
@@ -76,7 +76,7 @@ static void bad_reply(void)
{
struct error_frame* f = (struct error_frame*) buffer;
if (f->f.type != F_FRAME_ERROR)
- Error() << fmt::format("bad USB reply 0x{:2x}", f->f.type);
+ Error() << fmt::format("bad USB reply 0x{:02x}", f->f.type);
switch (f->error)
{
case F_ERROR_BAD_COMMAND:
@@ -221,6 +221,25 @@ Bytes usbRead(int side, int revolutions)
return buffer;
}
+Bytes usbReadQD(int side)
+{
+ struct read_frame f = {
+ .f = { .type = F_FRAME_READ_QD_CMD, .size = sizeof(f) },
+ .side = (uint8_t) side,
+ .revolutions = 1
+ };
+ usb_cmd_send(&f, f.f.size);
+
+ auto fluxmap = std::unique_ptr(new Fluxmap);
+
+ Bytes buffer(1024*1024);
+ int len = large_bulk_transfer(FLUXENGINE_DATA_IN_EP, buffer);
+ buffer.resize(len);
+
+ await_reply(F_FRAME_READ_REPLY);
+ return buffer;
+}
+
void usbWrite(int side, const Bytes& bytes)
{
unsigned safelen = bytes.size() & ~(FRAME_SIZE-1);
diff --git a/lib/usb.h b/lib/usb.h
index 3f47da941..fc1a700b3 100644
--- a/lib/usb.h
+++ b/lib/usb.h
@@ -10,6 +10,7 @@ extern void usbSeek(int track);
extern nanoseconds_t usbGetRotationalPeriod();
extern void usbTestBulkTransport();
extern Bytes usbRead(int side, int revolutions);
+extern Bytes usbReadQD(int side);
extern void usbWrite(int side, const Bytes& bytes);
extern void usbErase(int side);
extern void usbSetDrive(int drive, bool high_density);
diff --git a/mkninja.sh b/mkninja.sh
index 8c2668e15..3996ecab9 100644
--- a/mkninja.sh
+++ b/mkninja.sh
@@ -170,6 +170,7 @@ buildlibrary libbackend.a \
lib/fluxsink/sqlitefluxsink.cc \
lib/fluxsource/fluxsource.cc \
lib/fluxsource/hardwarefluxsource.cc \
+ lib/fluxsource/quickdiskfluxsource.cc \
lib/fluxsource/kryoflux.cc \
lib/fluxsource/sqlitefluxsource.cc \
lib/fluxsource/streamfluxsource.cc \
@@ -203,6 +204,7 @@ buildlibrary libfrontend.a \
src/fe-readibm.cc \
src/fe-readmac.cc \
src/fe-readmx.cc \
+ src/fe-readqd.cc \
src/fe-readvictor9k.cc \
src/fe-readzilogmcz.cc \
src/fe-rpm.cc \
diff --git a/protocol.h b/protocol.h
index 1f4abfbee..781a945a5 100644
--- a/protocol.h
+++ b/protocol.h
@@ -3,7 +3,7 @@
enum
{
- FLUXENGINE_VERSION = 8,
+ FLUXENGINE_VERSION = 9,
FLUXENGINE_VID = 0x1209,
FLUXENGINE_PID = 0x6e00,
@@ -54,6 +54,8 @@ enum
F_FRAME_BULK_TEST_REPLY, /* any_frame */
F_FRAME_READ_CMD, /* read_frame */
F_FRAME_READ_REPLY, /* any_frame */
+ F_FRAME_READ_QD_CMD, /* read_frame */
+ F_FRAME_READ_QD_REPLY, /* any_frame */
F_FRAME_WRITE_CMD, /* write_frame */
F_FRAME_WRITE_REPLY, /* any_frame */
F_FRAME_ERASE_CMD, /* erase_frame */
diff --git a/src/fe-readqd.cc b/src/fe-readqd.cc
new file mode 100644
index 000000000..67668006d
--- /dev/null
+++ b/src/fe-readqd.cc
@@ -0,0 +1,26 @@
+#include "globals.h"
+#include "flags.h"
+#include "reader.h"
+#include "fluxmap.h"
+#include "decoders/decoders.h"
+#include "image.h"
+#include "sector.h"
+#include "sectorset.h"
+#include "record.h"
+#include "ibm/ibm.h"
+#include "fmt/format.h"
+
+static FlagGroup flags { &readerFlags };
+
+int mainReadQd(int argc, const char* argv[])
+{
+ setReaderDefaultSource(":qd=1");
+ setReaderDefaultOutput("qd.img");
+ setReaderRevolutions(2);
+ flags.parseFlags(argc, argv);
+
+ IbmDecoder decoder(0);
+ readDiskCommand(decoder);
+ return 0;
+}
+
diff --git a/src/fluxengine.cc b/src/fluxengine.cc
index 26ce9a008..3cc11340e 100644
--- a/src/fluxengine.cc
+++ b/src/fluxengine.cc
@@ -20,6 +20,7 @@ extern command_cb mainReadFB100;
extern command_cb mainReadIBM;
extern command_cb mainReadMac;
extern command_cb mainReadMx;
+extern command_cb mainReadQd;
extern command_cb mainReadVictor9K;
extern command_cb mainReadZilogMCZ;
extern command_cb mainRpm;
@@ -71,6 +72,7 @@ static std::vector readables =
{ "ibm", mainReadIBM, "Reads the ubiquitous IBM format disks.", },
{ "mac", mainReadMac, "Reads Apple Macintosh disks.", },
{ "mx", mainReadMx, "Reads MX disks.", },
+ { "qd", mainReadQd, "Reads QuickDisk disks.", },
{ "victor9k", mainReadVictor9K, "Reads Victor 9000 disks.", },
{ "zilogmcz", mainReadZilogMCZ, "Reads Zilog MCZ disks.", },
};
diff --git a/tests/dataspec.cc b/tests/dataspec.cc
index 4c937caba..978d05c4b 100644
--- a/tests/dataspec.cc
+++ b/tests/dataspec.cc
@@ -85,6 +85,15 @@ static void test_fluxspec(void)
{{1, 9, 1}}));
assert((std::string)spec == ":d=1:s=1:t=9");
}
+
+ spec.set("");
+ assert(FluxSpec(spec).quickdisk == false);
+
+ spec.set(":qd=0");
+ assert(FluxSpec(spec).quickdisk == false);
+
+ spec.set(":qd=1");
+ assert(FluxSpec(spec).quickdisk == true);
}
static void test_imagespec(void)