Allow split decoders (into sector record and data record).

This commit is contained in:
David Given
2019-04-19 00:55:01 +02:00
parent b158692a3a
commit 5f8e0c846c
18 changed files with 375 additions and 194 deletions

View File

@@ -49,6 +49,9 @@ static const std::string BLOCK_ELEMENTS[] =
*/ */
nanoseconds_t Fluxmap::guessClock() const nanoseconds_t Fluxmap::guessClock() const
{ {
if (manualClockRate != 0.0)
return manualClockRate * 1000.0;
uint32_t buckets[256] = {}; uint32_t buckets[256] = {};
FluxmapReader fr(*this); FluxmapReader fr(*this);
@@ -248,6 +251,7 @@ RawRecordVector AbstractSoftSectorDecoder::extractRecords(const RawBits& rawbits
if (matchStart == -1) if (matchStart == -1)
return; return;
#if 0
records.push_back( records.push_back(
std::unique_ptr<RawRecord>( std::unique_ptr<RawRecord>(
new RawRecord( new RawRecord(
@@ -260,6 +264,7 @@ RawRecordVector AbstractSoftSectorDecoder::extractRecords(const RawBits& rawbits
) )
) )
); );
#endif
}; };
while (cursor < rawbits.size()) while (cursor < rawbits.size())
@@ -315,6 +320,7 @@ RawRecordVector AbstractHardSectorDecoder::extractRecords(const RawBits& rawbits
if ((end - previous) < sectors_must_be_bigger_than) if ((end - previous) < sectors_must_be_bigger_than)
return; return;
#if 0
records.push_back( records.push_back(
std::unique_ptr<RawRecord>( std::unique_ptr<RawRecord>(
new RawRecord( new RawRecord(
@@ -327,6 +333,7 @@ RawRecordVector AbstractHardSectorDecoder::extractRecords(const RawBits& rawbits
) )
) )
); );
#endif
previous = end; previous = end;
}; };
@@ -338,4 +345,69 @@ RawRecordVector AbstractHardSectorDecoder::extractRecords(const RawBits& rawbits
return records; return records;
} }
void AbstractStatefulDecoder::decodeToSectors(Track& track)
{
Sector sector;
sector.physicalSide = track.physicalSide;
sector.physicalTrack = track.physicalTrack;
FluxmapReader fmr(*track.fluxmap);
for (;;)
{
nanoseconds_t clockPeriod = findSector(fmr, track);
if (fmr.eof() || !clockPeriod)
break;
sector.status = Sector::MISSING;
sector.data.clear();
sector.clock = clockPeriod;
_recordStart = sector.position = fmr.tell();
decodeSingleSector(fmr, track, sector);
pushRecord(fmr, track, sector);
if (sector.status != Sector::MISSING)
track.sectors.push_back(sector);
}
}
void AbstractStatefulDecoder::pushRecord(FluxmapReader& fmr, Track& track, Sector& sector)
{
RawRecord record;
record.physicalSide = track.physicalSide;
record.physicalTrack = track.physicalTrack;
record.clock = sector.clock;
record.position = _recordStart;
Fluxmap::Position here = fmr.tell();
fmr.seek(_recordStart);
record.data = toBytes(fmr.readRawBits(here, sector.clock));
track.rawrecords.push_back(record);
_recordStart = here;
}
void AbstractStatefulDecoder::discardRecord(FluxmapReader& fmr)
{
_recordStart = fmr.tell();
}
void AbstractSplitDecoder::decodeSingleSector(FluxmapReader& fmr, Track& track, Sector& sector)
{
decodeHeader(fmr, track, sector);
if (sector.status == Sector::MISSING)
return;
pushRecord(fmr, track, sector);
nanoseconds_t clockPeriod = findData(fmr, track);
if (fmr.eof() || !clockPeriod)
return;
sector.clock = clockPeriod;
discardRecord(fmr);
Fluxmap::Position pos = fmr.tell();
decodeData(fmr, track, sector);
if (sector.status == Sector::DATA_MISSING)
{
fmr.seek(pos);
return;
}
}

View File

@@ -2,9 +2,12 @@
#define DECODERS_H #define DECODERS_H
#include "bytes.h" #include "bytes.h"
#include "sector.h"
#include "record.h"
class Sector; class Sector;
class Fluxmap; class Fluxmap;
class FluxmapReader;
class RawRecord; class RawRecord;
class RawBits; class RawBits;
class Track; class Track;
@@ -67,6 +70,26 @@ public:
class AbstractStatefulDecoder : public AbstractDecoder class AbstractStatefulDecoder : public AbstractDecoder
{ {
public:
void decodeToSectors(Track& track);
void discardRecord(FluxmapReader& fmr);
void pushRecord(FluxmapReader& fmr, Track& track, Sector& sector);
protected:
virtual nanoseconds_t findSector(FluxmapReader& fmr, Track& track) = 0;
virtual void decodeSingleSector(FluxmapReader& fmr, Track& track, Sector& sector) = 0;
private:
Fluxmap::Position _recordStart;
};
class AbstractSplitDecoder : public AbstractStatefulDecoder
{
void decodeSingleSector(FluxmapReader& fmr, Track& track, Sector& sector) override;
virtual nanoseconds_t findData(FluxmapReader& fmr, Track& track) = 0;
virtual void decodeHeader(FluxmapReader& fmr, Track& track, Sector& sector) = 0;
virtual void decodeData(FluxmapReader& fmr, Track& track, Sector& sector) = 0;
}; };
#endif #endif

View File

@@ -9,7 +9,7 @@
static DoubleFlag clockDecodeThreshold( static DoubleFlag clockDecodeThreshold(
{ "--bit-error-threshold" }, { "--bit-error-threshold" },
"Amount of error to tolerate in pulse timing.", "Amount of error to tolerate in pulse timing.",
0.20); 0.30);
int FluxmapReader::readOpcode(unsigned& ticks) int FluxmapReader::readOpcode(unsigned& ticks)
{ {
@@ -47,17 +47,17 @@ unsigned FluxmapReader::readNextMatchingOpcode(uint8_t opcode)
} }
} }
FluxPattern::FluxPattern(unsigned len, uint64_t pattern) FluxPattern::FluxPattern(unsigned bits, uint64_t pattern):
FluxMatcher(bits)
{ {
const uint64_t TOPBIT = 1ULL << 63; const uint64_t TOPBIT = 1ULL << 63;
assert(pattern != 0); assert(pattern != 0);
bits = len;
while (!(pattern & TOPBIT)) while (!(pattern & TOPBIT))
pattern <<= 1; pattern <<= 1;
length = 0; _length = 0;
while (pattern != TOPBIT) while (pattern != TOPBIT)
{ {
unsigned interval = 0; unsigned interval = 0;
@@ -67,59 +67,98 @@ FluxPattern::FluxPattern(unsigned len, uint64_t pattern)
interval++; interval++;
} }
while (!(pattern & TOPBIT)); while (!(pattern & TOPBIT));
intervals.push_back(interval); _intervals.push_back(interval);
length += interval; _length += interval;
} }
} }
bool FluxPattern::matches(const unsigned* end, double& clock) const unsigned FluxPattern::matches(const unsigned* end, double& clock) const
{ {
const unsigned* start = end - intervals.size(); const unsigned* start = end - _intervals.size();
unsigned candidatelength = std::accumulate(start, end, 0); unsigned candidatelength = std::accumulate(start, end, 0);
if (!candidatelength) if (!candidatelength)
return false; return 0;
clock = (double)candidatelength / (double)length; clock = (double)candidatelength / (double)_length;
for (unsigned i=0; i<intervals.size(); i++) for (unsigned i=0; i<_intervals.size(); i++)
{ {
double ii = clock * (double)intervals[i]; double ii = clock * (double)_intervals[i];
double ci = (double)start[i]; double ci = (double)start[i];
double error = fabs(1.0 - ii/ci); double error = fabs(1.0 - ii/ci);
if (error > clockDecodeThreshold) if (error > clockDecodeThreshold)
return false; return 0;
} }
return true; return _intervals.size();
} }
nanoseconds_t FluxmapReader::seekToPattern(const FluxPattern& pattern) FluxPatterns::FluxPatterns(unsigned bits, std::initializer_list<uint64_t> patterns):
FluxMatcher(bits)
{ {
unsigned length = pattern.intervals.size(); _intervals = 0;
unsigned candidates[length]; for (uint64_t p : patterns)
Position positions[length]; {
auto pattern = std::make_unique<FluxPattern>(bits, p);
_intervals = std::max(_intervals, pattern->intervals());
_patterns.push_back(std::move(pattern));
}
}
unsigned FluxPatterns::matches(const unsigned* intervals, double& clock) const
{
for (const auto& pattern : _patterns)
{
unsigned m = pattern->matches(intervals, clock);
if (m)
return m;
}
return 0;
}
void FluxmapReader::seek(nanoseconds_t ns)
{
unsigned ticks = ns / NS_PER_TICK;
if (ticks < _pos.ticks)
{
_pos.ticks = 0;
_pos.bytes = 0;
}
while (!eof() && (_pos.ticks < ticks))
{
unsigned t;
readOpcode(t);
}
}
nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern)
{
unsigned intervalCount = pattern.intervals();
unsigned candidates[intervalCount+1];
Fluxmap::Position positions[intervalCount+1];
for (unsigned& i : candidates) for (unsigned& i : candidates)
i = 0; i = 0;
for (Position& p : positions) for (Fluxmap::Position& p : positions)
p = tell(); p = tell();
while (!eof()) while (!eof())
{ {
unsigned interval = readNextMatchingOpcode(F_OP_PULSE); unsigned interval = readNextMatchingOpcode(F_OP_PULSE);
Position current = positions[0]; for (unsigned i=0; i<intervalCount; i++)
for (unsigned i=0; i<(length-1); i++)
{ {
positions[i] = positions[i+1]; positions[i] = positions[i+1];
candidates[i] = candidates[i+1]; candidates[i] = candidates[i+1];
} }
positions[length-1] = tell(); positions[intervalCount] = tell();
candidates[length-1] = interval; candidates[intervalCount] = interval;
double clock; double clock;
if (pattern.matches(&candidates[length], clock)) unsigned m = pattern.matches(&candidates[intervalCount+1], clock);
if (m)
{ {
seek(current); seek(positions[intervalCount-m]);
return clock * NS_PER_TICK; return clock * NS_PER_TICK;
} }
} }
@@ -154,3 +193,11 @@ std::vector<bool> FluxmapReader::readRawBits(unsigned count, nanoseconds_t clock
result.push_back(readRawBit(clockPeriod)); result.push_back(readRawBit(clockPeriod));
return result; return result;
} }
std::vector<bool> FluxmapReader::readRawBits(const Fluxmap::Position& until, nanoseconds_t clockPeriod)
{
std::vector<bool> result;
while (!eof() && (_pos.bytes < until.bytes))
result.push_back(readRawBit(clockPeriod));
return result;
}

