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:
David Given
2025-08-17 00:38:25 +02:00
parent 4ebda29171
commit f24e4029b4
25 changed files with 225 additions and 98 deletions

View File

@@ -3,7 +3,7 @@ syntax = "proto2";
import "lib/config/common.proto"; import "lib/config/common.proto";
import "lib/external/fl2.proto"; import "lib/external/fl2.proto";
// Next: 15 // Next: 14
message DriveProto message DriveProto
{ {
optional int32 drive = 1 optional int32 drive = 1
@@ -22,20 +22,18 @@ message DriveProto
optional double revolutions = 7 optional double revolutions = 7
[ default = 1.2, (help) = "number of revolutions to read" ]; [ default = 1.2, (help) = "number of revolutions to read" ];
optional int32 tracks = 8 optional string tracks = 8
[ default = 81, (help) = "Number of tracks supported by drive" ]; [ default = "c0-80h0-1", (help) = "Tracks supported by drive" ];
optional int32 heads = 9 optional int32 head_bias = 9 [
[ default = 2, (help) = "Number of heads supported by drive" ];
optional int32 head_bias = 10 [
default = 0, default = 0,
(help) = "Bias to apply to the head position (in tracks)" (help) = "Bias to apply to the head position (in tracks)"
]; ];
optional int32 group_offset = 11 [ optional int32 group_offset = 10 [
default = 0, default = 0,
(help) = "When writing groups, erase all tracks except this one in each group" (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 DriveType drive_type = 11 [ default = DRIVETYPE_UNKNOWN, (help) = "Type of drive" ];
optional double rotational_period_ms = 13 optional double rotational_period_ms = 12
[ default = 0, (help) = "Rotational period of the drive in milliseconds (0 to autodetect)"]; [ default = 0, (help) = "Rotational period of the drive in milliseconds (0 to autodetect)"];
enum ErrorBehaviour { enum ErrorBehaviour {
@@ -44,7 +42,7 @@ message DriveProto
RECALIBRATE = 2; RECALIBRATE = 2;
} }
optional ErrorBehaviour error_behaviour = 14 optional ErrorBehaviour error_behaviour = 13
[ default = JIGGLE, (help) = "what to do when an error occurs during reads" ]; [ default = JIGGLE, (help) = "what to do when an error occurs during reads" ];
} }

View File

@@ -16,6 +16,7 @@
#include <variant> #include <variant>
#include <optional> #include <optional>
#include <regex> #include <regex>
#include <ranges>
#include "fmt/format.h" #include "fmt/format.h"
#if defined(_WIN32) || defined(__WIN32__) #if defined(_WIN32) || defined(__WIN32__)

View File

@@ -9,6 +9,7 @@
#include <lexy/action/parse.hpp> #include <lexy/action/parse.hpp>
#include <lexy_ext/report_error.hpp> #include <lexy_ext/report_error.hpp>
#include "fmt/ranges.h" #include "fmt/ranges.h"
#include <ranges>
namespace namespace
{ {
@@ -142,5 +143,20 @@ std::vector<CylinderHead> parseCylinderHeadsString(const std::string& s)
error(fmt::format("track descriptor parse error: {}", error(fmt::format("track descriptor parse error: {}",
fmt::join(result.errors(), "; "))); 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);
}),
" "));
}

View File

@@ -3,8 +3,11 @@
struct CylinderHead struct CylinderHead
{ {
bool operator==(const CylinderHead&) const = default; bool operator==(const CylinderHead&) const = default;
std::strong_ordering operator<=>(const CylinderHead&) const = default;
unsigned cylinder, head; unsigned cylinder, head;
}; };
extern std::vector<CylinderHead> parseCylinderHeadsString(const std::string& s); extern std::vector<CylinderHead> parseCylinderHeadsString(const std::string& s);
extern std::string convertCylinderHeadsToString(
const std::vector<CylinderHead>& chs);

View File

@@ -1,5 +1,6 @@
#include "lib/core/globals.h" #include "lib/core/globals.h"
#include "lib/config/flags.h" #include "lib/config/flags.h"
#include "lib/config/config.h"
#include "lib/data/fluxmap.h" #include "lib/data/fluxmap.h"
#include "lib/core/bytes.h" #include "lib/core/bytes.h"
#include "protocol.h" #include "protocol.h"
@@ -31,8 +32,6 @@ namespace
_bytes{}, _bytes{},
_writer{_bytes.writer()} _writer{_bytes.writer()}
{ {
log("A2R: collecting data");
time_t now{std::time(nullptr)}; time_t now{std::time(nullptr)};
auto t = gmtime(&now); auto t = gmtime(&now);
_metadata["image_date"] = fmt::format("{:%FT%TZ}", *t); _metadata["image_date"] = fmt::format("{:%FT%TZ}", *t);
@@ -88,7 +87,12 @@ namespace
auto version_str_padded = fmt::format("{: <32}", "FluxEngine"); auto version_str_padded = fmt::format("{: <32}", "FluxEngine");
assert(version_str_padded.size() == 32); assert(version_str_padded.size() == 32);
writer.append(version_str_padded); 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); // write protected
writer.write_8(1); // synchronized writer.write_8(1); // synchronized
writeChunkAndData(A2R_CHUNK_INFO, info); writeChunkAndData(A2R_CHUNK_INFO, info);
@@ -203,7 +207,11 @@ namespace
uint32_t chunk_size = 10 + trackBytes.size(); 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_8(A2R_TIMING);
_strmWriter.write_le32(trackBytes.size()); _strmWriter.write_le32(trackBytes.size());
_strmWriter.write_le32(ticks_to_a2r(loopPoint)); _strmWriter.write_le32(ticks_to_a2r(loopPoint));

View File

@@ -10,6 +10,7 @@
#include "lib/config/proto.h" #include "lib/config/proto.h"
#include "lib/external/fl2.pb.h" #include "lib/external/fl2.pb.h"
#include "lib/external/fl2.h" #include "lib/external/fl2.h"
#include "lib/core/logger.h"
#include <fstream> #include <fstream>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
@@ -34,6 +35,8 @@ public:
~Fl2FluxSink() ~Fl2FluxSink()
{ {
log("FL2: writing {}", _filename);
FluxFileProto proto; FluxFileProto proto;
for (const auto& e : _data) for (const auto& e : _data)
{ {

View File

@@ -2,6 +2,7 @@
#define FLUXSINK_H #define FLUXSINK_H
#include "lib/config/flags.h" #include "lib/config/flags.h"
#include "lib/data/locations.h"
#include <ostream> #include <ostream>
class Fluxmap; class Fluxmap;
@@ -42,6 +43,10 @@ public:
/* Writes a fluxmap to a track and side. */ /* Writes a fluxmap to a track and side. */
virtual void writeFlux(int track, int side, const Fluxmap& fluxmap) = 0; 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. */ /* Returns whether this is writing to real hardware or not. */

View File

@@ -173,8 +173,7 @@ public:
_fileheader.revolutions = revolution; _fileheader.revolutions = revolution;
write_le32( write_le32(
_fileheader.track[strack], trackdataWriter.pos + sizeof(ScpHeader)); _fileheader.track[strack], trackdataWriter.pos + sizeof(ScpHeader));
trackdataWriter += trackdataWriter += Bytes((uint8_t*)&trackHeader, sizeof(trackHeader));
Bytes((uint8_t*)&trackHeader, sizeof(trackHeader));
trackdataWriter += fluxdata; trackdataWriter += fluxdata;
} }

View File

@@ -1,9 +1,13 @@
#include "lib/core/globals.h" #include "lib/core/globals.h"
#include "lib/data/fluxmap.h" #include "lib/data/fluxmap.h"
#include "lib/data/layout.h"
#include "lib/fluxsource/fluxsource.pb.h" #include "lib/fluxsource/fluxsource.pb.h"
#include "lib/fluxsource/fluxsource.h" #include "lib/fluxsource/fluxsource.h"
#include "lib/config/proto.h" #include "lib/config/proto.h"
#include "lib/data/locations.h"
#include "lib/core/logger.h"
#include <fstream> #include <fstream>
#include <ranges>
struct A2Rv2Flux struct A2Rv2Flux
{ {
@@ -78,8 +82,6 @@ public:
if (disktype == 1) if (disktype == 1)
{ {
/* 5.25" with quarter stepping. */ /* 5.25" with quarter stepping. */
_extraConfig.mutable_drive()->set_tracks(160);
_extraConfig.mutable_drive()->set_heads(1);
_extraConfig.mutable_drive()->set_drive_type( _extraConfig.mutable_drive()->set_drive_type(
DRIVETYPE_APPLE2); DRIVETYPE_APPLE2);
} }
@@ -94,12 +96,12 @@ public:
ByteReader bsr(stream); ByteReader bsr(stream);
for (;;) for (;;)
{ {
int location = bsr.read_8(); unsigned location = bsr.read_8();
if (location == 0xff) if (location == 0xff)
break; break;
auto key = (disktype == 1) ? std::make_pair(location, 0) auto key = (disktype == 1)
: std::make_pair(location >> 1, ? CylinderHead{location, 0}
location & 1); : CylinderHead{location >> 1, location & 1};
bsr.skip(1); bsr.skip(1);
uint32_t len = bsr.read_le32(); uint32_t len = bsr.read_le32();
@@ -115,6 +117,20 @@ public:
it->second->flux.push_back(bsr.read(len)); 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; break;
} }
@@ -130,7 +146,8 @@ public:
{ {
case 2: case 2:
{ {
auto i = _v2data.find(std::make_pair(track, head)); auto i =
_v2data.find(CylinderHead{(unsigned)track, (unsigned)head});
if (i != _v2data.end()) if (i != _v2data.end())
return std::make_unique<A2rv2FluxSourceIterator>( return std::make_unique<A2rv2FluxSourceIterator>(
*i->second); *i->second);
@@ -170,7 +187,7 @@ private:
Bytes _data; Bytes _data;
std::ifstream _if; std::ifstream _if;
int _version; 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( std::unique_ptr<FluxSource> FluxSource::createA2rFluxSource(

View File

@@ -1,10 +1,12 @@
#include "lib/core/globals.h" #include "lib/core/globals.h"
#include "lib/data/fluxmap.h" #include "lib/data/fluxmap.h"
#include "lib/data/locations.h"
#include "lib/fluxsource/fluxsource.pb.h" #include "lib/fluxsource/fluxsource.pb.h"
#include "lib/fluxsource/fluxsource.h" #include "lib/fluxsource/fluxsource.h"
#include "lib/external/catweasel.h" #include "lib/external/catweasel.h"
#include "lib/config/proto.h" #include "lib/config/proto.h"
#include <fstream> #include <fstream>
#include <ranges>
struct CwfHeader struct CwfHeader
{ {
@@ -14,8 +16,8 @@ struct CwfHeader
uint8_t version; // version of this file uint8_t version; // version of this file
uint8_t clock_rate; // clock rate used: 0, 1, 2 (2 = 28MHz) uint8_t clock_rate; // clock rate used: 0, 1, 2 (2 = 28MHz)
uint8_t drive_type; // type of drive uint8_t drive_type; // type of drive
uint8_t tracks; // number of tracks uint8_t cylinders; // number of cylinders
uint8_t sides; // number of sides uint8_t heads; // number of heads
uint8_t index_mark; // nonzero if index marks are included uint8_t index_mark; // nonzero if index marks are included
uint8_t step; // track stepping interval uint8_t step; // track stepping interval
uint8_t filler[15]; // reserved for expansion uint8_t filler[15]; // reserved for expansion
@@ -24,8 +26,8 @@ struct CwfHeader
struct CwfTrack struct CwfTrack
{ {
uint8_t track; // sequential uint8_t cylinder; // sequential
uint8_t side; uint8_t head;
uint8_t unused[2]; uint8_t unused[2];
uint8_t length[4]; // little-endian uint8_t length[4]; // little-endian
}; };
@@ -60,16 +62,16 @@ public:
error("unsupported clock rate"); error("unsupported clock rate");
} }
std::cout << fmt::format("CWF {}x{} = {} tracks, {} sides\n", std::cout << fmt::format("CWF {}x{} = {} cylinders, {} heads\n",
_header.tracks, _header.cylinders,
_header.step, _header.heads,
_header.tracks * _header.step, _header.cylinders * _header.step,
_header.sides); _header.heads);
std::cout << fmt::format( std::cout << fmt::format(
"CWF sample clock rate: {} MHz\n", 1e3 / _clockPeriod); "CWF sample clock rate: {} MHz\n", 1e3 / _clockPeriod);
int tracks = _header.tracks * _header.sides; int cylinders = _header.cylinders * _header.heads;
for (int i = 0; i < tracks; i++) for (int i = 0; i < cylinders; i++)
{ {
CwfTrack trackHeader; CwfTrack trackHeader;
_if.read((char*)&trackHeader, sizeof(trackHeader)); _if.read((char*)&trackHeader, sizeof(trackHeader));
@@ -78,19 +80,26 @@ public:
uint32_t length = uint32_t length =
Bytes(trackHeader.length, 4).reader().read_le32() - Bytes(trackHeader.length, 4).reader().read_le32() -
sizeof(CwfTrack); sizeof(CwfTrack);
unsigned track_number = trackHeader.track * _header.step; unsigned track_number = trackHeader.cylinder * _header.step;
off_t pos = _if.tellg(); off_t pos = _if.tellg();
_trackOffsets[std::make_pair(track_number, trackHeader.side)] = _trackOffsets[CylinderHead{track_number, trackHeader.head}] =
std::make_pair(pos, length); std::make_pair(pos, length);
_if.seekg(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: public:
std::unique_ptr<const Fluxmap> readSingleFlux(int track, int side) override 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()) if (p == _trackOffsets.end())
return std::make_unique<const Fluxmap>(); return std::make_unique<const Fluxmap>();
@@ -117,7 +126,7 @@ private:
std::ifstream _if; std::ifstream _if;
CwfHeader _header; CwfHeader _header;
nanoseconds_t _clockPeriod; 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( std::unique_ptr<FluxSource> FluxSource::createCwfFluxSource(

View File

@@ -1,5 +1,6 @@
#include "lib/core/globals.h" #include "lib/core/globals.h"
#include "lib/data/fluxmap.h" #include "lib/data/fluxmap.h"
#include "lib/data/locations.h"
#include "lib/fluxsource/fluxsource.pb.h" #include "lib/fluxsource/fluxsource.pb.h"
#include "lib/fluxsource/fluxsource.h" #include "lib/fluxsource/fluxsource.h"
#include "lib/external/catweasel.h" #include "lib/external/catweasel.h"
@@ -56,6 +57,20 @@ class DmkFluxSource : public FluxSource
public: public:
DmkFluxSource(const DmkFluxSourceProto& config): _path(config.directory()) 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: public:

View File

@@ -6,7 +6,11 @@
class EraseFluxSource : public TrivialFluxSource class EraseFluxSource : public TrivialFluxSource
{ {
public: public:
EraseFluxSource(const EraseFluxSourceProto& config) {} EraseFluxSource(const EraseFluxSourceProto& config)
{
_extraConfig.mutable_drive()->set_tracks("c0-255h0-1");
}
~EraseFluxSource() {} ~EraseFluxSource() {}
public: public:

View File

@@ -1,11 +1,13 @@
#include "lib/core/globals.h" #include "lib/core/globals.h"
#include "lib/data/fluxmap.h" #include "lib/data/fluxmap.h"
#include "lib/data/locations.h"
#include "lib/fluxsource/fluxsource.pb.h" #include "lib/fluxsource/fluxsource.pb.h"
#include "lib/external/fl2.pb.h" #include "lib/external/fl2.pb.h"
#include "lib/fluxsource/fluxsource.h" #include "lib/fluxsource/fluxsource.h"
#include "lib/config/proto.h" #include "lib/config/proto.h"
#include "lib/external/fl2.h" #include "lib/external/fl2.h"
#include "lib/data/fluxmap.h" #include "lib/data/fluxmap.h"
#include "lib/core/logger.h"
#include <fstream> #include <fstream>
class Fl2FluxSourceIterator : public FluxSourceIterator class Fl2FluxSourceIterator : public FluxSourceIterator
@@ -35,12 +37,20 @@ class Fl2FluxSource : public FluxSource
public: public:
Fl2FluxSource(const Fl2FluxSourceProto& config): _config(config) Fl2FluxSource(const Fl2FluxSourceProto& config): _config(config)
{ {
log("FL2: reading {}", _config.filename());
_proto = loadFl2File(_config.filename()); _proto = loadFl2File(_config.filename());
_extraConfig.mutable_drive()->set_rotational_period_ms( _extraConfig.mutable_drive()->set_rotational_period_ms(
_proto.rotational_period_ms()); _proto.rotational_period_ms());
if (_proto.has_drive_type()) if (_proto.has_drive_type())
_extraConfig.mutable_drive()->set_drive_type(_proto.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: public:

View File

@@ -11,7 +11,9 @@ std::unique_ptr<FluxSource> FluxSource::create(Config& config)
{ {
if (!config.hasFluxSource()) if (!config.hasFluxSource())
error("no flux source configured"); 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) std::unique_ptr<FluxSource> FluxSource::create(const FluxSourceProto& config)

View File

@@ -2,6 +2,7 @@
#define FLUXSOURCE_H #define FLUXSOURCE_H
#include "lib/config/flags.h" #include "lib/config/flags.h"
#include "lib/data/locations.h"
#include "lib/config/config.pb.h" #include "lib/config/config.pb.h"
class A2rFluxSourceProto; class A2rFluxSourceProto;
@@ -71,19 +72,23 @@ public:
return _extraConfig; return _extraConfig;
} }
/* Read flux from a given track and side. */ /* Read flux from a given cylinder and head. */
virtual std::unique_ptr<FluxSourceIterator> readFlux( 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. */ * place. */
virtual void recalibrate() {} 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 /* Is this real hardware? If so, then flux can be read indefinitely (among
* other things). */ * other things). */
@@ -113,9 +118,10 @@ class EmptyFluxSourceIterator : public FluxSourceIterator
class TrivialFluxSource : public FluxSource class TrivialFluxSource : public FluxSource
{ {
public: 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( virtual std::unique_ptr<const Fluxmap> readSingleFlux(
int track, int side) = 0; int cylinder, int head) = 0;
}; };
#endif #endif

View File

@@ -1,14 +1,31 @@
#include "lib/core/globals.h" #include "lib/core/globals.h"
#include "lib/data/fluxmap.h" #include "lib/data/fluxmap.h"
#include "lib/data/locations.h"
#include "lib/fluxsource/fluxsource.pb.h" #include "lib/fluxsource/fluxsource.pb.h"
#include "lib/fluxsource/fluxsource.h" #include "lib/fluxsource/fluxsource.h"
#include "lib/external/flx.h" #include "lib/external/flx.h"
#include "lib/core/logger.h"
#include <filesystem>
class FlxFluxSource : public TrivialFluxSource class FlxFluxSource : public TrivialFluxSource
{ {
public: public:
FlxFluxSource(const FlxFluxSourceProto& config): _path(config.directory()) 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: public:
@@ -16,6 +33,7 @@ public:
{ {
std::string path = std::string path =
fmt::format("{}/@TR{:02}S{}@.FLX", _path, track, side + 1); fmt::format("{}/@TR{:02}S{}@.FLX", _path, track, side + 1);
log("FLX: reading {}", path);
return readFlxBytes(Bytes::readFromFile(path)); return readFlxBytes(Bytes::readFromFile(path));
} }

View File

@@ -1,8 +1,10 @@
#include "lib/core/globals.h" #include "lib/core/globals.h"
#include "lib/data/fluxmap.h" #include "lib/data/fluxmap.h"
#include "lib/data/locations.h"
#include "lib/external/kryoflux.h" #include "lib/external/kryoflux.h"
#include "lib/fluxsource/fluxsource.pb.h" #include "lib/fluxsource/fluxsource.pb.h"
#include "lib/fluxsource/fluxsource.h" #include "lib/fluxsource/fluxsource.h"
#include <filesystem>
class KryofluxFluxSource : public TrivialFluxSource class KryofluxFluxSource : public TrivialFluxSource
{ {
@@ -10,6 +12,19 @@ public:
KryofluxFluxSource(const KryofluxFluxSourceProto& config): KryofluxFluxSource(const KryofluxFluxSourceProto& config):
_path(config.directory()) _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: public:

View File

@@ -31,7 +31,16 @@ private:
class MemoryFluxSource : public FluxSource class MemoryFluxSource : public FluxSource
{ {
public: 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: public:
std::unique_ptr<FluxSourceIterator> readFlux( std::unique_ptr<FluxSourceIterator> readFlux(

View File

@@ -1,5 +1,6 @@
#include "lib/core/globals.h" #include "lib/core/globals.h"
#include "lib/data/fluxmap.h" #include "lib/data/fluxmap.h"
#include "lib/data/locations.h"
#include "lib/external/kryoflux.h" #include "lib/external/kryoflux.h"
#include "lib/fluxsource/fluxsource.pb.h" #include "lib/fluxsource/fluxsource.pb.h"
#include "lib/core/utils.h" #include "lib/core/utils.h"
@@ -53,6 +54,15 @@ public:
if ((_header.cell_width != 0) && (_header.cell_width != 16)) if ((_header.cell_width != 0) && (_header.cell_width != 16))
error("currently only 16-bit cells in SCP files are supported"); 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 {}-{}", log("SCP tracks {}-{}, heads {}-{}",
trackno(_header.start_track), trackno(_header.start_track),
trackno(_header.end_track), trackno(_header.end_track),

View File

@@ -9,6 +9,7 @@ public:
TestPatternFluxSource(const TestPatternFluxSourceProto& config): TestPatternFluxSource(const TestPatternFluxSourceProto& config):
_config(config) _config(config)
{ {
_extraConfig.mutable_drive()->set_tracks("c0-255h0-1");
} }
~TestPatternFluxSource() {} ~TestPatternFluxSource() {}

View File

@@ -76,10 +76,9 @@ public:
trackHeaderWriter.write_le16(actualSectors); trackHeaderWriter.write_le16(actualSectors);
trackHeaderWriter.write_8(dataRate); trackHeaderWriter.write_8(dataRate);
trackHeaderWriter.write_8(recordingMode); trackHeaderWriter.write_8(recordingMode);
trackHeaderWriter.write_8(0); /* format gap length */ trackHeaderWriter.write_8(0); /* format gap length */
trackHeaderWriter.write_8(0); /* filler byte */ trackHeaderWriter.write_8(0); /* filler byte */
trackHeaderWriter.write_le16( trackHeaderWriter.write_le16(0); /* approximate track length */
0); /* approximate track length */
for (int sectorId = 0; sectorId < geometry.numSectors; for (int sectorId = 0; sectorId < geometry.numSectors;
sectorId++) sectorId++)
@@ -100,7 +99,7 @@ public:
trackHeaderWriter.write_8( trackHeaderWriter.write_8(
(sector->status == Sector::OK) (sector->status == Sector::OK)
? 0x00 ? 0x00
: 0x20); /* 8272 status 1 */ : 0x20); /* 8272 status 1 */
trackHeaderWriter.write_8(0); /* 8272 status 2 */ trackHeaderWriter.write_8(0); /* 8272 status 2 */
trackHeaderWriter.write_8(1); /* number of copies */ trackHeaderWriter.write_8(1); /* number of copies */
trackHeaderWriter.write_8(0); /* filler byte */ trackHeaderWriter.write_8(0); /* filler byte */
@@ -114,8 +113,7 @@ public:
uint32_t trackLabel = (('T') << 24) | ((track & 0xff) << 16) | uint32_t trackLabel = (('T') << 24) | ((track & 0xff) << 16) |
((track >> 8) << 8) | side; ((track >> 8) << 8) | side;
uint32_t trackHeaderAddress = uint32_t trackHeaderAddress = ldbs.put(trackHeader, trackLabel);
ldbs.put(trackHeader, trackLabel);
trackDirectoryWriter.write_be32(trackLabel); trackDirectoryWriter.write_be32(trackLabel);
trackDirectoryWriter.write_le32(trackHeaderAddress); trackDirectoryWriter.write_le32(trackHeaderAddress);
trackDirectorySize++; trackDirectorySize++;

View File

@@ -28,44 +28,26 @@ int mainConvert(int argc, const char* argv[])
globalConfig().setFluxSink(filenames[1]); globalConfig().setFluxSink(filenames[1]);
auto fluxSource = FluxSource::create(globalConfig()); auto fluxSource = FluxSource::create(globalConfig());
auto locations = globalConfig()->drive().tracks();
globalConfig().overrides()->set_tracks(locations);
#if 0 auto physicalLocations = Layout::computePhysicalLocations();
std::vector<std::shared_ptr<const TrackInfo>> locations; auto [minCylinder, maxCylinder, minHead, maxHead] =
std::map<std::shared_ptr<const TrackInfo>, Layout::getBounds(physicalLocations);
std::vector<std::shared_ptr<const Fluxmap>>> log("CONVERT: seen cylinders {}..{}, heads {}..{}",
data; minCylinder,
for (int track = 0; track < 255; track++) maxCylinder,
for (int side = 0; side < 2; side++) minHead,
{ maxHead);
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 fluxSink = FluxSink::create(globalConfig()); 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; return 0;
} }

View File

@@ -15,7 +15,7 @@ fluxengine read ibm --180 40track_drive
>>> >>>
drive { drive {
tracks: 40 tracks: "c0-40h0-1"
drive_type: DRIVETYPE_40TRACK drive_type: DRIVETYPE_40TRACK
} }

View File

@@ -22,8 +22,7 @@ usb {
} }
drive { drive {
tracks: 160 tracks: "c0-159h0"
heads: 1
drive_type: DRIVETYPE_APPLE2 drive_type: DRIVETYPE_APPLE2
} }

View File

@@ -7,8 +7,7 @@ usb {
} }
drive { drive {
tracks: 160 tracks: "c0-159h0"
heads: 1
drive_type: DRIVETYPE_APPLE2 drive_type: DRIVETYPE_APPLE2
} }