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)