View File

@@ -4,27 +4,62 @@
#include "fluxmap.h" #include "fluxmap.h"
#include "protocol.h" #include "protocol.h"
class FluxPattern class FluxMatcher
{ {
public: public:
FluxPattern(unsigned len, uint64_t pattern); FluxMatcher(unsigned bits):
_bits(bits)
{}
bool matches(const unsigned* intervals, double& clock) const; virtual ~FluxMatcher() {}
unsigned bits; /* Returns the number of intervals matched */
std::vector<unsigned> intervals; virtual unsigned matches(const unsigned* intervals, double& clock) const = 0;
unsigned length; virtual unsigned intervals() const = 0;
unsigned bits() const
{ return _bits; }
protected:
unsigned _bits;
};
class FluxPattern : public FluxMatcher
{
public:
FluxPattern(unsigned bits, uint64_t patterns);
unsigned matches(const unsigned* intervals, double& clock) const override;
unsigned intervals() const override
{ return _intervals.size(); }
private:
std::vector<unsigned> _intervals;
unsigned _length;
public:
friend void test_patternconstruction();
friend void test_patternmatching();
};
class FluxPatterns : public FluxMatcher
{
public:
FluxPatterns(unsigned bits, std::initializer_list<uint64_t> patterns);
unsigned matches(const unsigned* intervals, double& clock) const override;
unsigned intervals() const override
{ return _intervals; }
private:
unsigned _intervals;
std::vector<std::unique_ptr<FluxPattern>> _patterns;
}; };
class FluxmapReader class FluxmapReader
{ {
public:
struct Position
{
unsigned bytes;
unsigned ticks;
};
public: public:
FluxmapReader(const Fluxmap& fluxmap): FluxmapReader(const Fluxmap& fluxmap):
_fluxmap(fluxmap), _fluxmap(fluxmap),
@@ -46,30 +81,28 @@ public:
bool eof() const bool eof() const
{ return _pos.bytes == _size; } { return _pos.bytes == _size; }
Position tell() const Fluxmap::Position tell() const
{ return _pos; } { return _pos; }
void seek(const Position& pos) void seek(const Fluxmap::Position& pos)
{ _pos = pos; } { _pos = pos; _pendingZeroBits = 0; }
nanoseconds_t tellNs() const
{ return _pos.ticks * NS_PER_TICK; }
int readOpcode(unsigned& ticks); int readOpcode(unsigned& ticks);
unsigned readNextMatchingOpcode(uint8_t opcode); unsigned readNextMatchingOpcode(uint8_t opcode);
void seek(nanoseconds_t ns); void seek(nanoseconds_t ns);
void seekToIndexMark(); void seekToIndexMark();
nanoseconds_t seekToPattern(const FluxPattern& pattern); nanoseconds_t seekToPattern(const FluxMatcher& pattern);
bool readRawBit(nanoseconds_t clockPeriod); bool readRawBit(nanoseconds_t clockPeriod);
std::vector<bool> readRawBits(unsigned count, nanoseconds_t clockPeriod); std::vector<bool> readRawBits(unsigned count, nanoseconds_t clockPeriod);
std::vector<bool> readRawBits(const Fluxmap::Position& until, nanoseconds_t clockPeriod);
private: private:
const Fluxmap& _fluxmap; const Fluxmap& _fluxmap;
const uint8_t* _bytes; const uint8_t* _bytes;
const size_t _size; const size_t _size;
Position _pos; Fluxmap::Position _pos;
unsigned _pendingZeroBits; unsigned _pendingZeroBits;
}; };

