mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	Flux sources now add the locations of their data to _extraConfig ---
which is now honoured. Fix a bunch of bugs in some of the flux sources and sinks. The converter now actually works, maybe.
This commit is contained in:
		| @@ -3,7 +3,7 @@ syntax = "proto2"; | ||||
| import "lib/config/common.proto"; | ||||
| import "lib/external/fl2.proto"; | ||||
|  | ||||
| // Next: 15 | ||||
| // Next: 14 | ||||
| message DriveProto | ||||
| { | ||||
|     optional int32 drive = 1 | ||||
| @@ -22,20 +22,18 @@ message DriveProto | ||||
|     optional double revolutions = 7 | ||||
|         [ default = 1.2, (help) = "number of revolutions to read" ]; | ||||
|  | ||||
|     optional int32 tracks = 8 | ||||
|         [ default = 81, (help) = "Number of tracks supported by drive" ]; | ||||
|     optional int32 heads = 9 | ||||
|         [ default = 2, (help) = "Number of heads supported by drive" ]; | ||||
|     optional int32 head_bias = 10 [ | ||||
|     optional string tracks = 8 | ||||
|         [ default = "c0-80h0-1", (help) = "Tracks supported by drive" ]; | ||||
|     optional int32 head_bias = 9 [ | ||||
|         default = 0, | ||||
|         (help) = "Bias to apply to the head position (in tracks)" | ||||
|     ]; | ||||
|     optional int32 group_offset = 11 [ | ||||
|     optional int32 group_offset = 10 [ | ||||
|         default = 0, | ||||
|         (help) = "When writing groups, erase all tracks except this one in each group" | ||||
|     ]; | ||||
|     optional DriveType drive_type = 12 [ default = DRIVETYPE_UNKNOWN, (help) = "Type of drive" ]; | ||||
|     optional double rotational_period_ms = 13 | ||||
|     optional DriveType drive_type = 11 [ default = DRIVETYPE_UNKNOWN, (help) = "Type of drive" ]; | ||||
|     optional double rotational_period_ms = 12 | ||||
|         [ default = 0, (help) = "Rotational period of the drive in milliseconds (0 to autodetect)"]; | ||||
|  | ||||
|     enum ErrorBehaviour { | ||||
| @@ -44,7 +42,7 @@ message DriveProto | ||||
|         RECALIBRATE = 2; | ||||
|     } | ||||
|  | ||||
|     optional ErrorBehaviour error_behaviour = 14 | ||||
|     optional ErrorBehaviour error_behaviour = 13 | ||||
|         [ default = JIGGLE, (help) = "what to do when an error occurs during reads" ]; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,7 @@ | ||||
| #include <variant> | ||||
| #include <optional> | ||||
| #include <regex> | ||||
| #include <ranges> | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| #if defined(_WIN32) || defined(__WIN32__) | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| #include <lexy/action/parse.hpp> | ||||
| #include <lexy_ext/report_error.hpp> | ||||
| #include "fmt/ranges.h" | ||||
| #include <ranges> | ||||
|  | ||||
| namespace | ||||
| { | ||||
| @@ -142,5 +143,20 @@ std::vector<CylinderHead> parseCylinderHeadsString(const std::string& s) | ||||
|         error(fmt::format("track descriptor parse error: {}", | ||||
|             fmt::join(result.errors(), "; "))); | ||||
|     } | ||||
|     return result.value(); | ||||
| } | ||||
|      | ||||
|     std::vector<CylinderHead> results = result.value(); | ||||
|     std::sort(results.begin(), results.end()); | ||||
|     return results; | ||||
| } | ||||
|  | ||||
| std::string convertCylinderHeadsToString(const std::vector<CylinderHead>& chs) | ||||
| { | ||||
|     return fmt::format("{}", | ||||
|         fmt::join(chs | std::views::transform( | ||||
|                             [](const auto& ch) | ||||
|                             { | ||||
|                                 return fmt::format( | ||||
|                                     "c{}h{}", ch.cylinder, ch.head); | ||||
|                             }), | ||||
|             " ")); | ||||
| } | ||||
|   | ||||
| @@ -3,8 +3,11 @@ | ||||
| struct CylinderHead | ||||
| { | ||||
|     bool operator==(const CylinderHead&) const = default; | ||||
|     std::strong_ordering operator<=>(const CylinderHead&) const = default; | ||||
|  | ||||
|     unsigned cylinder, head; | ||||
| }; | ||||
|  | ||||
| extern std::vector<CylinderHead> parseCylinderHeadsString(const std::string& s); | ||||
| extern std::string convertCylinderHeadsToString( | ||||
|     const std::vector<CylinderHead>& chs); | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/config/flags.h" | ||||
| #include "lib/config/config.h" | ||||
| #include "lib/data/fluxmap.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "protocol.h" | ||||
| @@ -31,8 +32,6 @@ namespace | ||||
|             _bytes{}, | ||||
|             _writer{_bytes.writer()} | ||||
|         { | ||||
|             log("A2R: collecting data"); | ||||
|  | ||||
|             time_t now{std::time(nullptr)}; | ||||
|             auto t = gmtime(&now); | ||||
|             _metadata["image_date"] = fmt::format("{:%FT%TZ}", *t); | ||||
| @@ -88,7 +87,12 @@ namespace | ||||
|             auto version_str_padded = fmt::format("{: <32}", "FluxEngine"); | ||||
|             assert(version_str_padded.size() == 32); | ||||
|             writer.append(version_str_padded); | ||||
|             writer.write_8(A2R_DISK_35); | ||||
|  | ||||
|             writer.write_8( | ||||
|                 (globalConfig()->drive().drive_type() == DRIVETYPE_APPLE2) | ||||
|                     ? A2R_DISK_525 | ||||
|                     : A2R_DISK_35); | ||||
|  | ||||
|             writer.write_8(1); // write protected | ||||
|             writer.write_8(1); // synchronized | ||||
|             writeChunkAndData(A2R_CHUNK_INFO, info); | ||||
| @@ -203,7 +207,11 @@ namespace | ||||
|  | ||||
|             uint32_t chunk_size = 10 + trackBytes.size(); | ||||
|  | ||||
|             _strmWriter.write_8((cylinder << 1) | head); | ||||
|             if (globalConfig()->drive().drive_type() == DRIVETYPE_APPLE2) | ||||
|                 _strmWriter.write_8(cylinder); | ||||
|             else | ||||
|                 _strmWriter.write_8((cylinder << 1) | head); | ||||
|  | ||||
|             _strmWriter.write_8(A2R_TIMING); | ||||
|             _strmWriter.write_le32(trackBytes.size()); | ||||
|             _strmWriter.write_le32(ticks_to_a2r(loopPoint)); | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
| #include "lib/config/proto.h" | ||||
| #include "lib/external/fl2.pb.h" | ||||
| #include "lib/external/fl2.h" | ||||
| #include "lib/core/logger.h" | ||||
| #include <fstream> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/types.h> | ||||
| @@ -34,6 +35,8 @@ public: | ||||
|  | ||||
|     ~Fl2FluxSink() | ||||
|     { | ||||
|         log("FL2: writing {}", _filename); | ||||
|  | ||||
|         FluxFileProto proto; | ||||
|         for (const auto& e : _data) | ||||
|         { | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
| #define FLUXSINK_H | ||||
|  | ||||
| #include "lib/config/flags.h" | ||||
| #include "lib/data/locations.h" | ||||
| #include <ostream> | ||||
|  | ||||
| class Fluxmap; | ||||
| @@ -42,6 +43,10 @@ public: | ||||
|     /* Writes a fluxmap to a track and side. */ | ||||
|  | ||||
|     virtual void writeFlux(int track, int side, const Fluxmap& fluxmap) = 0; | ||||
|     void writeFlux(const CylinderHead& location, const Fluxmap& fluxmap) | ||||
|     { | ||||
|         writeFlux(location.cylinder, location.head, fluxmap); | ||||
|     } | ||||
|  | ||||
|     /* Returns whether this is writing to real hardware or not. */ | ||||
|  | ||||
|   | ||||
| @@ -173,8 +173,7 @@ public: | ||||
|         _fileheader.revolutions = revolution; | ||||
|         write_le32( | ||||
|             _fileheader.track[strack], trackdataWriter.pos + sizeof(ScpHeader)); | ||||
|         trackdataWriter += | ||||
|             Bytes((uint8_t*)&trackHeader, sizeof(trackHeader)); | ||||
|         trackdataWriter += Bytes((uint8_t*)&trackHeader, sizeof(trackHeader)); | ||||
|         trackdataWriter += fluxdata; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,9 +1,13 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/data/fluxmap.h" | ||||
| #include "lib/data/layout.h" | ||||
| #include "lib/fluxsource/fluxsource.pb.h" | ||||
| #include "lib/fluxsource/fluxsource.h" | ||||
| #include "lib/config/proto.h" | ||||
| #include "lib/data/locations.h" | ||||
| #include "lib/core/logger.h" | ||||
| #include <fstream> | ||||
| #include <ranges> | ||||
|  | ||||
| struct A2Rv2Flux | ||||
| { | ||||
| @@ -78,8 +82,6 @@ public: | ||||
|                 if (disktype == 1) | ||||
|                 { | ||||
|                     /* 5.25" with quarter stepping. */ | ||||
|                     _extraConfig.mutable_drive()->set_tracks(160); | ||||
|                     _extraConfig.mutable_drive()->set_heads(1); | ||||
|                     _extraConfig.mutable_drive()->set_drive_type( | ||||
|                         DRIVETYPE_APPLE2); | ||||
|                 } | ||||
| @@ -94,12 +96,12 @@ public: | ||||
|                 ByteReader bsr(stream); | ||||
|                 for (;;) | ||||
|                 { | ||||
|                     int location = bsr.read_8(); | ||||
|                     unsigned location = bsr.read_8(); | ||||
|                     if (location == 0xff) | ||||
|                         break; | ||||
|                     auto key = (disktype == 1) ? std::make_pair(location, 0) | ||||
|                                                : std::make_pair(location >> 1, | ||||
|                                                      location & 1); | ||||
|                     auto key = (disktype == 1) | ||||
|                                    ? CylinderHead{location, 0} | ||||
|                                    : CylinderHead{location >> 1, location & 1}; | ||||
|  | ||||
|                     bsr.skip(1); | ||||
|                     uint32_t len = bsr.read_le32(); | ||||
| @@ -115,6 +117,20 @@ public: | ||||
|                     it->second->flux.push_back(bsr.read(len)); | ||||
|                 } | ||||
|  | ||||
|                 auto keys = std::views::keys(_v2data); | ||||
|                 std::vector<CylinderHead> chs{keys.begin(), keys.end()}; | ||||
|                 auto [minCylinder, maxCylinder, minHead, maxHead] = | ||||
|                     Layout::getBounds(chs); | ||||
|                 log("A2R: reading A2R {} file with {} cylinders and {} head{}", | ||||
|                     (disktype == 1)   ? "Apple II" | ||||
|                     : (disktype == 2) ? "normal" | ||||
|                                       : "unknown", | ||||
|                     maxCylinder - minCylinder + 1, | ||||
|                     maxHead - minHead + 1, | ||||
|                     (maxHead == minHead) ? "" : "s"); | ||||
|  | ||||
|                 _extraConfig.mutable_drive()->set_tracks( | ||||
|                     convertCylinderHeadsToString(chs)); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
| @@ -130,7 +146,8 @@ public: | ||||
|         { | ||||
|             case 2: | ||||
|             { | ||||
|                 auto i = _v2data.find(std::make_pair(track, head)); | ||||
|                 auto i = | ||||
|                     _v2data.find(CylinderHead{(unsigned)track, (unsigned)head}); | ||||
|                 if (i != _v2data.end()) | ||||
|                     return std::make_unique<A2rv2FluxSourceIterator>( | ||||
|                         *i->second); | ||||
| @@ -170,7 +187,7 @@ private: | ||||
|     Bytes _data; | ||||
|     std::ifstream _if; | ||||
|     int _version; | ||||
|     std::map<std::pair<int, int>, std::unique_ptr<A2Rv2Flux>> _v2data; | ||||
|     std::map<CylinderHead, std::unique_ptr<A2Rv2Flux>> _v2data; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<FluxSource> FluxSource::createA2rFluxSource( | ||||
|   | ||||
| @@ -1,10 +1,12 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/data/fluxmap.h" | ||||
| #include "lib/data/locations.h" | ||||
| #include "lib/fluxsource/fluxsource.pb.h" | ||||
| #include "lib/fluxsource/fluxsource.h" | ||||
| #include "lib/external/catweasel.h" | ||||
| #include "lib/config/proto.h" | ||||
| #include <fstream> | ||||
| #include <ranges> | ||||
|  | ||||
| struct CwfHeader | ||||
| { | ||||
| @@ -14,8 +16,8 @@ struct CwfHeader | ||||
|     uint8_t version;      // version of this file | ||||
|     uint8_t clock_rate;   // clock rate used: 0, 1, 2 (2 = 28MHz) | ||||
|     uint8_t drive_type;   // type of drive | ||||
|     uint8_t tracks;       // number of tracks | ||||
|     uint8_t sides;        // number of sides | ||||
|     uint8_t cylinders;    // number of cylinders | ||||
|     uint8_t heads;        // number of heads | ||||
|     uint8_t index_mark;   // nonzero if index marks are included | ||||
|     uint8_t step;         // track stepping interval | ||||
|     uint8_t filler[15];   // reserved for expansion | ||||
| @@ -24,8 +26,8 @@ struct CwfHeader | ||||
|  | ||||
| struct CwfTrack | ||||
| { | ||||
|     uint8_t track; // sequential | ||||
|     uint8_t side; | ||||
|     uint8_t cylinder; // sequential | ||||
|     uint8_t head; | ||||
|     uint8_t unused[2]; | ||||
|     uint8_t length[4]; // little-endian | ||||
| }; | ||||
| @@ -60,16 +62,16 @@ public: | ||||
|                 error("unsupported clock rate"); | ||||
|         } | ||||
|  | ||||
|         std::cout << fmt::format("CWF {}x{} = {} tracks, {} sides\n", | ||||
|             _header.tracks, | ||||
|             _header.step, | ||||
|             _header.tracks * _header.step, | ||||
|             _header.sides); | ||||
|         std::cout << fmt::format("CWF {}x{} = {} cylinders, {} heads\n", | ||||
|             _header.cylinders, | ||||
|             _header.heads, | ||||
|             _header.cylinders * _header.step, | ||||
|             _header.heads); | ||||
|         std::cout << fmt::format( | ||||
|             "CWF sample clock rate: {} MHz\n", 1e3 / _clockPeriod); | ||||
|  | ||||
|         int tracks = _header.tracks * _header.sides; | ||||
|         for (int i = 0; i < tracks; i++) | ||||
|         int cylinders = _header.cylinders * _header.heads; | ||||
|         for (int i = 0; i < cylinders; i++) | ||||
|         { | ||||
|             CwfTrack trackHeader; | ||||
|             _if.read((char*)&trackHeader, sizeof(trackHeader)); | ||||
| @@ -78,19 +80,26 @@ public: | ||||
|             uint32_t length = | ||||
|                 Bytes(trackHeader.length, 4).reader().read_le32() - | ||||
|                 sizeof(CwfTrack); | ||||
|             unsigned track_number = trackHeader.track * _header.step; | ||||
|             unsigned track_number = trackHeader.cylinder * _header.step; | ||||
|  | ||||
|             off_t pos = _if.tellg(); | ||||
|             _trackOffsets[std::make_pair(track_number, trackHeader.side)] = | ||||
|             _trackOffsets[CylinderHead{track_number, trackHeader.head}] = | ||||
|                 std::make_pair(pos, length); | ||||
|             _if.seekg(pos + length); | ||||
|         } | ||||
|  | ||||
|         std::vector<CylinderHead> chs; | ||||
|         for (auto& [key, value] : _trackOffsets) | ||||
|             chs.push_back(key); | ||||
|         _extraConfig.mutable_drive()->set_tracks( | ||||
|             convertCylinderHeadsToString(chs)); | ||||
|     } | ||||
|  | ||||
| public: | ||||
|     std::unique_ptr<const Fluxmap> readSingleFlux(int track, int side) override | ||||
|     { | ||||
|         const auto& p = _trackOffsets.find(std::make_pair(track, side)); | ||||
|         const auto& p = | ||||
|             _trackOffsets.find(CylinderHead{(unsigned)track, (unsigned)side}); | ||||
|         if (p == _trackOffsets.end()) | ||||
|             return std::make_unique<const Fluxmap>(); | ||||
|  | ||||
| @@ -117,7 +126,7 @@ private: | ||||
|     std::ifstream _if; | ||||
|     CwfHeader _header; | ||||
|     nanoseconds_t _clockPeriod; | ||||
|     std::map<std::pair<int, int>, std::pair<off_t, size_t>> _trackOffsets; | ||||
|     std::map<CylinderHead, std::pair<off_t, size_t>> _trackOffsets; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<FluxSource> FluxSource::createCwfFluxSource( | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/data/fluxmap.h" | ||||
| #include "lib/data/locations.h" | ||||
| #include "lib/fluxsource/fluxsource.pb.h" | ||||
| #include "lib/fluxsource/fluxsource.h" | ||||
| #include "lib/external/catweasel.h" | ||||
| @@ -56,6 +57,20 @@ class DmkFluxSource : public FluxSource | ||||
| public: | ||||
|     DmkFluxSource(const DmkFluxSourceProto& config): _path(config.directory()) | ||||
|     { | ||||
|         std::vector<CylinderHead> chs; | ||||
|         for (const auto& di : std::filesystem::directory_iterator(_path)) | ||||
|         { | ||||
|             static const std::regex FILENAME_REGEX( | ||||
|                 "C_S([0-9]+)T([0-9]+)\\.[0-9]+"); | ||||
|  | ||||
|             std::string filename = di.path().filename(); | ||||
|             std::smatch dmatch; | ||||
|             if (std::regex_match(filename, dmatch, FILENAME_REGEX)) | ||||
|                 chs.push_back(CylinderHead{(unsigned)std::stoi(dmatch[2]), | ||||
|                     (unsigned)std::stoi(dmatch[1])}); | ||||
|         } | ||||
|         _extraConfig.mutable_drive()->set_tracks( | ||||
|             convertCylinderHeadsToString(chs)); | ||||
|     } | ||||
|  | ||||
| public: | ||||
|   | ||||
| @@ -6,7 +6,11 @@ | ||||
| class EraseFluxSource : public TrivialFluxSource | ||||
| { | ||||
| public: | ||||
|     EraseFluxSource(const EraseFluxSourceProto& config) {} | ||||
|     EraseFluxSource(const EraseFluxSourceProto& config) | ||||
|     { | ||||
|         _extraConfig.mutable_drive()->set_tracks("c0-255h0-1"); | ||||
|     } | ||||
|  | ||||
|     ~EraseFluxSource() {} | ||||
|  | ||||
| public: | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/data/fluxmap.h" | ||||
| #include "lib/data/locations.h" | ||||
| #include "lib/fluxsource/fluxsource.pb.h" | ||||
| #include "lib/external/fl2.pb.h" | ||||
| #include "lib/fluxsource/fluxsource.h" | ||||
| #include "lib/config/proto.h" | ||||
| #include "lib/external/fl2.h" | ||||
| #include "lib/data/fluxmap.h" | ||||
| #include "lib/core/logger.h" | ||||
| #include <fstream> | ||||
|  | ||||
| class Fl2FluxSourceIterator : public FluxSourceIterator | ||||
| @@ -35,12 +37,20 @@ class Fl2FluxSource : public FluxSource | ||||
| public: | ||||
|     Fl2FluxSource(const Fl2FluxSourceProto& config): _config(config) | ||||
|     { | ||||
|         log("FL2: reading {}", _config.filename()); | ||||
|         _proto = loadFl2File(_config.filename()); | ||||
|  | ||||
|         _extraConfig.mutable_drive()->set_rotational_period_ms( | ||||
|             _proto.rotational_period_ms()); | ||||
|         if (_proto.has_drive_type()) | ||||
|             _extraConfig.mutable_drive()->set_drive_type(_proto.drive_type()); | ||||
|  | ||||
|         std::vector<CylinderHead> chs; | ||||
|         for (const auto& trackFlux : _proto.track()) | ||||
|             chs.push_back(CylinderHead{ | ||||
|                 (unsigned)trackFlux.track(), (unsigned)trackFlux.head()}); | ||||
|         _extraConfig.mutable_drive()->set_tracks( | ||||
|             convertCylinderHeadsToString(chs)); | ||||
|     } | ||||
|  | ||||
| public: | ||||
|   | ||||
| @@ -11,7 +11,9 @@ std::unique_ptr<FluxSource> FluxSource::create(Config& config) | ||||
| { | ||||
|     if (!config.hasFluxSource()) | ||||
|         error("no flux source configured"); | ||||
|     return create(config->flux_source()); | ||||
|     auto fluxSource = create(config->flux_source()); | ||||
|     globalConfig().base()->MergeFrom(fluxSource->getExtraConfig()); | ||||
|     return fluxSource; | ||||
| } | ||||
|  | ||||
| std::unique_ptr<FluxSource> FluxSource::create(const FluxSourceProto& config) | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
| #define FLUXSOURCE_H | ||||
|  | ||||
| #include "lib/config/flags.h" | ||||
| #include "lib/data/locations.h" | ||||
| #include "lib/config/config.pb.h" | ||||
|  | ||||
| class A2rFluxSourceProto; | ||||
| @@ -71,19 +72,23 @@ public: | ||||
|         return _extraConfig; | ||||
|     } | ||||
|  | ||||
|     /* Read flux from a given track and side. */ | ||||
|     /* Read flux from a given cylinder and head. */ | ||||
|  | ||||
|     virtual std::unique_ptr<FluxSourceIterator> readFlux( | ||||
|         int track, int side) = 0; | ||||
|         int cylinder, int head) = 0; | ||||
|     std::unique_ptr<FluxSourceIterator> readFlux(const CylinderHead& location) | ||||
|     { | ||||
|         return readFlux(location.cylinder, location.head); | ||||
|     } | ||||
|  | ||||
|     /* Recalibrates; seeks to track 0 and ensures the head is in the right | ||||
|     /* Recalibrates; seeks to cylinder 0 and ensures the head is in the right | ||||
|      * place. */ | ||||
|  | ||||
|     virtual void recalibrate() {} | ||||
|  | ||||
|     /* Seeks to a given track (without recalibrating). */ | ||||
|     /* Seeks to a given cylinder (without recalibrating). */ | ||||
|  | ||||
|     virtual void seek(int track) {} | ||||
|     virtual void seek(int cylinder) {} | ||||
|  | ||||
|     /* Is this real hardware? If so, then flux can be read indefinitely (among | ||||
|      * other things). */ | ||||
| @@ -113,9 +118,10 @@ class EmptyFluxSourceIterator : public FluxSourceIterator | ||||
| class TrivialFluxSource : public FluxSource | ||||
| { | ||||
| public: | ||||
|     std::unique_ptr<FluxSourceIterator> readFlux(int track, int side) override; | ||||
|     std::unique_ptr<FluxSourceIterator> readFlux( | ||||
|         int cylinder, int head) override; | ||||
|     virtual std::unique_ptr<const Fluxmap> readSingleFlux( | ||||
|         int track, int side) = 0; | ||||
|         int cylinder, int head) = 0; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -1,14 +1,31 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/data/fluxmap.h" | ||||
| #include "lib/data/locations.h" | ||||
| #include "lib/fluxsource/fluxsource.pb.h" | ||||
| #include "lib/fluxsource/fluxsource.h" | ||||
| #include "lib/external/flx.h" | ||||
| #include "lib/core/logger.h" | ||||
| #include <filesystem> | ||||
|  | ||||
| class FlxFluxSource : public TrivialFluxSource | ||||
| { | ||||
| public: | ||||
|     FlxFluxSource(const FlxFluxSourceProto& config): _path(config.directory()) | ||||
|     { | ||||
|         std::vector<CylinderHead> chs; | ||||
|         for (const auto& di : std::filesystem::directory_iterator(_path)) | ||||
|         { | ||||
|             static const std::regex FILENAME_REGEX( | ||||
|                 "@TR([0-9]+)S([0-9]+)@\\.FLX"); | ||||
|  | ||||
|             std::string filename = di.path().filename(); | ||||
|             std::smatch dmatch; | ||||
|             if (std::regex_match(filename, dmatch, FILENAME_REGEX)) | ||||
|                 chs.push_back(CylinderHead{(unsigned)std::stoi(dmatch[1]), | ||||
|                     (unsigned)std::stoi(dmatch[2]) - 1}); | ||||
|         } | ||||
|         _extraConfig.mutable_drive()->set_tracks( | ||||
|             convertCylinderHeadsToString(chs)); | ||||
|     } | ||||
|  | ||||
| public: | ||||
| @@ -16,6 +33,7 @@ public: | ||||
|     { | ||||
|         std::string path = | ||||
|             fmt::format("{}/@TR{:02}S{}@.FLX", _path, track, side + 1); | ||||
|         log("FLX: reading {}", path); | ||||
|         return readFlxBytes(Bytes::readFromFile(path)); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,10 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/data/fluxmap.h" | ||||
| #include "lib/data/locations.h" | ||||
| #include "lib/external/kryoflux.h" | ||||
| #include "lib/fluxsource/fluxsource.pb.h" | ||||
| #include "lib/fluxsource/fluxsource.h" | ||||
| #include <filesystem> | ||||
|  | ||||
| class KryofluxFluxSource : public TrivialFluxSource | ||||
| { | ||||
| @@ -10,6 +12,19 @@ public: | ||||
|     KryofluxFluxSource(const KryofluxFluxSourceProto& config): | ||||
|         _path(config.directory()) | ||||
|     { | ||||
|         std::vector<CylinderHead> chs; | ||||
|         for (const auto& di : std::filesystem::directory_iterator(_path)) | ||||
|         { | ||||
|             static const std::regex FILENAME_REGEX("([0-9]+)\\.([0-9]+)\\.raw"); | ||||
|  | ||||
|             std::string filename = di.path().filename(); | ||||
|             std::smatch dmatch; | ||||
|             if (std::regex_match(filename, dmatch, FILENAME_REGEX)) | ||||
|                 chs.push_back(CylinderHead{(unsigned)std::stoi(dmatch[1]), | ||||
|                     (unsigned)std::stoi(dmatch[2])}); | ||||
|         } | ||||
|         _extraConfig.mutable_drive()->set_tracks( | ||||
|             convertCylinderHeadsToString(chs)); | ||||
|     } | ||||
|  | ||||
| public: | ||||
|   | ||||
| @@ -31,7 +31,16 @@ private: | ||||
| class MemoryFluxSource : public FluxSource | ||||
| { | ||||
| public: | ||||
|     MemoryFluxSource(const DiskFlux& flux): _flux(flux) {} | ||||
|     MemoryFluxSource(const DiskFlux& flux): _flux(flux) | ||||
|     { | ||||
|         std::vector<CylinderHead> chs; | ||||
|         for (const auto& trackFlux : flux.tracks) | ||||
|             chs.push_back( | ||||
|                 CylinderHead{(unsigned)trackFlux->trackInfo->physicalTrack, | ||||
|                     (unsigned)trackFlux->trackInfo->logicalTrack}); | ||||
|         _extraConfig.mutable_drive()->set_tracks( | ||||
|             convertCylinderHeadsToString(chs)); | ||||
|     } | ||||
|  | ||||
| public: | ||||
|     std::unique_ptr<FluxSourceIterator> readFlux( | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/data/fluxmap.h" | ||||
| #include "lib/data/locations.h" | ||||
| #include "lib/external/kryoflux.h" | ||||
| #include "lib/fluxsource/fluxsource.pb.h" | ||||
| #include "lib/core/utils.h" | ||||
| @@ -53,6 +54,15 @@ public: | ||||
|         if ((_header.cell_width != 0) && (_header.cell_width != 16)) | ||||
|             error("currently only 16-bit cells in SCP files are supported"); | ||||
|  | ||||
|         std::vector<CylinderHead> chs; | ||||
|         for (unsigned cylinder = trackno(_header.start_track); | ||||
|             cylinder <= trackno(_header.end_track); | ||||
|             cylinder++) | ||||
|             for (unsigned head = startSide; head <= endSide; head++) | ||||
|                 chs.push_back(CylinderHead{cylinder, head}); | ||||
|         _extraConfig.mutable_drive()->set_tracks( | ||||
|             convertCylinderHeadsToString(chs)); | ||||
|  | ||||
|         log("SCP tracks {}-{}, heads {}-{}", | ||||
|             trackno(_header.start_track), | ||||
|             trackno(_header.end_track), | ||||
|   | ||||
| @@ -9,6 +9,7 @@ public: | ||||
|     TestPatternFluxSource(const TestPatternFluxSourceProto& config): | ||||
|         _config(config) | ||||
|     { | ||||
|         _extraConfig.mutable_drive()->set_tracks("c0-255h0-1"); | ||||
|     } | ||||
|  | ||||
|     ~TestPatternFluxSource() {} | ||||
|   | ||||
| @@ -76,10 +76,9 @@ public: | ||||
|                 trackHeaderWriter.write_le16(actualSectors); | ||||
|                 trackHeaderWriter.write_8(dataRate); | ||||
|                 trackHeaderWriter.write_8(recordingMode); | ||||
|                 trackHeaderWriter.write_8(0); /* format gap length */ | ||||
|                 trackHeaderWriter.write_8(0); /* filler byte */ | ||||
|                 trackHeaderWriter.write_le16( | ||||
|                     0); /* approximate track length */ | ||||
|                 trackHeaderWriter.write_8(0);    /* format gap length */ | ||||
|                 trackHeaderWriter.write_8(0);    /* filler byte */ | ||||
|                 trackHeaderWriter.write_le16(0); /* approximate track length */ | ||||
|  | ||||
|                 for (int sectorId = 0; sectorId < geometry.numSectors; | ||||
|                     sectorId++) | ||||
| @@ -100,7 +99,7 @@ public: | ||||
|                         trackHeaderWriter.write_8( | ||||
|                             (sector->status == Sector::OK) | ||||
|                                 ? 0x00 | ||||
|                                 : 0x20);                 /* 8272 status 1 */ | ||||
|                                 : 0x20);              /* 8272 status 1 */ | ||||
|                         trackHeaderWriter.write_8(0); /* 8272 status 2 */ | ||||
|                         trackHeaderWriter.write_8(1); /* number of copies */ | ||||
|                         trackHeaderWriter.write_8(0); /* filler byte */ | ||||
| @@ -114,8 +113,7 @@ public: | ||||
|  | ||||
|                 uint32_t trackLabel = (('T') << 24) | ((track & 0xff) << 16) | | ||||
|                                       ((track >> 8) << 8) | side; | ||||
|                 uint32_t trackHeaderAddress = | ||||
|                     ldbs.put(trackHeader, trackLabel); | ||||
|                 uint32_t trackHeaderAddress = ldbs.put(trackHeader, trackLabel); | ||||
|                 trackDirectoryWriter.write_be32(trackLabel); | ||||
|                 trackDirectoryWriter.write_le32(trackHeaderAddress); | ||||
|                 trackDirectorySize++; | ||||
|   | ||||
| @@ -28,44 +28,26 @@ int mainConvert(int argc, const char* argv[]) | ||||
|     globalConfig().setFluxSink(filenames[1]); | ||||
|  | ||||
|     auto fluxSource = FluxSource::create(globalConfig()); | ||||
|     auto locations = globalConfig()->drive().tracks(); | ||||
|     globalConfig().overrides()->set_tracks(locations); | ||||
|  | ||||
| #if 0 | ||||
|     std::vector<std::shared_ptr<const TrackInfo>> locations; | ||||
|     std::map<std::shared_ptr<const TrackInfo>, | ||||
|         std::vector<std::shared_ptr<const Fluxmap>>> | ||||
|         data; | ||||
|     for (int track = 0; track < 255; track++) | ||||
|         for (int side = 0; side < 2; side++) | ||||
|         { | ||||
|             auto ti = std::make_shared<TrackInfo>(); | ||||
|             ti->physicalTrack = track; | ||||
|             ti->physicalSide = side; | ||||
|  | ||||
|             auto fsi = fluxSource->readFlux(track, side); | ||||
|             std::vector<std::shared_ptr<const Fluxmap>> fluxes; | ||||
|  | ||||
|             while (fsi->hasNext()) | ||||
|                 fluxes.push_back(fsi->next()); | ||||
|  | ||||
|             if (!fluxes.empty()) | ||||
|             { | ||||
|                 data[ti] = fluxes; | ||||
|                 locations.push_back(ti); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     auto [minTrack, maxTrack, minSide, maxSide] = Layout::getBounds(locations); | ||||
|     log("CONVERT: seen tracks {}..{}, sides {}..{}", | ||||
|         minTrack, | ||||
|         maxTrack, | ||||
|         minSide, | ||||
|         maxSide); | ||||
|  | ||||
|     globalConfig().set("tracks", fmt::format("{}-{}", minTrack, maxTrack)); | ||||
|     globalConfig().set("heads", fmt::format("{}-{}", minSide, maxSide)); | ||||
|     auto physicalLocations = Layout::computePhysicalLocations(); | ||||
|     auto [minCylinder, maxCylinder, minHead, maxHead] = | ||||
|         Layout::getBounds(physicalLocations); | ||||
|     log("CONVERT: seen cylinders {}..{}, heads {}..{}", | ||||
|         minCylinder, | ||||
|         maxCylinder, | ||||
|         minHead, | ||||
|         maxHead); | ||||
|  | ||||
|     auto fluxSink = FluxSink::create(globalConfig()); | ||||
| #endif | ||||
|  | ||||
|     for (const auto& physicalLocation : physicalLocations) | ||||
|     { | ||||
|         auto fi = fluxSource->readFlux(physicalLocation); | ||||
|         while (fi->hasNext()) | ||||
|             fluxSink->writeFlux(physicalLocation, *fi->next()); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -15,7 +15,7 @@ fluxengine read ibm --180 40track_drive | ||||
| >>> | ||||
|  | ||||
| drive { | ||||
|     tracks: 40 | ||||
|     tracks: "c0-40h0-1" | ||||
|     drive_type: DRIVETYPE_40TRACK | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -22,8 +22,7 @@ usb { | ||||
| } | ||||
|  | ||||
| drive { | ||||
|     tracks: 160 | ||||
|     heads: 1 | ||||
|     tracks: "c0-159h0" | ||||
|     drive_type: DRIVETYPE_APPLE2 | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -7,8 +7,7 @@ usb { | ||||
| } | ||||
|  | ||||
| drive { | ||||
|     tracks: 160 | ||||
|     heads: 1 | ||||
|     tracks: "c0-159h0" | ||||
|     drive_type: DRIVETYPE_APPLE2 | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user