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
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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(); }
|
||||||
|
|||||||
@@ -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*) §or->data[0], sector->data.size());
|
outputFile.write((const char*) sector->data.cbegin(), sector->data.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
lib/record.h
16
lib/record.h
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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() {}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user