mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Refactor FluxEngine to use dataspecs for source and destinations. Document
them.
This commit is contained in:
35
README.md
35
README.md
@@ -215,6 +215,41 @@ ready. What next?
|
||||
|
||||
### The programs
|
||||
|
||||
#### Source and destination specifiers
|
||||
|
||||
When reading from or writing to _a disk_ (or a file pretending to be a disk),
|
||||
use the `-s` and `-d` options to tell FluxEngine which bits of the disk you
|
||||
want to access. These use a common syntax:
|
||||
|
||||
```
|
||||
fe-readibm -s fakedisk.flux:t=0-79:s=0
|
||||
```
|
||||
|
||||
- To access a real disk, leave out the filename (so `:t=0-79:s=0`).
|
||||
|
||||
- To access only some tracks, use the `t=` modifier. To access only some
|
||||
sides, use the `s=` modifier.
|
||||
|
||||
- Inside a modifier, you can use a comma separated list of ranges. So
|
||||
`:t=0-3` and `:t=0,1,2,3` are equivalent.
|
||||
|
||||
- When specifying a range, you can also specify the step. For example,
|
||||
`:t=0-79x2` would be used when accessing a 40-track disk with double stepping.
|
||||
|
||||
Source and destination specifiers work entirely in *physical units*.
|
||||
FluxEngine is intended to be connected to an 80 (or 82) track double sided
|
||||
drive, and these are the units used. If the format you're trying to access
|
||||
lays out its tracks differently, then you'll need a specifier which tells
|
||||
FluxEngine how to find those tracks. See the 40-track disk example above.
|
||||
|
||||
If you _don't_ specify a modifier, you'll get the default, which should be
|
||||
sensible for the command you're using.
|
||||
|
||||
**Important note:** FluxEngine _always_ uses zero-based units (even if the
|
||||
*disk format says otherwise).
|
||||
|
||||
### The commands
|
||||
|
||||
The FluxEngine client software is a largely undocumented set of small tools.
|
||||
You'll have to play with them. They all support `--help`. They're not
|
||||
installed anywhere and after building you'll find them in the `.obj`
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "fmt/format.h"
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
static const std::regex MOD_REGEX("([a-z]*)=([-x+0-9,]*)");
|
||||
static const std::regex DATA_REGEX("([0-9]+)(?:(?:-([0-9]+))|(?:\\+([0-9]+)))?(?:x([0-9]+))?");
|
||||
@@ -34,6 +36,7 @@ DataSpec::Modifier DataSpec::parseMod(const std::string& spec)
|
||||
|
||||
Modifier m;
|
||||
m.name = match[1];
|
||||
m.source = spec;
|
||||
for (auto& data : split(match[2], ","))
|
||||
{
|
||||
int start = 0;
|
||||
@@ -90,3 +93,14 @@ void DataSpec::set(const std::string& spec)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DataSpec::operator std::string(void) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << filename;
|
||||
|
||||
for (const auto& mod : modifiers)
|
||||
ss << ':' << mod.second.source;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
{
|
||||
std::string name;
|
||||
std::set<unsigned> data;
|
||||
std::string source;
|
||||
|
||||
bool operator == (const Modifier& other) const
|
||||
{ return (name == other.name) && (data == other.data); }
|
||||
@@ -38,10 +39,31 @@ public:
|
||||
{ set(spec); }
|
||||
|
||||
void set(const std::string& spec);
|
||||
operator std::string () const;
|
||||
|
||||
std::string filename;
|
||||
std::map<std::string, Modifier> modifiers;
|
||||
std::vector<Location> locations;
|
||||
};
|
||||
|
||||
std::ostream& operator << (std::ostream& os, const DataSpec& dataSpec)
|
||||
{ os << (std::string)dataSpec; return os; }
|
||||
|
||||
class DataSpecFlag : public Flag
|
||||
{
|
||||
public:
|
||||
DataSpecFlag(const std::vector<std::string>& names, const std::string helptext,
|
||||
const std::string& defaultValue):
|
||||
Flag(names, helptext),
|
||||
value(defaultValue)
|
||||
{}
|
||||
|
||||
bool hasArgument() const { return true; }
|
||||
const std::string defaultValueAsString() const { return value; }
|
||||
void set(const std::string& value) { this->value.set(value); }
|
||||
|
||||
public:
|
||||
DataSpec value;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -82,9 +82,9 @@ void Flag::parseFlags(int argc, const char* argv[])
|
||||
void BoolFlag::set(const std::string& value)
|
||||
{
|
||||
if ((value == "true") || (value == "y"))
|
||||
_value = true;
|
||||
this->value = true;
|
||||
else if ((value == "false") || (value == "n"))
|
||||
_value = false;
|
||||
this->value = false;
|
||||
else
|
||||
Error() << "can't parse '" << value << "'; try 'true' or 'false'";
|
||||
}
|
||||
@@ -105,7 +105,7 @@ static void doHelp()
|
||||
}
|
||||
|
||||
if (flag->hasArgument())
|
||||
std::cout << " <default: \"" << flag->defaultValue() << "\">";
|
||||
std::cout << " <default: \"" << flag->defaultValueAsString() << "\">";
|
||||
std::cout << ": " << flag->helptext() << std::endl;
|
||||
}
|
||||
exit(0);
|
||||
|
||||
34
lib/flags.h
34
lib/flags.h
@@ -1,6 +1,8 @@
|
||||
#ifndef FLAGS_H
|
||||
#define FLAGS_H
|
||||
|
||||
class DataSpec;
|
||||
|
||||
class Flag
|
||||
{
|
||||
public:
|
||||
@@ -14,7 +16,7 @@ public:
|
||||
const std::string& helptext() const { return _helptext; }
|
||||
|
||||
virtual bool hasArgument() const = 0;
|
||||
virtual const std::string defaultValue() const = 0;
|
||||
virtual const std::string defaultValueAsString() const = 0;
|
||||
virtual void set(const std::string& value) = 0;
|
||||
|
||||
private:
|
||||
@@ -32,7 +34,7 @@ public:
|
||||
{}
|
||||
|
||||
bool hasArgument() const { return false; }
|
||||
const std::string defaultValue() const { return ""; }
|
||||
const std::string defaultValueAsString() const { return ""; }
|
||||
void set(const std::string& value) { _callback(); }
|
||||
|
||||
private:
|
||||
@@ -49,7 +51,7 @@ public:
|
||||
operator bool() const { return _value; }
|
||||
|
||||
bool hasArgument() const { return false; }
|
||||
const std::string defaultValue() const { return "false"; }
|
||||
const std::string defaultValueAsString() const { return "false"; }
|
||||
void set(const std::string& value) { _value = true; }
|
||||
|
||||
private:
|
||||
@@ -63,18 +65,16 @@ public:
|
||||
ValueFlag(const std::vector<std::string>& names, const std::string helptext,
|
||||
const T defaultValue):
|
||||
Flag(names, helptext),
|
||||
_defaultValue(defaultValue),
|
||||
_value(defaultValue)
|
||||
defaultValue(defaultValue),
|
||||
value(defaultValue)
|
||||
{}
|
||||
|
||||
T value() const { return _value; }
|
||||
operator T() const { return _value; }
|
||||
operator T() const { return value; }
|
||||
|
||||
bool hasArgument() const { return true; }
|
||||
|
||||
protected:
|
||||
T _defaultValue;
|
||||
T _value;
|
||||
T defaultValue;
|
||||
T value;
|
||||
};
|
||||
|
||||
class StringFlag : public ValueFlag<std::string>
|
||||
@@ -85,8 +85,8 @@ public:
|
||||
ValueFlag(names, helptext, defaultValue)
|
||||
{}
|
||||
|
||||
const std::string defaultValue() const { return _defaultValue; }
|
||||
void set(const std::string& value) { _value = value; }
|
||||
const std::string defaultValueAsString() const { return defaultValue; }
|
||||
void set(const std::string& value) { this->value = value; }
|
||||
};
|
||||
|
||||
class IntFlag : public ValueFlag<int>
|
||||
@@ -97,8 +97,8 @@ public:
|
||||
ValueFlag(names, helptext, defaultValue)
|
||||
{}
|
||||
|
||||
const std::string defaultValue() const { return std::to_string(_defaultValue); }
|
||||
void set(const std::string& value) { _value = std::stoi(value); }
|
||||
const std::string defaultValueAsString() const { return std::to_string(defaultValue); }
|
||||
void set(const std::string& value) { this->value = std::stoi(value); }
|
||||
};
|
||||
|
||||
class DoubleFlag : public ValueFlag<double>
|
||||
@@ -109,8 +109,8 @@ public:
|
||||
ValueFlag(names, helptext, defaultValue)
|
||||
{}
|
||||
|
||||
const std::string defaultValue() const { return std::to_string(_defaultValue); }
|
||||
void set(const std::string& value) { _value = std::stod(value); }
|
||||
const std::string defaultValueAsString() const { return std::to_string(defaultValue); }
|
||||
void set(const std::string& value) { this->value = std::stod(value); }
|
||||
};
|
||||
|
||||
class BoolFlag : public ValueFlag<double>
|
||||
@@ -121,7 +121,7 @@ public:
|
||||
ValueFlag(names, helptext, defaultValue)
|
||||
{}
|
||||
|
||||
const std::string defaultValue() const { return _defaultValue ? "true" : "false"; }
|
||||
const std::string defaultValueAsString() const { return defaultValue ? "true" : "false"; }
|
||||
void set(const std::string& value);
|
||||
};
|
||||
|
||||
|
||||
102
lib/reader.cc
102
lib/reader.cc
@@ -6,16 +6,11 @@
|
||||
#include "sql.h"
|
||||
#include "dataspec.h"
|
||||
#include "fmt/format.h"
|
||||
#include <regex>
|
||||
|
||||
static const std::regex SOURCE_REGEX("([^:]*)"
|
||||
"(?::t=([0-9]+)(?:-([0-9]+))?)?"
|
||||
"(?::s=([0-9]+)(?:-([0-9]+))?)?");
|
||||
|
||||
static StringFlag source(
|
||||
static DataSpecFlag source(
|
||||
{ "--source", "-s" },
|
||||
"source for data",
|
||||
"");
|
||||
":t=0-79:s=0-1");
|
||||
|
||||
static StringFlag destination(
|
||||
{ "--write-flux", "-f" },
|
||||
@@ -31,89 +26,53 @@ static IntFlag revolutions(
|
||||
"read this many revolutions of the disk",
|
||||
1);
|
||||
|
||||
static DataSpec readerspec("foo:f=7:z=9-10");
|
||||
|
||||
static std::string basefilename;
|
||||
static int starttrack = 0;
|
||||
static int endtrack = 79;
|
||||
static int startside = 0;
|
||||
static int endside = 1;
|
||||
static sqlite3* indb;
|
||||
static sqlite3* outdb;
|
||||
|
||||
void setReaderDefaults(int minTrack, int maxTrack, int minSide, int maxSide)
|
||||
void setReaderDefaultSource(const std::string& source)
|
||||
{
|
||||
starttrack = minTrack;
|
||||
endtrack = maxTrack;
|
||||
startside = minSide;
|
||||
endside = maxSide;
|
||||
::source.set(source);
|
||||
}
|
||||
|
||||
Fluxmap& ReaderTrack::read()
|
||||
std::unique_ptr<Fluxmap> ReaderTrack::read()
|
||||
{
|
||||
if (!_read)
|
||||
{
|
||||
std::cout << fmt::format("{0:>3}.{1}: ", track, side) << std::flush;
|
||||
reallyRead();
|
||||
std::cout << fmt::format("{0} ms in {1} bytes", int(_fluxmap->duration()/1e6), _fluxmap->bytes()) << std::endl;
|
||||
_read = true;
|
||||
std::cout << fmt::format("{0:>3}.{1}: ", track, side) << std::flush;
|
||||
std::unique_ptr<Fluxmap> fluxmap = reallyRead();
|
||||
std::cout << fmt::format(
|
||||
"{0} ms in {1} bytes", int(fluxmap->duration()/1e6), fluxmap->bytes()) << std::endl;
|
||||
|
||||
if (outdb)
|
||||
sqlWriteFlux(outdb, track, side, *_fluxmap);
|
||||
}
|
||||
if (outdb)
|
||||
sqlWriteFlux(outdb, track, side, *fluxmap);
|
||||
|
||||
return *_fluxmap.get();
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
void ReaderTrack::forceReread()
|
||||
{
|
||||
_read = false;
|
||||
}
|
||||
|
||||
void CapturedReaderTrack::reallyRead()
|
||||
std::unique_ptr<Fluxmap> CapturedReaderTrack::reallyRead()
|
||||
{
|
||||
usbSeek(track);
|
||||
_fluxmap = usbRead(side, revolutions);
|
||||
return usbRead(side, revolutions);
|
||||
}
|
||||
|
||||
void FileReaderTrack::reallyRead()
|
||||
std::unique_ptr<Fluxmap> FileReaderTrack::reallyRead()
|
||||
{
|
||||
if (!indb)
|
||||
{
|
||||
indb = sqlOpen(basefilename, SQLITE_OPEN_READONLY);
|
||||
indb = sqlOpen(source.value.filename, SQLITE_OPEN_READONLY);
|
||||
atexit([]() { sqlClose(indb); });
|
||||
}
|
||||
_fluxmap = sqlReadFlux(indb, track, side);
|
||||
return sqlReadFlux(indb, track, side);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<ReaderTrack>> readTracks()
|
||||
{
|
||||
auto f = source.value();
|
||||
std::smatch match;
|
||||
if (!std::regex_match(f, match, SOURCE_REGEX))
|
||||
Error() << "invalid source specifier '" << source.value() << "'";
|
||||
|
||||
basefilename = match[1];
|
||||
if (match[2].length() != 0)
|
||||
starttrack = endtrack = std::stoi(match[2]);
|
||||
if (match[3].length() != 0)
|
||||
endtrack = std::stoi(match[3]);
|
||||
if (match[4].length() != 0)
|
||||
startside = endside = std::stoi(match[4]);
|
||||
if (match[5].length() != 0)
|
||||
endside = std::stoi(match[5]);
|
||||
const DataSpec& dataSpec = source.value;
|
||||
|
||||
std::cout << "Reading from: "
|
||||
<< (basefilename.empty() ? "a real floppy disk" : basefilename) << std::endl
|
||||
<< "Tracks: "
|
||||
<< starttrack << " to " << endtrack << " inclusive" << std::endl
|
||||
<< "Sides: "
|
||||
<< startside << " to " << endside << " inclusive" << std::endl;
|
||||
std::cout << "Reading from: " << dataSpec << std::endl;
|
||||
|
||||
if (!destination.value().empty())
|
||||
if (!destination.value.empty())
|
||||
{
|
||||
outdb = sqlOpen(destination, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
|
||||
std::cout << "Writing a copy of the flux to " << destination.value() << std::endl;
|
||||
std::cout << "Writing a copy of the flux to " << destination.value << std::endl;
|
||||
sqlPrepareFlux(outdb);
|
||||
sqlStmt(outdb, "BEGIN;");
|
||||
atexit([]()
|
||||
@@ -125,18 +84,15 @@ std::vector<std::unique_ptr<ReaderTrack>> readTracks()
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<ReaderTrack>> tracks;
|
||||
for (int track=starttrack; track<=endtrack; track++)
|
||||
for (const auto& location : dataSpec.locations)
|
||||
{
|
||||
for (int side=startside; side<=endside; side++)
|
||||
{
|
||||
std::unique_ptr<ReaderTrack> t(
|
||||
basefilename.empty()
|
||||
? (ReaderTrack*)new CapturedReaderTrack()
|
||||
: (ReaderTrack*)new FileReaderTrack());
|
||||
t->track = track;
|
||||
t->side = side;
|
||||
tracks.push_back(std::move(t));
|
||||
}
|
||||
std::unique_ptr<ReaderTrack> t(
|
||||
dataSpec.filename.empty()
|
||||
? (ReaderTrack*)new CapturedReaderTrack()
|
||||
: (ReaderTrack*)new FileReaderTrack());
|
||||
t->track = location.track;
|
||||
t->side = location.side;
|
||||
tracks.push_back(std::move(t));
|
||||
}
|
||||
|
||||
if (justRead)
|
||||
|
||||
15
lib/reader.h
15
lib/reader.h
@@ -11,28 +11,23 @@ public:
|
||||
int track;
|
||||
int side;
|
||||
|
||||
Fluxmap& read();
|
||||
void forceReread();
|
||||
virtual void reallyRead() = 0;
|
||||
|
||||
protected:
|
||||
bool _read = false;
|
||||
std::unique_ptr<Fluxmap> _fluxmap;
|
||||
std::unique_ptr<Fluxmap> read();
|
||||
virtual std::unique_ptr<Fluxmap> reallyRead() = 0;
|
||||
};
|
||||
|
||||
class CapturedReaderTrack : public ReaderTrack
|
||||
{
|
||||
public:
|
||||
void reallyRead();
|
||||
std::unique_ptr<Fluxmap> reallyRead();
|
||||
};
|
||||
|
||||
class FileReaderTrack : public ReaderTrack
|
||||
{
|
||||
public:
|
||||
void reallyRead();
|
||||
std::unique_ptr<Fluxmap> reallyRead();
|
||||
};
|
||||
|
||||
extern void setReaderDefaults(int minTrack, int maxTrack, int minSide, int maxSide);
|
||||
extern void setReaderDefaultSource(const std::string& source);
|
||||
extern std::vector<std::unique_ptr<ReaderTrack>> readTracks();
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,62 +5,31 @@
|
||||
#include "sql.h"
|
||||
#include "protocol.h"
|
||||
#include "usb.h"
|
||||
#include "dataspec.h"
|
||||
#include "fmt/format.h"
|
||||
#include <regex>
|
||||
|
||||
static const std::regex DEST_REGEX("([^:]*)"
|
||||
"(?::t=([0-9]+)(?:-([0-9]+))?)?"
|
||||
"(?::s=([0-9]+)(?:-([0-9]+))?)?");
|
||||
|
||||
static StringFlag dest(
|
||||
static DataSpecFlag dest(
|
||||
{ "--dest", "-d" },
|
||||
"destination for data",
|
||||
"");
|
||||
":t=0-79:s=0-1");
|
||||
|
||||
static std::string basefilename;
|
||||
static int starttrack = 0;
|
||||
static int endtrack = 79;
|
||||
static int startside = 0;
|
||||
static int endside = 1;
|
||||
static sqlite3* outdb;
|
||||
|
||||
void setWriterDefaults(int minTrack, int maxTrack, int minSide, int maxSide)
|
||||
void setWriterDefaultDest(const std::string& dest)
|
||||
{
|
||||
starttrack = minTrack;
|
||||
endtrack = maxTrack;
|
||||
startside = minSide;
|
||||
endside = maxSide;
|
||||
::dest.set(dest);
|
||||
}
|
||||
|
||||
void writeTracks(
|
||||
int minTrack, int maxTrack,
|
||||
const std::function<Fluxmap(int track, int side)> producer)
|
||||
const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer)
|
||||
{
|
||||
auto f = dest.value();
|
||||
std::smatch match;
|
||||
if (!std::regex_match(f, match, DEST_REGEX))
|
||||
Error() << "invalid destination specifier '" << dest.value() << "'";
|
||||
|
||||
basefilename = match[1];
|
||||
if (match[2].length() != 0)
|
||||
starttrack = endtrack = std::stoi(match[2]);
|
||||
if (match[3].length() != 0)
|
||||
endtrack = std::stoi(match[3]);
|
||||
if (match[4].length() != 0)
|
||||
startside = endside = std::stoi(match[4]);
|
||||
if (match[5].length() != 0)
|
||||
endside = std::stoi(match[5]);
|
||||
const auto& spec = dest.value;
|
||||
|
||||
std::cout << "Writing to: "
|
||||
<< (basefilename.empty() ? "a real floppy disk" : basefilename) << std::endl
|
||||
<< "Tracks: "
|
||||
<< starttrack << " to " << endtrack << " inclusive" << std::endl
|
||||
<< "Sides: "
|
||||
<< startside << " to " << endside << " inclusive" << std::endl;
|
||||
std::cout << "Writing to: " << spec << std::endl;
|
||||
|
||||
if (!basefilename.empty())
|
||||
if (!spec.filename.empty())
|
||||
{
|
||||
outdb = sqlOpen(basefilename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
|
||||
outdb = sqlOpen(spec.filename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
|
||||
sqlPrepareFlux(outdb);
|
||||
sqlStmt(outdb, "BEGIN;");
|
||||
atexit([]()
|
||||
@@ -71,33 +40,31 @@ void writeTracks(
|
||||
);
|
||||
}
|
||||
|
||||
for (int track=starttrack; track<=endtrack; track++)
|
||||
for (const auto& location : spec.locations)
|
||||
{
|
||||
for (int side=startside; side<=endside; side++)
|
||||
std::cout << fmt::format("{0:>3}.{1}: ", location.track, location.side) << std::flush;
|
||||
std::unique_ptr<Fluxmap> fluxmap = producer(location.track, location.side);
|
||||
if (!fluxmap)
|
||||
{
|
||||
std::cout << fmt::format("{0:>3}.{1}: ", track, side) << std::flush;
|
||||
if ((track < minTrack) || (track > maxTrack))
|
||||
if (!outdb)
|
||||
{
|
||||
if (!outdb)
|
||||
{
|
||||
std::cout << "erasing" << std::endl;
|
||||
usbSeek(track);
|
||||
usbErase(side);
|
||||
}
|
||||
std::cout << "erasing" << std::endl;
|
||||
usbSeek(location.track);
|
||||
usbErase(location.side);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS, 2);
|
||||
if (outdb)
|
||||
sqlWriteFlux(outdb, location.track, location.side, *fluxmap);
|
||||
else
|
||||
{
|
||||
Fluxmap fluxmap = producer(track, side);
|
||||
fluxmap.precompensate(PRECOMPENSATION_THRESHOLD_TICKS, 2);
|
||||
if (outdb)
|
||||
sqlWriteFlux(outdb, track, side, fluxmap);
|
||||
else
|
||||
{
|
||||
usbSeek(track);
|
||||
usbWrite(side, fluxmap);
|
||||
}
|
||||
std::cout << fmt::format("{0} ms in {1} bytes", int(fluxmap.duration()/1e6), fluxmap.bytes()) << std::endl;
|
||||
usbSeek(location.track);
|
||||
usbWrite(location.side, *fluxmap);
|
||||
}
|
||||
std::cout << fmt::format(
|
||||
"{0} ms in {1} bytes", int(fluxmap->duration()/1e6), fluxmap->bytes()) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
|
||||
class Fluxmap;
|
||||
|
||||
extern void setWriterDefaults(int minTrack, int maxTrack, int minSide, int maxSide);
|
||||
extern void setWriterDefaultDest(const std::string& dest);
|
||||
|
||||
extern void writeTracks(
|
||||
int minTrack, int maxTrack,
|
||||
const std::function<Fluxmap(int track, int side)> producer);
|
||||
extern void writeTracks(const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer);
|
||||
|
||||
extern void fillBitmapTo(std::vector<bool>& bitmap,
|
||||
unsigned& cursor, unsigned terminateAt,
|
||||
const std::vector<bool>& pattern);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
setWriterDefaults(0, 81, 0, 1);
|
||||
setWriterDefaultDest(":t=0-81:s=0-1");
|
||||
Flag::parseFlags(argc, argv);
|
||||
|
||||
writeTracks(-1, -1, NULL);
|
||||
writeTracks(NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -45,12 +45,12 @@ int main(int argc, const char* argv[])
|
||||
{
|
||||
if ((track->track == trackFlag) && (track->side == sideFlag))
|
||||
{
|
||||
Fluxmap& fluxmap = track->read();
|
||||
std::unique_ptr<Fluxmap> fluxmap = track->read();
|
||||
|
||||
nanoseconds_t clockPeriod = fluxmap.guessClock();
|
||||
nanoseconds_t clockPeriod = fluxmap->guessClock();
|
||||
std::cout << fmt::format(" {:.2f} us clock; ", (double)clockPeriod/1000.0) << std::flush;
|
||||
|
||||
auto bitmap = fluxmap.decodeToBits(clockPeriod*clockScaleFlag);
|
||||
auto bitmap = fluxmap->decodeToBits(clockPeriod*clockScaleFlag);
|
||||
std::cout << fmt::format("{} bytes encoded.", bitmap.size()/8) << std::endl;
|
||||
|
||||
if (dumpFluxFlag)
|
||||
@@ -66,9 +66,9 @@ int main(int argc, const char* argv[])
|
||||
nanoseconds_t nextclock = clockPeriod;
|
||||
int ticks = 0;
|
||||
std::cout << fmt::format("{: 10.3f}:-", 0.0);
|
||||
for (int cursor=0; cursor<fluxmap.bytes(); cursor++)
|
||||
for (int cursor=0; cursor<fluxmap->bytes(); cursor++)
|
||||
{
|
||||
int interval = fluxmap[cursor];
|
||||
int interval = (*fluxmap)[cursor];
|
||||
if (interval == 0)
|
||||
interval = 0x100;
|
||||
ticks += interval;
|
||||
|
||||
@@ -30,7 +30,7 @@ static IntFlag retries(
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaults(0, 81, 0, 0);
|
||||
setReaderDefaultSource(":t=0-81:s=0");
|
||||
Flag::parseFlags(argc, argv);
|
||||
|
||||
bool failures = false;
|
||||
@@ -40,12 +40,12 @@ int main(int argc, const char* argv[])
|
||||
std::map<int, std::unique_ptr<Sector>> readSectors;
|
||||
for (int retry = ::retries; retry >= 0; retry--)
|
||||
{
|
||||
Fluxmap& fluxmap = track->read();
|
||||
std::unique_ptr<Fluxmap> fluxmap = track->read();
|
||||
|
||||
nanoseconds_t clockPeriod = fluxmap.guessClock();
|
||||
nanoseconds_t clockPeriod = fluxmap->guessClock();
|
||||
std::cout << fmt::format(" {:.1f} us clock; ", (double)clockPeriod/1000.0) << std::flush;
|
||||
|
||||
auto bitmap = fluxmap.decodeToBits(clockPeriod);
|
||||
auto bitmap = fluxmap->decodeToBits(clockPeriod);
|
||||
std::cout << fmt::format("{} bytes encoded; ", bitmap.size()/8) << std::flush;
|
||||
|
||||
auto records = decodeBitsToRecordsBrother(bitmap);
|
||||
@@ -103,7 +103,6 @@ int main(int argc, const char* argv[])
|
||||
|
||||
std::cout << std::endl
|
||||
<< " " << retry << " retries remaining" << std::endl;
|
||||
track->forceReread();
|
||||
}
|
||||
|
||||
int size = 0;
|
||||
|
||||
@@ -20,6 +20,7 @@ static SettableFlag dumpRecords(
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0-1");
|
||||
Flag::parseFlags(argc, argv);
|
||||
|
||||
bool failures = false;
|
||||
@@ -28,12 +29,12 @@ int main(int argc, const char* argv[])
|
||||
{
|
||||
int retries = 5;
|
||||
retry:
|
||||
Fluxmap& fluxmap = track->read();
|
||||
nanoseconds_t clockPeriod = fluxmap.guessClock();
|
||||
std::unique_ptr<Fluxmap> fluxmap = track->read();
|
||||
nanoseconds_t clockPeriod = fluxmap->guessClock();
|
||||
std::cout << fmt::format(" {:.1f} us clock; ", (double)clockPeriod/1000.0) << std::flush;
|
||||
|
||||
/* For MFM, the bit clock is half the detected clock. */
|
||||
auto bitmap = fluxmap.decodeToBits(clockPeriod/2);
|
||||
auto bitmap = fluxmap->decodeToBits(clockPeriod/2);
|
||||
std::cout << fmt::format("{} bytes encoded; ", bitmap.size()/8) << std::flush;
|
||||
|
||||
auto records = decodeBitsToRecordsMfm(bitmap);
|
||||
@@ -61,7 +62,6 @@ int main(int argc, const char* argv[])
|
||||
std::cout << std::endl
|
||||
<< " " << retries << " retries remaining" << std::endl;
|
||||
retries--;
|
||||
track->forceReread();
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,11 +32,6 @@ static DoubleFlag sectorSpacingMs(
|
||||
"Time between successive sector headers (milliseconds).",
|
||||
16.2);
|
||||
|
||||
static IntFlag trackOffset(
|
||||
{ "--track-offset" },
|
||||
"Number of tracks to offset when writing the image.",
|
||||
0);
|
||||
|
||||
static DoubleFlag postHeaderSpacingMs(
|
||||
{ "--post-header-spacing" },
|
||||
"Time between a sector's header and data records (milliseconds).",
|
||||
@@ -57,7 +52,7 @@ static int charToInt(char c)
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
setWriterDefaults(0, 81, 0, 0);
|
||||
setWriterDefaultDest(":t=0-77:s=0");
|
||||
Flag::parseFlags(argc, argv);
|
||||
|
||||
SectorSet allSectors;
|
||||
@@ -71,16 +66,14 @@ int main(int argc, const char* argv[])
|
||||
const std::string& skew = sectorSkew;
|
||||
|
||||
writeTracks(
|
||||
trackOffset, trackOffset+77,
|
||||
[&](int physicalTrack, int physicalSide) -> Fluxmap
|
||||
[&](int track, int side) -> std::unique_ptr<Fluxmap>
|
||||
{
|
||||
int logicalTrack = physicalTrack - trackOffset;
|
||||
if ((track < 0) || (track > 77) || (side != 0))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
std::cerr << "logical track " << logicalTrack << std::endl
|
||||
<< " ";
|
||||
|
||||
for (int sectorCount=0; sectorCount<geometry.sectors; sectorCount++)
|
||||
{
|
||||
int sectorId = charToInt(skew.at(sectorCount));
|
||||
@@ -89,10 +82,10 @@ int main(int argc, const char* argv[])
|
||||
double dataMs = headerMs + postHeaderSpacingMs;
|
||||
unsigned dataCursor = dataMs*1e3 / clockRateUs;
|
||||
|
||||
auto& sectorData = allSectors[{logicalTrack, 0, sectorId}];
|
||||
auto& sectorData = allSectors[{track, 0, sectorId}];
|
||||
|
||||
fillBitmapTo(bits, cursor, headerCursor, { true, false });
|
||||
writeBrotherSectorHeader(bits, cursor, logicalTrack, sectorId);
|
||||
writeBrotherSectorHeader(bits, cursor, track, sectorId);
|
||||
fillBitmapTo(bits, cursor, dataCursor, { true, false });
|
||||
writeBrotherSectorData(bits, cursor, sectorData->data);
|
||||
}
|
||||
@@ -104,8 +97,8 @@ int main(int argc, const char* argv[])
|
||||
// The pre-index gap is not normally reported.
|
||||
// std::cerr << "pre-index gap " << 200.0 - (double)cursor*clockRateUs/1e3 << std::endl;
|
||||
|
||||
Fluxmap fluxmap;
|
||||
fluxmap.appendBits(bits, clockRateUs*1e3);
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs*1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaults(0, 81, 0, 1);
|
||||
setWriterDefaults(0, 81, 0, 1);
|
||||
setReaderDefaultSource(":t=0-81:h=0-1");
|
||||
setWriterDefaultDest(":t=0-81:s=0-1");
|
||||
Flag::parseFlags(argc, argv);
|
||||
|
||||
auto tracks = readTracks();
|
||||
@@ -18,8 +18,7 @@ int main(int argc, const char* argv[])
|
||||
track->read();
|
||||
|
||||
writeTracks(
|
||||
0, 81,
|
||||
[&](int physicalTrack, int physicalSide) -> Fluxmap
|
||||
[&](int physicalTrack, int physicalSide) -> std::unique_ptr<Fluxmap>
|
||||
{
|
||||
for (auto& track : tracks)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include <assert.h>
|
||||
|
||||
@@ -39,30 +40,35 @@ static void test_dataspec(void)
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{0, 0}, {0, 1}, {1, 0}, {1, 1}, {2, 0}, {2, 1}}));
|
||||
assert((std::string)spec == "foo:s=0-1:t=0-2");
|
||||
|
||||
spec.set("bar");
|
||||
assert(spec.filename == "bar");
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{0, 0}, {0, 1}, {1, 0}, {1, 1}, {2, 0}, {2, 1}}));
|
||||
assert((std::string)spec == "bar:s=0-1:t=0-2");
|
||||
|
||||
spec.set(":t=0");
|
||||
assert(spec.filename.empty());
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{0, 0}, {0, 1}}));
|
||||
assert((std::string)spec == ":s=0-1:t=0");
|
||||
|
||||
spec.set(":s=1");
|
||||
assert(spec.filename.empty());
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{0, 1}}));
|
||||
assert((std::string)spec == ":s=1:t=0");
|
||||
|
||||
spec.set(":t=9");
|
||||
assert(spec.filename.empty());
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{9, 1}}));
|
||||
assert((std::string)spec == ":s=1:t=9");
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
|
||||
Reference in New Issue
Block a user