View File

@@ -99,45 +99,36 @@ static uint16_t checksum(const Bytes& bytes)
return (crchi << 8) | crclo; return (crchi << 8) | crclo;
} }
void Fb100Decoder::decodeToSectors(Track& track) nanoseconds_t Fb100Decoder::findSector(FluxmapReader& fmr, Track& track)
{ {
Sector sector; return fmr.seekToPattern(SECTOR_ID_PATTERN);
sector.physicalSide = track.physicalSide; }
sector.physicalTrack = track.physicalTrack;
RawRecord record;
FluxmapReader fmr(*track.fluxmap); void Fb100Decoder::decodeSingleSector(FluxmapReader& fmr, Track& track, Sector& sector)
{
auto rawbits = fmr.readRawBits(FB100_RECORD_SIZE*16, sector.clock);
for (;;) const Bytes bytes = decodeFmMfm(rawbits).slice(0, FB100_RECORD_SIZE);
{ ByteReader br(bytes);
nanoseconds_t clockPeriod = fmr.seekToPattern(SECTOR_ID_PATTERN); br.seek(1);
if (fmr.eof() || !clockPeriod) const Bytes id = br.read(FB100_ID_SIZE);
break; uint16_t wantIdCrc = br.read_be16();
uint16_t gotIdCrc = checksum(id);
const Bytes payload = br.read(FB100_PAYLOAD_SIZE);
uint16_t wantPayloadCrc = br.read_be16();
uint16_t gotPayloadCrc = checksum(payload);
sector.clock = record.clock = clockPeriod; if (wantIdCrc != gotIdCrc)
sector.position = record.position = fmr.tellNs(); return;
auto rawbits = fmr.readRawBits(FB100_RECORD_SIZE*16, clockPeriod);
record.bytes.writer().seekToEnd() += toBytes(rawbits);
const Bytes bytes = decodeFmMfm(rawbits).slice(0, FB100_RECORD_SIZE); uint8_t abssector = id[2];
ByteReader br(bytes); sector.logicalTrack = abssector >> 1;
br.seek(1); sector.logicalSide = 0;
const Bytes id = br.read(FB100_ID_SIZE); sector.logicalSector = abssector & 1;
uint16_t wantIdCrc = br.read_be16(); sector.data.writer().append(id.slice(5, 12)).append(payload);
uint16_t gotIdCrc = checksum(id);
const Bytes payload = br.read(FB100_PAYLOAD_SIZE);
uint16_t wantPayloadCrc = br.read_be16();
uint16_t gotPayloadCrc = checksum(payload);
if (wantIdCrc != gotIdCrc) if (wantPayloadCrc == gotPayloadCrc)
continue; sector.status = Sector::OK;
else
uint8_t abssector = id[2]; sector.status = Sector::BAD_CHECKSUM;
sector.logicalTrack = abssector & 1;
sector.logicalSide = 0;
sector.logicalSector = abssector & 1;
sector.data.clear().writer().append(id.slice(5, 12)).append(payload);
sector.status = (wantPayloadCrc == gotPayloadCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
track.sectors.push_back(sector);
}
} }

View File

@@ -14,7 +14,8 @@ class Fb100Decoder : public AbstractStatefulDecoder
public: public:
virtual ~Fb100Decoder() {} virtual ~Fb100Decoder() {}
void decodeToSectors(Track& track) override; nanoseconds_t findSector(FluxmapReader& fmr, Track& track) override;
void decodeSingleSector(FluxmapReader& fmr, Track& track, Sector& sector) override;
}; };
#endif #endif

