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