mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Allow split decoders (into sector record and data record).
This commit is contained in:
@@ -49,6 +49,9 @@ static const std::string BLOCK_ELEMENTS[] =
|
||||
*/
|
||||
nanoseconds_t Fluxmap::guessClock() const
|
||||
{
|
||||
if (manualClockRate != 0.0)
|
||||
return manualClockRate * 1000.0;
|
||||
|
||||
uint32_t buckets[256] = {};
|
||||
FluxmapReader fr(*this);
|
||||
|
||||
@@ -248,6 +251,7 @@ RawRecordVector AbstractSoftSectorDecoder::extractRecords(const RawBits& rawbits
|
||||
if (matchStart == -1)
|
||||
return;
|
||||
|
||||
#if 0
|
||||
records.push_back(
|
||||
std::unique_ptr<RawRecord>(
|
||||
new RawRecord(
|
||||
@@ -260,6 +264,7 @@ RawRecordVector AbstractSoftSectorDecoder::extractRecords(const RawBits& rawbits
|
||||
)
|
||||
)
|
||||
);
|
||||
#endif
|
||||
};
|
||||
|
||||
while (cursor < rawbits.size())
|
||||
@@ -315,6 +320,7 @@ RawRecordVector AbstractHardSectorDecoder::extractRecords(const RawBits& rawbits
|
||||
if ((end - previous) < sectors_must_be_bigger_than)
|
||||
return;
|
||||
|
||||
#if 0
|
||||
records.push_back(
|
||||
std::unique_ptr<RawRecord>(
|
||||
new RawRecord(
|
||||
@@ -327,6 +333,7 @@ RawRecordVector AbstractHardSectorDecoder::extractRecords(const RawBits& rawbits
|
||||
)
|
||||
)
|
||||
);
|
||||
#endif
|
||||
previous = end;
|
||||
};
|
||||
|
||||
@@ -338,4 +345,69 @@ RawRecordVector AbstractHardSectorDecoder::extractRecords(const RawBits& rawbits
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
#define DECODERS_H
|
||||
|
||||
#include "bytes.h"
|
||||
#include "sector.h"
|
||||
#include "record.h"
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
class FluxmapReader;
|
||||
class RawRecord;
|
||||
class RawBits;
|
||||
class Track;
|
||||
@@ -67,6 +70,26 @@ public:
|
||||
|
||||
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
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
static DoubleFlag clockDecodeThreshold(
|
||||
{ "--bit-error-threshold" },
|
||||
"Amount of error to tolerate in pulse timing.",
|
||||
0.20);
|
||||
0.30);
|
||||
|
||||
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;
|
||||
|
||||
assert(pattern != 0);
|
||||
bits = len;
|
||||
|
||||
while (!(pattern & TOPBIT))
|
||||
pattern <<= 1;
|
||||
|
||||
length = 0;
|
||||
_length = 0;
|
||||
while (pattern != TOPBIT)
|
||||
{
|
||||
unsigned interval = 0;
|
||||
@@ -67,59 +67,98 @@ FluxPattern::FluxPattern(unsigned len, uint64_t pattern)
|
||||
interval++;
|
||||
}
|
||||
while (!(pattern & TOPBIT));
|
||||
intervals.push_back(interval);
|
||||
length += interval;
|
||||
_intervals.push_back(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);
|
||||
if (!candidatelength)
|
||||
return false;
|
||||
clock = (double)candidatelength / (double)length;
|
||||
return 0;
|
||||
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 error = fabs(1.0 - ii/ci);
|
||||
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();
|
||||
unsigned candidates[length];
|
||||
Position positions[length];
|
||||
_intervals = 0;
|
||||
for (uint64_t p : patterns)
|
||||
{
|
||||
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)
|
||||
i = 0;
|
||||
for (Position& p : positions)
|
||||
for (Fluxmap::Position& p : positions)
|
||||
p = tell();
|
||||
|
||||
while (!eof())
|
||||
{
|
||||
unsigned interval = readNextMatchingOpcode(F_OP_PULSE);
|
||||
|
||||
Position current = positions[0];
|
||||
for (unsigned i=0; i<(length-1); i++)
|
||||
for (unsigned i=0; i<intervalCount; i++)
|
||||
{
|
||||
positions[i] = positions[i+1];
|
||||
candidates[i] = candidates[i+1];
|
||||
}
|
||||
positions[length-1] = tell();
|
||||
candidates[length-1] = interval;
|
||||
positions[intervalCount] = tell();
|
||||
candidates[intervalCount] = interval;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -154,3 +193,11 @@ std::vector<bool> FluxmapReader::readRawBits(unsigned count, nanoseconds_t clock
|
||||
result.push_back(readRawBit(clockPeriod));
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -4,27 +4,62 @@
|
||||
#include "fluxmap.h"
|
||||
#include "protocol.h"
|
||||
|
||||
class FluxPattern
|
||||
class FluxMatcher
|
||||
{
|
||||
public:
|
||||
FluxPattern(unsigned len, uint64_t pattern);
|
||||
FluxMatcher(unsigned bits):
|
||||
_bits(bits)
|
||||
{}
|
||||
|
||||
bool matches(const unsigned* intervals, double& clock) const;
|
||||
virtual ~FluxMatcher() {}
|
||||
|
||||
unsigned bits;
|
||||
std::vector<unsigned> intervals;
|
||||
unsigned length;
|
||||
/* Returns the number of intervals matched */
|
||||
virtual unsigned matches(const unsigned* intervals, double& clock) const = 0;
|
||||
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
|
||||
{
|
||||
public:
|
||||
struct Position
|
||||
{
|
||||
unsigned bytes;
|
||||
unsigned ticks;
|
||||
};
|
||||
|
||||
public:
|
||||
FluxmapReader(const Fluxmap& fluxmap):
|
||||
_fluxmap(fluxmap),
|
||||
@@ -46,30 +81,28 @@ public:
|
||||
bool eof() const
|
||||
{ return _pos.bytes == _size; }
|
||||
|
||||
Position tell() const
|
||||
Fluxmap::Position tell() const
|
||||
{ return _pos; }
|
||||
|
||||
void seek(const Position& pos)
|
||||
{ _pos = pos; }
|
||||
|
||||
nanoseconds_t tellNs() const
|
||||
{ return _pos.ticks * NS_PER_TICK; }
|
||||
void seek(const Fluxmap::Position& pos)
|
||||
{ _pos = pos; _pendingZeroBits = 0; }
|
||||
|
||||
int readOpcode(unsigned& ticks);
|
||||
unsigned readNextMatchingOpcode(uint8_t opcode);
|
||||
|
||||
void seek(nanoseconds_t ns);
|
||||
void seekToIndexMark();
|
||||
nanoseconds_t seekToPattern(const FluxPattern& pattern);
|
||||
nanoseconds_t seekToPattern(const FluxMatcher& pattern);
|
||||
|
||||
bool readRawBit(nanoseconds_t clockPeriod);
|
||||
std::vector<bool> readRawBits(unsigned count, nanoseconds_t clockPeriod);
|
||||
std::vector<bool> readRawBits(const Fluxmap::Position& until, nanoseconds_t clockPeriod);
|
||||
|
||||
private:
|
||||
const Fluxmap& _fluxmap;
|
||||
const uint8_t* _bytes;
|
||||
const size_t _size;
|
||||
Position _pos;
|
||||
Fluxmap::Position _pos;
|
||||
unsigned _pendingZeroBits;
|
||||
};
|
||||
|
||||
|
||||
@@ -99,25 +99,14 @@ static uint16_t checksum(const Bytes& bytes)
|
||||
return (crchi << 8) | crclo;
|
||||
}
|
||||
|
||||
void Fb100Decoder::decodeToSectors(Track& track)
|
||||
nanoseconds_t Fb100Decoder::findSector(FluxmapReader& fmr, Track& track)
|
||||
{
|
||||
Sector sector;
|
||||
sector.physicalSide = track.physicalSide;
|
||||
sector.physicalTrack = track.physicalTrack;
|
||||
RawRecord record;
|
||||
return fmr.seekToPattern(SECTOR_ID_PATTERN);
|
||||
}
|
||||
|
||||
FluxmapReader fmr(*track.fluxmap);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
nanoseconds_t clockPeriod = fmr.seekToPattern(SECTOR_ID_PATTERN);
|
||||
if (fmr.eof() || !clockPeriod)
|
||||
break;
|
||||
|
||||
sector.clock = record.clock = clockPeriod;
|
||||
sector.position = record.position = fmr.tellNs();
|
||||
auto rawbits = fmr.readRawBits(FB100_RECORD_SIZE*16, clockPeriod);
|
||||
record.bytes.writer().seekToEnd() += toBytes(rawbits);
|
||||
void Fb100Decoder::decodeSingleSector(FluxmapReader& fmr, Track& track, Sector& sector)
|
||||
{
|
||||
auto rawbits = fmr.readRawBits(FB100_RECORD_SIZE*16, sector.clock);
|
||||
|
||||
const Bytes bytes = decodeFmMfm(rawbits).slice(0, FB100_RECORD_SIZE);
|
||||
ByteReader br(bytes);
|
||||
@@ -130,14 +119,16 @@ void Fb100Decoder::decodeToSectors(Track& track)
|
||||
uint16_t gotPayloadCrc = checksum(payload);
|
||||
|
||||
if (wantIdCrc != gotIdCrc)
|
||||
continue;
|
||||
return;
|
||||
|
||||
uint8_t abssector = id[2];
|
||||
sector.logicalTrack = abssector & 1;
|
||||
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);
|
||||
}
|
||||
sector.data.writer().append(id.slice(5, 12)).append(payload);
|
||||
|
||||
if (wantPayloadCrc == gotPayloadCrc)
|
||||
sector.status = Sector::OK;
|
||||
else
|
||||
sector.status = Sector::BAD_CHECKSUM;
|
||||
}
|
||||
@@ -14,7 +14,8 @@ class Fb100Decoder : public AbstractStatefulDecoder
|
||||
public:
|
||||
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
|
||||
|
||||
@@ -2,11 +2,22 @@
|
||||
#define FLUXMAP_H
|
||||
|
||||
#include "bytes.h"
|
||||
#include "protocol.h"
|
||||
|
||||
class RawBits;
|
||||
|
||||
class Fluxmap
|
||||
{
|
||||
public:
|
||||
struct Position
|
||||
{
|
||||
unsigned bytes = 0;
|
||||
unsigned ticks = 0;
|
||||
|
||||
nanoseconds_t ns() const
|
||||
{ return ticks * NS_PER_TICK; }
|
||||
};
|
||||
|
||||
public:
|
||||
nanoseconds_t duration() const { return _duration; }
|
||||
size_t bytes() const { return _bytes.size(); }
|
||||
|
||||
@@ -141,7 +141,7 @@ void writeSectorsToFile(const SectorSet& sectors, const Geometry& geometry,
|
||||
if (sector)
|
||||
{
|
||||
outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*geometry.sectorSize, std::ios::beg);
|
||||
outputFile.write((const char*) §or->data[0], sector->data.size());
|
||||
outputFile.write((const char*) sector->data.cbegin(), sector->data.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
#include <string.h>
|
||||
#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)
|
||||
{
|
||||
@@ -122,72 +123,57 @@ uint8_t decode_side(uint8_t side)
|
||||
return !!(side & 0x40);
|
||||
}
|
||||
|
||||
void MacintoshDecoder::decodeToSectors(Track& track)
|
||||
nanoseconds_t MacintoshDecoder::findSector(FluxmapReader& fmr, Track& track)
|
||||
{
|
||||
Sector sector;
|
||||
sector.physicalSide = track.physicalSide;
|
||||
sector.physicalTrack = track.physicalTrack;
|
||||
RawRecord record;
|
||||
return fmr.seekToPattern(SECTOR_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
FluxmapReader fmr(*track.fluxmap);
|
||||
nanoseconds_t MacintoshDecoder::findData(FluxmapReader& fmr, Track& track)
|
||||
{
|
||||
return fmr.seekToPattern(SECTOR_OR_DATA_RECORD_PATTERN);
|
||||
}
|
||||
|
||||
bool headerIsValid = false;
|
||||
for (;;)
|
||||
{
|
||||
nanoseconds_t clockPeriod = fmr.seekToPattern(SECTOR_ID_PATTERN);
|
||||
if (fmr.eof() || !clockPeriod)
|
||||
break;
|
||||
sector.clock = record.clock = clockPeriod;
|
||||
sector.position = record.position = fmr.tellNs();
|
||||
void MacintoshDecoder::decodeHeader(FluxmapReader& fmr, Track& track, Sector& sector)
|
||||
{
|
||||
/* Skip ID (as we know it's a MAC_SECTOR_RECORD). */
|
||||
fmr.readRawBits(24, sector.clock);
|
||||
|
||||
Bytes idbytes = toBytes(fmr.readRawBits(24, clockPeriod)).slice(0, 3);
|
||||
record.bytes.clear().writer() += idbytes;
|
||||
switch (idbytes.reader().read_be24())
|
||||
{
|
||||
case MAC_SECTOR_RECORD:
|
||||
{
|
||||
auto header = toBytes(fmr.readRawBits(7*8, clockPeriod))
|
||||
.slice(0, 7);
|
||||
record.bytes.writer().seekToEnd() += header;
|
||||
/* Read header. */
|
||||
|
||||
auto header = toBytes(fmr.readRawBits(7*8, sector.clock)).slice(0, 7);
|
||||
|
||||
uint8_t encodedTrack = decode_data_gcr(header[0]);
|
||||
if (encodedTrack != (track.physicalTrack & 0x3f))
|
||||
break;
|
||||
return;
|
||||
|
||||
uint8_t encodedSector = decode_data_gcr(header[1]);
|
||||
uint8_t encodedSide = decode_data_gcr(header[2]);
|
||||
uint8_t formatByte = decode_data_gcr(header[3]);
|
||||
uint8_t wantedsum = decode_data_gcr(header[4]);
|
||||
|
||||
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
|
||||
headerIsValid = (wantedsum == gotsum);
|
||||
sector.logicalTrack = track.physicalTrack;
|
||||
sector.logicalSide = decode_side(encodedSide);
|
||||
sector.logicalSector = encodedSector;
|
||||
break;
|
||||
}
|
||||
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
|
||||
if (wantedsum == gotsum)
|
||||
sector.status = Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
case MAC_DATA_RECORD:
|
||||
{
|
||||
if (!headerIsValid)
|
||||
break;
|
||||
headerIsValid = false;
|
||||
void MacintoshDecoder::decodeData(FluxmapReader& fmr, Track& track, Sector& sector)
|
||||
{
|
||||
auto id = toBytes(fmr.readRawBits(24, sector.clock)).reader().read_be24();
|
||||
if (id != MAC_DATA_RECORD)
|
||||
return;
|
||||
|
||||
fmr.readRawBits(8, clockPeriod); /* skip spare byte */
|
||||
auto inputbuffer = toBytes(fmr.readRawBits(MAC_ENCODED_SECTOR_LENGTH*8, clockPeriod))
|
||||
/* Read data. */
|
||||
|
||||
fmr.readRawBits(8, sector.clock); /* skip spare byte */
|
||||
auto inputbuffer = toBytes(fmr.readRawBits(MAC_ENCODED_SECTOR_LENGTH*8, sector.clock))
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef MACINTOSH_H
|
||||
#define MACINTOSH_H
|
||||
|
||||
#define MAC_SECTOR_RECORD 0xd5aa96
|
||||
#define MAC_DATA_RECORD 0xd5aaad
|
||||
#define MAC_SECTOR_RECORD 0xd5aa96 /* 1101 0101 1010 1010 1001 0110 */
|
||||
#define MAC_DATA_RECORD 0xd5aaad /* 1101 0101 1010 1010 1010 1101 */
|
||||
|
||||
#define MAC_SECTOR_LENGTH 524 /* yes, really */
|
||||
#define MAC_ENCODED_SECTOR_LENGTH 703
|
||||
@@ -10,12 +10,15 @@
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
|
||||
class MacintoshDecoder : public AbstractStatefulDecoder
|
||||
class MacintoshDecoder : public AbstractSplitDecoder
|
||||
{
|
||||
public:
|
||||
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
|
||||
|
||||
@@ -184,9 +184,9 @@ void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename
|
||||
std::cout << "\nRaw (undecoded) records follow:\n\n";
|
||||
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;
|
||||
hexdump(std::cout, record.bytes);
|
||||
hexdump(std::cout, record.data);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
16
lib/record.h
16
lib/record.h
@@ -1,20 +1,18 @@
|
||||
#ifndef RECORD_H
|
||||
#define RECORD_H
|
||||
|
||||
#include "fluxmap.h"
|
||||
|
||||
class RawRecord
|
||||
{
|
||||
public:
|
||||
RawRecord() {}
|
||||
|
||||
RawRecord(nanoseconds_t position, nanoseconds_t clock, const Bytes& bytes):
|
||||
position(position),
|
||||
clock(clock),
|
||||
bytes(bytes)
|
||||
{}
|
||||
|
||||
nanoseconds_t position;
|
||||
nanoseconds_t clock;
|
||||
Bytes bytes;
|
||||
Fluxmap::Position position;
|
||||
nanoseconds_t clock = 0;
|
||||
int physicalTrack = 0;
|
||||
int physicalSide = 0;
|
||||
Bytes data;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,7 @@ const std::string Sector::statusToString(Status status)
|
||||
case Status::OK: return "OK";
|
||||
case Status::BAD_CHECKSUM: return "bad checksum";
|
||||
case Status::MISSING: return "sector not found";
|
||||
case Status::DATA_MISSING: return "present but no data found";
|
||||
case Status::CONFLICT: return "conflicting data";
|
||||
default: return fmt::format("unknown error {}", status);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define SECTOR_H
|
||||
|
||||
#include "bytes.h"
|
||||
#include "fluxmap.h"
|
||||
|
||||
/*
|
||||
* Note that sectors here used zero-based numbering throughout (to make the
|
||||
@@ -16,6 +17,7 @@ public:
|
||||
OK,
|
||||
BAD_CHECKSUM,
|
||||
MISSING,
|
||||
DATA_MISSING,
|
||||
CONFLICT,
|
||||
INTERNAL_ERROR
|
||||
};
|
||||
@@ -23,7 +25,7 @@ public:
|
||||
static const std::string statusToString(Status status);
|
||||
|
||||
Status status = Status::INTERNAL_ERROR;
|
||||
nanoseconds_t position = 0;
|
||||
Fluxmap::Position position;
|
||||
nanoseconds_t clock = 0;
|
||||
int physicalTrack = 0;
|
||||
int physicalSide = 0;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
|
||||
class Victor9kDecoder : public AbstractSoftSectorDecoder
|
||||
class Victor9kDecoder : public AbstractStatefulDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~Victor9kDecoder() {}
|
||||
|
||||
@@ -53,6 +53,8 @@ int main(int argc, const char* argv[])
|
||||
nanoseconds_t clockPeriod = track->fluxmap->guessClock();
|
||||
std::cout << fmt::format(" {:.2f} us clock detected; ", (double)clockPeriod/1000.0) << std::flush;
|
||||
|
||||
FluxmapReader fmr(*track->fluxmap);
|
||||
#if 0
|
||||
clockPeriod *= clockScaleFlag;
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dumpBitstreamFlag)
|
||||
{
|
||||
std::cout << "Aligned bitstream of length " << bitmap.size()
|
||||
<< " follows:" << std::endl
|
||||
<< std::endl;
|
||||
fmr.seek(seekFlag*1000000.0);
|
||||
|
||||
size_t cursor = seekFlag*1000000.0 / clockPeriod;
|
||||
while (cursor < bitmap.size())
|
||||
std::cout << fmt::format("Aligned bitstream from {:.3f}ms follows:\n",
|
||||
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++)
|
||||
{
|
||||
if (cursor >= bitmap.size())
|
||||
if (fmr.eof())
|
||||
break;
|
||||
std::cout << (bitmap[cursor] ? 'X' : '-');
|
||||
cursor++;
|
||||
bool b = fmr.readRawBit(clockPeriod);
|
||||
std::cout << (b ? 'X' : '-');
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
@@ -17,7 +17,7 @@ static StringFlag outputFilename(
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0");
|
||||
setReaderDefaultSource(":t=0-79x2:s=0");
|
||||
setReaderRevolutions(2);
|
||||
setDecoderManualClockRate(4.0);
|
||||
Flag::parseFlags(argc, argv);
|
||||
|
||||
@@ -5,46 +5,46 @@
|
||||
|
||||
typedef std::vector<unsigned> ivector;
|
||||
|
||||
static void test_patternconstruction()
|
||||
void test_patternconstruction()
|
||||
{
|
||||
{
|
||||
FluxPattern fp(16, 0x0003);
|
||||
assert(fp.bits == 16);
|
||||
assert(fp.intervals == ivector{ 1 });
|
||||
assert(fp._bits == 16);
|
||||
assert(fp._intervals == ivector{ 1 });
|
||||
}
|
||||
|
||||
{
|
||||
FluxPattern fp(16, 0xc000);
|
||||
assert(fp.bits == 16);
|
||||
assert(fp.intervals == ivector{ 1 });
|
||||
assert(fp._bits == 16);
|
||||
assert(fp._intervals == ivector{ 1 });
|
||||
}
|
||||
|
||||
{
|
||||
FluxPattern fp(16, 0x0050);
|
||||
assert(fp.bits == 16);
|
||||
assert(fp.intervals == ivector{ 2 });
|
||||
assert(fp._bits == 16);
|
||||
assert(fp._intervals == ivector{ 2 });
|
||||
}
|
||||
|
||||
{
|
||||
FluxPattern fp(16, 0x0070);
|
||||
assert(fp.bits == 16);
|
||||
assert((fp.intervals == ivector{ 1, 1 }));
|
||||
assert(fp._bits == 16);
|
||||
assert((fp._intervals == ivector{ 1, 1 }));
|
||||
}
|
||||
|
||||
{
|
||||
FluxPattern fp(16, 0x0070);
|
||||
assert(fp.bits == 16);
|
||||
assert((fp.intervals == ivector{ 1, 1 }));
|
||||
assert(fp._bits == 16);
|
||||
assert((fp._intervals == ivector{ 1, 1 }));
|
||||
}
|
||||
|
||||
{
|
||||
FluxPattern fp(16, 0x0110);
|
||||
assert(fp.bits == 16);
|
||||
assert((fp.intervals == ivector{ 4 }));
|
||||
assert(fp._bits == 16);
|
||||
assert((fp._intervals == ivector{ 4 }));
|
||||
}
|
||||
}
|
||||
|
||||
static void test_patternmatching()
|
||||
void test_patternmatching()
|
||||
{
|
||||
FluxPattern fp(16, 0x000b);
|
||||
const unsigned matching[] = { 100, 100, 200, 100 };
|
||||
@@ -59,11 +59,21 @@ static void test_patternmatching()
|
||||
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[])
|
||||
{
|
||||
test_patternconstruction();
|
||||
test_patternmatching();
|
||||
test_patternsmatching();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user