View File

@@ -2,11 +2,22 @@
#define FLUXMAP_H #define FLUXMAP_H
#include "bytes.h" #include "bytes.h"
#include "protocol.h"
class RawBits; class RawBits;
class Fluxmap class Fluxmap
{ {
public:
struct Position
{
unsigned bytes = 0;
unsigned ticks = 0;
nanoseconds_t ns() const
{ return ticks * NS_PER_TICK; }
};
public: public:
nanoseconds_t duration() const { return _duration; } nanoseconds_t duration() const { return _duration; }
size_t bytes() const { return _bytes.size(); } size_t bytes() const { return _bytes.size(); }

View File

@@ -141,7 +141,7 @@ void writeSectorsToFile(const SectorSet& sectors, const Geometry& geometry,
if (sector) if (sector)
{ {
outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*geometry.sectorSize, std::ios::beg); outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*geometry.sectorSize, std::ios::beg);
outputFile.write((const char*) &sector->data[0], sector->data.size()); outputFile.write((const char*) sector->data.cbegin(), sector->data.size());
} }
} }
} }

View File

@@ -12,7 +12,8 @@
#include <string.h> #include <string.h>
#include <algorithm> #include <algorithm>
const FluxPattern SECTOR_ID_PATTERN(16, 0xd5aa); const FluxPattern SECTOR_RECORD_PATTERN(24, MAC_SECTOR_RECORD);
const FluxPatterns SECTOR_OR_DATA_RECORD_PATTERN(24, { MAC_SECTOR_RECORD, MAC_DATA_RECORD });
static int decode_data_gcr(uint8_t gcr) static int decode_data_gcr(uint8_t gcr)
{ {
@@ -122,72 +123,57 @@ uint8_t decode_side(uint8_t side)
return !!(side & 0x40); return !!(side & 0x40);
} }
void MacintoshDecoder::decodeToSectors(Track& track) nanoseconds_t MacintoshDecoder::findSector(FluxmapReader& fmr, Track& track)
{ {
Sector sector; return fmr.seekToPattern(SECTOR_RECORD_PATTERN);
sector.physicalSide = track.physicalSide; }
sector.physicalTrack = track.physicalTrack;
RawRecord record; nanoseconds_t MacintoshDecoder::findData(FluxmapReader& fmr, Track& track)
{
FluxmapReader fmr(*track.fluxmap); return fmr.seekToPattern(SECTOR_OR_DATA_RECORD_PATTERN);
}
bool headerIsValid = false;
for (;;) void MacintoshDecoder::decodeHeader(FluxmapReader& fmr, Track& track, Sector& sector)
{ {
nanoseconds_t clockPeriod = fmr.seekToPattern(SECTOR_ID_PATTERN); /* Skip ID (as we know it's a MAC_SECTOR_RECORD). */
if (fmr.eof() || !clockPeriod) fmr.readRawBits(24, sector.clock);
break;
sector.clock = record.clock = clockPeriod; /* Read header. */
sector.position = record.position = fmr.tellNs();
auto header = toBytes(fmr.readRawBits(7*8, sector.clock)).slice(0, 7);
Bytes idbytes = toBytes(fmr.readRawBits(24, clockPeriod)).slice(0, 3);
record.bytes.clear().writer() += idbytes; uint8_t encodedTrack = decode_data_gcr(header[0]);
switch (idbytes.reader().read_be24()) if (encodedTrack != (track.physicalTrack & 0x3f))
{ return;
case MAC_SECTOR_RECORD:
{ uint8_t encodedSector = decode_data_gcr(header[1]);
auto header = toBytes(fmr.readRawBits(7*8, clockPeriod)) uint8_t encodedSide = decode_data_gcr(header[2]);
.slice(0, 7); uint8_t formatByte = decode_data_gcr(header[3]);
record.bytes.writer().seekToEnd() += header; uint8_t wantedsum = decode_data_gcr(header[4]);
uint8_t encodedTrack = decode_data_gcr(header[0]); sector.logicalTrack = track.physicalTrack;
if (encodedTrack != (track.physicalTrack & 0x3f)) sector.logicalSide = decode_side(encodedSide);
break; sector.logicalSector = encodedSector;
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
uint8_t encodedSector = decode_data_gcr(header[1]); if (wantedsum == gotsum)
uint8_t encodedSide = decode_data_gcr(header[2]); sector.status = Sector::DATA_MISSING; /* unintuitive but correct */
uint8_t formatByte = decode_data_gcr(header[3]); }
uint8_t wantedsum = decode_data_gcr(header[4]);
void MacintoshDecoder::decodeData(FluxmapReader& fmr, Track& track, Sector& sector)
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f; {
headerIsValid = (wantedsum == gotsum); auto id = toBytes(fmr.readRawBits(24, sector.clock)).reader().read_be24();
sector.logicalTrack = track.physicalTrack; if (id != MAC_DATA_RECORD)
sector.logicalSide = decode_side(encodedSide); return;
sector.logicalSector = encodedSector;
break; /* Read data. */
}
fmr.readRawBits(8, sector.clock); /* skip spare byte */
case MAC_DATA_RECORD: auto inputbuffer = toBytes(fmr.readRawBits(MAC_ENCODED_SECTOR_LENGTH*8, sector.clock))
{ .slice(0, MAC_ENCODED_SECTOR_LENGTH);
if (!headerIsValid)
break; for (unsigned i=0; i<inputbuffer.size(); i++)
headerIsValid = false; inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
fmr.readRawBits(8, clockPeriod); /* skip spare byte */ sector.status = Sector::BAD_CHECKSUM;
auto inputbuffer = toBytes(fmr.readRawBits(MAC_ENCODED_SECTOR_LENGTH*8, clockPeriod)) sector.data = decode_crazy_data(inputbuffer, sector.status);
.slice(0, MAC_ENCODED_SECTOR_LENGTH);
record.bytes.writer().seekToEnd() += inputbuffer;
for (unsigned i=0; i<inputbuffer.size(); i++)
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
sector.status = Sector::BAD_CHECKSUM;
sector.data = decode_crazy_data(inputbuffer, sector.status);
track.sectors.push_back(sector);
break;
}
}
track.rawrecords.push_back(record);
}
} }

