From 08c1671f21002161e1d77caaea0fd5f0b2b2f35b Mon Sep 17 00:00:00 2001 From: Thomas Daede Date: Thu, 2 Dec 2021 02:47:18 -0800 Subject: [PATCH 01/37] Add Make target to build compdb. --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 841c7dca..dd8c95a5 100644 --- a/Makefile +++ b/Makefile @@ -64,3 +64,5 @@ clean: @mkdir -p $(OBJDIR) @sh $< > $@ +compdb: + @ninja -f .obj/build.ninja -t compdb > compile_commands.json From ce1daf6a2b14dba48ad93521f2e22aef9372f3d4 Mon Sep 17 00:00:00 2001 From: Thomas Daede Date: Sat, 4 Dec 2021 03:33:02 -0800 Subject: [PATCH 02/37] Add option to rescale flux sink pulses. Note that this does not affect the decoder used for verify, but at least for tame rescaling the decoders are still able to lock. This is a precursor to automatic scaling via the disk speed measurement. --- doc/using.md | 8 ++++++++ lib/fluxmap.cc | 19 +++++++++++++++++++ lib/fluxmap.h | 1 + lib/fluxsink/fluxsink.proto | 1 + lib/writer.cc | 2 ++ 5 files changed, 31 insertions(+) diff --git a/doc/using.md b/doc/using.md index e1c68e5e..ac53fefd 100644 --- a/doc/using.md +++ b/doc/using.md @@ -338,6 +338,14 @@ behaviour. has no effect on the _drive_, so it doesn't help with flippy disks, but is useful for using very old drives with FluxEngine itself. If you use this option, then any index marks in the sampled flux are, of course, garbage. + + - `--flux_sink.rescale=X` + + When writing a floppy on a drive that doesn't match the source file rpm, + the flux periods can be scaled to compensate. For example, to write + a PC-98 1.2MB (360rpm) floppy using a 300rpm floppy drive: + + `--flux_sink.rescale=1.2` ## Visualisation diff --git a/lib/fluxmap.cc b/lib/fluxmap.cc index d89ecab1..f1b814ae 100644 --- a/lib/fluxmap.cc +++ b/lib/fluxmap.cc @@ -108,3 +108,22 @@ std::vector Fluxmap::split() { maps.push_back(map); return maps; } + +void Fluxmap::rescale(double scale) { + if (scale != 1.0) { + auto bytesOrig = _bytes; + _bytes = Bytes(); + _duration = 0; + _ticks = 0; + int lastEvent = 0; + for (unsigned i=0; i split(); + void rescale(double scale); private: uint8_t& findLastByte(); diff --git a/lib/fluxsink/fluxsink.proto b/lib/fluxsink/fluxsink.proto index 07963dcc..bfe323b1 100644 --- a/lib/fluxsink/fluxsink.proto +++ b/lib/fluxsink/fluxsink.proto @@ -29,6 +29,7 @@ message Fl2FluxSinkProto { } message FluxSinkProto { + optional double rescale = 7 [ default = 1.0, (help) = "amount to multiply pulse periods by" ]; oneof dest { string fluxfile = 1 [(help) = "name of destination flux file"]; HardwareFluxSinkProto drive = 2; diff --git a/lib/writer.cc b/lib/writer.cc index 3fdc3ae4..e80fb531 100644 --- a/lib/writer.cc +++ b/lib/writer.cc @@ -40,6 +40,7 @@ void writeTracks( } else { + fluxmap->rescale(config.flux_sink().rescale()); /* Precompensation actually seems to make things worse, so let's leave * it disabled for now. */ //fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS, 2); @@ -77,6 +78,7 @@ void writeTracksAndVerify( } else { + fluxmap->rescale(config.flux_sink().rescale()); std::sort(sectors.begin(), sectors.end(), sectorPointerSortPredicate); for (int retry = 0;; retry++) From 5ada533b06fcc08c0622b90fe413226eefe8d2dc Mon Sep 17 00:00:00 2001 From: "Howard M. Harte" Date: Sat, 25 Dec 2021 09:10:53 -0800 Subject: [PATCH 03/37] reader: Fix conflicting sector error message. The conflicting sector error message incorrectly displays the track instead of the sector in conflict. --- lib/reader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/reader.cc b/lib/reader.cc index 6ae2167e..659315a2 100644 --- a/lib/reader.cc +++ b/lib/reader.cc @@ -52,7 +52,7 @@ static std::set> collect_sectors(std::set(sectorid), std::get<1>(sectorid), std::get<2>(sectorid)); + std::get<2>(sectorid)); replacing->status = replacement->status = Sector::CONFLICT; } } From d120790da743b547796997269062a35a224e455e Mon Sep 17 00:00:00 2001 From: David Given Date: Fri, 31 Dec 2021 20:24:51 +0000 Subject: [PATCH 04/37] Update firmware to version 16. --- .../CortexM3/ARM_GCC_541/Release/FluxEngine.hex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex b/FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex index 51b9850d..4f737a9a 100644 --- a/FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex +++ b/FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex @@ -21,7 +21,7 @@ :4005000014BF0A461A46002808BF19468448FFF7D8FE0321084602F031F9264602F04EF994F8643043B1EA6EEB689B1A41F28832934201D9FFF7FCFE00F07EFF18B9794820 :40054000FFF7BFFE04E000F07DFF0028F7D10BE000F072FF10B902F031F9F9E77248FFF7B0FE032001F098F8032000F077FF0128D4D16E48FFF7EEFE6D490320FFF734FE85 :4005800094F876106B48FFF79CFE94F87630023B142B00F2D683DFE813F01500D4031E00D4032400D4035000D4037600D403D900D403C101D4030803D4032C03D40333036C -:4005C000D4034D0303238DF820308DF821300F238DF822302AE394F87800FFF703FF564B21E340F2DC57FFF7DFFE00232375E068227D02F0FF012AB9EB681B1ABB42F7DD44 +:4005C000D4034D0303238DF820308DF8213010238DF822302AE394F87800FFF703FF564B21E340F2DC57FFF7DFFE00232375E068227D02F0FF012AB9EB681B1ABB42F7DD43 :400600000B4611E083B10022174696F87810F068277594F814E0BEF1000F02D1EB681B1AF7E701329142F3DA07228DF8202004228DF82120ADF82230F8E20220FFF782FD2F :400640004FF000080DF1200A02F0B8F84FF480790027C9EB0803DA1907F80A200137402FF9D10220FFF76EFD3A465146022000F04DFFB9F10109EBD108F10108B8F1400F58 :40068000E2D12E4B38E04FF0010A4FF000080DF1200B02F093F84FF0000959460120FFF7A3FD08EB090300270493049B1BF807203B44DBB29A4209D08DE80C0041463B4641 @@ -4615,12 +4615,12 @@ :0200000490105A :04000000BC90ACAF55 :0200000490303A -:0200000090CF9F +:0200000090D09E :0200000490402A :4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0 :400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080 :400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040 :4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 :0200000490501A -:0C00000000012E16106900002E30A138FF +:0C00000000012E16106900002E30A139FE :00000001FF \ No newline at end of file From a32ea6e5f8d7e0bd9e93e110e098b52ea7e205db Mon Sep 17 00:00:00 2001 From: David Given Date: Sat, 1 Jan 2022 22:45:27 +0100 Subject: [PATCH 05/37] Add support for selecting the Shugart or IBMPC GreaseWeazle bus types. --- doc/greaseweazle.md | 2 ++ lib/usb/greaseweazleusb.cc | 13 ++++++++----- lib/usb/usb.cc | 2 +- lib/usb/usb.h | 3 ++- lib/usb/usb.proto | 13 +++++++++++++ 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/doc/greaseweazle.md b/doc/greaseweazle.md index 753dcbda..b1ba8f02 100644 --- a/doc/greaseweazle.md +++ b/doc/greaseweazle.md @@ -30,6 +30,8 @@ Supported features with the GreaseWeazle include: - simple reading and writing of disks, seeking etc - erasing disks - determining disk rotation speed + - both Shugart and normal IBM buses (via + `--usb.greaseweazle.bus_type=SHUGART` or `IBMPC`; the default is `IBMPC`) What doesn't work ----------------- diff --git a/lib/usb/greaseweazleusb.cc b/lib/usb/greaseweazleusb.cc index 9d3efe07..5e7ede07 100644 --- a/lib/usb/greaseweazleusb.cc +++ b/lib/usb/greaseweazleusb.cc @@ -4,6 +4,7 @@ #include "fluxmap.h" #include "bytes.h" #include "fmt/format.h" +#include "lib/usb/usb.pb.h" #include "greaseweazle.h" #include "serial.h" @@ -61,8 +62,9 @@ private: } public: - GreaseWeazleUsb(const std::string& port): - _serial(SerialPort::openSerialPort(port)) + GreaseWeazleUsb(const std::string& port, const GreaseWeazleProto& config): + _serial(SerialPort::openSerialPort(port)), + _config(config) { int version = getVersion(); if (version >= 29) @@ -79,7 +81,7 @@ public: /* Configure the hardware. */ - do_command({ CMD_SET_BUS_TYPE, 3, BUS_IBMPC }); + do_command({ CMD_SET_BUS_TYPE, 3, (uint8_t)config.bus_type() }); } int getVersion() @@ -384,14 +386,15 @@ private: }; std::unique_ptr _serial; + const GreaseWeazleProto& _config; int _version; nanoseconds_t _clock; nanoseconds_t _revolutions; }; -USB* createGreaseWeazleUsb(const std::string& port) +USB* createGreaseWeazleUsb(const std::string& port, const GreaseWeazleProto& config) { - return new GreaseWeazleUsb(port); + return new GreaseWeazleUsb(port, config); } // vim: sw=4 ts=4 et diff --git a/lib/usb/usb.cc b/lib/usb/usb.cc index 32e6acae..079272c7 100644 --- a/lib/usb/usb.cc +++ b/lib/usb/usb.cc @@ -66,7 +66,7 @@ USB* get_usb_impl() case GREASEWEAZLE_ID: std::cerr << fmt::format("Using GreaseWeazle {} on {}\n", candidate->serial, candidate->serialPort); - return createGreaseWeazleUsb(candidate->serialPort); + return createGreaseWeazleUsb(candidate->serialPort, config.usb().greaseweazle()); default: Error() << "internal"; diff --git a/lib/usb/usb.h b/lib/usb/usb.h index 28605770..f76baad8 100644 --- a/lib/usb/usb.h +++ b/lib/usb/usb.h @@ -5,6 +5,7 @@ #include "flags.h" class Fluxmap; +class GreaseWeazleProto; namespace libusbp { class device; } class USB @@ -33,7 +34,7 @@ protected: extern USB& getUsb(); extern USB* createFluxengineUsb(libusbp::device& device); -extern USB* createGreaseWeazleUsb(const std::string& serialPort); +extern USB* createGreaseWeazleUsb(const std::string& serialPort, const GreaseWeazleProto& config); static inline int usbGetVersion() { return getUsb().getVersion(); } static inline void usbRecalibrate() { getUsb().recalibrate(); } diff --git a/lib/usb/usb.proto b/lib/usb/usb.proto index f78f5fd2..f3d7c4b7 100644 --- a/lib/usb/usb.proto +++ b/lib/usb/usb.proto @@ -2,9 +2,22 @@ syntax = "proto2"; import "lib/common.proto"; +message GreaseWeazleProto { + enum BusType { + BUSTYPE_INVALID = 0; + IBMPC = 1; + SHUGART = 2; + }; + + optional BusType bus_type = 1 + [(help) = "which FDD bus type is in use", default = IBMPC]; +} + message UsbProto { oneof device { string serial = 1 [(help) = "serial number of FluxEngine or GreaseWeazle device to use"]; } + + optional GreaseWeazleProto greaseweazle = 2 [(help) = "GreaseWeazle-specific options"]; } From 18af881fe533975b06ba6100e2d03eca32b93544 Mon Sep 17 00:00:00 2001 From: David Given Date: Tue, 4 Jan 2022 23:49:39 +0100 Subject: [PATCH 06/37] Attach the underlying raw records to Sector structures; add a .raw exporter for getting the MFM/FM/GCR stream. --- doc/using.md | 22 +++++++--- lib/decoders/decoders.cc | 1 + lib/imagewriter/imagewriter.cc | 6 ++- lib/imagewriter/imagewriter.h | 16 +++---- lib/imagewriter/imagewriter.proto | 2 + lib/imagewriter/rawimagewriter.cc | 73 +++++++++++++++++++++++++++++++ lib/sector.h | 3 ++ mkninja.sh | 1 + 8 files changed, 106 insertions(+), 18 deletions(-) create mode 100644 lib/imagewriter/rawimagewriter.cc diff --git a/doc/using.md b/doc/using.md index 5cecf464..6086c71e 100644 --- a/doc/using.md +++ b/doc/using.md @@ -282,14 +282,22 @@ FluxEngine also supports a number of file system image formats. When using the - `` - Write to a [LDBS generic image - file](https://www.seasip.info/Unix/LibDsk/ldbs.html). **Write only.** - - - `` + Write to a [LDBS generic image + file](https://www.seasip.info/Unix/LibDsk/ldbs.html). **Write only.** - Write to a [D64 image - file](http://unusedino.de/ec64/technical/formats/d64.html), commonly used - by Commodore 64 emulators. **Write only.** + - `` + + Write to a [D64 image + file](http://unusedino.de/ec64/technical/formats/d64.html), commonly used by + Commodore 64 emulators. **Write only.** + + - `` + + Write undecoded data to a raw binary file. **Write only.** This gives you the + underlying MFM, FM or GCR stream, without actually doing the decode into + user-visible bytes. However, the decode is still done in order to check for + correctness. Individual records are separated by three `\\0` bytes and tracks + are seperated by four `\\0` bytes; tracks are emitted in CHS order. ### High density disks diff --git a/lib/decoders/decoders.cc b/lib/decoders/decoders.cc index 0dee46e4..c206c1f9 100644 --- a/lib/decoders/decoders.cc +++ b/lib/decoders/decoders.cc @@ -132,6 +132,7 @@ void AbstractDecoder::pushRecord(const Fluxmap::Position& start, const Fluxmap:: auto record = std::make_shared(); _trackdata->records.push_back(record); + _sector->records.push_back(record); record->startTime = start.ns(); record->endTime = end.ns(); diff --git a/lib/imagewriter/imagewriter.cc b/lib/imagewriter/imagewriter.cc index e8535acf..1a23ba73 100644 --- a/lib/imagewriter/imagewriter.cc +++ b/lib/imagewriter/imagewriter.cc @@ -29,6 +29,9 @@ std::unique_ptr ImageWriter::create(const ImageWriterProto& config) case ImageWriterProto::kNsi: return ImageWriter::createNsiImageWriter(config); + case ImageWriterProto::kRaw: + return ImageWriter::createRawImageWriter(config); + default: Error() << "bad output image config"; return std::unique_ptr(); @@ -45,8 +48,9 @@ void ImageWriter::updateConfigForFilename(ImageWriterProto* proto, const std::st {".diskcopy", [&]() { proto->mutable_diskcopy(); }}, {".img", [&]() { proto->mutable_img(); }}, {".ldbs", [&]() { proto->mutable_ldbs(); }}, - {".st", [&]() { proto->mutable_img(); }}, {".nsi", [&]() { proto->mutable_nsi(); }}, + {".raw", [&]() { proto->mutable_raw(); }}, + {".st", [&]() { proto->mutable_img(); }}, {".xdf", [&]() { proto->mutable_img(); }}, }; diff --git a/lib/imagewriter/imagewriter.h b/lib/imagewriter/imagewriter.h index 321b9355..143c4af9 100644 --- a/lib/imagewriter/imagewriter.h +++ b/lib/imagewriter/imagewriter.h @@ -14,16 +14,12 @@ public: static std::unique_ptr create(const ImageWriterProto& config); static void updateConfigForFilename(ImageWriterProto* proto, const std::string& filename); - static std::unique_ptr createImgImageWriter( - const ImageWriterProto& config); - static std::unique_ptr createLDBSImageWriter( - const ImageWriterProto& config); - static std::unique_ptr createD64ImageWriter( - const ImageWriterProto& config); - static std::unique_ptr createDiskCopyImageWriter( - const ImageWriterProto& config); - static std::unique_ptr createNsiImageWriter( - const ImageWriterProto& config); + static std::unique_ptr createD64ImageWriter(const ImageWriterProto& config); + static std::unique_ptr createDiskCopyImageWriter(const ImageWriterProto& config); + static std::unique_ptr createImgImageWriter(const ImageWriterProto& config); + static std::unique_ptr createLDBSImageWriter(const ImageWriterProto& config); + static std::unique_ptr createNsiImageWriter(const ImageWriterProto& config); + static std::unique_ptr createRawImageWriter(const ImageWriterProto& config); public: void printMap(const Image& sectors); diff --git a/lib/imagewriter/imagewriter.proto b/lib/imagewriter/imagewriter.proto index 6585e026..22490fbf 100644 --- a/lib/imagewriter/imagewriter.proto +++ b/lib/imagewriter/imagewriter.proto @@ -29,6 +29,7 @@ message LDBSOutputProto { message DiskCopyOutputProto {} message NsiOutputProto {} +message RawOutputProto {} message ImageWriterProto { optional string filename = 1 [(help) = "filename of output sector image"]; @@ -38,6 +39,7 @@ message ImageWriterProto { LDBSOutputProto ldbs = 4; DiskCopyOutputProto diskcopy = 5; NsiOutputProto nsi = 6; + RawOutputProto raw = 7; } } diff --git a/lib/imagewriter/rawimagewriter.cc b/lib/imagewriter/rawimagewriter.cc new file mode 100644 index 00000000..b97512cd --- /dev/null +++ b/lib/imagewriter/rawimagewriter.cc @@ -0,0 +1,73 @@ +#include "globals.h" +#include "flags.h" +#include "sector.h" +#include "imagewriter/imagewriter.h" +#include "fmt/format.h" +#include "decoders/decoders.h" +#include "image.h" +#include "arch/northstar/northstar.h" +#include "lib/imagewriter/imagewriter.pb.h" +#include +#include +#include + +class RawImageWriter : public ImageWriter +{ +public: + RawImageWriter(const ImageWriterProto& config): + ImageWriter(config) + {} + + void writeImage(const Image& image) + { + const Geometry& geometry = image.getGeometry(); + + size_t trackSize = geometry.numSectors * geometry.sectorSize; + + if (geometry.numTracks * trackSize == 0) { + std::cout << "RAW: no sectors in output; skipping image file generation." << std::endl; + return; + } + + std::cout << fmt::format("RAW: writing {} cylinders, {} sides\n", + geometry.numTracks, geometry.numSides); + + std::ofstream outputFile(_config.filename(), std::ios::out | std::ios::binary); + if (!outputFile.is_open()) + Error() << "RAW: cannot open output file"; + + unsigned sectorFileOffset; + for (int track = 0; track < geometry.numTracks * geometry.numSides; track++) + { + int side = (track < geometry.numTracks) ? 0 : 1; + + std::vector> records; + for (int sectorId = 0; sectorId < geometry.numSectors; sectorId++) + { + const auto& sector = image.get(track % geometry.numTracks, side, sectorId); + if (sector) + records.insert(records.end(), sector->records.begin(), sector->records.end()); + } + + std::sort(records.begin(), records.end(), + [&](std::shared_ptr left, std::shared_ptr right) { + return left->startTime < right->startTime; + }); + + for (const auto& record : records) + { + record->rawData.writeTo(outputFile); + Bytes(3).writeTo(outputFile); + } + Bytes(1).writeTo(outputFile); + } + } +}; + + +std::unique_ptr ImageWriter::createRawImageWriter( + const ImageWriterProto& config) +{ + return std::unique_ptr(new RawImageWriter(config)); +} + diff --git a/lib/sector.h b/lib/sector.h index 6ad2862e..1c90cefc 100644 --- a/lib/sector.h +++ b/lib/sector.h @@ -4,6 +4,8 @@ #include "bytes.h" #include "fluxmap.h" +class Record; + /* * Note that sectors here used zero-based numbering throughout (to make the * maths easier); traditionally floppy disk use 0-based track numbering and @@ -38,6 +40,7 @@ public: int logicalSide = 0; int logicalSector = 0; Bytes data; + std::vector> records; std::tuple key() const { return std::make_tuple(logicalTrack, logicalSide, logicalSector, status); } diff --git a/mkninja.sh b/mkninja.sh index d80f02ec..d0915e33 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -470,6 +470,7 @@ buildlibrary libbackend.a \ lib/imagewriter/imgimagewriter.cc \ lib/imagewriter/ldbsimagewriter.cc \ lib/imagewriter/nsiimagewriter.cc \ + lib/imagewriter/rawimagewriter.cc \ lib/ldbs.cc \ lib/proto.cc \ lib/reader.cc \ From c4e29e74b233f6fbcd57297e540758564de60863 Mon Sep 17 00:00:00 2001 From: David Given Date: Mon, 10 Jan 2022 23:49:25 +0100 Subject: [PATCH 07/37] Support HCS as well as CHS format in the image reader/writer. --- lib/imagereader/d88imagereader.cc | 1 - lib/imagereader/dimimagereader.cc | 1 - lib/imagereader/fdiimagereader.cc | 1 - lib/imagereader/imagereader.cc | 17 --------- lib/imagereader/imagereader.proto | 7 ++++ lib/imagereader/imagereaderimpl.h | 8 ----- lib/imagereader/imgimagereader.cc | 43 +++++++++++------------ lib/imagewriter/imgimagewriter.cc | 44 ++++++++++++------------ lib/imginputoutpututils.cc | 57 +++++++++++++++++++++++++++++++ lib/imginputoutpututils.h | 12 +++++++ mkninja.sh | 1 + 11 files changed, 119 insertions(+), 73 deletions(-) delete mode 100644 lib/imagereader/imagereaderimpl.h create mode 100644 lib/imginputoutpututils.cc create mode 100644 lib/imginputoutpututils.h diff --git a/lib/imagereader/d88imagereader.cc b/lib/imagereader/d88imagereader.cc index 0e1af925..852702da 100644 --- a/lib/imagereader/d88imagereader.cc +++ b/lib/imagereader/d88imagereader.cc @@ -5,7 +5,6 @@ #include "image.h" #include "proto.h" #include "lib/config.pb.h" -#include "imagereader/imagereaderimpl.h" #include "fmt/format.h" #include #include diff --git a/lib/imagereader/dimimagereader.cc b/lib/imagereader/dimimagereader.cc index 5c40b86d..2b34ac33 100644 --- a/lib/imagereader/dimimagereader.cc +++ b/lib/imagereader/dimimagereader.cc @@ -5,7 +5,6 @@ #include "image.h" #include "proto.h" #include "lib/config.pb.h" -#include "imagereader/imagereaderimpl.h" #include "fmt/format.h" #include #include diff --git a/lib/imagereader/fdiimagereader.cc b/lib/imagereader/fdiimagereader.cc index b16de4f1..bd439902 100644 --- a/lib/imagereader/fdiimagereader.cc +++ b/lib/imagereader/fdiimagereader.cc @@ -4,7 +4,6 @@ #include "imagereader/imagereader.h" #include "image.h" #include "lib/config.pb.h" -#include "imagereader/imagereaderimpl.h" #include "fmt/format.h" #include #include diff --git a/lib/imagereader/imagereader.cc b/lib/imagereader/imagereader.cc index b6fbee96..ddd77b5b 100644 --- a/lib/imagereader/imagereader.cc +++ b/lib/imagereader/imagereader.cc @@ -86,20 +86,3 @@ void ImageReader::updateConfigForFilename(ImageReaderProto* proto, const std::st ImageReader::ImageReader(const ImageReaderProto& config): _config(config) {} - -void getTrackFormat(const ImgInputOutputProto& config, - ImgInputOutputProto::TrackdataProto& trackdata, unsigned track, unsigned side) -{ - trackdata.Clear(); - for (const ImgInputOutputProto::TrackdataProto& f : config.trackdata()) - { - if (f.has_track() && f.has_up_to_track() && ((track < f.track()) || (track > f.up_to_track()))) - continue; - if (f.has_track() && !f.has_up_to_track() && (track != f.track())) - continue; - if (f.has_side() && (f.side() != side)) - continue; - - trackdata.MergeFrom(f); - } -} diff --git a/lib/imagereader/imagereader.proto b/lib/imagereader/imagereader.proto index 88676606..3d31de86 100644 --- a/lib/imagereader/imagereader.proto +++ b/lib/imagereader/imagereader.proto @@ -3,6 +3,12 @@ syntax = "proto2"; import "lib/common.proto"; message ImgInputOutputProto { + enum Order { + UNDEFINED = 0; + CHS = 1; + HCS = 2; + } + message SectorsProto { repeated int32 sector = 1 [(help) = "sector ID"]; } @@ -30,6 +36,7 @@ message ImgInputOutputProto { optional int32 sides = 6 [default=0, (help) = "number of sides in image"]; optional int32 physical_offset = 7 [default=0, (help) = "logical:physical track offset"]; optional int32 physical_step = 8 [default=1, (help) = "logical:physical track step"]; + optional Order order = 9 [default=CHS, (help) = "the order in which to emit tracks in the image"]; } message DiskCopyInputProto {} diff --git a/lib/imagereader/imagereaderimpl.h b/lib/imagereader/imagereaderimpl.h deleted file mode 100644 index ed80cba7..00000000 --- a/lib/imagereader/imagereaderimpl.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef IMAGEREADERIMPL_H -#define IMAGEREADERIMPL_H - -extern void getTrackFormat(const ImgInputOutputProto& config, - ImgInputOutputProto::TrackdataProto& trackdata, unsigned track, unsigned side); - -#endif - diff --git a/lib/imagereader/imgimagereader.cc b/lib/imagereader/imgimagereader.cc index 61f1ea68..81a525c1 100644 --- a/lib/imagereader/imgimagereader.cc +++ b/lib/imagereader/imgimagereader.cc @@ -4,7 +4,7 @@ #include "imagereader/imagereader.h" #include "image.h" #include "lib/config.pb.h" -#include "imagereader/imagereaderimpl.h" +#include "imginputoutpututils.h" #include "fmt/format.h" #include #include @@ -27,34 +27,31 @@ public: Error() << "IMG: bad configuration; did you remember to set the tracks, sides and trackdata fields?"; std::unique_ptr image(new Image); - int trackCount = 0; - for (int track = 0; track < _config.img().tracks(); track++) - { + for (const auto& p : getTrackOrdering(_config.img())) + { + int track = p.first; + int side = p.second; + if (inputFile.eof()) break; int physicalCylinder = track * _config.img().physical_step() + _config.img().physical_offset(); - for (int side = 0; side < _config.img().sides(); side++) - { - ImgInputOutputProto::TrackdataProto trackdata; - getTrackFormat(_config.img(), trackdata, track, side); + ImgInputOutputProto::TrackdataProto trackdata; + getTrackFormat(_config.img(), trackdata, track, side); - for (int sectorId : getSectors(trackdata)) - { - Bytes data(trackdata.sector_size()); - inputFile.read((char*) data.begin(), data.size()); + for (int sectorId : getSectors(trackdata)) + { + Bytes data(trackdata.sector_size()); + inputFile.read((char*) data.begin(), data.size()); - const auto& sector = image->put(physicalCylinder, side, sectorId); - sector->status = Sector::OK; - sector->logicalTrack = track; - sector->physicalCylinder = physicalCylinder; - sector->logicalSide = sector->physicalHead = side; - sector->logicalSector = sectorId; - sector->data = data; - } - } - - trackCount++; + const auto& sector = image->put(physicalCylinder, side, sectorId); + sector->status = Sector::OK; + sector->logicalTrack = track; + sector->physicalCylinder = physicalCylinder; + sector->logicalSide = sector->physicalHead = side; + sector->logicalSector = sectorId; + sector->data = data; + } } image->calculateSize(); diff --git a/lib/imagewriter/imgimagewriter.cc b/lib/imagewriter/imgimagewriter.cc index 7f67a433..1bb163f7 100644 --- a/lib/imagewriter/imgimagewriter.cc +++ b/lib/imagewriter/imgimagewriter.cc @@ -4,7 +4,7 @@ #include "imagewriter/imagewriter.h" #include "image.h" #include "lib/config.pb.h" -#include "imagereader/imagereaderimpl.h" +#include "imginputoutpututils.h" #include "fmt/format.h" #include #include @@ -28,31 +28,31 @@ public: if (!outputFile.is_open()) Error() << "cannot open output file"; - for (int track = 0; track < tracks; track++) + for (const auto& p : getTrackOrdering(_config.img(), tracks, sides)) { - for (int side = 0; side < sides; side++) + int track = p.first; + int side = p.second; + + ImgInputOutputProto::TrackdataProto trackdata; + getTrackFormat(_config.img(), trackdata, track, side); + + auto sectors = getSectors(trackdata, geometry.numSectors); + if (sectors.empty()) { - ImgInputOutputProto::TrackdataProto trackdata; - getTrackFormat(_config.img(), trackdata, track, side); + int maxSector = geometry.firstSector + geometry.numSectors - 1; + for (int i=geometry.firstSector; i<=maxSector; i++) + sectors.push_back(i); + } - auto sectors = getSectors(trackdata, geometry.numSectors); - if (sectors.empty()) - { - int maxSector = geometry.firstSector + geometry.numSectors - 1; - for (int i=geometry.firstSector; i<=maxSector; i++) - sectors.push_back(i); - } + int sectorSize = trackdata.has_sector_size() ? trackdata.sector_size() : geometry.sectorSize; - int sectorSize = trackdata.has_sector_size() ? trackdata.sector_size() : geometry.sectorSize; - - for (int sectorId : sectors) - { - const auto& sector = image.get(track, side, sectorId); - if (sector) - sector->data.slice(0, sectorSize).writeTo(outputFile); - else - outputFile.seekp(sectorSize, std::ios::cur); - } + for (int sectorId : sectors) + { + const auto& sector = image.get(track, side, sectorId); + if (sector) + sector->data.slice(0, sectorSize).writeTo(outputFile); + else + outputFile.seekp(sectorSize, std::ios::cur); } } diff --git a/lib/imginputoutpututils.cc b/lib/imginputoutpututils.cc new file mode 100644 index 00000000..641c7d8f --- /dev/null +++ b/lib/imginputoutpututils.cc @@ -0,0 +1,57 @@ +#include "globals.h" +#include "lib/imagereader/imagereader.pb.h" +#include "imginputoutpututils.h" + +std::vector> getTrackOrdering(const ImgInputOutputProto& config, + unsigned numTracks, unsigned numSides) +{ + int tracks = config.has_tracks() ? config.tracks() : numTracks; + int sides = config.has_sides() ? config.sides() : numSides; + + std::vector> ordering; + switch (config.order()) + { + case ImgInputOutputProto::CHS: + { + for (int track = 0; track < tracks; track++) + { + for (int side = 0; side < sides; side++) + ordering.push_back(std::make_pair(track, side)); + } + break; + } + + case ImgInputOutputProto::HCS: + { + for (int side = 0; side < sides; side++) + { + for (int track = 0; track < tracks; track++) + ordering.push_back(std::make_pair(track, side)); + } + break; + } + + default: + Error() << "IMG: invalid track ordering"; + } + + return ordering; +} + +void getTrackFormat(const ImgInputOutputProto& config, + ImgInputOutputProto::TrackdataProto& trackdata, unsigned track, unsigned side) +{ + trackdata.Clear(); + for (const ImgInputOutputProto::TrackdataProto& f : config.trackdata()) + { + if (f.has_track() && f.has_up_to_track() && ((track < f.track()) || (track > f.up_to_track()))) + continue; + if (f.has_track() && !f.has_up_to_track() && (track != f.track())) + continue; + if (f.has_side() && (f.side() != side)) + continue; + + trackdata.MergeFrom(f); + } +} + diff --git a/lib/imginputoutpututils.h b/lib/imginputoutpututils.h new file mode 100644 index 00000000..d12a6ac4 --- /dev/null +++ b/lib/imginputoutpututils.h @@ -0,0 +1,12 @@ +#ifndef IMGINPUTOUTPUTUTILS_H +#define IMGINPUTOUTPUTUTILS_H + +extern std::vector> getTrackOrdering(const ImgInputOutputProto& config, + unsigned numTracks = 0, unsigned numSides = 0); + +extern void getTrackFormat(const ImgInputOutputProto& config, + ImgInputOutputProto::TrackdataProto& trackdata, unsigned track, unsigned side); + +#endif + + diff --git a/mkninja.sh b/mkninja.sh index d0915e33..45f7b607 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -471,6 +471,7 @@ buildlibrary libbackend.a \ lib/imagewriter/ldbsimagewriter.cc \ lib/imagewriter/nsiimagewriter.cc \ lib/imagewriter/rawimagewriter.cc \ + lib/imginputoutpututils.cc \ lib/ldbs.cc \ lib/proto.cc \ lib/reader.cc \ From c658a764d054926a4671855f2a61afc6a34c2d54 Mon Sep 17 00:00:00 2001 From: Paul Devine Date: Mon, 10 Jan 2022 15:06:29 -0800 Subject: [PATCH 08/37] correct media size in README --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d1aedb4c..431767ec 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ at least, check the CRC so what data's there is probably good. | [Micropolis](doc/disk-micropolis.md) | 🦄 | | Micropolis 100tpi drives | | [Northstar(doc/disk-northstar.md) | 🦖 | 🦖 | 5.25" hard sectors | | [TI DS990 FD1000](doc/disk-tids990.md) | 🦄 | 🦄 | 8" | -| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8" | +| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 5.25" GCR encoded | | [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8" _and_ hard sectors | {: .datatable } @@ -230,3 +230,6 @@ As an exception, `dep/snowhouse` contains the snowhouse assertion library, taken from https://github.com/banditcpp/snowhouse. It is Boost Standard License 1.0 licensed. Please see the contents of the directory for the full text. +As an exception, `dep/libusbp` contains the libusbp library, taken from +https://github.com/pololu/libusbp. It is MIT licensed. Please see the contents +of the directory for the full text. From 8744114790386e8660baf33b009dc2734d966928 Mon Sep 17 00:00:00 2001 From: Paul Devine Date: Mon, 10 Jan 2022 15:33:29 -0800 Subject: [PATCH 09/37] adding support for double-sided Victor9k disks --- mkninja.sh | 2 ++ src/formats/victor9k_ss.textpb | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mkninja.sh b/mkninja.sh index 27bbc0d4..8bc59eae 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -443,6 +443,7 @@ FORMATS="\ northstar87 \ tids990 \ victor9k_ss \ + victor9k_ds \ zilogmcz \ " @@ -547,6 +548,7 @@ encodedecodetest mac800 scripts/mac800_test.textpb encodedecodetest n88basic encodedecodetest tids990 encodedecodetest victor9k_ss +encodedecodetest victor9k_ds # vim: sw=4 ts=4 et diff --git a/src/formats/victor9k_ss.textpb b/src/formats/victor9k_ss.textpb index 1b72f03c..4feb922a 100644 --- a/src/formats/victor9k_ss.textpb +++ b/src/formats/victor9k_ss.textpb @@ -41,13 +41,13 @@ image_reader { } trackdata { track: 38 - up_to_track: 48 + up_to_track: 47 sector_range { sector_count: 15 } } trackdata { - track: 49 + track: 48 up_to_track: 59 sector_range { sector_count: 14 @@ -111,13 +111,13 @@ image_writer { } trackdata { track: 38 - up_to_track: 48 + up_to_track: 47 sector_range { sector_count: 15 } } trackdata { - track: 49 + track: 48 up_to_track: 59 sector_range { sector_count: 14 @@ -190,7 +190,7 @@ encoder { trackdata { head: 0 min_cylinder: 38 - max_cylinder: 48 + max_cylinder: 47 original_period_ms: 187.6 sector_range { sector_count: 15 @@ -198,7 +198,7 @@ encoder { } trackdata { head: 0 - min_cylinder: 49 + min_cylinder: 48 max_cylinder: 59 original_period_ms: 175.3 sector_range { From 085fca7e314b91dae0aa639a06d0f8576c6d9bd7 Mon Sep 17 00:00:00 2001 From: Paul Devine Date: Mon, 10 Jan 2022 15:34:37 -0800 Subject: [PATCH 10/37] adding Victor9k double sided src format --- src/formats/victor9k_ds.textpb | 459 +++++++++++++++++++++++++++++++++ 1 file changed, 459 insertions(+) create mode 100644 src/formats/victor9k_ds.textpb diff --git a/src/formats/victor9k_ds.textpb b/src/formats/victor9k_ds.textpb new file mode 100644 index 00000000..568a9910 --- /dev/null +++ b/src/formats/victor9k_ds.textpb @@ -0,0 +1,459 @@ +comment: 'Victor 9000 / Sirius One 1224kB DSHD GCR variable sector)' + +image_reader { + filename: "victor9k_ds.img" + img { + tracks: 80 + sides: 2 + trackdata { + sector_size: 512 + sector_range { + start_sector: 0 + } + } + trackdata { + side: 0 + track: 0 + up_to_track: 3 + sector_range { + sector_count: 19 + } + } + trackdata { + side: 0 + track: 4 + up_to_track: 15 + sector_range { + sector_count: 18 + } + } + trackdata { + side: 0 + track: 16 + up_to_track: 26 + sector_range { + sector_count: 17 + } + } + trackdata { + side: 0 + track: 27 + up_to_track: 37 + sector_range { + sector_count: 16 + } + } + trackdata { + side: 0 + track: 38 + up_to_track: 47 + sector_range { + sector_count: 15 + } + } + trackdata { + side: 0 + track: 48 + up_to_track: 59 + sector_range { + sector_count: 14 + } + } + trackdata { + side: 0 + track: 60 + up_to_track: 70 + sector_range { + sector_count: 13 + } + } + trackdata { + side: 0 + track: 71 + up_to_track: 79 + sector_range { + sector_count: 12 + } + } + trackdata { + side: 1 + track: 0 + up_to_track: 7 + sector_range { + sector_count: 18 + } + } + trackdata { + side: 1 + track: 8 + up_to_track: 18 + sector_range { + sector_count: 17 + } + } + trackdata { + side: 1 + track: 19 + up_to_track: 29 + sector_range { + sector_count: 16 + } + } + trackdata { + side: 1 + track: 30 + up_to_track: 39 + sector_range { + sector_count: 15 + } + } + trackdata { + side: 1 + track: 40 + up_to_track: 51 + sector_range { + sector_count: 14 + } + } + trackdata { + side: 1 + track: 52 + up_to_track: 62 + sector_range { + sector_count: 13 + } + } + trackdata { + side: 1 + track: 63 + up_to_track: 74 + sector_range { + sector_count: 12 + } + } + trackdata { + side: 1 + track: 75 + up_to_track: 79 + sector_range { + sector_count: 11 + } + } + } +} + +image_writer { + filename: "victor9k_ds.img" + img { + tracks: 80 + sides: 2 + trackdata { + sector_size: 512 + sector_range { + start_sector: 0 + } + } + trackdata { + side: 0 + track: 0 + up_to_track: 3 + sector_range { + sector_count: 19 + } + } + trackdata { + side: 0 + track: 4 + up_to_track: 15 + sector_range { + sector_count: 18 + } + } + trackdata { + side: 0 + track: 16 + up_to_track: 26 + sector_range { + sector_count: 17 + } + } + trackdata { + side: 0 + track: 27 + up_to_track: 37 + sector_range { + sector_count: 16 + } + } + trackdata { + side: 0 + track: 38 + up_to_track: 47 + sector_range { + sector_count: 15 + } + } + trackdata { + side: 0 + track: 48 + up_to_track: 59 + sector_range { + sector_count: 14 + } + } + trackdata { + side: 0 + track: 60 + up_to_track: 70 + sector_range { + sector_count: 13 + } + } + trackdata { + side: 0 + track: 71 + up_to_track: 79 + sector_range { + sector_count: 12 + } + } + trackdata { + side: 1 + track: 0 + up_to_track: 7 + sector_range { + sector_count: 18 + } + } + trackdata { + side: 1 + track: 8 + up_to_track: 18 + sector_range { + sector_count: 17 + } + } + trackdata { + side: 1 + track: 19 + up_to_track: 29 + sector_range { + sector_count: 16 + } + } + trackdata { + side: 1 + track: 30 + up_to_track: 39 + sector_range { + sector_count: 15 + } + } + trackdata { + side: 1 + track: 40 + up_to_track: 51 + sector_range { + sector_count: 14 + } + } + trackdata { + side: 1 + track: 52 + up_to_track: 62 + sector_range { + sector_count: 13 + } + } + trackdata { + side: 1 + track: 63 + up_to_track: 74 + sector_range { + sector_count: 12 + } + } + trackdata { + side: 1 + track: 75 + up_to_track: 79 + sector_range { + sector_count: 11 + } + } + } +} + +encoder { + victor9k { + trackdata { + original_data_rate_khz: 500 + post_index_gap_us: 1000.0 + pre_header_sync_bits: 120 + post_header_gap_bits: 48 + pre_data_sync_bits: 40 + post_data_gap_bits: 200 + } + trackdata { + head: 0 + min_cylinder: 0 + max_cylinder: 3 + original_period_ms: 237.9 + sector_range { + start_sector: 0 + sector_count: 19 + } + } + trackdata { + head: 0 + min_cylinder: 4 + max_cylinder: 15 + original_period_ms: 224.5 + sector_range { + sector_count: 18 + } + } + trackdata { + head: 0 + min_cylinder: 16 + max_cylinder: 26 + original_period_ms: 212.2 + sector_range { + sector_count: 17 + } + } + trackdata { + head: 0 + min_cylinder: 27 + max_cylinder: 37 + original_period_ms: 199.9 + sector_range { + sector_count: 16 + } + } + trackdata { + head: 0 + min_cylinder: 38 + max_cylinder: 47 + original_period_ms: 187.6 + sector_range { + sector_count: 15 + } + } + trackdata { + head: 0 + min_cylinder: 48 + max_cylinder: 59 + original_period_ms: 175.3 + sector_range { + sector_count: 14 + } + } + trackdata { + head: 0 + min_cylinder: 60 + max_cylinder: 70 + original_period_ms: 163.0 + sector_range { + sector_count: 13 + } + } + trackdata { + head: 0 + min_cylinder: 71 + max_cylinder: 79 + original_period_ms: 149.6 + sector_range { + sector_count: 12 + } + } + trackdata { + head: 1 + min_cylinder: 0 + max_cylinder: 7 + original_period_ms: 224.5 + sector_range { + start_sector: 0 + sector_count: 18 + } + } + trackdata { + head: 1 + min_cylinder: 8 + max_cylinder: 18 + original_period_ms: 212.2 + sector_range { + sector_count: 17 + } + } + trackdata { + head: 1 + min_cylinder: 19 + max_cylinder: 29 + original_period_ms: 199.9 + sector_range { + sector_count: 16 + } + } + trackdata { + head: 1 + min_cylinder: 30 + max_cylinder: 39 + original_period_ms: 187.6 + sector_range { + sector_count: 15 + } + } + trackdata { + head: 1 + min_cylinder: 40 + max_cylinder: 51 + original_period_ms: 175.3 + sector_range { + sector_count: 14 + } + } + trackdata { + head: 1 + min_cylinder: 52 + max_cylinder: 62 + original_period_ms: 163.0 + sector_range { + sector_count: 13 + } + } + trackdata { + head: 1 + min_cylinder: 63 + max_cylinder: 74 + original_period_ms: 149.6 + sector_range { + sector_count: 12 + } + } + trackdata { + head: 1 + min_cylinder: 75 + max_cylinder: 79 + original_period_ms: 144.0 + sector_range { + sector_count: 11 + } + } + } +} + +decoder { + victor9k {} +} + +cylinders { + start: 0 + end: 79 +} + +heads { + start: 0 + end: 1 +} + From 45d7b284e3e76a11af49d909d4f8a87a9a20cb2a Mon Sep 17 00:00:00 2001 From: lady ada Date: Sat, 15 Jan 2022 20:57:36 -0500 Subject: [PATCH 11/37] 1) update CI to add zip on commit for quick testing 2) fix gw readflux command (should be 8 bytes long but was packed as 10) 3) add support for non-gw VID/PID (generic serial port that is gw compatible) 4) manually set/clear DTR on serial port devices - this seems to be essential for tinyusb/arduino CDC not tested with a gw but ought not to have broken anything! --- .github/workflows/ccpp.yml | 9 +++++++++ lib/usb/greaseweazleusb.cc | 2 +- lib/usb/serial.cc | 10 +++++++++- lib/usb/usb.cc | 4 +++- lib/usb/usbfinder.cc | 25 +++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 78543494..08f525f4 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -53,3 +53,12 @@ jobs: run: | make + - name: zip + run: | + zip -9 fluxengine.zip fluxengine.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex + + - name: Upload build artifacts + uses: actions/upload-artifact@v2 + with: + name: ${{ github.event.repository.name }}.${{ github.sha }} + path: fluxengine.zip diff --git a/lib/usb/greaseweazleusb.cc b/lib/usb/greaseweazleusb.cc index 5e7ede07..200199a9 100644 --- a/lib/usb/greaseweazleusb.cc +++ b/lib/usb/greaseweazleusb.cc @@ -131,7 +131,7 @@ public: .write_8(CMD_READ_FLUX) .write_8(cmd.size()) .write_le32(0) //ticks default value (guessed) - .write_le32(2);//guessed + .write_le16(2);//revolutions do_command(cmd); } } diff --git a/lib/usb/serial.cc b/lib/usb/serial.cc index 80efa83c..04773071 100644 --- a/lib/usb/serial.cc +++ b/lib/usb/serial.cc @@ -45,8 +45,16 @@ COMMTIMEOUTS commtimeouts = {0}; commtimeouts.ReadIntervalTimeout = 100; SetCommTimeouts(_handle, &commtimeouts); + + if (!EscapeCommFunction(_handle, CLRDTR)) + Error() << fmt::format("Couldn't clear DTR: {}", + get_last_error_string()); + Sleep(200); + if (!EscapeCommFunction(_handle, SETDTR)) + Error() << fmt::format("Couldn't set DTR: {}", + get_last_error_string()); - PurgeComm(_handle, PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR); + PurgeComm(_handle, PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR); } ~SerialPortImpl() override diff --git a/lib/usb/usb.cc b/lib/usb/usb.cc index 079272c7..2e54e66f 100644 --- a/lib/usb/usb.cc +++ b/lib/usb/usb.cc @@ -69,7 +69,9 @@ USB* get_usb_impl() return createGreaseWeazleUsb(candidate->serialPort, config.usb().greaseweazle()); default: - Error() << "internal"; + std::cerr << fmt::format("Using Unknown GW Compatible {} on {}\n", candidate->serial, candidate->serialPort); + GreaseWeazleProto gwconfig = config.usb().greaseweazle(); + return createGreaseWeazleUsb(candidate->serialPort, gwconfig ); } } diff --git a/lib/usb/usbfinder.cc b/lib/usb/usbfinder.cc index 95b74d76..557a383f 100644 --- a/lib/usb/usbfinder.cc +++ b/lib/usb/usbfinder.cc @@ -45,5 +45,30 @@ std::vector> findUsbDevices(const std::set(); + candidate->device = it; + uint32_t id = (it.get_vendor_id() << 16) | it.get_product_id(); + candidate->id = id; + candidate->serial = get_serial_number(it); + printf("USB ID %04x %04x: ", it.get_vendor_id(), it.get_product_id()); + try + { + libusbp::serial_port port(candidate->device, 0, true); + candidate->serialPort = port.get_name(); + printf("generic serialPort found\n"); + candidates.push_back(std::move(candidate)); + } + catch(const libusbp::error & error) + { + // not a serial port! + printf("not a port!\n"); + continue; + } + + } + } return candidates; } From fafadc333326698d4b19d9b89b33405744af943e Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sun, 16 Jan 2022 20:07:08 -0600 Subject: [PATCH 12/37] Fix a markup typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ac96472d..0cb4538f 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ at least, check the CRC so what data's there is probably good. | [DVK MX](doc/disk-mx.md) | 🦖 | | Soviet PDP-11 clone | | [VDS Eco1](doc/disk-eco1.md) | 🦖 | | 8" mixed format | | [Micropolis](doc/disk-micropolis.md) | 🦄 | | Micropolis 100tpi drives | -| [Northstar(doc/disk-northstar.md) | 🦖 | 🦖 | 5.25" hard sectors | +| [Northstar](doc/disk-northstar.md) | 🦖 | 🦖 | 5.25" hard sectors | | [TI DS990 FD1000](doc/disk-tids990.md) | 🦄 | 🦄 | 8" | | [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8" | | [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8" _and_ hard sectors | From 0e7e9d56435df6a55abc582dd6e221f3fe1b41c1 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sun, 16 Jan 2022 20:08:01 -0600 Subject: [PATCH 13/37] Line columns up again --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0cb4538f..64b953b8 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ at least, check the CRC so what data's there is probably good. | [DVK MX](doc/disk-mx.md) | 🦖 | | Soviet PDP-11 clone | | [VDS Eco1](doc/disk-eco1.md) | 🦖 | | 8" mixed format | | [Micropolis](doc/disk-micropolis.md) | 🦄 | | Micropolis 100tpi drives | -| [Northstar](doc/disk-northstar.md) | 🦖 | 🦖 | 5.25" hard sectors | +| [Northstar](doc/disk-northstar.md) | 🦖 | 🦖 | 5.25" hard sectors | | [TI DS990 FD1000](doc/disk-tids990.md) | 🦄 | 🦄 | 8" | | [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8" | | [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8" _and_ hard sectors | From b8c58b12fd25fc2f02739f54400dc8c911421e49 Mon Sep 17 00:00:00 2001 From: Thomas Daede Date: Mon, 17 Jan 2022 16:53:50 -0800 Subject: [PATCH 14/37] Add option to rescale flux source pulses. This actually scales by the reciprotal of the flux sink, to allow the same value to be set for both options. --- doc/using.md | 12 +++++++----- lib/fluxsource/fluxsource.proto | 1 + lib/reader.cc | 1 + 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/doc/using.md b/doc/using.md index fd8e0851..636a0c18 100644 --- a/doc/using.md +++ b/doc/using.md @@ -348,13 +348,15 @@ behaviour. useful for using very old drives with FluxEngine itself. If you use this option, then any index marks in the sampled flux are, of course, garbage. - - `--flux_sink.rescale=X` + - `--flux_source.rescale=X, --flux_sink.rescale=X` - When writing a floppy on a drive that doesn't match the source file rpm, - the flux periods can be scaled to compensate. For example, to write - a PC-98 1.2MB (360rpm) floppy using a 300rpm floppy drive: + When reading or writing a floppy on a drive that doesn't match the + original drive RPM, the flux periods can be scaled to compensate. - `--flux_sink.rescale=1.2` + For example, to read or write a PC-98 1.2MB (360rpm) floppy using a 300rpm + floppy drive: + + `--flux_source.rescale=1.2 --flux_sink.rescale=1.2` ## Visualisation diff --git a/lib/fluxsource/fluxsource.proto b/lib/fluxsource/fluxsource.proto index c5ce6243..8019f31f 100644 --- a/lib/fluxsource/fluxsource.proto +++ b/lib/fluxsource/fluxsource.proto @@ -39,6 +39,7 @@ message Fl2FluxSourceProto { } message FluxSourceProto { + optional double rescale = 9 [ default = 1.0, (help) = "amount to divide pulse periods by" ]; oneof source { string fluxfile = 1 [default = "name of source flux file"]; HardwareFluxSourceProto drive = 2; diff --git a/lib/reader.cc b/lib/reader.cc index 659315a2..e3f29bb4 100644 --- a/lib/reader.cc +++ b/lib/reader.cc @@ -25,6 +25,7 @@ static std::shared_ptr readFluxmap(FluxSource& fluxsource, unsigned cyl { std::cout << fmt::format("{0:>3}.{1}: ", cylinder, head) << std::flush; std::shared_ptr fluxmap = fluxsource.readFlux(cylinder, head); + fluxmap->rescale(1.0/config.flux_source().rescale()); std::cout << fmt::format( "{0:.0} ms in {1} bytes\n", fluxmap->duration()/1e6, From de8f8b7d9137d8830594c7fbe70cd3ccaf28f02c Mon Sep 17 00:00:00 2001 From: Thomas Daede Date: Mon, 17 Jan 2022 17:02:26 -0800 Subject: [PATCH 15/37] FDI: Add support for automatically setting format. --- doc/using.md | 3 ++- lib/imagereader/fdiimagereader.cc | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/doc/using.md b/doc/using.md index fd8e0851..85fd4001 100644 --- a/doc/using.md +++ b/doc/using.md @@ -267,7 +267,8 @@ FluxEngine also supports a number of file system image formats. When using the - `` Read from a [FDI image file](https://www.pc98.org/project/doc/hdi.html), - commonly used by PC-98 emulators. **Read Only.** + commonly used by PC-98 emulators. Supports automatically configuring + the encoder. **Read Only.** - `` diff --git a/lib/imagereader/fdiimagereader.cc b/lib/imagereader/fdiimagereader.cc index bd439902..ab17c358 100644 --- a/lib/imagereader/fdiimagereader.cc +++ b/lib/imagereader/fdiimagereader.cc @@ -3,6 +3,7 @@ #include "sector.h" #include "imagereader/imagereader.h" #include "image.h" +#include "proto.h" #include "lib/config.pb.h" #include "fmt/format.h" #include @@ -75,6 +76,34 @@ public: trackCount++; } + if (config.encoder().format_case() == EncoderProto::FormatCase::FORMAT_NOT_SET) + { + auto ibm = config.mutable_encoder()->mutable_ibm(); + auto trackdata = ibm->add_trackdata(); + trackdata->set_clock_rate_khz(500); + auto sectors = trackdata->mutable_sectors(); + switch (fddType) { + case 0x90: + std::cout << "FDI: automatically setting format to 1.2MB (1024 byte sectors)\n"; + config.mutable_cylinders()->set_end(76); + trackdata->set_track_length_ms(167); + trackdata->set_sector_size(1024); + for (int i = 0; i < 9; i++) + sectors->add_sector(i); + break; + case 0x30: + std::cout << "FDI: automatically setting format to 1.44MB\n"; + trackdata->set_track_length_ms(200); + trackdata->set_sector_size(512); + for (int i = 0; i < 18; i++) + sectors->add_sector(i); + break; + default: + Error() << fmt::format("FDI: unknown fdd type 0x%02x, could not determine write profile automatically", fddType); + break; + } + } + image->calculateSize(); const Geometry& geometry = image->getGeometry(); std::cout << fmt::format("FDI: read {} tracks, {} sides, {} kB total\n", From ffcac3a12d3af9351f4169b977d5759f910182c9 Mon Sep 17 00:00:00 2001 From: David Given Date: Thu, 20 Jan 2022 00:08:26 +0100 Subject: [PATCH 16/37] Add initial clang-format file. --- .clang-format | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..8bda3a44 --- /dev/null +++ b/.clang-format @@ -0,0 +1,40 @@ +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +AlignArrayOfStructures: Left +AlignEscapedNewlines: Left +AllowAllArgumentsOnNextLine: 'true' +AllowAllConstructorInitializersOnNextLine: 'false' +AllowAllParametersOfDeclarationOnNextLine: 'true' +AllowShortBlocksOnASingleLine: 'true' +AllowShortCaseLabelsOnASingleLine: 'true' +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: None +AllowShortLoopsOnASingleLine: 'false' +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: 'true' +AlwaysBreakTemplateDeclarations: 'Yes' +BinPackArguments: 'false' +BinPackParameters: 'false' +BreakConstructorInitializers: 'AfterColon' +BreakBeforeBraces: Allman +BreakInheritanceList: AfterColon +BreakStringLiterals: 'true' +IndentCaseLabels: 'true' +IndentWidth: '4' +ColumnLimit: '80' +ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' +IndentWrappedFunctionNames: 'false' +KeepEmptyLinesAtTheStartOfBlocks: 'true' +PointerAlignment: Left +ReflowComments: 'true' +SortUsingDeclarations: 'true' +SpaceAfterTemplateKeyword: 'true' +SpaceBeforeAssignmentOperators: 'true' +SpaceBeforeCtorInitializerColon: 'false' +SpaceBeforeInheritanceColon: 'true' +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: 'false' +... From a9933c7764acff53d7016fef9eb22185f00d2f57 Mon Sep 17 00:00:00 2001 From: Paul Devine Date: Wed, 19 Jan 2022 16:21:40 -0800 Subject: [PATCH 17/37] updated documentation for double-sided, made note about documentaiton bug for track format --- doc/disk-victor9k.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/doc/disk-victor9k.md b/doc/disk-victor9k.md index 01f49699..9bba7dc5 100644 --- a/doc/disk-victor9k.md +++ b/doc/disk-victor9k.md @@ -16,8 +16,8 @@ the speed zone allocation on head 1 differ from head 0... | 1 | 4-15 | 0-7 | 18 | 224.5 | | 2 | 16-26 | 8-18 | 17 | 212.2 | | 3 | 27-37 | 19-29 | 16 | 199.9 | -| 4 | 38-48 | 30-40 | 15 | 187.6 | -| 5 | 49-59 | 41-51 | 14 | 175.3 | +| 4 | 38-47\* | 30-39\* | 15 | 187.6 | +| 5 | 48-59 | 40-51 | 14 | 175.3 | | 6 | 60-70 | 52-62 | 13 | 163.0 | | 7 | 71-79 | 63-74 | 12 | 149.6 | | 8 | | 75-79 | 11 | 144.0 | @@ -26,8 +26,13 @@ the speed zone allocation on head 1 differ from head 0... FluxEngine, the disk always spins at 360 rpm, which corresponds to a rotational period of 166 ms.) -FluxEngine can read and write the single-sided variant of these. (Double-sided -will be trivial to do, it's just not done yet.) +\*The Victor 9000 Hardware Reference Manual has a bug in the documentation +and lists Zone 4 as ending with track 48 on head 0 and track 40 on head 1. +The above table matches observed data on various disks and the assembly +code in the boot loader, which ends Zone 4 with track 47 on head 0 +and track 39 on Head 1. + +FluxEngine can read and write both the single-sided and double-sided variants. Reading discs ------------- @@ -35,10 +40,14 @@ Reading discs Just do: ``` -fluxengine read victor9k-ss +fluxengine read + ``` -You should end up with an `victor9k.img` which is 627200 bytes long. +...where `` can be `victor9k_ss` or `victor9k_ds`. + +For `victor9k_ss` you should end up with an `victor9k.img` which is 627200 bytes long. +For `victor9k_ds` you should end up with an `victor9k.img` which is 1224192 bytes long. **Big warning!** The image is triangular, where each track occupies a different amount of space. Victor disk images are complicated due to the way the tracks @@ -50,7 +59,7 @@ Writing discs Just do: ``` -fluxengine read victor9k-ss -i victor9k.img +fluxengine read victor9k_ss -i victor9k.img ``` **Big warning!** This uses the same triangular disk image that reading uses. From c751810b84d51a87fe3ccbc548d3be14cbc4e744 Mon Sep 17 00:00:00 2001 From: David Given Date: Thu, 20 Jan 2022 20:24:10 +0100 Subject: [PATCH 18/37] Don't use string::contains. --- .clang-format | 2 ++ lib/usb/usbfinder.cc | 61 ++++++++++++++++++++++---------------------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/.clang-format b/.clang-format index 8bda3a44..451661f2 100644 --- a/.clang-format +++ b/.clang-format @@ -30,6 +30,8 @@ IndentWrappedFunctionNames: 'false' KeepEmptyLinesAtTheStartOfBlocks: 'true' PointerAlignment: Left ReflowComments: 'true' +IncludeBlocks: Preserve +SortIncludes: 'false' SortUsingDeclarations: 'true' SpaceAfterTemplateKeyword: 'true' SpaceBeforeAssignmentOperators: 'true' diff --git a/lib/usb/usbfinder.cc b/lib/usb/usbfinder.cc index 95b74d76..0653b49c 100644 --- a/lib/usb/usbfinder.cc +++ b/lib/usb/usbfinder.cc @@ -9,41 +9,42 @@ static const std::string get_serial_number(const libusbp::device& device) { - try - { - return device.get_serial_number(); - } - catch (const libusbp::error& e) - { - if (e.has_code(LIBUSBP_ERROR_NO_SERIAL_NUMBER)) - return "n/a"; - throw; - } + try + { + return device.get_serial_number(); + } + catch (const libusbp::error& e) + { + if (e.has_code(LIBUSBP_ERROR_NO_SERIAL_NUMBER)) + return "n/a"; + throw; + } } -std::vector> findUsbDevices(const std::set& ids) +std::vector> findUsbDevices( + const std::set& ids) { - std::vector> candidates; - for (const auto& it : libusbp::list_connected_devices()) - { - auto candidate = std::make_unique(); - candidate->device = it; + std::vector> candidates; + for (const auto& it : libusbp::list_connected_devices()) + { + auto candidate = std::make_unique(); + candidate->device = it; - uint32_t id = (it.get_vendor_id() << 16) | it.get_product_id(); - if (ids.contains(id)) - { - candidate->id = id; - candidate->serial = get_serial_number(it); + uint32_t id = (it.get_vendor_id() << 16) | it.get_product_id(); + if (ids.find(id) != ids.end()) + { + candidate->id = id; + candidate->serial = get_serial_number(it); - if (id == GREASEWEAZLE_ID) - { - libusbp::serial_port port(candidate->device); - candidate->serialPort = port.get_name(); - } + if (id == GREASEWEAZLE_ID) + { + libusbp::serial_port port(candidate->device); + candidate->serialPort = port.get_name(); + } - candidates.push_back(std::move(candidate)); - } - } + candidates.push_back(std::move(candidate)); + } + } - return candidates; + return candidates; } From 283b9871cbb94d3d7f43f2f39ad7bba4cab962d6 Mon Sep 17 00:00:00 2001 From: Paul Devine Date: Mon, 10 Jan 2022 15:06:29 -0800 Subject: [PATCH 19/37] correct media size in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64b953b8..63efb01e 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ at least, check the CRC so what data's there is probably good. | [Micropolis](doc/disk-micropolis.md) | 🦄 | | Micropolis 100tpi drives | | [Northstar](doc/disk-northstar.md) | 🦖 | 🦖 | 5.25" hard sectors | | [TI DS990 FD1000](doc/disk-tids990.md) | 🦄 | 🦄 | 8" | -| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8" | +| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 5.25" GCR encoded | | [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8" _and_ hard sectors | {: .datatable } From 5a0aadabc14ef592275ba86530842b82f2d4b367 Mon Sep 17 00:00:00 2001 From: Paul Devine Date: Mon, 10 Jan 2022 15:33:29 -0800 Subject: [PATCH 20/37] adding support for double-sided Victor9k disks --- mkninja.sh | 2 ++ src/formats/victor9k_ss.textpb | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mkninja.sh b/mkninja.sh index 45f7b607..e3a3e5ce 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -528,6 +528,7 @@ FORMATS="\ northstar87 \ tids990 \ victor9k_ss \ + victor9k_ds \ zilogmcz \ " @@ -639,6 +640,7 @@ encodedecodetest mac800 scripts/mac800_test.textpb encodedecodetest n88basic encodedecodetest tids990 encodedecodetest victor9k_ss +encodedecodetest victor9k_ds # vim: sw=4 ts=4 et diff --git a/src/formats/victor9k_ss.textpb b/src/formats/victor9k_ss.textpb index 1b72f03c..4feb922a 100644 --- a/src/formats/victor9k_ss.textpb +++ b/src/formats/victor9k_ss.textpb @@ -41,13 +41,13 @@ image_reader { } trackdata { track: 38 - up_to_track: 48 + up_to_track: 47 sector_range { sector_count: 15 } } trackdata { - track: 49 + track: 48 up_to_track: 59 sector_range { sector_count: 14 @@ -111,13 +111,13 @@ image_writer { } trackdata { track: 38 - up_to_track: 48 + up_to_track: 47 sector_range { sector_count: 15 } } trackdata { - track: 49 + track: 48 up_to_track: 59 sector_range { sector_count: 14 @@ -190,7 +190,7 @@ encoder { trackdata { head: 0 min_cylinder: 38 - max_cylinder: 48 + max_cylinder: 47 original_period_ms: 187.6 sector_range { sector_count: 15 @@ -198,7 +198,7 @@ encoder { } trackdata { head: 0 - min_cylinder: 49 + min_cylinder: 48 max_cylinder: 59 original_period_ms: 175.3 sector_range { From fac606c4759d0a3c9c30164ce379bfc6c3038cdf Mon Sep 17 00:00:00 2001 From: Paul Devine Date: Mon, 10 Jan 2022 15:34:37 -0800 Subject: [PATCH 21/37] adding Victor9k double sided src format --- src/formats/victor9k_ds.textpb | 459 +++++++++++++++++++++++++++++++++ 1 file changed, 459 insertions(+) create mode 100644 src/formats/victor9k_ds.textpb diff --git a/src/formats/victor9k_ds.textpb b/src/formats/victor9k_ds.textpb new file mode 100644 index 00000000..568a9910 --- /dev/null +++ b/src/formats/victor9k_ds.textpb @@ -0,0 +1,459 @@ +comment: 'Victor 9000 / Sirius One 1224kB DSHD GCR variable sector)' + +image_reader { + filename: "victor9k_ds.img" + img { + tracks: 80 + sides: 2 + trackdata { + sector_size: 512 + sector_range { + start_sector: 0 + } + } + trackdata { + side: 0 + track: 0 + up_to_track: 3 + sector_range { + sector_count: 19 + } + } + trackdata { + side: 0 + track: 4 + up_to_track: 15 + sector_range { + sector_count: 18 + } + } + trackdata { + side: 0 + track: 16 + up_to_track: 26 + sector_range { + sector_count: 17 + } + } + trackdata { + side: 0 + track: 27 + up_to_track: 37 + sector_range { + sector_count: 16 + } + } + trackdata { + side: 0 + track: 38 + up_to_track: 47 + sector_range { + sector_count: 15 + } + } + trackdata { + side: 0 + track: 48 + up_to_track: 59 + sector_range { + sector_count: 14 + } + } + trackdata { + side: 0 + track: 60 + up_to_track: 70 + sector_range { + sector_count: 13 + } + } + trackdata { + side: 0 + track: 71 + up_to_track: 79 + sector_range { + sector_count: 12 + } + } + trackdata { + side: 1 + track: 0 + up_to_track: 7 + sector_range { + sector_count: 18 + } + } + trackdata { + side: 1 + track: 8 + up_to_track: 18 + sector_range { + sector_count: 17 + } + } + trackdata { + side: 1 + track: 19 + up_to_track: 29 + sector_range { + sector_count: 16 + } + } + trackdata { + side: 1 + track: 30 + up_to_track: 39 + sector_range { + sector_count: 15 + } + } + trackdata { + side: 1 + track: 40 + up_to_track: 51 + sector_range { + sector_count: 14 + } + } + trackdata { + side: 1 + track: 52 + up_to_track: 62 + sector_range { + sector_count: 13 + } + } + trackdata { + side: 1 + track: 63 + up_to_track: 74 + sector_range { + sector_count: 12 + } + } + trackdata { + side: 1 + track: 75 + up_to_track: 79 + sector_range { + sector_count: 11 + } + } + } +} + +image_writer { + filename: "victor9k_ds.img" + img { + tracks: 80 + sides: 2 + trackdata { + sector_size: 512 + sector_range { + start_sector: 0 + } + } + trackdata { + side: 0 + track: 0 + up_to_track: 3 + sector_range { + sector_count: 19 + } + } + trackdata { + side: 0 + track: 4 + up_to_track: 15 + sector_range { + sector_count: 18 + } + } + trackdata { + side: 0 + track: 16 + up_to_track: 26 + sector_range { + sector_count: 17 + } + } + trackdata { + side: 0 + track: 27 + up_to_track: 37 + sector_range { + sector_count: 16 + } + } + trackdata { + side: 0 + track: 38 + up_to_track: 47 + sector_range { + sector_count: 15 + } + } + trackdata { + side: 0 + track: 48 + up_to_track: 59 + sector_range { + sector_count: 14 + } + } + trackdata { + side: 0 + track: 60 + up_to_track: 70 + sector_range { + sector_count: 13 + } + } + trackdata { + side: 0 + track: 71 + up_to_track: 79 + sector_range { + sector_count: 12 + } + } + trackdata { + side: 1 + track: 0 + up_to_track: 7 + sector_range { + sector_count: 18 + } + } + trackdata { + side: 1 + track: 8 + up_to_track: 18 + sector_range { + sector_count: 17 + } + } + trackdata { + side: 1 + track: 19 + up_to_track: 29 + sector_range { + sector_count: 16 + } + } + trackdata { + side: 1 + track: 30 + up_to_track: 39 + sector_range { + sector_count: 15 + } + } + trackdata { + side: 1 + track: 40 + up_to_track: 51 + sector_range { + sector_count: 14 + } + } + trackdata { + side: 1 + track: 52 + up_to_track: 62 + sector_range { + sector_count: 13 + } + } + trackdata { + side: 1 + track: 63 + up_to_track: 74 + sector_range { + sector_count: 12 + } + } + trackdata { + side: 1 + track: 75 + up_to_track: 79 + sector_range { + sector_count: 11 + } + } + } +} + +encoder { + victor9k { + trackdata { + original_data_rate_khz: 500 + post_index_gap_us: 1000.0 + pre_header_sync_bits: 120 + post_header_gap_bits: 48 + pre_data_sync_bits: 40 + post_data_gap_bits: 200 + } + trackdata { + head: 0 + min_cylinder: 0 + max_cylinder: 3 + original_period_ms: 237.9 + sector_range { + start_sector: 0 + sector_count: 19 + } + } + trackdata { + head: 0 + min_cylinder: 4 + max_cylinder: 15 + original_period_ms: 224.5 + sector_range { + sector_count: 18 + } + } + trackdata { + head: 0 + min_cylinder: 16 + max_cylinder: 26 + original_period_ms: 212.2 + sector_range { + sector_count: 17 + } + } + trackdata { + head: 0 + min_cylinder: 27 + max_cylinder: 37 + original_period_ms: 199.9 + sector_range { + sector_count: 16 + } + } + trackdata { + head: 0 + min_cylinder: 38 + max_cylinder: 47 + original_period_ms: 187.6 + sector_range { + sector_count: 15 + } + } + trackdata { + head: 0 + min_cylinder: 48 + max_cylinder: 59 + original_period_ms: 175.3 + sector_range { + sector_count: 14 + } + } + trackdata { + head: 0 + min_cylinder: 60 + max_cylinder: 70 + original_period_ms: 163.0 + sector_range { + sector_count: 13 + } + } + trackdata { + head: 0 + min_cylinder: 71 + max_cylinder: 79 + original_period_ms: 149.6 + sector_range { + sector_count: 12 + } + } + trackdata { + head: 1 + min_cylinder: 0 + max_cylinder: 7 + original_period_ms: 224.5 + sector_range { + start_sector: 0 + sector_count: 18 + } + } + trackdata { + head: 1 + min_cylinder: 8 + max_cylinder: 18 + original_period_ms: 212.2 + sector_range { + sector_count: 17 + } + } + trackdata { + head: 1 + min_cylinder: 19 + max_cylinder: 29 + original_period_ms: 199.9 + sector_range { + sector_count: 16 + } + } + trackdata { + head: 1 + min_cylinder: 30 + max_cylinder: 39 + original_period_ms: 187.6 + sector_range { + sector_count: 15 + } + } + trackdata { + head: 1 + min_cylinder: 40 + max_cylinder: 51 + original_period_ms: 175.3 + sector_range { + sector_count: 14 + } + } + trackdata { + head: 1 + min_cylinder: 52 + max_cylinder: 62 + original_period_ms: 163.0 + sector_range { + sector_count: 13 + } + } + trackdata { + head: 1 + min_cylinder: 63 + max_cylinder: 74 + original_period_ms: 149.6 + sector_range { + sector_count: 12 + } + } + trackdata { + head: 1 + min_cylinder: 75 + max_cylinder: 79 + original_period_ms: 144.0 + sector_range { + sector_count: 11 + } + } + } +} + +decoder { + victor9k {} +} + +cylinders { + start: 0 + end: 79 +} + +heads { + start: 0 + end: 1 +} + From c4700f3cb88bf8591b19b744bbd5035600716a2b Mon Sep 17 00:00:00 2001 From: Paul Devine Date: Wed, 19 Jan 2022 16:21:40 -0800 Subject: [PATCH 22/37] updated documentation for double-sided, made note about documentaiton bug for track format --- doc/disk-victor9k.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/doc/disk-victor9k.md b/doc/disk-victor9k.md index 01f49699..9bba7dc5 100644 --- a/doc/disk-victor9k.md +++ b/doc/disk-victor9k.md @@ -16,8 +16,8 @@ the speed zone allocation on head 1 differ from head 0... | 1 | 4-15 | 0-7 | 18 | 224.5 | | 2 | 16-26 | 8-18 | 17 | 212.2 | | 3 | 27-37 | 19-29 | 16 | 199.9 | -| 4 | 38-48 | 30-40 | 15 | 187.6 | -| 5 | 49-59 | 41-51 | 14 | 175.3 | +| 4 | 38-47\* | 30-39\* | 15 | 187.6 | +| 5 | 48-59 | 40-51 | 14 | 175.3 | | 6 | 60-70 | 52-62 | 13 | 163.0 | | 7 | 71-79 | 63-74 | 12 | 149.6 | | 8 | | 75-79 | 11 | 144.0 | @@ -26,8 +26,13 @@ the speed zone allocation on head 1 differ from head 0... FluxEngine, the disk always spins at 360 rpm, which corresponds to a rotational period of 166 ms.) -FluxEngine can read and write the single-sided variant of these. (Double-sided -will be trivial to do, it's just not done yet.) +\*The Victor 9000 Hardware Reference Manual has a bug in the documentation +and lists Zone 4 as ending with track 48 on head 0 and track 40 on head 1. +The above table matches observed data on various disks and the assembly +code in the boot loader, which ends Zone 4 with track 47 on head 0 +and track 39 on Head 1. + +FluxEngine can read and write both the single-sided and double-sided variants. Reading discs ------------- @@ -35,10 +40,14 @@ Reading discs Just do: ``` -fluxengine read victor9k-ss +fluxengine read + ``` -You should end up with an `victor9k.img` which is 627200 bytes long. +...where `` can be `victor9k_ss` or `victor9k_ds`. + +For `victor9k_ss` you should end up with an `victor9k.img` which is 627200 bytes long. +For `victor9k_ds` you should end up with an `victor9k.img` which is 1224192 bytes long. **Big warning!** The image is triangular, where each track occupies a different amount of space. Victor disk images are complicated due to the way the tracks @@ -50,7 +59,7 @@ Writing discs Just do: ``` -fluxengine read victor9k-ss -i victor9k.img +fluxengine read victor9k_ss -i victor9k.img ``` **Big warning!** This uses the same triangular disk image that reading uses. From a4f44933ec35165be51d8844533c3669eb10d239 Mon Sep 17 00:00:00 2001 From: Thomas Daede Date: Sat, 22 Jan 2022 07:30:24 -0800 Subject: [PATCH 23/37] Add NFD r0 image reader. This format is very similar to D88, but used mostly for PC-98 rather than PC-88. --- doc/using.md | 10 ++ lib/imagereader/imagereader.cc | 4 + lib/imagereader/imagereader.h | 1 + lib/imagereader/imagereader.proto | 2 + lib/imagereader/nfdimagereader.cc | 149 ++++++++++++++++++++++++++++++ mkninja.sh | 1 + 6 files changed, 167 insertions(+) create mode 100644 lib/imagereader/nfdimagereader.cc diff --git a/doc/using.md b/doc/using.md index 85fd4001..cc11e150 100644 --- a/doc/using.md +++ b/doc/using.md @@ -281,6 +281,16 @@ FluxEngine also supports a number of file system image formats. When using the The D88 reader should be used with the `ibm` profile and will override most encoding parameters on a track-by-track basis. + - `` + + Read from a [NFD r0 image file](https://www.pc98.org/project/doc/nfdr0.html), + commonly used by various Japanese PC emulators, including the NEC PC-98. **Read Only.** + + Only r0 version files are currently supported. + + The NFD reader should be used with the `ibm` profile and will override + most encoding parameters on a track-by-track basis. + - `` Write to a [LDBS generic image diff --git a/lib/imagereader/imagereader.cc b/lib/imagereader/imagereader.cc index ddd77b5b..3b33b97d 100644 --- a/lib/imagereader/imagereader.cc +++ b/lib/imagereader/imagereader.cc @@ -38,6 +38,9 @@ std::unique_ptr ImageReader::create(const ImageReaderProto& config) case ImageReaderProto::kD64: return ImageReader::createD64ImageReader(config); + case ImageReaderProto::kNfd: + return ImageReader::createNFDImageReader(config); + case ImageReaderProto::kNsi: return ImageReader::createNsiImageReader(config); @@ -65,6 +68,7 @@ void ImageReader::updateConfigForFilename(ImageReaderProto* proto, const std::st {".imd", [&]() { proto->mutable_imd(); }}, {".img", [&]() { proto->mutable_img(); }}, {".st", [&]() { proto->mutable_img(); }}, + {".nfd", [&]() { proto->mutable_nfd(); }}, {".nsi", [&]() { proto->mutable_nsi(); }}, {".td0", [&]() { proto->mutable_td0(); }}, {".xdf", [&]() { proto->mutable_img(); }}, diff --git a/lib/imagereader/imagereader.h b/lib/imagereader/imagereader.h index e17ac02b..1996f84d 100644 --- a/lib/imagereader/imagereader.h +++ b/lib/imagereader/imagereader.h @@ -27,6 +27,7 @@ public: static std::unique_ptr createDimImageReader(const ImageReaderProto& config); static std::unique_ptr createFdiImageReader(const ImageReaderProto& config); static std::unique_ptr createD88ImageReader(const ImageReaderProto& config); + static std::unique_ptr createNFDImageReader(const ImageReaderProto& config); public: virtual std::unique_ptr readImage() = 0; diff --git a/lib/imagereader/imagereader.proto b/lib/imagereader/imagereader.proto index 3d31de86..dd5002f8 100644 --- a/lib/imagereader/imagereader.proto +++ b/lib/imagereader/imagereader.proto @@ -48,6 +48,7 @@ message Td0InputProto {} message DimInputProto {} message FdiInputProto {} message D88InputProto {} +message NFDInputProto {} message ImageReaderProto { optional string filename = 1 [(help) = "filename of input sector image"]; @@ -62,6 +63,7 @@ message ImageReaderProto { DimInputProto dim = 9; FdiInputProto fdi = 10; D88InputProto d88 = 11; + NFDInputProto nfd = 12; } } diff --git a/lib/imagereader/nfdimagereader.cc b/lib/imagereader/nfdimagereader.cc new file mode 100644 index 00000000..cd41b350 --- /dev/null +++ b/lib/imagereader/nfdimagereader.cc @@ -0,0 +1,149 @@ +#include "globals.h" +#include "flags.h" +#include "sector.h" +#include "imagereader/imagereader.h" +#include "image.h" +#include "proto.h" +#include "lib/config.pb.h" +#include "fmt/format.h" +#include +#include +#include + +// reader based on this partial documentation of the D88 format: +// https://www.pc98.org/project/doc/d88.html + +class NFDImageReader : public ImageReader +{ +public: + NFDImageReader(const ImageReaderProto& config): + ImageReader(config) + {} + + std::unique_ptr readImage() + { + std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary); + if (!inputFile.is_open()) + Error() << "cannot open input file"; + + Bytes fileId(14); // read first entry of track table as well + inputFile.read((char*) fileId.begin(), fileId.size()); + + if (fileId == Bytes("T98FDDIMAGE.R1")) { + Error() << "NFD: r1 images are not currently supported"; + } + if (fileId != Bytes("T98FDDIMAGE.R0")) { + Error() << "NFD: could not find NFD header"; + } + + Bytes header(0x10a10); + inputFile.seekg( 0, std::ios::beg ); + inputFile.read((char*) header.begin(), header.size()); + + ByteReader headerReader(header); + + char heads = headerReader.seek(0x115).read_8(); + if (heads != 2) { + Error() << "NFD: unsupported number of heads"; + } + + if (config.encoder().format_case() != EncoderProto::FormatCase::FORMAT_NOT_SET) + std::cout << "NFD: overriding configured format"; + + auto ibm = config.mutable_encoder()->mutable_ibm(); + config.mutable_cylinders()->set_end(0); + std::cout << "NFD: HD 1.2MB mode\n"; + if (config.flux_sink().dest_case() == FluxSinkProto::DestCase::kDrive) { + config.mutable_flux_sink()->mutable_drive()->set_high_density(true); + } + + std::unique_ptr image(new Image); + for (int track = 0; track < 163; track++) + { + auto trackdata = ibm->add_trackdata(); + trackdata->set_clock_rate_khz(500); + trackdata->set_track_length_ms(167); + auto sectors = trackdata->mutable_sectors(); + int currentTrackCylinder = -1; + int currentTrackHead = -1; + int trackSectorSize = -1; + + for (int sectorInTrack = 0; sectorInTrack < 26; sectorInTrack++){ + headerReader.seek(0x120 + track * 26 * 16 + sectorInTrack * 16); + int cylinder = headerReader.read_8(); + int head = headerReader.read_8(); + int sectorId = headerReader.read_8(); + int sectorSize = 128 << headerReader.read_8(); + int mfm = headerReader.read_8(); + int ddam = headerReader.read_8(); + int status = headerReader.read_8(); + headerReader.skip(9); // skip ST0, ST1, ST2, PDA, reserved(5) + if (cylinder == 0xFF) + continue; + if (ddam != 0) + Error() << "NFD: nonzero ddam currently unsupported"; + if (status != 0) + Error() << "NFD: nonzero fdd status codes are currently unsupported"; + if (currentTrackCylinder < 0) { + currentTrackCylinder = cylinder; + currentTrackHead = head; + } else if (currentTrackCylinder != cylinder) { + Error() << "NFD: all sectors in a track must belong to the same cylinder"; + } else if (currentTrackHead != head) { + Error() << "NFD: all sectors in a track must belong to the same head"; + } + if (trackSectorSize < 0) { + trackSectorSize = sectorSize; + // this is the first sector we've read, use it settings for per-track data + trackdata->set_cylinder(cylinder); + trackdata->set_head(head); + trackdata->set_sector_size(sectorSize); + trackdata->set_use_fm(!mfm); + if (!mfm) { + trackdata->set_gap_fill_byte(0xffff); + trackdata->set_idam_byte(0xf57e); + trackdata->set_dam_byte(0xf56f); + } + // create timings to approximately match N88-BASIC + if (sectorSize <= 128) { + trackdata->set_gap0(0x1b); + trackdata->set_gap2(0x09); + trackdata->set_gap3(0x1b); + } else if (sectorSize <= 256) { + trackdata->set_gap0(0x36); + trackdata->set_gap3(0x36); + } + } else if (trackSectorSize != sectorSize) { + Error() << "NFD: multiple sector sizes per track are currently unsupported"; + } + Bytes data(sectorSize); + inputFile.read((char*) data.begin(), data.size()); + const auto& sector = image->put(cylinder, head, sectorId); + sector->status = Sector::OK; + sector->logicalTrack = cylinder; + sector->physicalCylinder = cylinder; + sector->logicalSide = sector->physicalHead = head; + sector->logicalSector = sectorId; + sector->data = data; + + sectors->add_sector(sectorId); + if (config.cylinders().end() < cylinder) + config.mutable_cylinders()->set_end(cylinder); + } + } + + image->calculateSize(); + const Geometry& geometry = image->getGeometry(); + std::cout << fmt::format("NFD: read {} tracks, {} sides\n", + geometry.numTracks, geometry.numSides); + return image; + } + +}; + +std::unique_ptr ImageReader::createNFDImageReader( + const ImageReaderProto& config) +{ + return std::unique_ptr(new NFDImageReader(config)); +} + diff --git a/mkninja.sh b/mkninja.sh index e3a3e5ce..1c4b6553 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -469,6 +469,7 @@ buildlibrary libbackend.a \ lib/imagewriter/imagewriter.cc \ lib/imagewriter/imgimagewriter.cc \ lib/imagewriter/ldbsimagewriter.cc \ + lib/imagereader/nfdimagereader.cc \ lib/imagewriter/nsiimagewriter.cc \ lib/imagewriter/rawimagewriter.cc \ lib/imginputoutpututils.cc \ From 6e83ed5654de84b94a378166b473754a54246004 Mon Sep 17 00:00:00 2001 From: David Given Date: Sat, 22 Jan 2022 20:37:58 +0100 Subject: [PATCH 24/37] Update clang-format file. --- .clang-format | 1 + 1 file changed, 1 insertion(+) diff --git a/.clang-format b/.clang-format index 8bda3a44..7750ce63 100644 --- a/.clang-format +++ b/.clang-format @@ -30,6 +30,7 @@ IndentWrappedFunctionNames: 'false' KeepEmptyLinesAtTheStartOfBlocks: 'true' PointerAlignment: Left ReflowComments: 'true' +SortIncludes: 'false' SortUsingDeclarations: 'true' SpaceAfterTemplateKeyword: 'true' SpaceBeforeAssignmentOperators: 'true' From d6db2128df06bead99931647d45f3550d0ce6e77 Mon Sep 17 00:00:00 2001 From: David Given Date: Sat, 22 Jan 2022 20:39:20 +0100 Subject: [PATCH 25/37] Toggle DTR on Unixes as well as on Windows. --- lib/usb/serial.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/usb/serial.cc b/lib/usb/serial.cc index 04773071..5f1ec151 100644 --- a/lib/usb/serial.cc +++ b/lib/usb/serial.cc @@ -9,6 +9,8 @@ #include #include #include +#include +#include #if defined __WIN32__ #include @@ -146,6 +148,15 @@ t.c_cc[VMIN] = 1; cfsetspeed(&t, 9600); tcsetattr(_fd, TCSANOW, &t); + + /* Toggle DTR to reset the device. */ + + int flag = TIOCM_DTR; + if (ioctl(_fd, TIOCMBIC, &flag) == -1) + Error() << fmt::format("cannot clear DTR on serial port: {}", strerror(errno)); + usleep(200000); + if (ioctl(_fd, TIOCMBIS, &flag) == -1) + Error() << fmt::format("cannot set DTR on serial port: {}", strerror(errno)); } ~SerialPortImpl() override From 745e0685a4fc7db43dfae9c312f7d4d65e8262cd Mon Sep 17 00:00:00 2001 From: David Given Date: Sat, 22 Jan 2022 20:41:27 +0100 Subject: [PATCH 26/37] Allow specifying the GreaseWeazle serial port directly (for devices which don't have the GreaseWeazle VID/PID). --- lib/usb/usb.cc | 117 ++++++++++++++++++++++++------------------- lib/usb/usb.proto | 8 ++- lib/usb/usbfinder.cc | 84 +++++++++++-------------------- 3 files changed, 102 insertions(+), 107 deletions(-) diff --git a/lib/usb/usb.cc b/lib/usb/usb.cc index 2e54e66f..95c4577d 100644 --- a/lib/usb/usb.cc +++ b/lib/usb/usb.cc @@ -13,72 +13,87 @@ static USB* usb = NULL; -USB::~USB() -{} +USB::~USB() {} static std::unique_ptr selectDevice() { - auto candidates = findUsbDevices({ FLUXENGINE_ID, GREASEWEAZLE_ID }); - if (candidates.size() == 0) - Error() << "no devices found (is one plugged in? Do you have the appropriate " - "permissions?"; + auto candidates = findUsbDevices({FLUXENGINE_ID, GREASEWEAZLE_ID}); + if (candidates.size() == 0) + Error() << "no devices found (is one plugged in? Do you have the " + "appropriate permissions?"; - if (config.usb().has_serial()) - { - auto wantedSerial = config.usb().serial(); - for (auto& c : candidates) - { - if (c->serial == wantedSerial) - return std::move(c); - } - Error() << "serial number not found (try without one to list or autodetect devices)"; - } + if (config.usb().has_serial()) + { + auto wantedSerial = config.usb().serial(); + for (auto& c : candidates) + { + if (c->serial == wantedSerial) + return std::move(c); + } + Error() << "serial number not found (try without one to list or " + "autodetect devices)"; + } - if (candidates.size() == 1) - return std::move(candidates[0]); + if (candidates.size() == 1) + return std::move(candidates[0]); - std::cerr << "More than one device detected; use --usb.serial= to select one:\n"; - for (const auto& c : candidates) - { - std::cerr << " "; - switch (c->id) - { - case FLUXENGINE_ID: - std::cerr << fmt::format("FluxEngine: {}\n", c->serial); - break; + std::cerr << "More than one device detected; use --usb.serial= to " + "select one:\n"; + for (const auto& c : candidates) + { + std::cerr << " "; + switch (c->id) + { + case FLUXENGINE_ID: + std::cerr << fmt::format("FluxEngine: {}\n", c->serial); + break; - case GREASEWEAZLE_ID: - std::cerr << fmt::format("GreaseWeazle: {} on {}\n", c->serial, c->serialPort); - break; - } - } - exit(1); + case GREASEWEAZLE_ID: + std::cerr << fmt::format( + "GreaseWeazle: {} on {}\n", c->serial, c->serialPort); + break; + } + } + exit(1); } USB* get_usb_impl() { - auto candidate = selectDevice(); - switch (candidate->id) - { - case FLUXENGINE_ID: - std::cerr << fmt::format("Using FluxEngine {}\n", candidate->serial); - return createFluxengineUsb(candidate->device); + /* Special case for certain configurations. */ - case GREASEWEAZLE_ID: - std::cerr << fmt::format("Using GreaseWeazle {} on {}\n", candidate->serial, candidate->serialPort); - return createGreaseWeazleUsb(candidate->serialPort, config.usb().greaseweazle()); + if (config.usb().has_greaseweazle() && + config.usb().greaseweazle().has_port()) + { + const auto& conf = config.usb().greaseweazle(); + std::cerr << fmt::format( + "Using GreaseWeazle on serial port {}\n", conf.port()); + return createGreaseWeazleUsb(conf.port(), conf); + } - default: - std::cerr << fmt::format("Using Unknown GW Compatible {} on {}\n", candidate->serial, candidate->serialPort); - GreaseWeazleProto gwconfig = config.usb().greaseweazle(); - return createGreaseWeazleUsb(candidate->serialPort, gwconfig ); - } + /* Otherwise, select a device by USB ID. */ + + auto candidate = selectDevice(); + switch (candidate->id) + { + case FLUXENGINE_ID: + std::cerr << fmt::format( + "Using FluxEngine {}\n", candidate->serial); + return createFluxengineUsb(candidate->device); + + case GREASEWEAZLE_ID: + std::cerr << fmt::format("Using GreaseWeazle {} on {}\n", + candidate->serial, + candidate->serialPort); + return createGreaseWeazleUsb( + candidate->serialPort, config.usb().greaseweazle()); + + default: Error() << "internal"; + } } USB& getUsb() { - if (!usb) - usb = get_usb_impl(); - return *usb; + if (!usb) + usb = get_usb_impl(); + return *usb; } - diff --git a/lib/usb/usb.proto b/lib/usb/usb.proto index f3d7c4b7..38ab5a9b 100644 --- a/lib/usb/usb.proto +++ b/lib/usb/usb.proto @@ -9,7 +9,9 @@ message GreaseWeazleProto { SHUGART = 2; }; - optional BusType bus_type = 1 + optional string port = 1 + [(help) = "GreaseWeazle serial port to use"]; + optional BusType bus_type = 2 [(help) = "which FDD bus type is in use", default = IBMPC]; } @@ -19,5 +21,7 @@ message UsbProto { [(help) = "serial number of FluxEngine or GreaseWeazle device to use"]; } - optional GreaseWeazleProto greaseweazle = 2 [(help) = "GreaseWeazle-specific options"]; + oneof config { + GreaseWeazleProto greaseweazle = 2 [(help) = "GreaseWeazle-specific options"]; + } } diff --git a/lib/usb/usbfinder.cc b/lib/usb/usbfinder.cc index 557a383f..dbba55a3 100644 --- a/lib/usb/usbfinder.cc +++ b/lib/usb/usbfinder.cc @@ -9,66 +9,42 @@ static const std::string get_serial_number(const libusbp::device& device) { - try - { - return device.get_serial_number(); - } - catch (const libusbp::error& e) - { - if (e.has_code(LIBUSBP_ERROR_NO_SERIAL_NUMBER)) - return "n/a"; - throw; - } + try + { + return device.get_serial_number(); + } + catch (const libusbp::error& e) + { + if (e.has_code(LIBUSBP_ERROR_NO_SERIAL_NUMBER)) + return "n/a"; + throw; + } } -std::vector> findUsbDevices(const std::set& ids) +std::vector> findUsbDevices( + const std::set& ids) { - std::vector> candidates; - for (const auto& it : libusbp::list_connected_devices()) - { - auto candidate = std::make_unique(); - candidate->device = it; + std::vector> candidates; + for (const auto& it : libusbp::list_connected_devices()) + { + auto candidate = std::make_unique(); + candidate->device = it; - uint32_t id = (it.get_vendor_id() << 16) | it.get_product_id(); - if (ids.contains(id)) - { - candidate->id = id; - candidate->serial = get_serial_number(it); - - if (id == GREASEWEAZLE_ID) - { - libusbp::serial_port port(candidate->device); - candidate->serialPort = port.get_name(); - } - - candidates.push_back(std::move(candidate)); - } - } - - if (candidates.size() == 0) { - for (const auto& it : libusbp::list_connected_devices()) + uint32_t id = (it.get_vendor_id() << 16) | it.get_product_id(); + if (ids.contains(id)) { - auto candidate = std::make_unique(); - candidate->device = it; - uint32_t id = (it.get_vendor_id() << 16) | it.get_product_id(); - candidate->id = id; - candidate->serial = get_serial_number(it); - printf("USB ID %04x %04x: ", it.get_vendor_id(), it.get_product_id()); - try + candidate->id = id; + candidate->serial = get_serial_number(it); + + if (id == GREASEWEAZLE_ID) { - libusbp::serial_port port(candidate->device, 0, true); - candidate->serialPort = port.get_name(); - printf("generic serialPort found\n"); - candidates.push_back(std::move(candidate)); - } - catch(const libusbp::error & error) - { - // not a serial port! - printf("not a port!\n"); - continue; + libusbp::serial_port port(candidate->device); + candidate->serialPort = port.get_name(); } - } - } - return candidates; + candidates.push_back(std::move(candidate)); + } + } + + return candidates; } From 83d44bd03e162470afd15ba401b55629a8cdd27b Mon Sep 17 00:00:00 2001 From: David Given Date: Sat, 22 Jan 2022 20:46:40 +0100 Subject: [PATCH 27/37] Update documentation. --- doc/greaseweazle.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/greaseweazle.md b/doc/greaseweazle.md index b1ba8f02..656715a9 100644 --- a/doc/greaseweazle.md +++ b/doc/greaseweazle.md @@ -16,6 +16,13 @@ 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. +**If you are using GreaseWeazle-compatible hardware** such as the +[adafruit-floppy](https://github.com/adafruit/Adafruit_Floppy) project, then +FluxEngine will still work; however, as the USB VID/PID won't be that of a real +GreaseWeazle, the the FluxEngine client can't autodetect it. Instead, you'll +need to specify the serial port manually with something like +`--usb.greaseweazle.port=/dev/ttyACM0` or `--usb.greaseweazle.port=COM5`. + **If you were using a previous version on Windows** you might have installed the WinUSB driver. That's no longer needed, and will in fact not work. You'll need to use Zadig to restore the old driver; to do this, make sure the left From 3e4d4cc002fe87c0b9fe18b8c1a6e3c982d86148 Mon Sep 17 00:00:00 2001 From: David Given Date: Sat, 22 Jan 2022 22:29:34 +0100 Subject: [PATCH 28/37] Hotfix for Windows build. --- lib/usb/serial.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/usb/serial.cc b/lib/usb/serial.cc index 5f1ec151..1c6bd3ca 100644 --- a/lib/usb/serial.cc +++ b/lib/usb/serial.cc @@ -9,8 +9,6 @@ #include #include #include -#include -#include #if defined __WIN32__ #include @@ -123,6 +121,7 @@ #else #include + #include class SerialPortImpl : public SerialPort { From 82d8c40b5f6255809485009bb1a1559f0ce077a6 Mon Sep 17 00:00:00 2001 From: Thomas Daede Date: Tue, 25 Jan 2022 09:53:56 -0800 Subject: [PATCH 29/37] Add documentation for PC-98, X68000, etc. --- README.md | 2 ++ doc/disk-ibm.md | 46 ++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 63efb01e..5ce44cbd 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,8 @@ people who've had it work). | [Brother 240kB](doc/disk-brother.md) | 🦄 | 🦄 | | | [Brother FB-100](doc/disk-fb100.md) | 🦖 | | Tandy Model 100, Husky Hunter, knitting machines | | [Macintosh 800kB](doc/disk-macintosh.md) | 🦄 | 🦄 | and probably the 400kB too | +| [NEC PC-98](doc/disk-ibm.md) | 🦄 | 🦄 | trimode drive not required | +| [Sharp X68000](doc/disk-ibm.md) | 🦄 | 🦄 | | | [TRS-80](doc/disk-trs80.md) | 🦖 | 🦖* | a minor variation of the IBM scheme | {: .datatable } diff --git a/doc/disk-ibm.md b/doc/disk-ibm.md index a2945200..188dd8df 100644 --- a/doc/disk-ibm.md +++ b/doc/disk-ibm.md @@ -14,6 +14,10 @@ metadata. Systems which use IBM scheme disks include but are not limited to: - the TRS-80 - late era Commodore machines (the 1571 and so on) - most CP/M machines + - NEC PC-88 series + - NEC PC-98 series + - Sharp X68000 + - Fujitsu FM Towns - etc FluxEngine supports reading these. However, some variants are more peculiar @@ -93,11 +97,12 @@ correct format to use. Mixed-format disks ------------------ -Some disks, usually those belonging to early CP/M machines, have more than one -format on the disk at once. Typically, the first few tracks will be low-density -FM encoded and will be read by the machine's ROM; those tracks contain new -floppy drive handling code capable of coping with MFM data, and so the rest of -the disk will use that, allowing them to store more data. +Some disks, such as those belonging to early CP/M machines, or N88-Basic disks +(for PC-88 and PC-98), have more than one format on the disk at once. Typically, +the first few tracks will be low-density FM encoded and will be read by the +machine's ROM; those tracks contain new floppy drive handling code capable of +coping with MFM data, and so the rest of the disk will use that, allowing them +to store more data. FluxEngine can read these fine, but it tends to get a bit confused when it sees tracks with differing numbers of sectors --- if track 0 has 32 sectors but @@ -105,6 +110,31 @@ track 1 has 16, it will assume that sectors 16..31 are missing on track 1 and size the image file accordingly. This can be worked around by specifying the size of each track; see the `eco1` read profile for an example. -Writing can be made to work too, but there is currently no example. Please [get -in touch](https://github.com/davidgiven/fluxengine/issues/new) if you have -specific requirements (nothing's come up yet). +N88-Basic format floppies can be written by either specifying the `n88basic` +format, or by using D88 or NFD format images which include explicit sector +layout information. + +Writing other formats can be made to work too, by creating a custom format +specifier, using the `n88basic` format as an example. +Please [get in touch](https://github.com/davidgiven/fluxengine/issues/new) if +you have specific requirements. + +360rpm 3.5" disks +----------------- + +Japanese PCs (NEC PC-98, Sharp X68000, Fujitsu FM Towns) spin their floppy +drives at 360rpm rather than the more typical 300rpm. This was done in order +to be fully backwards compatible with 5.25" disks, while using the exact +same floppy controller. Later models of the PC-9821, as well as most USB floppy +drives, feature "tri-mode" support which in addition to normal 300rpm modes, +can change their speed to read and write 360rpm DD and HD disks. + +Neither the FluxEngine or Greaseweazle hardware can currently command a +tri-mode drive to spin at 360rpm, however an older 360rpm-only drive will work +to read these formats. + +Alternately, the FluxEngine software can resale the flux pulses to enable +reading and writing these formats with a plain 300rpm drive. To do this, +specify the following two additional options: + + --flux_source.rescale=1.2 --flux_sink.rescale=1.2 From 77257b0989afaa4c2d8fabd0526519c42e384e6c Mon Sep 17 00:00:00 2001 From: Thomas Daede Date: Wed, 26 Jan 2022 07:40:42 -0800 Subject: [PATCH 30/37] Support time-based reads for Greaseweazle V24+. This substantially speeds up read and verify operations, allowing only 1.2 revs rather than 2 revs to be read. Fixes #426 --- lib/usb/greaseweazleusb.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/usb/greaseweazleusb.cc b/lib/usb/greaseweazleusb.cc index 200199a9..74215f69 100644 --- a/lib/usb/greaseweazleusb.cc +++ b/lib/usb/greaseweazleusb.cc @@ -278,14 +278,13 @@ public: if (hardSectorThreshold != 0) Error() << "hard sectors are currently unsupported on the GreaseWeazel"; - int revolutions = (readTime+_revolutions-1) / _revolutions; - 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) @@ -302,8 +301,8 @@ public: cmd.writer() .write_8(CMD_READ_FLUX) .write_8(cmd.size()) - .write_le32(0) //ticks default value (guessed) - .write_le32(revolutions + (synced ? 1 : 0)); + .write_le32((readTime + (synced ? _revolutions : 0)) / _clock) + .write_le32(0); do_command(cmd); } } From 6860317a5871f65a9de154d1d558fedcceb2e518 Mon Sep 17 00:00:00 2001 From: Thomas Daede Date: Wed, 26 Jan 2022 11:45:28 -0800 Subject: [PATCH 31/37] Add flag to skip verification during write. Fixes #425 --- src/fe-write.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/fe-write.cc b/src/fe-write.cc index c36efa02..cb418636 100644 --- a/src/fe-write.cc +++ b/src/fe-write.cc @@ -55,6 +55,13 @@ static StringFlag destHeads( setRange(config.mutable_heads(), value); }); +static ActionFlag noVerifyFlag( + { "--no-verify", "-n" }, + "skip verification of write", + []{ + config.clear_decoder(); + }); + int mainWrite(int argc, const char* argv[]) { if (argc == 1) From 9bb6e159001c0664f7f6f46b771ebaf2f1a414ff Mon Sep 17 00:00:00 2001 From: David Given Date: Wed, 26 Jan 2022 22:09:50 +0100 Subject: [PATCH 32/37] Don't crash when trying to read flux from a fl2 file where there isn't any flux. --- lib/fluxsource/fl2fluxsource.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fluxsource/fl2fluxsource.cc b/lib/fluxsource/fl2fluxsource.cc index 0cbfb1b8..8ffcfe49 100644 --- a/lib/fluxsource/fl2fluxsource.cc +++ b/lib/fluxsource/fl2fluxsource.cc @@ -31,7 +31,7 @@ public: return std::make_unique(track.flux()); } - return std::unique_ptr(); + return std::make_unique(); } void recalibrate() {} From 3369029e9a35a28cd79ba15c4adf5df68cfb5001 Mon Sep 17 00:00:00 2001 From: David Given Date: Wed, 26 Jan 2022 23:13:22 +0100 Subject: [PATCH 33/37] Don't use signed ints for the track/side/sector values. --- lib/sector.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/sector.h b/lib/sector.h index 1c90cefc..1bfd0706 100644 --- a/lib/sector.h +++ b/lib/sector.h @@ -34,11 +34,11 @@ public: nanoseconds_t headerEndTime = 0; nanoseconds_t dataStartTime = 0; nanoseconds_t dataEndTime = 0; - int physicalCylinder = 0; - int physicalHead = 0; - int logicalTrack = 0; - int logicalSide = 0; - int logicalSector = 0; + unsigned physicalCylinder = 0; + unsigned physicalHead = 0; + unsigned logicalTrack = 0; + unsigned logicalSide = 0; + unsigned logicalSector = 0; Bytes data; std::vector> records; From 6ac9d34aacb8677ca28b3955be8c2d7495def91f Mon Sep 17 00:00:00 2001 From: David Given Date: Wed, 26 Jan 2022 23:13:40 +0100 Subject: [PATCH 34/37] Don't rely on the geometry autodetector for Brother formats. --- src/formats/brother120.textpb | 12 +++++++++++- src/formats/brother240.textpb | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/formats/brother120.textpb b/src/formats/brother120.textpb index 65beef8e..a6f10179 100644 --- a/src/formats/brother120.textpb +++ b/src/formats/brother120.textpb @@ -17,7 +17,17 @@ image_reader { image_writer { filename: "brother120.img" - img {} + img { + tracks: 39 + sides: 1 + trackdata { + sector_size: 256 + sector_range { + start_sector: 0 + sector_count: 12 + } + } + } } encoder { diff --git a/src/formats/brother240.textpb b/src/formats/brother240.textpb index 3506476f..fdbcd196 100644 --- a/src/formats/brother240.textpb +++ b/src/formats/brother240.textpb @@ -17,7 +17,17 @@ image_reader { image_writer { filename: "brother240.img" - img {} + img { + tracks: 78 + sides: 1 + trackdata { + sector_size: 256 + sector_range { + start_sector: 0 + sector_count: 12 + } + } + } } encoder { From 8fb2ad19868a8c080252dc0c4ad1581d1d80f777 Mon Sep 17 00:00:00 2001 From: David Given Date: Wed, 26 Jan 2022 23:53:16 +0100 Subject: [PATCH 35/37] Automatically set the number of heads and cylinders where appropriate. --- lib/imagereader/d88imagereader.cc | 15 +++++++++++++++ lib/imagereader/dimimagereader.cc | 15 +++++++++++++++ lib/imagereader/fdiimagereader.cc | 15 +++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/lib/imagereader/d88imagereader.cc b/lib/imagereader/d88imagereader.cc index 852702da..cfdaf93d 100644 --- a/lib/imagereader/d88imagereader.cc +++ b/lib/imagereader/d88imagereader.cc @@ -165,6 +165,21 @@ public: const Geometry& geometry = image->getGeometry(); std::cout << fmt::format("D88: read {} tracks, {} sides\n", geometry.numTracks, geometry.numSides); + + if (!config.has_heads()) + { + auto* heads = config.mutable_heads(); + heads->set_start(0); + heads->set_end(geometry.numSides - 1); + } + + if (!config.has_cylinders()) + { + auto* cylinders = config.mutable_cylinders(); + cylinders->set_start(0); + cylinders->set_end(geometry.numTracks - 1); + } + return image; } diff --git a/lib/imagereader/dimimagereader.cc b/lib/imagereader/dimimagereader.cc index 2b34ac33..d9612c7c 100644 --- a/lib/imagereader/dimimagereader.cc +++ b/lib/imagereader/dimimagereader.cc @@ -136,6 +136,21 @@ public: std::cout << fmt::format("DIM: read {} tracks, {} sides, {} kB total\n", geometry.numTracks, geometry.numSides, ((int)inputFile.tellg() - 256) / 1024); + + if (!config.has_heads()) + { + auto* heads = config.mutable_heads(); + heads->set_start(0); + heads->set_end(geometry.numSides - 1); + } + + if (!config.has_cylinders()) + { + auto* cylinders = config.mutable_cylinders(); + cylinders->set_start(0); + cylinders->set_end(geometry.numTracks - 1); + } + return image; } diff --git a/lib/imagereader/fdiimagereader.cc b/lib/imagereader/fdiimagereader.cc index ab17c358..3c65dd73 100644 --- a/lib/imagereader/fdiimagereader.cc +++ b/lib/imagereader/fdiimagereader.cc @@ -109,6 +109,21 @@ public: std::cout << fmt::format("FDI: read {} tracks, {} sides, {} kB total\n", geometry.numTracks, geometry.numSides, ((int)inputFile.tellg() - headerSize) / 1024); + + if (!config.has_heads()) + { + auto* heads = config.mutable_heads(); + heads->set_start(0); + heads->set_end(geometry.numSides - 1); + } + + if (!config.has_cylinders()) + { + auto* cylinders = config.mutable_cylinders(); + cylinders->set_start(0); + cylinders->set_end(geometry.numTracks - 1); + } + return image; } From 2439736cb413fd4da6fa632f97eac045ce131351 Mon Sep 17 00:00:00 2001 From: David Given Date: Wed, 26 Jan 2022 23:53:44 +0100 Subject: [PATCH 36/37] When autoconfiguring, set up the decoder to allow disk verification. --- lib/imagereader/dimimagereader.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/imagereader/dimimagereader.cc b/lib/imagereader/dimimagereader.cc index d9612c7c..161ca225 100644 --- a/lib/imagereader/dimimagereader.cc +++ b/lib/imagereader/dimimagereader.cc @@ -129,6 +129,8 @@ public: Error() << fmt::format("DIM: unknown media byte 0x%02x, could not determine write profile automatically", mediaByte); break; } + + config.mutable_decoder()->mutable_ibm(); } image->calculateSize(); From 77d6d0d5be2efb96b2ef4bc06da013e6427bedd2 Mon Sep 17 00:00:00 2001 From: David Given Date: Thu, 27 Jan 2022 20:49:38 +0100 Subject: [PATCH 37/37] Fix --no-verify. --- src/fe-write.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/fe-write.cc b/src/fe-write.cc index cb418636..fcc3dab9 100644 --- a/src/fe-write.cc +++ b/src/fe-write.cc @@ -17,6 +17,7 @@ #include static FlagGroup flags; +static bool verify = true; static StringFlag sourceImage( { "--input", "-i" }, @@ -59,7 +60,7 @@ static ActionFlag noVerifyFlag( { "--no-verify", "-n" }, "skip verification of write", []{ - config.clear_decoder(); + verify = false; }); int mainWrite(int argc, const char* argv[]) @@ -75,7 +76,7 @@ int mainWrite(int argc, const char* argv[]) std::unique_ptr fluxSink(FluxSink::create(config.flux_sink())); std::unique_ptr decoder; - if (config.has_decoder()) + if (config.has_decoder() && verify) decoder = AbstractDecoder::create(config.decoder()); std::unique_ptr fluxSource;