diff --git a/lib/fluxsource/cwffluxsource.cc b/lib/fluxsource/cwffluxsource.cc new file mode 100644 index 00000000..bf383e0e --- /dev/null +++ b/lib/fluxsource/cwffluxsource.cc @@ -0,0 +1,215 @@ +#include "globals.h" +#include "fluxmap.h" +#include "lib/fluxsource/fluxsource.pb.h" +#include "fluxsource/fluxsource.h" +#include "proto.h" +#include "fmt/format.h" +#include + +struct CwfHeader +{ + char file_id[4]; // file ID - almost always "CWSF" + uint8_t creator; // usually just the CW_TYPE + uint8_t file_type; // indexed, etc.? + 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 cylinders; // number of cylinders + uint8_t sides; // number of sides + uint8_t index_mark; // nonzero if index marks are included + uint8_t step; // cylinder stepping interval + uint8_t filler[15]; // reserved for expansion + uint8_t comment[100]; // brief comment +}; + +struct CwfTrack +{ + uint8_t track; // sequential + uint8_t side; + uint8_t unused[2]; + uint8_t length[4]; // little-endian +}; + +class CwfFluxSource : public FluxSource +{ +public: + CwfFluxSource(const CwfFluxSourceProto& config): + _config(config) + { + _if.open(_config.filename(), std::ios::in | std::ios::binary); + if (!_if.is_open()) + Error() << fmt::format("cannot open input file '{}': {}", + _config.filename(), strerror(errno)); + + _if.read((char*) &_header, sizeof(_header)); + check_for_error(); + + if ((_header.file_id[0] != 'C') + || (_header.file_id[1] != 'W') + || (_header.file_id[2] != 'S') + || (_header.file_id[3] != 'F')) + Error() << "input not a CWF file"; + + switch (_header.clock_rate) + { + case 1: _clockRate = 14161000.0; break; + case 2: _clockRate = 28322000.0; break; + default: + Error() << "unsupported clock rate"; + } + + std::cout << fmt::format("CWF {}x{} = {} cylinders, {} sides\n", + _header.cylinders, _header.step, _header.cylinders*_header.step, _header.sides); + std::cout << fmt::format("CWF sample clock rate: {} MHz\n", _clockRate / 1e6); + + int tracks = _header.cylinders*_header.sides; + for (int i=0; i readFlux(int track, int side) + { + const auto& p = _trackOffsets.find(std::make_pair(track, side)); + if (p == _trackOffsets.end()) + return std::unique_ptr(); + + off_t pos = p->second.first;; + size_t length = p->second.second; + _if.seekg(pos); + + std::unique_ptr fluxmap(new Fluxmap); + uint32_t pending = 0; + bool oldindex = true; + for (size_t cursor = 0; cursor < length; cursor++) + { + uint32_t b = _if.get(); + bool index = !!(b & 0x80); + b &= 0x7f; + if (b == 0x7f) + { + pending += 0x7f; + continue; + } + b += pending; + pending = 0; + + double interval_us = b * (1e6/_clockRate); + fluxmap->appendInterval(interval_us / US_PER_TICK); + fluxmap->appendPulse(); + + if (index && !oldindex) + fluxmap->appendIndex(); + oldindex = index; + } + check_for_error(); + + return fluxmap; + } + + void recalibrate() {} + +private: + void check_for_error() + { + if (_if.fail()) + Error() << fmt::format("SCP read I/O error: {}", strerror(errno)); + } + +private: + const CwfFluxSourceProto& _config; + std::ifstream _if; + CwfHeader _header; + nanoseconds_t _clockRate; + std::map, std::pair> _trackOffsets; +}; + +std::unique_ptr FluxSource::createCwfFluxSource(const CwfFluxSourceProto& config) +{ + return std::unique_ptr(new CwfFluxSource(config)); +} + +#if 0 +#include "globals.h" +#include "fluxmap.h" +#include "sql.h" +#include "bytes.h" +#include "protocol.h" + +static std::ifstream inputFile; +static sqlite3* outputDb; +static CwfHeader header; +static double clockRate; + +static void syntax() +{ + std::cout << "Syntax: fluxengine convert cwftoflux \n"; + exit(0); +} + +static void check_for_error() +{ + if (inputFile.fail()) + Error() << fmt::format("I/O error: {}", strerror(errno)); +} + +static void read_header() +{ +} + +static void read_track() +{ + CwfTrack trackheader; + inputFile.read((char*) &trackheader, sizeof(trackheader)); + check_for_error(); + + uint32_t length = Bytes(trackheader.length, 4).reader().read_le32() - sizeof(CwfTrack); + unsigned track_number = trackheader.track * header.step; + std::cout << fmt::format("{}.{}: {} input bytes; ", track_number, trackheader.side, length) + << std::flush; + + std::vector inputdata(length); + inputFile.read((char*) &inputdata[0], length); + check_for_error(); + + + std::cout << fmt::format(" {} ms in {} output bytes\n", + fluxmap.duration() / 1e6, fluxmap.bytes()); + sqlWriteFlux(outputDb, track_number, trackheader.side, fluxmap); +} + +int mainConvertCwfToFlux(int argc, const char* argv[]) +{ + if (argc != 3) + syntax(); + + inputFile.open(argv[1], std::ios::in | std::ios::binary); + if (!inputFile.is_open()) + Error() << fmt::format("cannot open input file '{}'", argv[1]); + + outputDb = sqlOpen(argv[2], SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + sqlPrepareFlux(outputDb); + sqlWriteIntProperty(outputDb, "version", FLUX_VERSION_CURRENT); + sqlStmt(outputDb, "BEGIN;"); + + read_header(); + inputFile.seekg(sizeof(header), std::ios::beg); + for (unsigned i=0; i<(header.cylinders*header.sides); i++) + read_track(); + + sqlStmt(outputDb, "COMMIT;"); + sqlClose(outputDb); + return 0; +} +#endif diff --git a/lib/fluxsource/fluxsource.cc b/lib/fluxsource/fluxsource.cc index 4f9e6672..e6a4251a 100644 --- a/lib/fluxsource/fluxsource.cc +++ b/lib/fluxsource/fluxsource.cc @@ -36,6 +36,9 @@ std::unique_ptr FluxSource::create(const FluxSourceProto& config) case FluxSourceProto::kScp: return createScpFluxSource(config.scp()); + + case FluxSourceProto::kCwf: + return createCwfFluxSource(config.cwf()); } Error() << "bad input disk configuration"; @@ -49,6 +52,7 @@ void FluxSource::updateConfigForFilename(const std::string& filename) { { std::regex("^(.*\\.flux)$"), [&](const auto& s) { f->set_fluxfile(s); }}, { std::regex("^(.*\\.scp)$"), [&](const auto& s) { f->mutable_scp()->set_filename(s); }}, + { std::regex("^(.*\\.cwf)$"), [&](const auto& s) { f->mutable_cwf()->set_filename(s); }}, { std::regex("^erase:$"), [&](const auto& s) { f->mutable_erase(); }}, { std::regex("^kryoflux:(.*)$"), [&](const auto& s) { f->mutable_kryoflux()->set_directory(s); }}, { std::regex("^testpattern:(.*)"), [&](const auto& s) { f->mutable_test_pattern(); }}, diff --git a/lib/fluxsource/fluxsource.h b/lib/fluxsource/fluxsource.h index e784f561..acbaf824 100644 --- a/lib/fluxsource/fluxsource.h +++ b/lib/fluxsource/fluxsource.h @@ -3,14 +3,15 @@ #include "flags.h" -class Fluxmap; -class FluxSpec; -class FluxSourceProto; -class HardwareFluxSourceProto; -class TestPatternFluxSourceProto; +class CwfFluxSourceProto; class EraseFluxSourceProto; +class FluxSourceProto; +class FluxSpec; +class Fluxmap; +class HardwareFluxSourceProto; class KryofluxFluxSourceProto; class ScpFluxSourceProto; +class TestPatternFluxSourceProto; class FluxSource { @@ -18,12 +19,13 @@ public: virtual ~FluxSource() {} private: - static std::unique_ptr createSqliteFluxSource(const std::string& filename); + static std::unique_ptr createCwfFluxSource(const CwfFluxSourceProto& config); + static std::unique_ptr createEraseFluxSource(const EraseFluxSourceProto& config); static std::unique_ptr createHardwareFluxSource(const HardwareFluxSourceProto& config); static std::unique_ptr createKryofluxFluxSource(const KryofluxFluxSourceProto& config); static std::unique_ptr createScpFluxSource(const ScpFluxSourceProto& config); + static std::unique_ptr createSqliteFluxSource(const std::string& filename); static std::unique_ptr createTestPatternFluxSource(const TestPatternFluxSourceProto& config); - static std::unique_ptr createEraseFluxSource(const EraseFluxSourceProto& config); public: static std::unique_ptr create(const FluxSourceProto& spec); diff --git a/lib/fluxsource/fluxsource.proto b/lib/fluxsource/fluxsource.proto index 04a0fd34..e28a1ec4 100644 --- a/lib/fluxsource/fluxsource.proto +++ b/lib/fluxsource/fluxsource.proto @@ -29,6 +29,11 @@ message ScpFluxSourceProto { (help) = ".scp file to read flux from"]; } +message CwfFluxSourceProto { + optional string filename = 1 [default = "flux.xwf", + (help) = ".cwf file to read flux from"]; +} + message FluxSourceProto { oneof source { string fluxfile = 1; @@ -37,6 +42,7 @@ message FluxSourceProto { EraseFluxSourceProto erase = 4; KryofluxFluxSourceProto kryoflux = 5; ScpFluxSourceProto scp = 6; + CwfFluxSourceProto cwf = 7; } } diff --git a/mkninja.sh b/mkninja.sh index 51857a1c..ce22d223 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -320,6 +320,7 @@ buildlibrary libbackend.a \ lib/fluxsink/scpfluxsink.cc \ lib/fluxsink/sqlitefluxsink.cc \ lib/fluxsink/vcdfluxsink.cc \ + lib/fluxsource/cwffluxsource.cc \ lib/fluxsource/erasefluxsource.cc \ lib/fluxsource/fluxsource.cc \ lib/fluxsource/hardwarefluxsource.cc \ @@ -416,7 +417,6 @@ buildlibrary libfrontend.a \ $OBJDIR/writables.cc \ src/fe-analysedriveresponse.cc \ src/fe-analyselayout.cc \ - src/fe-cwftoflux.cc \ src/fe-image.cc \ src/fe-inspect.cc \ src/fe-rawread.cc \ diff --git a/src/fe-cwftoflux.cc b/src/fe-cwftoflux.cc deleted file mode 100644 index 27ca3bb2..00000000 --- a/src/fe-cwftoflux.cc +++ /dev/null @@ -1,141 +0,0 @@ -#include "globals.h" -#include "fluxmap.h" -#include "sql.h" -#include "bytes.h" -#include "protocol.h" -#include "fmt/format.h" -#include - -struct CwfHeader -{ - char file_id[4]; // file ID - almost always "CWSF" - uint8_t creator; // usually just the CW_TYPE - uint8_t file_type; // indexed, etc.? - 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 cylinders; // number of cylinders - uint8_t sides; // number of sides - uint8_t index_mark; // nonzero if index marks are included - uint8_t step; // cylinder stepping interval - uint8_t filler[15]; // reserved for expansion - uint8_t comment[100]; // brief comment -}; - -struct CwfTrack -{ - uint8_t track; // sequential - uint8_t side; - uint8_t unused[2]; - uint8_t length[4]; // little-endian -}; - -static std::ifstream inputFile; -static sqlite3* outputDb; -static CwfHeader header; -static double clockRate; - -static void syntax() -{ - std::cout << "Syntax: fluxengine convert cwftoflux \n"; - exit(0); -} - -static void check_for_error() -{ - if (inputFile.fail()) - Error() << fmt::format("I/O error: {}", strerror(errno)); -} - -static void read_header() -{ - inputFile.read((char*) &header, sizeof(header)); - check_for_error(); - - if ((header.file_id[0] != 'C') - || (header.file_id[1] != 'W') - || (header.file_id[2] != 'S') - || (header.file_id[3] != 'F')) - Error() << "input not a CWF file"; - - switch (header.clock_rate) - { - case 1: clockRate = 14161000.0; break; - case 2: clockRate = 28322000.0; break; - default: - Error() << "unsupported clock rate"; - } - - std::cout << fmt::format("{}x{} = {} tracks, {} sides\n", - header.cylinders, header.step, header.cylinders*header.step, header.sides); - std::cout << fmt::format("sample clock rate: {} MHz\n", clockRate / 1e6); -} - -static void read_track() -{ - CwfTrack trackheader; - inputFile.read((char*) &trackheader, sizeof(trackheader)); - check_for_error(); - - uint32_t length = Bytes(trackheader.length, 4).reader().read_le32() - sizeof(CwfTrack); - unsigned track_number = trackheader.track * header.step; - std::cout << fmt::format("{}.{}: {} input bytes; ", track_number, trackheader.side, length) - << std::flush; - - std::vector inputdata(length); - inputFile.read((char*) &inputdata[0], length); - check_for_error(); - - Fluxmap fluxmap; - uint32_t pending = 0; - bool oldindex = true; - for (unsigned cursor = 0; cursor < length; cursor++) - { - uint32_t b = inputdata[cursor]; - bool index = !!(b & 0x80); - b &= 0x7f; - if (b == 0x7f) - { - pending += 0x7f; - continue; - } - b += pending; - pending = 0; - - double interval_us = b * (1e6/clockRate); - fluxmap.appendInterval(interval_us / US_PER_TICK); - fluxmap.appendPulse(); - - if (index && !oldindex) - fluxmap.appendIndex(); - oldindex = index; - } - - std::cout << fmt::format(" {} ms in {} output bytes\n", - fluxmap.duration() / 1e6, fluxmap.bytes()); - sqlWriteFlux(outputDb, track_number, trackheader.side, fluxmap); -} - -int mainConvertCwfToFlux(int argc, const char* argv[]) -{ - if (argc != 3) - syntax(); - - inputFile.open(argv[1], std::ios::in | std::ios::binary); - if (!inputFile.is_open()) - Error() << fmt::format("cannot open input file '{}'", argv[1]); - - outputDb = sqlOpen(argv[2], SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); - sqlPrepareFlux(outputDb); - sqlWriteIntProperty(outputDb, "version", FLUX_VERSION_CURRENT); - sqlStmt(outputDb, "BEGIN;"); - - read_header(); - inputFile.seekg(sizeof(header), std::ios::beg); - for (unsigned i=0; i<(header.cylinders*header.sides); i++) - read_track(); - - sqlStmt(outputDb, "COMMIT;"); - sqlClose(outputDb); - return 0; -} diff --git a/src/fluxengine.cc b/src/fluxengine.cc index f6a7909f..d917b910 100644 --- a/src/fluxengine.cc +++ b/src/fluxengine.cc @@ -4,9 +4,7 @@ typedef int command_cb(int agrc, const char* argv[]); extern command_cb mainAnalyseDriveResponse; extern command_cb mainAnalyseLayout; -extern command_cb mainConvertCwfToFlux; extern command_cb mainConvertImage; -extern command_cb mainConvertScpToFlux; extern command_cb mainInspect; extern command_cb mainRawRead; extern command_cb mainRawWrite; @@ -46,7 +44,6 @@ static std::vector commands = static std::vector convertables = { - { "cwftoflux", mainConvertCwfToFlux, "Converts CatWeasel stream files to flux.", }, { "image", mainConvertImage, "Converts one disk image to another.", }, };