View File

@@ -1,8 +1,8 @@
#ifndef MACINTOSH_H #ifndef MACINTOSH_H
#define MACINTOSH_H #define MACINTOSH_H
#define MAC_SECTOR_RECORD 0xd5aa96 #define MAC_SECTOR_RECORD 0xd5aa96 /* 1101 0101 1010 1010 1001 0110 */
#define MAC_DATA_RECORD 0xd5aaad #define MAC_DATA_RECORD 0xd5aaad /* 1101 0101 1010 1010 1010 1101 */
#define MAC_SECTOR_LENGTH 524 /* yes, really */ #define MAC_SECTOR_LENGTH 524 /* yes, really */
#define MAC_ENCODED_SECTOR_LENGTH 703 #define MAC_ENCODED_SECTOR_LENGTH 703
@@ -10,12 +10,15 @@
class Sector; class Sector;
class Fluxmap; class Fluxmap;
class MacintoshDecoder : public AbstractStatefulDecoder class MacintoshDecoder : public AbstractSplitDecoder
{ {
public: public:
virtual ~MacintoshDecoder() {} virtual ~MacintoshDecoder() {}
void decodeToSectors(Track& track) override; nanoseconds_t findSector(FluxmapReader& fmr, Track& track) override;
nanoseconds_t findData(FluxmapReader& fmr, Track& track) override;
void decodeHeader(FluxmapReader& fmr, Track& track, Sector& sector) override;
void decodeData(FluxmapReader& fmr, Track& track, Sector& sector) override;
}; };
#endif #endif

