From 284083101732921a4eab9857f7849374663fa1c0 Mon Sep 17 00:00:00 2001 From: David Given Date: Sat, 28 Sep 2024 17:03:59 +0200 Subject: [PATCH 01/15] Initial boilerplate for the Applesauce. --- lib/usb/applesauce.h | 6 ++++++ lib/usb/usb.cc | 9 +++++++++ lib/usb/usbfinder.cc | 6 +++++- lib/usb/usbfinder.h | 3 ++- 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 lib/usb/applesauce.h diff --git a/lib/usb/applesauce.h b/lib/usb/applesauce.h new file mode 100644 index 000000000..9c080075f --- /dev/null +++ b/lib/usb/applesauce.h @@ -0,0 +1,6 @@ +#pragma once + +#define APPLESAUCE_VID 0x16c0 +#define APPLESAUCE_PID 0x0483 + +#define APPLESAUCE_ID ((APPLESAUCE_VID << 16) | APPLESAUCE_PID) diff --git a/lib/usb/usb.cc b/lib/usb/usb.cc index a66547964..b79e880fc 100644 --- a/lib/usb/usb.cc +++ b/lib/usb/usb.cc @@ -9,6 +9,7 @@ #include "lib/proto.h" #include "usbfinder.h" #include "lib/logger.h" +#include "applesauce.h" #include "greaseweazle.h" static USB* usb = NULL; @@ -54,6 +55,11 @@ static std::shared_ptr selectDevice() std::cerr << fmt::format( "Greaseweazle: {} on {}\n", c->serial, c->serialPort); break; + + case APPLESAUCE_ID: + std::cerr << fmt::format( + "Applesauce: {} on {}\n", c->serial, c->serialPort); + break; } } exit(1); @@ -87,6 +93,9 @@ USB* get_usb_impl() return createGreaseweazleUsb( candidate->serialPort, globalConfig()->usb().greaseweazle()); + case APPLESAUCE_ID: + error("Applesauce not supported yet"); + default: error("internal"); } diff --git a/lib/usb/usbfinder.cc b/lib/usb/usbfinder.cc index d3a870033..20b546a37 100644 --- a/lib/usb/usbfinder.cc +++ b/lib/usb/usbfinder.cc @@ -3,12 +3,13 @@ #include "usb.h" #include "lib/bytes.h" #include "usbfinder.h" +#include "applesauce.h" #include "greaseweazle.h" #include "protocol.h" #include "libusbp.hpp" static const std::set VALID_DEVICES = { - GREASEWEAZLE_ID, FLUXENGINE_ID}; + GREASEWEAZLE_ID, FLUXENGINE_ID, APPLESAUCE_ID}; static const std::string get_serial_number(const libusbp::device& device) { @@ -71,6 +72,9 @@ std::string getDeviceName(DeviceType type) case DEVICE_FLUXENGINE: return "FluxEngine"; + case DEVICE_APPLESAUCE: + return "Applesauce"; + default: return "unknown"; } diff --git a/lib/usb/usbfinder.h b/lib/usb/usbfinder.h index 2034a7d81..435c7e3ba 100644 --- a/lib/usb/usbfinder.h +++ b/lib/usb/usbfinder.h @@ -7,7 +7,8 @@ enum DeviceType { DEVICE_FLUXENGINE, - DEVICE_GREASEWEAZLE + DEVICE_GREASEWEAZLE, + DEVICE_APPLESAUCE, }; extern std::string getDeviceName(DeviceType type); From 8fa1a887ce4a7d03ad7647e8d2694a63cfccc3c5 Mon Sep 17 00:00:00 2001 From: David Given Date: Sat, 28 Sep 2024 20:04:54 +0200 Subject: [PATCH 02/15] Initial work towards Applesauce support --- you can connect to the device and test bandwidth. --- build.py | 1 + lib/usb/applesauceusb.cc | 335 +++++++++++++++++++++++++++++++++++++ lib/usb/greaseweazleusb.cc | 5 - lib/usb/serial.cc | 27 +++ lib/usb/serial.h | 4 + lib/usb/usb.cc | 14 +- lib/usb/usb.h | 8 +- lib/usb/usb.proto | 6 + lib/usb/usbfinder.cc | 10 +- 9 files changed, 400 insertions(+), 10 deletions(-) create mode 100644 lib/usb/applesauceusb.cc diff --git a/build.py b/build.py index ed97ddec3..42403bc05 100644 --- a/build.py +++ b/build.py @@ -90,6 +90,7 @@ "./lib/proto.cc", "./lib/readerwriter.cc", "./lib/sector.cc", + "./lib/usb/applesauceusb.cc", "./lib/usb/fluxengineusb.cc", "./lib/usb/greaseweazle.cc", "./lib/usb/greaseweazleusb.cc", diff --git a/lib/usb/applesauceusb.cc b/lib/usb/applesauceusb.cc new file mode 100644 index 000000000..95a6a9851 --- /dev/null +++ b/lib/usb/applesauceusb.cc @@ -0,0 +1,335 @@ +#include "lib/globals.h" +#include "protocol.h" +#include "lib/fluxmap.h" +#include "lib/bytes.h" +#include "lib/usb/usb.pb.h" +#include "applesauce.h" +#include "serial.h" +#include "usb.h" +#include + +static uint32_t ss_rand_next(uint32_t x) +{ + return (x & 1) ? (x >> 1) ^ 0x80000062 : x >> 1; +} + +class ApplesauceUsb : public USB +{ +public: + ApplesauceUsb(const std::string& port, const ApplesauceProto& config): + _serial(SerialPort::openSerialPort(port)), + _config(config) + { + std::string s = sendrecv("?"); + if (s != "Applesauce") + error( + "Applesauce device not responding (expected 'Applesauce', got " + "'{}')", + s); + } + + ~ApplesauceUsb() + { + sendrecv("disconnect"); + } + +private: + std::string sendrecv(const std::string& command) + { + _serial->writeLine(command); + return _serial->readLine(); + } + + void connect() + { + if (!_connected) + { + std::string probe = sendrecv("connect"); + if (probe != ".") + error("Applesauce could not find any drives"); + _connected = true; + } + } + +public: + int getVersion() override + { + // do_command({CMD_GET_INFO, 3, GETINFO_FIRMWARE}); + // + // Bytes response = _serial->readBytes(32); + // ByteReader br(response); + // + // br.seek(4); + // nanoseconds_t freq = br.read_le32(); + // _clock = 1000000000 / freq; + // + // br.seek(0); + // return br.read_be16(); + error("unsupported operation getVersion on the Greaseweazle"); + } + + void seek(int track) override + { + // do_command({CMD_SEEK, 3, (uint8_t)track}); + error("unsupported operation seek on the Greaseweazle"); + } + + nanoseconds_t getRotationalPeriod(int hardSectorCount) override + { + // if (hardSectorCount != 0) + // error("hard sectors are currently unsupported on the + // Greaseweazle"); + + // /* The Greaseweazle doesn't have a command to fetch the period + // directly, + // * so we have to do a flux read. */ + + // switch (_version) + // { + // case V22: + // do_command({CMD_READ_FLUX, 2}); + // break; + + // case V24: + // case V29: + // { + // Bytes cmd(8); + // cmd.writer() + // .write_8(CMD_READ_FLUX) + // .write_8(cmd.size()) + // .write_le32(0) // ticks default value (guessed) + // .write_le16(2); // revolutions + // do_command(cmd); + // } + // } + + // uint32_t ticks_gw = 0; + // uint32_t firstindex = ~0; + // uint32_t secondindex = ~0; + // for (;;) + // { + // uint8_t b = _serial->readByte(); + // if (!b) + // break; + + // if (b == 255) + // { + // switch (_serial->readByte()) + // { + // case FLUXOP_INDEX: + // { + // uint32_t index = read_28() + ticks_gw; + // if (firstindex == ~0) + // firstindex = index; + // else if (secondindex == ~0) + // secondindex = index; + // break; + // } + + // case FLUXOP_SPACE: + // ticks_gw += read_28(); + // break; + + // default: + // error("bad opcode in Greaseweazle stream"); + // } + // } + // else + // { + // if (b < 250) + // ticks_gw += b; + // else + // { + // int delta = 250 + (b - 250) * 255 + _serial->readByte() - + // 1; ticks_gw += delta; + // } + // } + // } + + // if (secondindex == ~0) + // error( + // "unable to determine disk rotational period (is a disk in the + // " "drive?)"); + // do_command({CMD_GET_FLUX_STATUS, 2}); + + // _revolutions = (nanoseconds_t)(secondindex - firstindex) * _clock; + // return _revolutions; + error("unsupported operation getRotationalPeriod on the Greaseweazle"); + } + + void testBulkWrite() override + { + int max = std::stoi(sendrecv("data:?max")); + fmt::print("Writing data: "); + + if (sendrecv(fmt::format("data:>{}", max)) != ".") + error("Cannot write to Applesauce"); + + Bytes junk(max); + uint32_t seed = 0; + for (int i = 0; i < max; i++) + { + junk[i] = seed; + seed = ss_rand_next(seed); + } + double start_time = getCurrentTime(); + _serial->write(junk); + _serial->readLine(); + double elapsed_time = getCurrentTime() - start_time; + + std::cout << fmt::format( + "transferred {} bytes from PC -> device in {} ms ({} kb/s)\n", + max, + int(elapsed_time * 1000.0), + int((max / 1024.0) / elapsed_time)); + } + + void testBulkRead() override + { + int max = std::stoi(sendrecv("data:?max")); + fmt::print("Reading data: "); + + if (sendrecv(fmt::format("data:<{}", max)) != ".") + error("Cannot read from Applesauce"); + + double start_time = getCurrentTime(); + _serial->readBytes(max); + double elapsed_time = getCurrentTime() - start_time; + + std::cout << fmt::format( + "transferred {} bytes from device -> PC in {} ms ({} kb/s)\n", + max, + int(elapsed_time * 1000.0), + int((max / 1024.0) / elapsed_time)); + } + + Bytes read(int side, + bool synced, + nanoseconds_t readTime, + nanoseconds_t hardSectorThreshold) override + { + // if (hardSectorThreshold != 0) + // error("hard sectors are currently unsupported on the Greaseweazle"); + // + // do_command({CMD_HEAD, 3, (uint8_t)side}); + // + // switch (_version) + // { + // case V22: + // { + // int revolutions = (readTime + _revolutions - 1) / _revolutions; + // Bytes cmd(4); + // cmd.writer() + // .write_8(CMD_READ_FLUX) + // .write_8(cmd.size()) + // .write_le32(revolutions + (synced ? 1 : 0)); + // do_command(cmd); + // break; + // } + // + // case V24: + // case V29: + // { + // Bytes cmd(8); + // cmd.writer() + // .write_8(CMD_READ_FLUX) + // .write_8(cmd.size()) + // .write_le32( + // (readTime + (synced ? _revolutions : 0)) / _clock) + // .write_le16(0); + // do_command(cmd); + // } + // } + // + // Bytes buffer; + // ByteWriter bw(buffer); + // for (;;) + // { + // uint8_t b = _serial->readByte(); + // if (!b) + // break; + // bw.write_8(b); + // } + // + // do_command({CMD_GET_FLUX_STATUS, 2}); + // + // Bytes fldata = greaseWeazleToFluxEngine(buffer, _clock); + // if (synced) + // fldata = stripPartialRotation(fldata); + // return fldata; + error("unsupported operation read on the Greaseweazle"); + } + + void write(int side, + const Bytes& fldata, + nanoseconds_t hardSectorThreshold) override + { + // if (hardSectorThreshold != 0) + // error("hard sectors are currently unsupported on the Greaseweazle"); + // + // do_command({CMD_HEAD, 3, (uint8_t)side}); + // switch (_version) + // { + // case V22: + // do_command({CMD_WRITE_FLUX, 3, 1}); + // break; + // + // case V24: + // case V29: + // do_command({CMD_WRITE_FLUX, 4, 1, 1}); + // break; + // } + // _serial->write(fluxEngineToGreaseweazle(fldata, _clock)); + // _serial->readByte(); /* synchronise */ + // + // do_command({CMD_GET_FLUX_STATUS, 2}); + error("unsupported operation write on the Greaseweazle"); + } + + void erase(int side, nanoseconds_t hardSectorThreshold) override + { + // if (hardSectorThreshold != 0) + // error("hard sectors are currently unsupported on the Greaseweazle"); + // + // do_command({CMD_HEAD, 3, (uint8_t)side}); + // + // Bytes cmd(6); + // ByteWriter bw(cmd); + // bw.write_8(CMD_ERASE_FLUX); + // bw.write_8(cmd.size()); + // bw.write_le32(200e6 / _clock); + // do_command(cmd); + // _serial->readByte(); /* synchronise */ + // + // do_command({CMD_GET_FLUX_STATUS, 2}); + error("unsupported operation erase on the Greaseweazle"); + } + + void setDrive(int drive, bool high_density, int index_mode) override + { + if (drive != 0) + error("the Applesauce only supports drive 0"); + + connect(); + sendrecv("drive:enable"); + sendrecv("motor:on"); + sendrecv(fmt::format("dpc:density{}", high_density)); + } + + void measureVoltages(struct voltages_frame* voltages) override + { + error("unsupported operation on the Greaseweazle"); + } + +private: + std::unique_ptr _serial; + const ApplesauceProto& _config; + bool _connected = false; +}; + +USB* createApplesauceUsb(const std::string& port, const ApplesauceProto& config) +{ + return new ApplesauceUsb(port, config); +} + +// vim: sw=4 ts=4 et diff --git a/lib/usb/greaseweazleusb.cc b/lib/usb/greaseweazleusb.cc index 4496adc26..b3192bd62 100644 --- a/lib/usb/greaseweazleusb.cc +++ b/lib/usb/greaseweazleusb.cc @@ -124,11 +124,6 @@ class GreaseweazleUsb : public USB return br.read_be16(); } - void recalibrate() override - { - seek(0); - } - void seek(int track) override { do_command({CMD_SEEK, 3, (uint8_t)track}); diff --git a/lib/usb/serial.cc b/lib/usb/serial.cc index f1d8aea61..4101072b4 100644 --- a/lib/usb/serial.cc +++ b/lib/usb/serial.cc @@ -254,6 +254,11 @@ uint8_t SerialPort::readByte() return b; } +void SerialPort::writeByte(uint8_t b) +{ + this->write(&b, 1); +} + void SerialPort::write(const Bytes& bytes) { int ptr = 0; @@ -264,6 +269,28 @@ void SerialPort::write(const Bytes& bytes) } } +void SerialPort::writeLine(const std::string& chars) +{ + this->write((const uint8_t*)&chars[0], chars.size()); + writeByte('\n'); +} + +std::string SerialPort::readLine() +{ + std::string s; + + for (;;) + { + uint8_t b = readByte(); + if (b == '\r') + continue; + if (b == '\n') + return s; + + s += b; + } +} + std::unique_ptr SerialPort::openSerialPort(const std::string& path) { return std::make_unique(path); diff --git a/lib/usb/serial.h b/lib/usb/serial.h index 7f5941026..0fd7416af 100644 --- a/lib/usb/serial.h +++ b/lib/usb/serial.h @@ -17,8 +17,12 @@ class SerialPort void read(Bytes& bytes); Bytes readBytes(size_t count); uint8_t readByte(); + void writeByte(uint8_t b); void write(const Bytes& bytes); + void writeLine(const std::string& chars); + std::string readLine(); + private: uint8_t _readbuffer[4096]; size_t _readbuffer_ptr = 0; diff --git a/lib/usb/usb.cc b/lib/usb/usb.cc index b79e880fc..201c31921 100644 --- a/lib/usb/usb.cc +++ b/lib/usb/usb.cc @@ -77,6 +77,14 @@ USB* get_usb_impl() return createGreaseweazleUsb(conf.port(), conf); } + if (globalConfig()->usb().has_applesauce() && + globalConfig()->usb().applesauce().has_port()) + { + const auto& conf = globalConfig()->usb().applesauce(); + log("Using Applesauce on serial port {}", conf.port()); + return createApplesauceUsb(conf.port(), conf); + } + /* Otherwise, select a device by USB ID. */ auto candidate = selectDevice(); @@ -94,7 +102,11 @@ USB* get_usb_impl() candidate->serialPort, globalConfig()->usb().greaseweazle()); case APPLESAUCE_ID: - error("Applesauce not supported yet"); + log("Using Applesauce {} on {}", + candidate->serial, + candidate->serialPort); + return createApplesauceUsb( + candidate->serialPort, globalConfig()->usb().applesauce()); default: error("internal"); diff --git a/lib/usb/usb.h b/lib/usb/usb.h index 7686ad071..bc0cd9102 100644 --- a/lib/usb/usb.h +++ b/lib/usb/usb.h @@ -6,6 +6,7 @@ class Fluxmap; class GreaseweazleProto; +class ApplesauceProto; namespace libusbp { class device; @@ -17,7 +18,10 @@ class USB virtual ~USB(); virtual int getVersion() = 0; - virtual void recalibrate() = 0; + virtual void recalibrate() + { + seek(0); + }; virtual void seek(int track) = 0; virtual nanoseconds_t getRotationalPeriod(int hardSectorCount) = 0; virtual void testBulkWrite() = 0; @@ -41,6 +45,8 @@ extern USB& getUsb(); extern USB* createFluxengineUsb(libusbp::device& device); extern USB* createGreaseweazleUsb( const std::string& serialPort, const GreaseweazleProto& config); +extern USB* createApplesauceUsb( + const std::string& serialPort, const ApplesauceProto& config); static inline int usbGetVersion() { diff --git a/lib/usb/usb.proto b/lib/usb/usb.proto index 2c61a91a6..15439b7e0 100644 --- a/lib/usb/usb.proto +++ b/lib/usb/usb.proto @@ -16,9 +16,15 @@ message GreaseweazleProto { [(help) = "which FDD bus type is in use", default = IBMPC]; } +message ApplesauceProto { + optional string port = 1 + [(help) = "Applesauce serial port to use"]; +} + message UsbProto { optional string serial = 1 [(help) = "serial number of FluxEngine or Greaseweazle device to use"]; optional GreaseweazleProto greaseweazle = 2 [(help) = "Greaseweazle-specific options"]; + optional ApplesauceProto applesauce = 3 [(help) = "Applesauce-specific options"]; } diff --git a/lib/usb/usbfinder.cc b/lib/usb/usbfinder.cc index 20b546a37..1d98174f4 100644 --- a/lib/usb/usbfinder.cc +++ b/lib/usb/usbfinder.cc @@ -42,13 +42,17 @@ std::vector> findUsbDevices() candidate->serial = get_serial_number(it); if (id == GREASEWEAZLE_ID) + candidate->type = DEVICE_GREASEWEAZLE; + else if (id == APPLESAUCE_ID) + candidate->type = DEVICE_APPLESAUCE; + else if (id == FLUXENGINE_ID) + candidate->type = DEVICE_FLUXENGINE; + + if ((id == GREASEWEAZLE_ID) || (id == APPLESAUCE_ID)) { libusbp::serial_port port(candidate->device); candidate->serialPort = port.get_name(); - candidate->type = DEVICE_GREASEWEAZLE; } - else if (id == FLUXENGINE_ID) - candidate->type = DEVICE_FLUXENGINE; candidates.push_back(std::move(candidate)); } From 14255a507690950002141bee854b6493f5735b23 Mon Sep 17 00:00:00 2001 From: David Given Date: Sat, 28 Sep 2024 23:07:05 +0200 Subject: [PATCH 03/15] Successfully read raw Applesauce data. --- lib/usb/applesauceusb.cc | 206 +++++++++++++-------------------------- 1 file changed, 68 insertions(+), 138 deletions(-) diff --git a/lib/usb/applesauceusb.cc b/lib/usb/applesauceusb.cc index 95a6a9851..8b1ff2894 100644 --- a/lib/usb/applesauceusb.cc +++ b/lib/usb/applesauceusb.cc @@ -3,6 +3,7 @@ #include "lib/fluxmap.h" #include "lib/bytes.h" #include "lib/usb/usb.pb.h" +#include "lib/utils.h" #include "applesauce.h" #include "serial.h" #include "usb.h" @@ -13,6 +14,12 @@ static uint32_t ss_rand_next(uint32_t x) return (x & 1) ? (x >> 1) ^ 0x80000062 : x >> 1; } +class ApplesauceException : public ErrorException +{ +public: + ApplesauceException(const std::string& s): ErrorException(s) {} +}; + class ApplesauceUsb : public USB { public: @@ -36,19 +43,40 @@ class ApplesauceUsb : public USB private: std::string sendrecv(const std::string& command) { + fmt::print(fmt::format("> {}\n", command)); _serial->writeLine(command); - return _serial->readLine(); + auto r = _serial->readLine(); + fmt::print(fmt::format("< {}\n", r)); + return r; + } + + bool doCommand(const std::string& command) + { + return sendrecv(command) == "."; + } + + std::string doCommandX(const std::string& command) + { + std::string r = sendrecv(command); + if (r != ".") + throw ApplesauceException( + fmt::format("low-level Applesauce error: '{}'", r)); + r = _serial->readLine(); + fmt::print(fmt::format("<< {}\n", r)); + return r; } void connect() { if (!_connected) { - std::string probe = sendrecv("connect"); - if (probe != ".") + if (!doCommand("connect")) error("Applesauce could not find any drives"); + doCommand("drive:enable"); + doCommand("motor:on"); _connected = true; } + } public: @@ -70,91 +98,29 @@ class ApplesauceUsb : public USB void seek(int track) override { - // do_command({CMD_SEEK, 3, (uint8_t)track}); - error("unsupported operation seek on the Greaseweazle"); + if (track == 0) + doCommand("head:zero"); + else + doCommand(fmt::format("head:track{}", track)); } nanoseconds_t getRotationalPeriod(int hardSectorCount) override { - // if (hardSectorCount != 0) - // error("hard sectors are currently unsupported on the - // Greaseweazle"); - - // /* The Greaseweazle doesn't have a command to fetch the period - // directly, - // * so we have to do a flux read. */ + if (hardSectorCount != 0) + error("hard sectors are currently unsupported on the Applesauce"); - // switch (_version) - // { - // case V22: - // do_command({CMD_READ_FLUX, 2}); - // break; - - // case V24: - // case V29: - // { - // Bytes cmd(8); - // cmd.writer() - // .write_8(CMD_READ_FLUX) - // .write_8(cmd.size()) - // .write_le32(0) // ticks default value (guessed) - // .write_le16(2); // revolutions - // do_command(cmd); - // } - // } - - // uint32_t ticks_gw = 0; - // uint32_t firstindex = ~0; - // uint32_t secondindex = ~0; - // for (;;) - // { - // uint8_t b = _serial->readByte(); - // if (!b) - // break; - - // if (b == 255) - // { - // switch (_serial->readByte()) - // { - // case FLUXOP_INDEX: - // { - // uint32_t index = read_28() + ticks_gw; - // if (firstindex == ~0) - // firstindex = index; - // else if (secondindex == ~0) - // secondindex = index; - // break; - // } - - // case FLUXOP_SPACE: - // ticks_gw += read_28(); - // break; - - // default: - // error("bad opcode in Greaseweazle stream"); - // } - // } - // else - // { - // if (b < 250) - // ticks_gw += b; - // else - // { - // int delta = 250 + (b - 250) * 255 + _serial->readByte() - - // 1; ticks_gw += delta; - // } - // } - // } - - // if (secondindex == ~0) - // error( - // "unable to determine disk rotational period (is a disk in the - // " "drive?)"); - // do_command({CMD_GET_FLUX_STATUS, 2}); - - // _revolutions = (nanoseconds_t)(secondindex - firstindex) * _clock; - // return _revolutions; - error("unsupported operation getRotationalPeriod on the Greaseweazle"); + connect(); + try + { + double rpm = std::stod(doCommandX("sync:?speed")) / 1000.0; + sendrecv("X"); + fmt::print("< {}\n", _serial->readLine()); + return 60e9 / rpm; + } + catch (const ApplesauceException& e) + { + return 0; + } } void testBulkWrite() override @@ -162,7 +128,7 @@ class ApplesauceUsb : public USB int max = std::stoi(sendrecv("data:?max")); fmt::print("Writing data: "); - if (sendrecv(fmt::format("data:>{}", max)) != ".") + if (!doCommand(fmt::format("data:>{}", max))) error("Cannot write to Applesauce"); Bytes junk(max); @@ -189,7 +155,7 @@ class ApplesauceUsb : public USB int max = std::stoi(sendrecv("data:?max")); fmt::print("Reading data: "); - if (sendrecv(fmt::format("data:<{}", max)) != ".") + if (!doCommand(fmt::format("data:<{}", max))) error("Cannot read from Applesauce"); double start_time = getCurrentTime(); @@ -208,56 +174,22 @@ class ApplesauceUsb : public USB nanoseconds_t readTime, nanoseconds_t hardSectorThreshold) override { - // if (hardSectorThreshold != 0) - // error("hard sectors are currently unsupported on the Greaseweazle"); - // - // do_command({CMD_HEAD, 3, (uint8_t)side}); - // - // switch (_version) - // { - // case V22: - // { - // int revolutions = (readTime + _revolutions - 1) / _revolutions; - // Bytes cmd(4); - // cmd.writer() - // .write_8(CMD_READ_FLUX) - // .write_8(cmd.size()) - // .write_le32(revolutions + (synced ? 1 : 0)); - // do_command(cmd); - // break; - // } - // - // case V24: - // case V29: - // { - // Bytes cmd(8); - // cmd.writer() - // .write_8(CMD_READ_FLUX) - // .write_8(cmd.size()) - // .write_le32( - // (readTime + (synced ? _revolutions : 0)) / _clock) - // .write_le16(0); - // do_command(cmd); - // } - // } - // - // Bytes buffer; - // ByteWriter bw(buffer); - // for (;;) - // { - // uint8_t b = _serial->readByte(); - // if (!b) - // break; - // bw.write_8(b); - // } - // - // do_command({CMD_GET_FLUX_STATUS, 2}); - // - // Bytes fldata = greaseWeazleToFluxEngine(buffer, _clock); - // if (synced) - // fldata = stripPartialRotation(fldata); - // return fldata; - error("unsupported operation read on the Greaseweazle"); + if (hardSectorThreshold != 0) + error("hard sectors are currently unsupported on the Applesauce"); + + doCommand(fmt::format("disk:side{}", side)); + doCommand(synced ? "sync:on" : "sync:off"); + doCommand("data:clear"); + doCommandX("disk:read"); + // _serial->readLine(); + + int bufferSize = std::stoi(sendrecv("data:?size")); + + doCommand(fmt::format("data:<{}", bufferSize)); + + Bytes rawData = _serial->readBytes(bufferSize); + + return Bytes(); } void write(int side, @@ -311,9 +243,7 @@ class ApplesauceUsb : public USB error("the Applesauce only supports drive 0"); connect(); - sendrecv("drive:enable"); - sendrecv("motor:on"); - sendrecv(fmt::format("dpc:density{}", high_density)); + doCommand(fmt::format("dpc:density{}", high_density ? '+' : '-')); } void measureVoltages(struct voltages_frame* voltages) override From d394b21920a0c27d7cbef34adc1ee9c3a9ad3ec2 Mon Sep 17 00:00:00 2001 From: David Given Date: Sat, 28 Sep 2024 23:39:30 +0200 Subject: [PATCH 04/15] Got our first track read! --- build.py | 1 + lib/usb/applesauce.cc | 34 ++++++++++++++++++++++++++++++++++ lib/usb/applesauce.h | 2 ++ lib/usb/applesauceusb.cc | 3 +-- 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 lib/usb/applesauce.cc diff --git a/build.py b/build.py index 42403bc05..33b981924 100644 --- a/build.py +++ b/build.py @@ -90,6 +90,7 @@ "./lib/proto.cc", "./lib/readerwriter.cc", "./lib/sector.cc", + "./lib/usb/applesauce.cc", "./lib/usb/applesauceusb.cc", "./lib/usb/fluxengineusb.cc", "./lib/usb/greaseweazle.cc", diff --git a/lib/usb/applesauce.cc b/lib/usb/applesauce.cc new file mode 100644 index 000000000..fb3e4677e --- /dev/null +++ b/lib/usb/applesauce.cc @@ -0,0 +1,34 @@ +#include "lib/globals.h" +#include "usb.h" +#include "protocol.h" +#include "lib/bytes.h" +#include "greaseweazle.h" +#include "lib/fluxmap.h" +#include "lib/a2r.h" + +static uint32_t a2r_to_ticks(uint32_t a2rticks) +{ + return a2rticks * A2R_NS_PER_TICK / NS_PER_TICK; +} + +Bytes applesauceToFluxEngine(const Bytes& asdata) +{ + ByteReader br(asdata); + Fluxmap fluxmap; + + unsigned a2rTicksSinceLastPulse = 0; + while (!br.eof()) + { + uint8_t b = br.read_8(); + a2rTicksSinceLastPulse += b; + if (b != 255) + { + fluxmap.appendInterval(a2r_to_ticks(a2rTicksSinceLastPulse)); + fluxmap.appendPulse(); + a2rTicksSinceLastPulse = 0; + } + } + + return fluxmap.rawBytes(); +} + diff --git a/lib/usb/applesauce.h b/lib/usb/applesauce.h index 9c080075f..87837a3d7 100644 --- a/lib/usb/applesauce.h +++ b/lib/usb/applesauce.h @@ -4,3 +4,5 @@ #define APPLESAUCE_PID 0x0483 #define APPLESAUCE_ID ((APPLESAUCE_VID << 16) | APPLESAUCE_PID) + +extern Bytes applesauceToFluxEngine(const Bytes& asdata); diff --git a/lib/usb/applesauceusb.cc b/lib/usb/applesauceusb.cc index 8b1ff2894..6f5d0a7db 100644 --- a/lib/usb/applesauceusb.cc +++ b/lib/usb/applesauceusb.cc @@ -188,8 +188,7 @@ class ApplesauceUsb : public USB doCommand(fmt::format("data:<{}", bufferSize)); Bytes rawData = _serial->readBytes(bufferSize); - - return Bytes(); + return applesauceToFluxEngine(rawData); } void write(int side, From 6b28f36b97edb8fa1ae973d57669d8804769e753 Mon Sep 17 00:00:00 2001 From: David Given Date: Sat, 28 Sep 2024 23:44:33 +0200 Subject: [PATCH 05/15] getVersion() doesn't need to be a public USB method. --- lib/usb/applesauce.cc | 1 - lib/usb/applesauceusb.cc | 61 ++++++-------------------------------- lib/usb/fluxengineusb.cc | 4 +-- lib/usb/greaseweazleusb.cc | 4 ++- lib/usb/usb.h | 5 ---- 5 files changed, 14 insertions(+), 61 deletions(-) diff --git a/lib/usb/applesauce.cc b/lib/usb/applesauce.cc index fb3e4677e..c287266a6 100644 --- a/lib/usb/applesauce.cc +++ b/lib/usb/applesauce.cc @@ -31,4 +31,3 @@ Bytes applesauceToFluxEngine(const Bytes& asdata) return fluxmap.rawBytes(); } - diff --git a/lib/usb/applesauceusb.cc b/lib/usb/applesauceusb.cc index 6f5d0a7db..060f48e49 100644 --- a/lib/usb/applesauceusb.cc +++ b/lib/usb/applesauceusb.cc @@ -76,26 +76,9 @@ class ApplesauceUsb : public USB doCommand("motor:on"); _connected = true; } - } public: - int getVersion() override - { - // do_command({CMD_GET_INFO, 3, GETINFO_FIRMWARE}); - // - // Bytes response = _serial->readBytes(32); - // ByteReader br(response); - // - // br.seek(4); - // nanoseconds_t freq = br.read_le32(); - // _clock = 1000000000 / freq; - // - // br.seek(0); - // return br.read_be16(); - error("unsupported operation getVersion on the Greaseweazle"); - } - void seek(int track) override { if (track == 0) @@ -177,11 +160,11 @@ class ApplesauceUsb : public USB if (hardSectorThreshold != 0) error("hard sectors are currently unsupported on the Applesauce"); + connect(); doCommand(fmt::format("disk:side{}", side)); doCommand(synced ? "sync:on" : "sync:off"); doCommand("data:clear"); doCommandX("disk:read"); - // _serial->readLine(); int bufferSize = std::stoi(sendrecv("data:?size")); @@ -195,45 +178,19 @@ class ApplesauceUsb : public USB const Bytes& fldata, nanoseconds_t hardSectorThreshold) override { - // if (hardSectorThreshold != 0) - // error("hard sectors are currently unsupported on the Greaseweazle"); - // - // do_command({CMD_HEAD, 3, (uint8_t)side}); - // switch (_version) - // { - // case V22: - // do_command({CMD_WRITE_FLUX, 3, 1}); - // break; - // - // case V24: - // case V29: - // do_command({CMD_WRITE_FLUX, 4, 1, 1}); - // break; - // } - // _serial->write(fluxEngineToGreaseweazle(fldata, _clock)); - // _serial->readByte(); /* synchronise */ - // - // do_command({CMD_GET_FLUX_STATUS, 2}); + if (hardSectorThreshold != 0) + error("hard sectors are currently unsupported on the Applesauce"); error("unsupported operation write on the Greaseweazle"); } void erase(int side, nanoseconds_t hardSectorThreshold) override { - // if (hardSectorThreshold != 0) - // error("hard sectors are currently unsupported on the Greaseweazle"); - // - // do_command({CMD_HEAD, 3, (uint8_t)side}); - // - // Bytes cmd(6); - // ByteWriter bw(cmd); - // bw.write_8(CMD_ERASE_FLUX); - // bw.write_8(cmd.size()); - // bw.write_le32(200e6 / _clock); - // do_command(cmd); - // _serial->readByte(); /* synchronise */ - // - // do_command({CMD_GET_FLUX_STATUS, 2}); - error("unsupported operation erase on the Greaseweazle"); + if (hardSectorThreshold != 0) + error("hard sectors are currently unsupported on the Applesauce"); + + connect(); + doCommand(fmt::format("disk:side{}", side)); + doCommand("disk:wipe"); } void setDrive(int drive, bool high_density, int index_mode) override diff --git a/lib/usb/fluxengineusb.cc b/lib/usb/fluxengineusb.cc index d9b9c42ae..e7ec6c76f 100644 --- a/lib/usb/fluxengineusb.cc +++ b/lib/usb/fluxengineusb.cc @@ -122,8 +122,7 @@ class FluxEngineUsb : public USB } } -public: - int getVersion() override + int getVersion() { struct any_frame f = { .f = {.type = F_FRAME_GET_VERSION_CMD, .size = sizeof(f)} @@ -133,6 +132,7 @@ class FluxEngineUsb : public USB return r->version; } +public: void seek(int track) override { struct seek_frame f = { diff --git a/lib/usb/greaseweazleusb.cc b/lib/usb/greaseweazleusb.cc index b3192bd62..0566f7706 100644 --- a/lib/usb/greaseweazleusb.cc +++ b/lib/usb/greaseweazleusb.cc @@ -109,7 +109,8 @@ class GreaseweazleUsb : public USB do_command({CMD_SET_BUS_TYPE, 3, (uint8_t)config.bus_type()}); } - int getVersion() override +private: + int getVersion() { do_command({CMD_GET_INFO, 3, GETINFO_FIRMWARE}); @@ -124,6 +125,7 @@ class GreaseweazleUsb : public USB return br.read_be16(); } +public: void seek(int track) override { do_command({CMD_SEEK, 3, (uint8_t)track}); diff --git a/lib/usb/usb.h b/lib/usb/usb.h index bc0cd9102..d9a612c86 100644 --- a/lib/usb/usb.h +++ b/lib/usb/usb.h @@ -17,7 +17,6 @@ class USB public: virtual ~USB(); - virtual int getVersion() = 0; virtual void recalibrate() { seek(0); @@ -48,10 +47,6 @@ extern USB* createGreaseweazleUsb( extern USB* createApplesauceUsb( const std::string& serialPort, const ApplesauceProto& config); -static inline int usbGetVersion() -{ - return getUsb().getVersion(); -} static inline void usbRecalibrate() { getUsb().recalibrate(); From 38b8cd2574df0d7823d71d1d1271162c65683e32 Mon Sep 17 00:00:00 2001 From: David Given Date: Sun, 29 Sep 2024 13:05:46 +0200 Subject: [PATCH 06/15] Reads mostly work; writes not yet. --- build.py | 1 + lib/decoders/fluxmapreader.cc | 6 +-- lib/usb/applesauce.cc | 33 +++++++++++++- lib/usb/applesauce.h | 1 + lib/usb/applesauceusb.cc | 82 ++++++++++++++++++++++++----------- lib/usb/usb.proto | 2 + tests/applesauce.cc | 74 +++++++++++++++++++++++++++++++ tests/build.py | 1 + 8 files changed, 171 insertions(+), 29 deletions(-) create mode 100644 tests/applesauce.cc diff --git a/build.py b/build.py index 33b981924..309d2c288 100644 --- a/build.py +++ b/build.py @@ -211,6 +211,7 @@ "lib/proto.h": "./lib/proto.h", "lib/readerwriter.h": "./lib/readerwriter.h", "lib/sector.h": "./lib/sector.h", + "lib/usb/applesauce.h": "./lib/usb/applesauce.h", "lib/usb/greaseweazle.h": "./lib/usb/greaseweazle.h", "lib/usb/usb.h": "./lib/usb/usb.h", "lib/usb/usbfinder.h": "./lib/usb/usbfinder.h", diff --git a/lib/decoders/fluxmapreader.cc b/lib/decoders/fluxmapreader.cc index 4ce8f7c8e..3fbfb89cc 100644 --- a/lib/decoders/fluxmapreader.cc +++ b/lib/decoders/fluxmapreader.cc @@ -47,7 +47,7 @@ bool FluxmapReader::findEvent(int event, unsigned& ticks) { ticks = 0; - for (;;) + while (!eof()) { unsigned thisTicks; int thisEvent; @@ -57,11 +57,11 @@ bool FluxmapReader::findEvent(int event, unsigned& ticks) if (thisEvent == F_EOF) return false; - if (eof()) - return false; if ((event == thisEvent) || (event & thisEvent)) return true; } + + return false; } unsigned FluxmapReader::readInterval(nanoseconds_t clock) diff --git a/lib/usb/applesauce.cc b/lib/usb/applesauce.cc index c287266a6..0bcce8c45 100644 --- a/lib/usb/applesauce.cc +++ b/lib/usb/applesauce.cc @@ -4,13 +4,44 @@ #include "lib/bytes.h" #include "greaseweazle.h" #include "lib/fluxmap.h" +#include "lib/decoders/fluxmapreader.h" #include "lib/a2r.h" -static uint32_t a2r_to_ticks(uint32_t a2rticks) +static double a2r_to_ticks(double a2rticks) { return a2rticks * A2R_NS_PER_TICK / NS_PER_TICK; } +static double ticks_to_a2r(double flticks) +{ + return flticks * NS_PER_TICK / A2R_NS_PER_TICK; +} + +Bytes fluxEngineToApplesauce(const Bytes& fldata) +{ + Fluxmap fluxmap(fldata); + FluxmapReader fmr(fluxmap); + Bytes asdata; + ByteWriter bw(asdata); + + while (!fmr.eof()) + { + unsigned ticks; + if (!fmr.findEvent(F_BIT_PULSE, ticks)) + break; + + uint32_t applesauceTicks = ticks_to_a2r(ticks); + while (applesauceTicks >= 255) + { + bw.write_8(255); + applesauceTicks -= 255; + } + bw.write_8(applesauceTicks); + } + + return asdata; +} + Bytes applesauceToFluxEngine(const Bytes& asdata) { ByteReader br(asdata); diff --git a/lib/usb/applesauce.h b/lib/usb/applesauce.h index 87837a3d7..2cef0151d 100644 --- a/lib/usb/applesauce.h +++ b/lib/usb/applesauce.h @@ -6,3 +6,4 @@ #define APPLESAUCE_ID ((APPLESAUCE_VID << 16) | APPLESAUCE_PID) extern Bytes applesauceToFluxEngine(const Bytes& asdata); +extern Bytes fluxEngineToApplesauce(const Bytes& fldata); diff --git a/lib/usb/applesauceusb.cc b/lib/usb/applesauceusb.cc index 060f48e49..283cda23a 100644 --- a/lib/usb/applesauceusb.cc +++ b/lib/usb/applesauceusb.cc @@ -43,26 +43,33 @@ class ApplesauceUsb : public USB private: std::string sendrecv(const std::string& command) { - fmt::print(fmt::format("> {}\n", command)); + if (_config.verbose()) + fmt::print(fmt::format("> {}\n", command)); _serial->writeLine(command); auto r = _serial->readLine(); - fmt::print(fmt::format("< {}\n", r)); + if (_config.verbose()) + fmt::print(fmt::format("< {}\n", r)); return r; } - bool doCommand(const std::string& command) + void checkCommandResult(const std::string& result) { - return sendrecv(command) == "."; + if (result != ".") + throw ApplesauceException( + fmt::format("low-level Applesauce error: '{}'", result)); + } + + void doCommand(const std::string& command) + { + checkCommandResult(sendrecv(command)); } std::string doCommandX(const std::string& command) { - std::string r = sendrecv(command); - if (r != ".") - throw ApplesauceException( - fmt::format("low-level Applesauce error: '{}'", r)); - r = _serial->readLine(); - fmt::print(fmt::format("<< {}\n", r)); + doCommand(command); + std::string r = _serial->readLine(); + if (_config.verbose()) + fmt::print(fmt::format("<< {}\n", r)); return r; } @@ -70,11 +77,18 @@ class ApplesauceUsb : public USB { if (!_connected) { - if (!doCommand("connect")) - error("Applesauce could not find any drives"); - doCommand("drive:enable"); - doCommand("motor:on"); - _connected = true; + try + { + doCommand("connect"); + doCommand("drive:enable"); + doCommand("motor:on"); + doCommand("head:zero"); + _connected = true; + } + catch (const ApplesauceException& e) + { + error("Applesauce could not connect to a drive"); + } } } @@ -95,10 +109,12 @@ class ApplesauceUsb : public USB connect(); try { - double rpm = std::stod(doCommandX("sync:?speed")) / 1000.0; - sendrecv("X"); - fmt::print("< {}\n", _serial->readLine()); - return 60e9 / rpm; + double period_us = std::stod(doCommandX("sync:?speed")); + _serial->writeByte('X'); + std::string r = _serial->readLine(); + if (_config.verbose()) + fmt::print("<< {}\n", r); + return period_us * 1e3; } catch (const ApplesauceException& e) { @@ -111,8 +127,7 @@ class ApplesauceUsb : public USB int max = std::stoi(sendrecv("data:?max")); fmt::print("Writing data: "); - if (!doCommand(fmt::format("data:>{}", max))) - error("Cannot write to Applesauce"); + doCommand(fmt::format("data:>{}", max)); Bytes junk(max); uint32_t seed = 0; @@ -138,8 +153,7 @@ class ApplesauceUsb : public USB int max = std::stoi(sendrecv("data:?max")); fmt::print("Reading data: "); - if (!doCommand(fmt::format("data:<{}", max))) - error("Cannot read from Applesauce"); + doCommand(fmt::format("data:<{}", max)); double start_time = getCurrentTime(); _serial->readBytes(max); @@ -161,7 +175,7 @@ class ApplesauceUsb : public USB error("hard sectors are currently unsupported on the Applesauce"); connect(); - doCommand(fmt::format("disk:side{}", side)); + doCommand(fmt::format("head:side{}", side)); doCommand(synced ? "sync:on" : "sync:off"); doCommand("data:clear"); doCommandX("disk:read"); @@ -180,7 +194,25 @@ class ApplesauceUsb : public USB { if (hardSectorThreshold != 0) error("hard sectors are currently unsupported on the Applesauce"); - error("unsupported operation write on the Greaseweazle"); + + if (sendrecv("disk:?write") == "-") + error("cannot write --- disk is write protected"); + if (sendrecv("?safe") == "+") + error("cannot write --- Applesauce 'safe' switch is on"); + + connect(); + doCommand(fmt::format("head:side{}", side)); + doCommand("sync:on"); + doCommand("data:clear"); + + Bytes asdata = fluxEngineToApplesauce(fldata); + doCommand(fmt::format("data:>{}", asdata.size())); + _serial->write(asdata); + checkCommandResult(_serial->readLine()); + + doCommand("disk:wclear"); + doCommand("disk:wcmd"); + doCommandX("disk:write"); } void erase(int side, nanoseconds_t hardSectorThreshold) override diff --git a/lib/usb/usb.proto b/lib/usb/usb.proto index 15439b7e0..14f865fcc 100644 --- a/lib/usb/usb.proto +++ b/lib/usb/usb.proto @@ -19,6 +19,8 @@ message GreaseweazleProto { message ApplesauceProto { optional string port = 1 [(help) = "Applesauce serial port to use"]; + optional bool verbose = 2 + [(help) = "Enable verbose protocol logging", default = false]; } message UsbProto { diff --git a/tests/applesauce.cc b/tests/applesauce.cc new file mode 100644 index 000000000..93841174b --- /dev/null +++ b/tests/applesauce.cc @@ -0,0 +1,74 @@ +#include +#include +#include +#include "lib/globals.h" +#include "lib/fluxmap.h" +#include "lib/usb/applesauce.h" + +static void test_convert(const Bytes& asbytes, const Bytes& flbytes) +{ + Bytes astoflbytes = applesauceToFluxEngine(asbytes); + Bytes fltoasbytes = fluxEngineToApplesauce(flbytes); + + if (astoflbytes != flbytes) + { + std::cout << "Applesauce to FluxEngine conversion failed.\n"; + std::cout << "Applesauce bytes:" << std::endl; + hexdump(std::cout, asbytes); + std::cout << std::endl << "Produced this:" << std::endl; + hexdump(std::cout, astoflbytes); + std::cout << std::endl << "Expected this:" << std::endl; + hexdump(std::cout, flbytes); + abort(); + } + + if (fltoasbytes != asbytes) + { + std::cout << "FluxEngine to Applesauce conversion failed.\n"; + std::cout << "FluxEngine bytes:" << std::endl; + hexdump(std::cout, flbytes); + std::cout << std::endl << "Produced this:" << std::endl; + hexdump(std::cout, fltoasbytes); + std::cout << std::endl << "Expected this:" << std::endl; + hexdump(std::cout, asbytes); + abort(); + } +} + +static void test_conversions() +{ + /* Simple one-byte intervals. */ + + test_convert(Bytes{0x20, 0x20, 0x20, 0x20}, Bytes{0xb0, 0xb0, 0xb0, 0xb0}); + + /* Long, multibyte intervals. */ + + test_convert(Bytes{0xff, 0x1f, 0x20, 0xff, 0xff, 0x20}, + Bytes{0x3f, + 0x3f, + 0x3f, + 0x3f, + 0x3f, + 0x3f, + 0xb3, + 0xb0, + 0x3f, + 0x3f, + 0x3f, + 0x3f, + 0x3f, + 0x3f, + 0x3f, + 0x3f, + 0x3f, + 0x3f, + 0x3f, + 0x3f, + 0xb9}); +} + +int main(int argc, const char* argv[]) +{ + test_conversions(); + return 0; +} diff --git a/tests/build.py b/tests/build.py index 0a9513757..08eb68e1f 100644 --- a/tests/build.py +++ b/tests/build.py @@ -18,6 +18,7 @@ "agg", "amiga", "applesingle", + "applesauce", "bitaccumulator", "bytes", "compression", From 8f7203e38b3bdc86df3ed81fa9601f3e60bc44dd Mon Sep 17 00:00:00 2001 From: David Given Date: Sun, 29 Sep 2024 23:12:02 +0200 Subject: [PATCH 07/15] Fix testpattern: to actually work. --- lib/fluxsource/fluxsource.proto | 2 +- lib/fluxsource/testpatternfluxsource.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/fluxsource/fluxsource.proto b/lib/fluxsource/fluxsource.proto index 7564717ce..ab62bb65e 100644 --- a/lib/fluxsource/fluxsource.proto +++ b/lib/fluxsource/fluxsource.proto @@ -6,7 +6,7 @@ message HardwareFluxSourceProto {} message TestPatternFluxSourceProto { optional double interval_us = 1 [default = 4.0, (help) = "interval between pulses"]; - optional double sequence_length_us = 2 [default = 200.0, (help) = "length of test sequence"]; + optional double sequence_length_ms = 2 [default = 166.0, (help) = "length of test sequence"]; } message EraseFluxSourceProto {} diff --git a/lib/fluxsource/testpatternfluxsource.cc b/lib/fluxsource/testpatternfluxsource.cc index b5a85c454..3b7cce883 100644 --- a/lib/fluxsource/testpatternfluxsource.cc +++ b/lib/fluxsource/testpatternfluxsource.cc @@ -18,9 +18,9 @@ class TestPatternFluxSource : public TrivialFluxSource { auto fluxmap = std::make_unique(); - while (fluxmap->duration() < (_config.sequence_length_us() * 1000000.0)) + while (fluxmap->duration() < (_config.sequence_length_ms() * 1e6)) { - fluxmap->appendInterval(_config.interval_us()); + fluxmap->appendInterval(_config.interval_us() * TICKS_PER_US); fluxmap->appendPulse(); } From 22b78cee56f2369ddda19127509a8535fb1098e9 Mon Sep 17 00:00:00 2001 From: David Given Date: Sun, 29 Sep 2024 23:39:32 +0200 Subject: [PATCH 08/15] Report which flag could not be recognised on error. --- lib/flags.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/flags.cc b/lib/flags.cc index 6f3a543f6..a83ccdb38 100644 --- a/lib/flags.cc +++ b/lib/flags.cc @@ -156,7 +156,7 @@ std::vector FlagGroup::parseFlagsWithFilenames(int argc, globalConfig().applyOption(path); } else - error("unrecognised flag; try --help"); + error("unrecognised flag '-{}'; try --help", key); } else { From 98279e44efcf7168a3920320c6e5d786a6bfb814 Mon Sep 17 00:00:00 2001 From: David Given Date: Sun, 29 Sep 2024 23:39:59 +0200 Subject: [PATCH 09/15] Remove stray logging. --- lib/globals.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/globals.cc b/lib/globals.cc index bd28af65f..857498056 100644 --- a/lib/globals.cc +++ b/lib/globals.cc @@ -14,5 +14,4 @@ double getCurrentTime(void) void warning(const std::string msg) { log(msg); - fmt::print("Warning: {}\n", msg); } From 6fd98e3c672bd2f216ec8ec71fc1295f6f5837f0 Mon Sep 17 00:00:00 2001 From: David Given Date: Sun, 29 Sep 2024 23:40:19 +0200 Subject: [PATCH 10/15] Writing with the Applesauce now mostly works, although there's still stuff to be dealt with. --- lib/usb/applesauceusb.cc | 101 +++++++++++++++++++++++++++++++++------ lib/usb/usb.proto | 2 + 2 files changed, 89 insertions(+), 14 deletions(-) diff --git a/lib/usb/applesauceusb.cc b/lib/usb/applesauceusb.cc index 283cda23a..8e24af51e 100644 --- a/lib/usb/applesauceusb.cc +++ b/lib/usb/applesauceusb.cc @@ -7,6 +7,7 @@ #include "applesauce.h" #include "serial.h" #include "usb.h" +#include "lib/decoders/fluxmapreader.h" #include static uint32_t ss_rand_next(uint32_t x) @@ -14,6 +15,58 @@ static uint32_t ss_rand_next(uint32_t x) return (x & 1) ? (x >> 1) ^ 0x80000062 : x >> 1; } +static Bytes applesauceReadDataToFluxEngine( + const Bytes& asdata, nanoseconds_t clock) +{ + ByteReader br(asdata); + Fluxmap fluxmap; + + double a2rTicksSinceLastPulse = 0; + while (!br.eof()) + { + uint8_t b = br.read_8(); + a2rTicksSinceLastPulse += b; + if (b != 255) + { + fluxmap.appendInterval( + a2rTicksSinceLastPulse * clock / NS_PER_TICK); + fluxmap.appendPulse(); + a2rTicksSinceLastPulse = 0; + } + } + + return fluxmap.rawBytes(); +} + +static Bytes fluxEngineToApplesauceWriteData( + const Bytes& fldata, nanoseconds_t clock) +{ + Fluxmap fluxmap(fldata); + FluxmapReader fmr(fluxmap); + Bytes asdata; + ByteWriter bw(asdata); + + while (!fmr.eof()) + { + unsigned ticks; + if (!fmr.findEvent(F_BIT_PULSE, ticks)) + break; + + uint32_t applesauceTicks = ticks * NS_PER_TICK / clock; + while (applesauceTicks >= 0xffff) + { + bw.write_le16(0xffff); + applesauceTicks -= 0xffff; + } + if (applesauceTicks == 0) + error("bad data!"); + bw.write_le16(applesauceTicks); + } + + bw.write_le16(0); + return asdata; +} + class ApplesauceException : public ErrorException { public: @@ -33,6 +86,8 @@ class ApplesauceUsb : public USB "Applesauce device not responding (expected 'Applesauce', got " "'{}')", s); + + doCommand("client:v2"); } ~ApplesauceUsb() @@ -173,52 +228,70 @@ class ApplesauceUsb : public USB { if (hardSectorThreshold != 0) error("hard sectors are currently unsupported on the Applesauce"); + bool shortRead = readTime < 400e6; + warning( + "applesauce: timed reads not supported; using read of {} " + "revolutions", + shortRead ? "1.25" : "2.25"); connect(); doCommand(fmt::format("head:side{}", side)); - doCommand(synced ? "sync:on" : "sync:off"); + doCommand("sync:on"); doCommand("data:clear"); - doCommandX("disk:read"); + std::string r = doCommandX(shortRead ? "disk:read" : "disk:xread"); + auto rsplit = split(r, '|'); + if (rsplit.size() < 2) + error("unrecognised Applesauce response to disk:read: '{}'", r); - int bufferSize = std::stoi(sendrecv("data:?size")); + int bufferSize = std::stoi(rsplit[0]); + nanoseconds_t tickSize = std::stod(rsplit[1]) / 1e3; doCommand(fmt::format("data:<{}", bufferSize)); Bytes rawData = _serial->readBytes(bufferSize); - return applesauceToFluxEngine(rawData); + Bytes b = applesauceReadDataToFluxEngine(rawData, tickSize); + return b; + } + +private: + void checkWritable() + { + if (sendrecv("disk:?write") == "-") + error("cannot write --- disk is write protected"); + if (sendrecv("?safe") == "+") + error("cannot write --- Applesauce 'safe' switch is on"); } +public: void write(int side, const Bytes& fldata, nanoseconds_t hardSectorThreshold) override { if (hardSectorThreshold != 0) error("hard sectors are currently unsupported on the Applesauce"); - - if (sendrecv("disk:?write") == "-") - error("cannot write --- disk is write protected"); - if (sendrecv("?safe") == "+") - error("cannot write --- Applesauce 'safe' switch is on"); + checkWritable(); connect(); doCommand(fmt::format("head:side{}", side)); doCommand("sync:on"); + doCommand("disk:wipe"); doCommand("data:clear"); + doCommand("disk:wclear"); - Bytes asdata = fluxEngineToApplesauce(fldata); + Bytes asdata = + fluxEngineToApplesauceWriteData(fldata, _config.write_clock()); doCommand(fmt::format("data:>{}", asdata.size())); _serial->write(asdata); checkCommandResult(_serial->readLine()); - - doCommand("disk:wclear"); - doCommand("disk:wcmd"); - doCommandX("disk:write"); + doCommand("disk:wcmd0,0"); + doCommand("disk:write"); } void erase(int side, nanoseconds_t hardSectorThreshold) override { if (hardSectorThreshold != 0) error("hard sectors are currently unsupported on the Applesauce"); + checkWritable(); connect(); doCommand(fmt::format("disk:side{}", side)); diff --git a/lib/usb/usb.proto b/lib/usb/usb.proto index 14f865fcc..873a3e727 100644 --- a/lib/usb/usb.proto +++ b/lib/usb/usb.proto @@ -21,6 +21,8 @@ message ApplesauceProto { [(help) = "Applesauce serial port to use"]; optional bool verbose = 2 [(help) = "Enable verbose protocol logging", default = false]; + optional float write_clock = 3 + [(help) = "Write clock speed", default = 22.5]; } message UsbProto { From 045e5e7303c7afcc499850d1d0c8b749ef20ce29 Mon Sep 17 00:00:00 2001 From: David Given Date: Mon, 30 Sep 2024 22:02:09 +0200 Subject: [PATCH 11/15] Rename write_clock to write_clock_ns, because it is. Tweak the default value for correctness. --- lib/usb/applesauceusb.cc | 4 ++-- lib/usb/usb.proto | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/usb/applesauceusb.cc b/lib/usb/applesauceusb.cc index 8e24af51e..13dfefb41 100644 --- a/lib/usb/applesauceusb.cc +++ b/lib/usb/applesauceusb.cc @@ -52,7 +52,7 @@ static Bytes fluxEngineToApplesauceWriteData( if (!fmr.findEvent(F_BIT_PULSE, ticks)) break; - uint32_t applesauceTicks = ticks * NS_PER_TICK / clock; + uint32_t applesauceTicks = (double)ticks * NS_PER_TICK / clock; while (applesauceTicks >= 0xffff) { bw.write_le16(0xffff); @@ -279,7 +279,7 @@ class ApplesauceUsb : public USB doCommand("disk:wclear"); Bytes asdata = - fluxEngineToApplesauceWriteData(fldata, _config.write_clock()); + fluxEngineToApplesauceWriteData(fldata, _config.write_clock_ns()); doCommand(fmt::format("data:>{}", asdata.size())); _serial->write(asdata); checkCommandResult(_serial->readLine()); diff --git a/lib/usb/usb.proto b/lib/usb/usb.proto index 873a3e727..610b98018 100644 --- a/lib/usb/usb.proto +++ b/lib/usb/usb.proto @@ -21,8 +21,8 @@ message ApplesauceProto { [(help) = "Applesauce serial port to use"]; optional bool verbose = 2 [(help) = "Enable verbose protocol logging", default = false]; - optional float write_clock = 3 - [(help) = "Write clock speed", default = 22.5]; + optional float write_clock_ns = 3 + [(help) = "Write clock speed", default = 22.45]; } message UsbProto { From d00fba02a0ebd3ad5597b6f6b14832185b39ae62 Mon Sep 17 00:00:00 2001 From: David Given Date: Mon, 30 Sep 2024 22:03:17 +0200 Subject: [PATCH 12/15] Rename greaseWeazle to greaseweazle (as it needs doing). --- lib/usb/greaseweazle.cc | 2 +- lib/usb/greaseweazle.h | 2 +- lib/usb/greaseweazleusb.cc | 2 +- tests/greaseweazle.cc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/usb/greaseweazle.cc b/lib/usb/greaseweazle.cc index 8b418e4f7..b03135552 100644 --- a/lib/usb/greaseweazle.cc +++ b/lib/usb/greaseweazle.cc @@ -53,7 +53,7 @@ Bytes fluxEngineToGreaseweazle(const Bytes& fldata, nanoseconds_t clock) return gwdata; } -Bytes greaseWeazleToFluxEngine(const Bytes& gwdata, nanoseconds_t clock) +Bytes greaseweazleToFluxEngine(const Bytes& gwdata, nanoseconds_t clock) { Bytes fldata; ByteReader br(gwdata); diff --git a/lib/usb/greaseweazle.h b/lib/usb/greaseweazle.h index 273816305..a6063d688 100644 --- a/lib/usb/greaseweazle.h +++ b/lib/usb/greaseweazle.h @@ -10,7 +10,7 @@ #define EP_IN 0x83 extern Bytes fluxEngineToGreaseweazle(const Bytes& fldata, nanoseconds_t clock); -extern Bytes greaseWeazleToFluxEngine(const Bytes& gwdata, nanoseconds_t clock); +extern Bytes greaseweazleToFluxEngine(const Bytes& gwdata, nanoseconds_t clock); extern Bytes stripPartialRotation(const Bytes& fldata); /* Copied from diff --git a/lib/usb/greaseweazleusb.cc b/lib/usb/greaseweazleusb.cc index 0566f7706..042e18fc8 100644 --- a/lib/usb/greaseweazleusb.cc +++ b/lib/usb/greaseweazleusb.cc @@ -356,7 +356,7 @@ class GreaseweazleUsb : public USB Bytes fldata = greaseWeazleToFluxEngine(buffer, _clock); if (synced) fldata = stripPartialRotation(fldata); - return fldata; + return fldata;reaseweazle } void write(int side, diff --git a/tests/greaseweazle.cc b/tests/greaseweazle.cc index 8ddbc8dda..e0b9c23b8 100644 --- a/tests/greaseweazle.cc +++ b/tests/greaseweazle.cc @@ -11,7 +11,7 @@ static void test_convert(const Bytes& gwbytes, const Bytes& flbytes) { - Bytes gwtoflbytes = greaseWeazleToFluxEngine(gwbytes, 2 * NS_PER_TICK); + Bytes gwtoflbytes = greaseweazleToFluxEngine(gwbytes, 2 * NS_PER_TICK); Bytes fltogwbytes = fluxEngineToGreaseweazle(flbytes, 2 * NS_PER_TICK); if (gwtoflbytes != flbytes) From 33f3c97b03e0a10f77ebb93cd32345f81294047a Mon Sep 17 00:00:00 2001 From: David Given Date: Tue, 1 Oct 2024 21:30:53 +0200 Subject: [PATCH 13/15] Crudely hack in a lock against writing, as I'll have to wait for the v3 firmware. --- lib/usb/applesauceusb.cc | 17 ++++++++++++----- lib/usb/greaseweazleusb.cc | 4 ++-- lib/usb/usb.proto | 2 -- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/usb/applesauceusb.cc b/lib/usb/applesauceusb.cc index 13dfefb41..18d402738 100644 --- a/lib/usb/applesauceusb.cc +++ b/lib/usb/applesauceusb.cc @@ -10,6 +10,9 @@ #include "lib/decoders/fluxmapreader.h" #include +class ApplesauceUsb; +static ApplesauceUsb* instance; + static uint32_t ss_rand_next(uint32_t x) { return (x & 1) ? (x >> 1) ^ 0x80000062 : x >> 1; @@ -38,8 +41,7 @@ static Bytes applesauceReadDataToFluxEngine( return fluxmap.rawBytes(); } -static Bytes fluxEngineToApplesauceWriteData( - const Bytes& fldata, nanoseconds_t clock) +static Bytes fluxEngineToApplesauceWriteData(const Bytes& fldata) { Fluxmap fluxmap(fldata); FluxmapReader fmr(fluxmap); @@ -52,7 +54,7 @@ static Bytes fluxEngineToApplesauceWriteData( if (!fmr.findEvent(F_BIT_PULSE, ticks)) break; - uint32_t applesauceTicks = (double)ticks * NS_PER_TICK / clock; + uint32_t applesauceTicks = (double)ticks * NS_PER_TICK; while (applesauceTicks >= 0xffff) { bw.write_le16(0xffff); @@ -88,6 +90,8 @@ class ApplesauceUsb : public USB s); doCommand("client:v2"); + + atexit([](){ delete instance; }); } ~ApplesauceUsb() @@ -260,6 +264,8 @@ class ApplesauceUsb : public USB error("cannot write --- disk is write protected"); if (sendrecv("?safe") == "+") error("cannot write --- Applesauce 'safe' switch is on"); + if (sendrecv("?vers") < "0300") + error("cannot write --- need Applesauce firmware 2.0 or above"); } public: @@ -279,7 +285,7 @@ class ApplesauceUsb : public USB doCommand("disk:wclear"); Bytes asdata = - fluxEngineToApplesauceWriteData(fldata, _config.write_clock_ns()); + fluxEngineToApplesauceWriteData(fldata); doCommand(fmt::format("data:>{}", asdata.size())); _serial->write(asdata); checkCommandResult(_serial->readLine()); @@ -320,7 +326,8 @@ class ApplesauceUsb : public USB USB* createApplesauceUsb(const std::string& port, const ApplesauceProto& config) { - return new ApplesauceUsb(port, config); + instance = new ApplesauceUsb(port, config); + return instance; } // vim: sw=4 ts=4 et diff --git a/lib/usb/greaseweazleusb.cc b/lib/usb/greaseweazleusb.cc index 042e18fc8..69be470f1 100644 --- a/lib/usb/greaseweazleusb.cc +++ b/lib/usb/greaseweazleusb.cc @@ -353,10 +353,10 @@ class GreaseweazleUsb : public USB do_command({CMD_GET_FLUX_STATUS, 2}); - Bytes fldata = greaseWeazleToFluxEngine(buffer, _clock); + Bytes fldata = greaseweazleToFluxEngine(buffer, _clock); if (synced) fldata = stripPartialRotation(fldata); - return fldata;reaseweazle + return fldata; } void write(int side, diff --git a/lib/usb/usb.proto b/lib/usb/usb.proto index 610b98018..14f865fcc 100644 --- a/lib/usb/usb.proto +++ b/lib/usb/usb.proto @@ -21,8 +21,6 @@ message ApplesauceProto { [(help) = "Applesauce serial port to use"]; optional bool verbose = 2 [(help) = "Enable verbose protocol logging", default = false]; - optional float write_clock_ns = 3 - [(help) = "Write clock speed", default = 22.45]; } message UsbProto { From 3a0c02459db5ac1d4c689698df3246b1552c61d3 Mon Sep 17 00:00:00 2001 From: David Given Date: Tue, 1 Oct 2024 21:50:33 +0200 Subject: [PATCH 14/15] Add documentation for the Applesauce. --- README.md | 21 +++++++------- doc/applesauce.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++ doc/using.md | 6 ++-- 3 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 doc/applesauce.md diff --git a/README.md b/README.md index f02797ec1..736653872 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,8 @@ FluxEngine (If you're reading this on GitHub, the formatting's a bit messed up. [Try the version on cowlark.com instead.](http://cowlark.com/fluxengine/)) -**Breaking news!** As of 2022-09-09, there's new [filesystem -support](doc/filesystem.md). Read (and sometimes write) files directly from -(and to) your disks, with eight different file systems! It works in the GUI, -too, which is available for Linux (and other Unix clones), Windows and OSX. See -the details below. +**Breaking news!** As of 2024-10-01, the FluxEngine client software works +(to a point) with [Applesauce](doc/applesauce.md) hardware.
screenshot of the GUI in action @@ -35,12 +32,14 @@ Don't believe me? Watch the demo reel!
**New!** The FluxEngine client software now works with -[Greaseweazle](https://github.com/keirf/Greaseweazle/wiki) hardware. So, if you -can't find a PSoC5 development kit, or don't want to use the Cypress Windows -tools for programming it, you can use one of these instead. Very nearly all -FluxEngine features are available with the Greaseweazle and it works out-of-the -box. See the [dedicated Greaseweazle documentation page](doc/greaseweazle.md) -for more information. +[Greaseweazle](https://github.com/keirf/Greaseweazle/wiki) and +[Applesauce](https://applesaucefdc.com/) hardware. So, if you can't find a PSoC5 +development kit, or don't want to use the Cypress Windows tools for programming +it, you can use one of these instead. Very nearly all FluxEngine features are +available with the Greaseweazle and it works out-of-the box; the Applesauce is a +bit less supported but still works. See the [dedicated Greaseweazle +documentation page](doc/greaseweazle.md) or the [Applesauce +page](doc/applesauce.md) for more information. Where? ------ diff --git a/doc/applesauce.md b/doc/applesauce.md new file mode 100644 index 000000000..3f7b55a74 --- /dev/null +++ b/doc/applesauce.md @@ -0,0 +1,72 @@ +Using the FluxEngine client software with Applesauce hardware +=============================================================== + +The FluxEngine isn't the only project which does this; another one is the +[Applesauce](https://applesaucefdc.com/), a proprietary but feature-rich +off-the-shelf imaging device. Its native client (which is a lot better than +FluxEngine) only works on OSX, so if you want to use it anywhere else, +the FluxEngine client works. + +The Applesauce works rather differently to the FluxEngine hardware or the +[Greaseweazle](greaseweazle.md), so there are some caveats. + + - Rather than streaming the flux data from the device to the PC, the Applesauce + has a fixed buffer in RAM used to capture a complete image of a track. This is + then downloaded later. The advantage is that USB bandwidth isn't an issue; the + downside is that the buffer can only hold so much data. In fact, the Applesauce + can only capture 1.25 revolutions or 2.25 revolutions, nothing else. When used + with the FluxEngine the capture time will be ignored apart from used to + determine whether you want a 'long' or 'short' capture. + + - The current (v2) firmware only supports reading, not writing (via clients + other than the official one, of course). The new (v3) firmware will support + writing, but it's not out yet, so for the time being the FluxEngine client is + read only. + + - You can only do synchronous reads, i.e., reads starting from the index mark. + +Other than this, the FluxEngine software supports the Applesauce almost +out-of-the-box --- just plug it in and nearly everything should work. The +FluxEngine software will autodetect it. If you have more than one device plugged +in, use `--usb.serial=` to specify which one you want to use. + +I am aware that having _software_ called FluxEngine and _hardware_ called +FluxEngine makes things complicated when you're not using the FluxEngine client +software with a FluxEngine board, but I'm afraid it's too late to change that +now. Sorry. + +What works +---------- + +Supported features with the Greaseweazle include: + + - simple reading of disks, seeking etc + - erasing disks + - hard sectored disks + - determining disk rotation speed + - normal IBM buses + +I don't know what happens if you try to use an Apple Superdrive or a Apple II +disk with FluxEngine. If you've got one, [please get in +touch](https://github.com/davidgiven/fluxengine/issues/new)! + +What doesn't work +----------------- + + - voltage measurement + - writing + +Who to contact +-------------- + +I want to make it clear that the FluxEngine code is _not_ supported by the +Applesauce team. If you have any problems, please [contact +me](https://github.com/davidgiven/fluxengine/issues/new) and not them. + +In addition, the Applesauce release cycle is not synchronised to the +FluxEngine release cycle, so it's possible you'll have a version of the +Applesauce firmware which is not supported by FluxEngine. Hopefully, it'll +detect this and complain. Again, [file an +issue](https://github.com/davidgiven/fluxengine/issues/new) and I'll look into +it. + diff --git a/doc/using.md b/doc/using.md index 6008cec9b..646b86766 100644 --- a/doc/using.md +++ b/doc/using.md @@ -50,9 +50,9 @@ file while changing the decoder options, to save disk wear. It's also much faste ### Connecting it up -To use, simply plug your FluxEngine (or [Greaseweazle](greaseweazle.md)) into -your computer and run the client. If a single device is plugged in, it will be -automatically detected and used. +To use, simply plug your FluxEngine (or [Greaseweazle](greaseweazle.md) or +[Applesauce](applesauce.md)) into your computer and run the client. If a single +device is plugged in, it will be automatically detected and used. If _more_ than one device is plugged in, you need to specify which one to use with the `--usb.serial` parameter, which takes the device serial number as a From 65fb3e9e76f6abb161e52dd9c15197d70fe5fab0 Mon Sep 17 00:00:00 2001 From: David Given Date: Tue, 1 Oct 2024 22:26:02 +0200 Subject: [PATCH 15/15] Detect index marks and interleave them into the flux stream. --- lib/usb/applesauceusb.cc | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/lib/usb/applesauceusb.cc b/lib/usb/applesauceusb.cc index 18d402738..5c8cf8977 100644 --- a/lib/usb/applesauceusb.cc +++ b/lib/usb/applesauceusb.cc @@ -18,23 +18,28 @@ static uint32_t ss_rand_next(uint32_t x) return (x & 1) ? (x >> 1) ^ 0x80000062 : x >> 1; } -static Bytes applesauceReadDataToFluxEngine( - const Bytes& asdata, nanoseconds_t clock) +static Bytes applesauceReadDataToFluxEngine(const Bytes& asdata, + nanoseconds_t clock, + const std::vector& indexMarks) { ByteReader br(asdata); Fluxmap fluxmap; + auto indexIt = indexMarks.begin(); + fluxmap.appendIndex(); - double a2rTicksSinceLastPulse = 0; + unsigned totalTicks = 0; while (!br.eof()) { uint8_t b = br.read_8(); - a2rTicksSinceLastPulse += b; + fluxmap.appendInterval(b * clock / NS_PER_TICK); if (b != 255) - { - fluxmap.appendInterval( - a2rTicksSinceLastPulse * clock / NS_PER_TICK); fluxmap.appendPulse(); - a2rTicksSinceLastPulse = 0; + + totalTicks += b; + if ((indexIt != indexMarks.end()) && (totalTicks > *indexIt)) + { + fluxmap.appendIndex(); + indexIt++; } } @@ -91,7 +96,11 @@ class ApplesauceUsb : public USB doCommand("client:v2"); - atexit([](){ delete instance; }); + atexit( + []() + { + delete instance; + }); } ~ApplesauceUsb() @@ -242,7 +251,7 @@ class ApplesauceUsb : public USB doCommand(fmt::format("head:side{}", side)); doCommand("sync:on"); doCommand("data:clear"); - std::string r = doCommandX(shortRead ? "disk:read" : "disk:xread"); + std::string r = doCommandX(shortRead ? "disk:read" : "disk:readx"); auto rsplit = split(r, '|'); if (rsplit.size() < 2) error("unrecognised Applesauce response to disk:read: '{}'", r); @@ -250,10 +259,14 @@ class ApplesauceUsb : public USB int bufferSize = std::stoi(rsplit[0]); nanoseconds_t tickSize = std::stod(rsplit[1]) / 1e3; + std::vector indexMarks; + for (auto i = rsplit.begin() + 2; i != rsplit.end(); i++) + indexMarks.push_back(std::stoi(*i)); + doCommand(fmt::format("data:<{}", bufferSize)); Bytes rawData = _serial->readBytes(bufferSize); - Bytes b = applesauceReadDataToFluxEngine(rawData, tickSize); + Bytes b = applesauceReadDataToFluxEngine(rawData, tickSize, indexMarks); return b; } @@ -284,8 +297,7 @@ class ApplesauceUsb : public USB doCommand("data:clear"); doCommand("disk:wclear"); - Bytes asdata = - fluxEngineToApplesauceWriteData(fldata); + Bytes asdata = fluxEngineToApplesauceWriteData(fldata); doCommand(fmt::format("data:>{}", asdata.size())); _serial->write(asdata); checkCommandResult(_serial->readLine());