Skip to content

Commit

Permalink
Merge pull request #108 from davidgiven/scp
Browse files Browse the repository at this point in the history
Add very beta support for scp import and export
  • Loading branch information
davidgiven authored Sep 21, 2019
2 parents 4107039 + e8f7b51 commit 7b4a8d6
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 69 deletions.
18 changes: 13 additions & 5 deletions doc/using.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,19 @@ directory.
file format in a non-backwards-compatible way; this tool will upgrade flux
files to the new format.
- `fluxengine convert`: converts various formats to various other formats.
You can use this to convert Catweasel or Supercard Pro flux files to
FluxEngine's native format, for flux files to various other formats useful
for debugging (including VCD which can be loaded into
[sigrok](http://sigrok.org)).
- `fluxengine convert`: converts flux files from various formats to various
other formats. You can use this to convert Catweasel flux files to
FluxEngine's native format, FluxEngine flux files to various other formats
useful for debugging (including VCD which can be loaded into
[sigrok](http://sigrok.org)), and bidirectional conversion to and from
Supercard Pro `.scp` format.
**Important SCP note:** import (`fluxengine convert scptoflux`) should be
fairly robust, but export (`fluxengine convert fluxtoscp`) should only be
done with great caution as FluxEngine files contain features which can't be
represented very well in `.scp` format and they're probably pretty dubious.
As ever, please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new) with any reports.
Commands which normally take `--source` or `--dest` get a sensible default if
left unspecified. `fluxengine read ibm` on its own will read drive 0 and
Expand Down
86 changes: 54 additions & 32 deletions lib/flags.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ void FlagGroup::addFlag(Flag* flag)
_flags.push_back(flag);
}

void FlagGroup::parseFlags(int argc, const char* argv[])
std::vector<std::string> FlagGroup::parseFlagsWithFilenames(int argc, const char* argv[])
{
if (_initialised)
throw std::runtime_error("called parseFlags() twice");
Expand Down Expand Up @@ -66,6 +66,7 @@ void FlagGroup::parseFlags(int argc, const char* argv[])

/* Now actually parse them. */

std::vector<std::string> filenames;
int index = 1;
while (index < argc)
{
Expand All @@ -76,52 +77,73 @@ void FlagGroup::parseFlags(int argc, const char* argv[])
std::string value;
bool usesthat = false;

if ((thisarg.size() == 0) || (thisarg[0] != '-'))
Error() << "non-option parameter " << thisarg << " seen (try --help)";
if ((thisarg.size() > 1) && (thisarg[1] == '-'))
if (thisarg.size() == 0)
{
/* Long option. */

auto equals = thisarg.rfind('=');
if (equals != std::string::npos)
{
key = thisarg.substr(0, equals);
value = thisarg.substr(equals+1);
}
else
{
key = thisarg;
value = thatarg;
usesthat = true;
}
/* Ignore this argument. */
}
else if (thisarg[0] != '-')
{
/* This is a filename. */
filenames.push_back(thisarg);
}
else
{
/* Short option. */
/* This is a flag. */

if (thisarg.size() > 2)
if ((thisarg.size() > 1) && (thisarg[1] == '-'))
{
key = thisarg.substr(0, 2);
value = thisarg.substr(2);
/* Long option. */

auto equals = thisarg.rfind('=');
if (equals != std::string::npos)
{
key = thisarg.substr(0, equals);
value = thisarg.substr(equals+1);
}
else
{
key = thisarg;
value = thatarg;
usesthat = true;
}
}
else
{
key = thisarg;
value = thatarg;
usesthat = true;
/* Short option. */

if (thisarg.size() > 2)
{
key = thisarg.substr(0, 2);
value = thisarg.substr(2);
}
else
{
key = thisarg;
value = thatarg;
usesthat = true;
}
}
}

auto flag = flags_by_name.find(key);
if (flag == flags_by_name.end())
Error() << "unknown flag '" << key << "'; try --help";
auto flag = flags_by_name.find(key);
if (flag == flags_by_name.end())
Error() << "unknown flag '" << key << "'; try --help";

flag->second->set(value);
flag->second->set(value);
if (usesthat && flag->second->hasArgument())
index++;
}

index++;
if (usesthat && flag->second->hasArgument())
index++;
}

return filenames;
}

void FlagGroup::parseFlags(int argc, const char* argv[])
{
auto filenames = parseFlagsWithFilenames(argc, argv);
if (!filenames.empty())
Error() << "non-option parameter " << *filenames.begin() << " seen (try --help)";
}

void FlagGroup::checkInitialised() const
Expand Down
1 change: 1 addition & 0 deletions lib/flags.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class FlagGroup

public:
void parseFlags(int argc, const char* argv[]);
std::vector<std::string> parseFlagsWithFilenames(int argc, const char* argv[]);
void addFlag(Flag* flag);
void checkInitialised() const;

Expand Down
1 change: 1 addition & 0 deletions mkninja.sh
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ buildlibrary libfrontend.a \
src/fe-cwftoflux.cc \
src/fe-erase.cc \
src/fe-fluxtoau.cc \
src/fe-fluxtoscp.cc \
src/fe-fluxtovcd.cc \
src/fe-inspect.cc \
src/fe-readadfs.cc \
Expand Down
188 changes: 188 additions & 0 deletions src/fe-fluxtoscp.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "sql.h"
#include "bytes.h"
#include "protocol.h"
#include "dataspec.h"
#include "fmt/format.h"
#include "decoders/fluxmapreader.h"
#include "scp.h"
#include <fstream>
#include <algorithm>

static FlagGroup flags { };

static SettableFlag fortyTrackMode(
{ "--48", "-4" },
"set 48 tpi mode; only every other physical track is emitted"
);

static SettableFlag singleSided(
{ "--single-sided", "-s" },
"only emit side 0"
);

static IntFlag diskType(
{ "--disk-type" },
"sets the SCP disk type byte",
0xff
);

static sqlite3* inputDb;

static void syntax()
{
std::cout << "Syntax: fluxengine convert fluxtoscp <fluxfile> <scpfile>\n";
exit(0);
}

static void write_le32(uint8_t dest[4], uint32_t v)
{
dest[0] = v;
dest[1] = v >> 8;
dest[2] = v >> 16;
dest[3] = v >> 24;
}

static int strackno(int track, int side)
{
if (fortyTrackMode)
track /= 2;
if (singleSided)
return track;
else
return (track << 1) | side;
}

int mainConvertFluxToScp(int argc, const char* argv[])
{
auto filenames = flags.parseFlagsWithFilenames(argc, argv);
if (filenames.size() != 2)
syntax();

inputDb = sqlOpen(filenames[0], SQLITE_OPEN_READONLY);
auto tracks = sqlFindFlux(inputDb);

int maxTrack = 0;
int maxSide = 0;
for (auto p : tracks)
{
if (singleSided && (p.second == 1))
continue;
maxTrack = std::max(maxTrack, (int)p.first);
maxSide = std::max(maxSide, (int)p.second);
}
int maxStrack = strackno(maxTrack, maxSide);

std::cout << fmt::format("Writing {} {} SCP file containing {} SCP tracks\n",
fortyTrackMode ? "48 tpi" : "96 tpi",
singleSided ? "single sided" : "double sided",
maxStrack + 1
);

ScpHeader fileheader = {0};
fileheader.file_id[0] = 'S';
fileheader.file_id[1] = 'C';
fileheader.file_id[2] = 'P';
fileheader.file_id[3] = 0x18; /* Version 1.8 of the spec */
fileheader.type = diskType;
fileheader.revolutions = 5;
fileheader.start_track = 0;
fileheader.end_track = maxStrack;
fileheader.flags = SCP_FLAG_INDEXED | (fortyTrackMode ? 0 : SCP_FLAG_96TPI);
fileheader.cell_width = 0;
fileheader.heads = singleSided ? 1 : 0;

Bytes trackdata;
ByteWriter trackdataWriter(trackdata);

int trackstep = 1 + fortyTrackMode;
int maxside = singleSided ? 0 : 1;
for (int track = 0; track <= maxTrack; track += trackstep)
{
for (int side = 0; side <= maxside; side++)
{
int strack = strackno(track, side);
std::cout << fmt::format("FE track {}.{}, SCP track {}: ", track, side, strack) << std::flush;

auto fluxmap = sqlReadFlux(inputDb, track, side);
ScpTrack trackheader = {0};
trackheader.track_id[0] = 'T';
trackheader.track_id[1] = 'R';
trackheader.track_id[2] = 'K';
trackheader.strack = strack;

FluxmapReader fmr(*fluxmap);
Bytes fluxdata;
ByteWriter fluxdataWriter(fluxdata);

int revolution = 0;
unsigned revTicks = 0;
unsigned totalTicks = 0;
unsigned ticksSinceLastPulse = 0;
uint32_t startOffset = 0;
while (revolution < 5)
{
unsigned ticks;
int opcode = fmr.readOpcode(ticks);
if (ticks)
{
ticksSinceLastPulse += ticks;
totalTicks += ticks;
revTicks += ticks;
}

switch (opcode)
{
case -1: /* end of flux, treat like an index marker */
case F_OP_INDEX:
{
auto* revheader = &trackheader.revolution[revolution];
write_le32(revheader->offset, startOffset + sizeof(ScpTrack));
write_le32(revheader->length, (fluxdataWriter.pos - startOffset) / 2);
write_le32(revheader->index, revTicks * NS_PER_TICK / 25);
revolution++;
revheader++;
revTicks = 0;
startOffset = fluxdataWriter.pos;
break;
}

case F_OP_PULSE:
{
unsigned t = ticksSinceLastPulse * NS_PER_TICK / 25;
while (t >= 0x10000)
{
fluxdataWriter.write_be16(0);
t -= 0x10000;
}
fluxdataWriter.write_be16(t);
ticksSinceLastPulse = 0;
break;
}
}
}

write_le32(fileheader.track[strack], trackdataWriter.pos + sizeof(ScpHeader));
trackdataWriter += Bytes((uint8_t*)&trackheader, sizeof(trackheader));
trackdataWriter += fluxdata;

std::cout << fmt::format("{} ms in {} bytes\n",
totalTicks * MS_PER_TICK,
fluxdata.size());
}
}

sqlClose(inputDb);

std::cout << "Writing output file...\n";
std::ofstream of(filenames[1], std::ios::out | std::ios::binary);
if (!of.is_open())
Error() << "cannot open output file";
of.write((const char*) &fileheader, sizeof(fileheader));
of.write((const char*) trackdata.begin(), trackdata.size());
of.close();

return 0;
}
Loading

0 comments on commit 7b4a8d6

Please sign in to comment.