View File

@@ -184,9 +184,9 @@ void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename
std::cout << "\nRaw (undecoded) records follow:\n\n"; std::cout << "\nRaw (undecoded) records follow:\n\n";
for (auto& record : track->rawrecords) for (auto& record : track->rawrecords)
{ {
std::cout << fmt::format("I+{:.2f}us", record.clock / 1000.0) std::cout << fmt::format("I+{:.2f}us", record.position.ns() / 1000.0)
<< std::endl; << std::endl;
hexdump(std::cout, record.bytes); hexdump(std::cout, record.data);
std::cout << std::endl; std::cout << std::endl;
} }
} }

View File

@@ -1,20 +1,18 @@
#ifndef RECORD_H #ifndef RECORD_H
#define RECORD_H #define RECORD_H
#include "fluxmap.h"
class RawRecord class RawRecord
{ {
public: public:
RawRecord() {} RawRecord() {}
RawRecord(nanoseconds_t position, nanoseconds_t clock, const Bytes& bytes): Fluxmap::Position position;
position(position), nanoseconds_t clock = 0;
clock(clock), int physicalTrack = 0;
bytes(bytes) int physicalSide = 0;
{} Bytes data;
nanoseconds_t position;
nanoseconds_t clock;
Bytes bytes;
}; };
#endif #endif

