From 4c776d584b56224adbd6ec9e01a21ee956ca51ca Mon Sep 17 00:00:00 2001 From: dg Date: Fri, 7 Apr 2023 15:00:20 +0000 Subject: [PATCH] Add read support for A2R v2 files. --- lib/build.mk | 1 + lib/bytes.cc | 6 + lib/bytes.h | 1 + lib/fluxsource/a2rfluxsource.cc | 182 +++++++++++++++++++++++++++++ lib/fluxsource/fl2fluxsource.cc | 13 --- lib/fluxsource/fluxsource.cc | 9 ++ lib/fluxsource/fluxsource.h | 16 +++ lib/fluxsource/fluxsource.proto | 19 ++- lib/fluxsource/memoryfluxsource.cc | 14 --- lib/fluxsource/scpfluxsource.cc | 4 +- 10 files changed, 231 insertions(+), 34 deletions(-) create mode 100644 lib/fluxsource/a2rfluxsource.cc diff --git a/lib/build.mk b/lib/build.mk index e14e6b7d..b05ad524 100644 --- a/lib/build.mk +++ b/lib/build.mk @@ -19,6 +19,7 @@ LIBFLUXENGINE_SRCS = \ lib/fluxsink/hardwarefluxsink.cc \ lib/fluxsink/scpfluxsink.cc \ lib/fluxsink/vcdfluxsink.cc \ + lib/fluxsource/a2rfluxsource.cc \ lib/fluxsource/cwffluxsource.cc \ lib/fluxsource/erasefluxsource.cc \ lib/fluxsource/fl2fluxsource.cc \ diff --git a/lib/bytes.cc b/lib/bytes.cc index a78114f2..71c3be67 100644 --- a/lib/bytes.cc +++ b/lib/bytes.cc @@ -47,6 +47,12 @@ Bytes::Bytes(const std::string& s): _high(s.size()) {} +Bytes::Bytes(const char* s): + _data(createVector((const uint8_t*)s, strlen(s))), + _low(0), + _high(strlen(s)) +{} + Bytes::Bytes(std::initializer_list data): _data(createVector(data)), _low(0), diff --git a/lib/bytes.h b/lib/bytes.h index 070de86a..cf30ad55 100644 --- a/lib/bytes.h +++ b/lib/bytes.h @@ -12,6 +12,7 @@ public: Bytes(); Bytes(unsigned size); Bytes(const uint8_t* ptr, size_t len); + Bytes(const char* data); Bytes(const std::string& data); Bytes(std::initializer_list data); Bytes(std::shared_ptr> data); diff --git a/lib/fluxsource/a2rfluxsource.cc b/lib/fluxsource/a2rfluxsource.cc new file mode 100644 index 00000000..9d64ff2d --- /dev/null +++ b/lib/fluxsource/a2rfluxsource.cc @@ -0,0 +1,182 @@ +#include "globals.h" +#include "fluxmap.h" +#include "lib/fluxsource/fluxsource.pb.h" +#include "fluxsource/fluxsource.h" +#include "proto.h" +#include "fmt/format.h" +#include + +struct A2Rv2Flux +{ + std::vector flux; + nanoseconds_t index; +}; + +class A2rv2FluxSourceIterator : public FluxSourceIterator +{ +public: + A2rv2FluxSourceIterator(A2Rv2Flux& flux): _flux(flux) {} + + bool hasNext() const override + { + return _count != _flux.flux.size(); + } + + std::unique_ptr next() override + { + nanoseconds_t index = _flux.index; + Bytes& asbytes = _flux.flux[_count++]; + ByteReader br(asbytes); + + auto fluxmap = std::make_unique(); + while (!br.eof()) + { + unsigned aticks = 0; + for (;;) + { + unsigned i = br.read_8(); + aticks += i; + if (i != 0xff) + break; + } + + nanoseconds_t interval = aticks * 125; + if ((index >= 0) && (index < interval)) + { + fluxmap->appendInterval(index); + fluxmap->appendIndex(); + interval -= index; + } + index -= interval; + + fluxmap->appendInterval(interval / NS_PER_TICK); + fluxmap->appendPulse(); + } + + return fluxmap; + } + +private: + A2Rv2Flux& _flux; + int _count = 0; +}; + +class A2rFluxSource : public FluxSource +{ +public: + A2rFluxSource(const A2rFluxSourceProto& config): _config(config) + { + _data = Bytes::readFromFile(_config.filename()); + ByteReader br(_data); + + switch (br.read_be32()) + { + case 0x41325232: + { + _version = 2; + Bytes info = findChunk("INFO"); + int disktype = info[33]; + if (disktype == 1) + { + /* 5.25" with quarter stepping. */ + ::config.set_tpi(48); + ::config.mutable_drive()->set_tracks(160); + ::config.mutable_drive()->set_heads(1); + ::config.mutable_drive()->set_head_width(4); + ::config.mutable_drive()->set_tpi(48 * 4); + } + else + { + /* 3.5". 96 is wrong but that's what we use. */ + ::config.set_tpi(96); + ::config.mutable_drive()->set_tpi(96); + } + + Bytes stream = findChunk("STRM"); + ByteReader bsr(stream); + for (;;) + { + int location = bsr.read_8(); + if (location == 0xff) + break; + auto key = (disktype == 1) ? std::make_pair(location, 0) + : std::make_pair(location >> 1, + location & 1); + + bsr.skip(1); + uint32_t len = bsr.read_le32(); + nanoseconds_t index = (nanoseconds_t)bsr.read_le32() * 125; + auto it = _v2data.find(key); + if (it == _v2data.end()) + { + _v2data[key] = std::make_unique(); + it = _v2data.find(key); + it->second->index = index; + } + + it->second->flux.push_back(bsr.read(len)); + } + + break; + } + + default: + Error() << "unsupported A2R version"; + } + } + +public: + std::unique_ptr readFlux(int track, int head) override + { + switch (_version) + { + case 2: + { + auto i = _v2data.find(std::make_pair(track, head)); + if (i != _v2data.end()) + return std::make_unique( + *i->second); + else + return std::make_unique(); + } + + default: + Error() << "unsupported A2R version"; + } + } + + void recalibrate() {} + +private: + Bytes findChunk(Bytes id) + { + uint32_t offset = 8; + while (offset < _data.size()) + { + ByteReader br(_data); + br.seek(offset); + if (br.read(4) == id) + { + uint32_t size = br.read_le32(); + return br.read(size); + } + + offset += br.read_le32() + 8; + } + + Error() << "A2R file missing chunk"; + } + +private: + const A2rFluxSourceProto& _config; + Bytes _data; + std::ifstream _if; + int _version; + std::map, std::unique_ptr> _v2data; +}; + +std::unique_ptr FluxSource::createA2rFluxSource( + const A2rFluxSourceProto& config) +{ + return std::unique_ptr(new A2rFluxSource(config)); +} diff --git a/lib/fluxsource/fl2fluxsource.cc b/lib/fluxsource/fl2fluxsource.cc index 0f21bfeb..e8681a48 100644 --- a/lib/fluxsource/fl2fluxsource.cc +++ b/lib/fluxsource/fl2fluxsource.cc @@ -31,19 +31,6 @@ private: int _count = 0; }; -class EmptyFluxSourceIterator : public FluxSourceIterator -{ - bool hasNext() const override - { - return false; - } - - std::unique_ptr next() override - { - Error() << "no flux to read"; - } -}; - class Fl2FluxSource : public FluxSource { public: diff --git a/lib/fluxsource/fluxsource.cc b/lib/fluxsource/fluxsource.cc index 5751a89d..c9fa5873 100644 --- a/lib/fluxsource/fluxsource.cc +++ b/lib/fluxsource/fluxsource.cc @@ -34,6 +34,9 @@ std::unique_ptr FluxSource::create(const FluxSourceProto& config) case FluxSourceProto::SCP: return createScpFluxSource(config.scp()); + case FluxSourceProto::A2R: + return createA2rFluxSource(config.a2r()); + case FluxSourceProto::CWF: return createCwfFluxSource(config.cwf()); @@ -68,6 +71,12 @@ void FluxSource::updateConfigForFilename( proto->set_type(FluxSourceProto::SCP); proto->mutable_scp()->set_filename(s); }}, + {std::regex("^(.*\\.a2r)$"), + [](auto& s, auto* proto) + { + proto->set_type(FluxSourceProto::A2R); + proto->mutable_a2r()->set_filename(s); + }}, {std::regex("^(.*\\.cwf)$"), [](auto& s, auto* proto) { diff --git a/lib/fluxsource/fluxsource.h b/lib/fluxsource/fluxsource.h index a7dc04ad..6804a409 100644 --- a/lib/fluxsource/fluxsource.h +++ b/lib/fluxsource/fluxsource.h @@ -3,6 +3,7 @@ #include "flags.h" +class A2rFluxSourceProto; class CwfFluxSourceProto; class DiskFlux; class EraseFluxSourceProto; @@ -31,6 +32,8 @@ public: virtual ~FluxSource() {} private: + static std::unique_ptr createA2rFluxSource( + const A2rFluxSourceProto& config); static std::unique_ptr createCwfFluxSource( const CwfFluxSourceProto& config); static std::unique_ptr createEraseFluxSource( @@ -67,6 +70,19 @@ public: } }; +class EmptyFluxSourceIterator : public FluxSourceIterator +{ + bool hasNext() const override + { + return false; + } + + std::unique_ptr next() override + { + Error() << "no flux to read"; + } +}; + class TrivialFluxSource : public FluxSource { public: diff --git a/lib/fluxsource/fluxsource.proto b/lib/fluxsource/fluxsource.proto index 6b30342e..6dea13a3 100644 --- a/lib/fluxsource/fluxsource.proto +++ b/lib/fluxsource/fluxsource.proto @@ -20,6 +20,11 @@ message ScpFluxSourceProto { (help) = ".scp file to read flux from"]; } +message A2rFluxSourceProto { + optional string filename = 1 [default = "flux.a2r", + (help) = ".a2r file to read flux from"]; +} + message CwfFluxSourceProto { optional string filename = 1 [default = "flux.cwf", (help) = ".cwf file to read flux from"]; @@ -34,7 +39,7 @@ message FlxFluxSourceProto { optional string directory = 1 [(help) = "path to FLX stream directory"]; } -// NEXT: 11 +// NEXT: 12 message FluxSourceProto { enum FluxSourceType { NOT_SET = 0; @@ -46,17 +51,19 @@ message FluxSourceProto { CWF = 6; FLUX = 7; FLX = 8; + A2R = 9; } optional FluxSourceType type = 9 [default = NOT_SET, (help) = "flux source type"]; - optional HardwareFluxSourceProto drive = 2; - optional TestPatternFluxSourceProto test_pattern = 3; - optional EraseFluxSourceProto erase = 4; - optional KryofluxFluxSourceProto kryoflux = 5; - optional ScpFluxSourceProto scp = 6; + optional A2rFluxSourceProto a2r = 11; optional CwfFluxSourceProto cwf = 7; + optional EraseFluxSourceProto erase = 4; optional Fl2FluxSourceProto fl2 = 8; optional FlxFluxSourceProto flx = 10; + optional HardwareFluxSourceProto drive = 2; + optional KryofluxFluxSourceProto kryoflux = 5; + optional ScpFluxSourceProto scp = 6; + optional TestPatternFluxSourceProto test_pattern = 3; } diff --git a/lib/fluxsource/memoryfluxsource.cc b/lib/fluxsource/memoryfluxsource.cc index b7aa930f..cd0e6d12 100644 --- a/lib/fluxsource/memoryfluxsource.cc +++ b/lib/fluxsource/memoryfluxsource.cc @@ -31,20 +31,6 @@ private: int _count = 0; }; -class EmptyFluxSourceIterator : public FluxSourceIterator -{ - bool hasNext() const override - { - return false; - } - - std::unique_ptr next() override - { - Error() << "no flux to read"; - throw nullptr; - } -}; - class MemoryFluxSource : public FluxSource { public: diff --git a/lib/fluxsource/scpfluxsource.cc b/lib/fluxsource/scpfluxsource.cc index 61b89bfa..59257cf1 100644 --- a/lib/fluxsource/scpfluxsource.cc +++ b/lib/fluxsource/scpfluxsource.cc @@ -42,7 +42,9 @@ public: (_header.file_id[2] != 'P')) Error() << "input not a SCP file"; - ::config.set_tpi((_header.flags & SCP_FLAG_96TPI) ? 96 : 48); + int tpi = (_header.flags & SCP_FLAG_96TPI) ? 96 : 48; + ::config.set_tpi(tpi); + ::config.mutable_drive()->set_tpi(tpi); _resolution = 25 * (_header.resolution + 1); int startSide = (_header.heads == 2) ? 1 : 0;