View File

@@ -9,6 +9,7 @@ const std::string Sector::statusToString(Status status)
case Status::OK: return "OK"; case Status::OK: return "OK";
case Status::BAD_CHECKSUM: return "bad checksum"; case Status::BAD_CHECKSUM: return "bad checksum";
case Status::MISSING: return "sector not found"; case Status::MISSING: return "sector not found";
case Status::DATA_MISSING: return "present but no data found";
case Status::CONFLICT: return "conflicting data"; case Status::CONFLICT: return "conflicting data";
default: return fmt::format("unknown error {}", status); default: return fmt::format("unknown error {}", status);
} }

View File

@@ -2,6 +2,7 @@
#define SECTOR_H #define SECTOR_H
#include "bytes.h" #include "bytes.h"
#include "fluxmap.h"
/* /*
* Note that sectors here used zero-based numbering throughout (to make the * Note that sectors here used zero-based numbering throughout (to make the
@@ -16,6 +17,7 @@ public:
OK, OK,
BAD_CHECKSUM, BAD_CHECKSUM,
MISSING, MISSING,
DATA_MISSING,
CONFLICT, CONFLICT,
INTERNAL_ERROR INTERNAL_ERROR
}; };
@@ -23,7 +25,7 @@ public:
static const std::string statusToString(Status status); static const std::string statusToString(Status status);
Status status = Status::INTERNAL_ERROR; Status status = Status::INTERNAL_ERROR;
nanoseconds_t position = 0; Fluxmap::Position position;
nanoseconds_t clock = 0; nanoseconds_t clock = 0;
int physicalTrack = 0; int physicalTrack = 0;
int physicalSide = 0; int physicalSide = 0;

View File

@@ -9,7 +9,7 @@
class Sector; class Sector;
class Fluxmap; class Fluxmap;
class Victor9kDecoder : public AbstractSoftSectorDecoder class Victor9kDecoder : public AbstractStatefulDecoder
{ {
public: public:
virtual ~Victor9kDecoder() {} virtual ~Victor9kDecoder() {}

View File

@@ -53,6 +53,8 @@ int main(int argc, const char* argv[])
nanoseconds_t clockPeriod = track->fluxmap->guessClock(); nanoseconds_t clockPeriod = track->fluxmap->guessClock();
std::cout << fmt::format(" {:.2f} us clock detected; ", (double)clockPeriod/1000.0) << std::flush; std::cout << fmt::format(" {:.2f} us clock detected; ", (double)clockPeriod/1000.0) << std::flush;
FluxmapReader fmr(*track->fluxmap);
#if 0
clockPeriod *= clockScaleFlag; clockPeriod *= clockScaleFlag;
std::cout << fmt::format("{:.2f} us bit clock; ", (double)clockPeriod/1000.0) << std::flush; std::cout << fmt::format("{:.2f} us bit clock; ", (double)clockPeriod/1000.0) << std::flush;
@@ -116,23 +118,24 @@ int main(int argc, const char* argv[])
lasttransition = transition; lasttransition = transition;
} }
} }
#endif
if (dumpBitstreamFlag) if (dumpBitstreamFlag)
{ {
std::cout << "Aligned bitstream of length " << bitmap.size() fmr.seek(seekFlag*1000000.0);
<< " follows:" << std::endl
<< std::endl;
size_t cursor = seekFlag*1000000.0 / clockPeriod; std::cout << fmt::format("Aligned bitstream from {:.3f}ms follows:\n",
while (cursor < bitmap.size()) fmr.tell().ns() / 1000000.0);
while (!fmr.eof())
{ {
std::cout << fmt::format("{: 10.3f} : ", (double)cursor / clockPeriod); std::cout << fmt::format("{: 10.3f} : ", fmr.tell().ns() / 1000000.0);
for (unsigned i=0; i<60; i++) for (unsigned i=0; i<60; i++)
{ {
if (cursor >= bitmap.size()) if (fmr.eof())
break; break;
std::cout << (bitmap[cursor] ? 'X' : '-'); bool b = fmr.readRawBit(clockPeriod);
cursor++; std::cout << (b ? 'X' : '-');
} }
std::cout << std::endl; std::cout << std::endl;

View File

@@ -17,7 +17,7 @@ static StringFlag outputFilename(
int main(int argc, const char* argv[]) int main(int argc, const char* argv[])
{ {
setReaderDefaultSource(":t=0-79:s=0"); setReaderDefaultSource(":t=0-79x2:s=0");
setReaderRevolutions(2); setReaderRevolutions(2);
setDecoderManualClockRate(4.0); setDecoderManualClockRate(4.0);
Flag::parseFlags(argc, argv); Flag::parseFlags(argc, argv);

View File

@@ -5,46 +5,46 @@
typedef std::vector<unsigned> ivector; typedef std::vector<unsigned> ivector;
static void test_patternconstruction() void test_patternconstruction()
{ {
{ {
FluxPattern fp(16, 0x0003); FluxPattern fp(16, 0x0003);
assert(fp.bits == 16); assert(fp._bits == 16);
assert(fp.intervals == ivector{ 1 }); assert(fp._intervals == ivector{ 1 });
} }
{ {
FluxPattern fp(16, 0xc000); FluxPattern fp(16, 0xc000);
assert(fp.bits == 16); assert(fp._bits == 16);
assert(fp.intervals == ivector{ 1 }); assert(fp._intervals == ivector{ 1 });
} }
{ {
FluxPattern fp(16, 0x0050); FluxPattern fp(16, 0x0050);
assert(fp.bits == 16); assert(fp._bits == 16);
assert(fp.intervals == ivector{ 2 }); assert(fp._intervals == ivector{ 2 });
} }
{ {
FluxPattern fp(16, 0x0070); FluxPattern fp(16, 0x0070);
assert(fp.bits == 16); assert(fp._bits == 16);
assert((fp.intervals == ivector{ 1, 1 })); assert((fp._intervals == ivector{ 1, 1 }));
} }
{ {
FluxPattern fp(16, 0x0070); FluxPattern fp(16, 0x0070);
assert(fp.bits == 16); assert(fp._bits == 16);
assert((fp.intervals == ivector{ 1, 1 })); assert((fp._intervals == ivector{ 1, 1 }));
} }
{ {
FluxPattern fp(16, 0x0110); FluxPattern fp(16, 0x0110);
assert(fp.bits == 16); assert(fp._bits == 16);
assert((fp.intervals == ivector{ 4 })); assert((fp._intervals == ivector{ 4 }));
} }
} }
static void test_patternmatching() void test_patternmatching()
{ {
FluxPattern fp(16, 0x000b); FluxPattern fp(16, 0x000b);
const unsigned matching[] = { 100, 100, 200, 100 }; const unsigned matching[] = { 100, 100, 200, 100 };
@@ -59,11 +59,21 @@ static void test_patternmatching()
assert(fp.matches(&closematch2[4], clock)); assert(fp.matches(&closematch2[4], clock));
} }
void test_patternsmatching()
{
FluxPatterns fp(16, { 0x000b, 0x0015 });
const unsigned matching1[] = { 100, 100, 200, 100 };
const unsigned matching2[] = { 100, 100, 200, 200 };
double clock;
assert(fp.matches(&matching1[4], clock));
assert(fp.matches(&matching2[4], clock));
}
int main(int argc, const char* argv[]) int main(int argc, const char* argv[])
{ {
test_patternconstruction(); test_patternconstruction();
test_patternmatching(); test_patternmatching();
test_patternsmatching();
return 0; return 0;
} }