Merge pull request #677 from davidgiven/errors

Clean up error handling.
This commit is contained in:
noreply@github.com
2023-05-09 23:13:49 +00:00
170 changed files with 5519 additions and 5128 deletions

View File

@@ -11,56 +11,54 @@
static const FluxPattern SECTOR_PATTERN(32, AESLANIER_RECORD_SEPARATOR);
/* This is actually M2FM, rather than MFM, but it our MFM/FM decoder copes fine with it. */
/* This is actually M2FM, rather than MFM, but it our MFM/FM decoder copes fine
* with it. */
class AesLanierDecoder : public Decoder
{
public:
AesLanierDecoder(const DecoderProto& config):
Decoder(config)
{}
AesLanierDecoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(SECTOR_PATTERN);
}
{
return seekToPattern(SECTOR_PATTERN);
}
void decodeSectorRecord() override
{
/* Skip ID mark (we know it's a AESLANIER_RECORD_SEPARATOR). */
{
/* Skip ID mark (we know it's a AESLANIER_RECORD_SEPARATOR). */
readRawBits(16);
readRawBits(16);
const auto& rawbits = readRawBits(AESLANIER_RECORD_SIZE*16);
const auto& bytes = decodeFmMfm(rawbits).slice(0, AESLANIER_RECORD_SIZE);
const auto& reversed = bytes.reverseBits();
const auto& rawbits = readRawBits(AESLANIER_RECORD_SIZE * 16);
const auto& bytes =
decodeFmMfm(rawbits).slice(0, AESLANIER_RECORD_SIZE);
const auto& reversed = bytes.reverseBits();
_sector->logicalTrack = reversed[1];
_sector->logicalSide = 0;
_sector->logicalSector = reversed[2];
_sector->logicalTrack = reversed[1];
_sector->logicalSide = 0;
_sector->logicalSector = reversed[2];
/* Check header 'checksum' (which seems far too simple to mean much). */
/* Check header 'checksum' (which seems far too simple to mean much). */
{
uint8_t wanted = reversed[3];
uint8_t got = reversed[1] + reversed[2];
if (wanted != got)
return;
}
{
uint8_t wanted = reversed[3];
uint8_t got = reversed[1] + reversed[2];
if (wanted != got)
return;
}
/* Check data checksum, which also includes the header and is
* significantly better. */
/* Check data checksum, which also includes the header and is
* significantly better. */
_sector->data = reversed.slice(1, AESLANIER_SECTOR_LENGTH);
uint16_t wanted = reversed.reader().seek(0x101).read_le16();
uint16_t got = crc16ref(MODBUS_POLY_REF, _sector->data);
_sector->status = (wanted == got) ? Sector::OK : Sector::BAD_CHECKSUM;
}
_sector->data = reversed.slice(1, AESLANIER_SECTOR_LENGTH);
uint16_t wanted = reversed.reader().seek(0x101).read_le16();
uint16_t got = crc16ref(MODBUS_POLY_REF, _sector->data);
_sector->status = (wanted == got) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createAesLanierDecoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new AesLanierDecoder(config));
return std::unique_ptr<Decoder>(new AesLanierDecoder(config));
}

View File

@@ -8,15 +8,13 @@ uint8_t agatChecksum(const Bytes& bytes)
{
uint16_t checksum = 0;
for (uint8_t b : bytes)
{
if (checksum > 0xff)
checksum = (checksum + 1) & 0xff;
for (uint8_t b : bytes)
{
if (checksum > 0xff)
checksum = (checksum + 1) & 0xff;
checksum += b;
}
checksum += b;
}
return checksum & 0xff;
return checksum & 0xff;
}

View File

@@ -9,13 +9,14 @@
#include "fmt/format.h"
#include <string.h>
// clang-format off
/*
* data: X X X X X X X X X - - X - X - X - X X - X - X - = 0xff956a
* flux: 01 01 01 01 01 01 01 01 01 00 10 01 00 01 00 01 00 01 01 00 01 00 01 00 = 0x555549111444
*
* data: X X X X X X X X - X X - X - X - X - - X - X - X = 0xff6a95
* flux: 01 01 01 01 01 01 01 01 00 01 01 00 01 00 01 00 01 00 10 01 00 01 00 01 = 0x555514444911
*
*
* Each pattern is prefixed with this one:
*
* data: - - - X - - X - = 0x12
@@ -30,65 +31,59 @@
* 0100010010010010 = MFM encoded
* 1000100100100100 = with trailing zero
* - - - X - - X - = effective bitstream = 0x12
*
*/
// clang-format on
static const FluxPattern SECTOR_PATTERN(64, SECTOR_ID);
static const FluxPattern DATA_PATTERN(64, DATA_ID);
static const FluxMatchers ALL_PATTERNS = {
&SECTOR_PATTERN,
&DATA_PATTERN
};
static const FluxMatchers ALL_PATTERNS = {&SECTOR_PATTERN, &DATA_PATTERN};
class AgatDecoder : public Decoder
{
public:
AgatDecoder(const DecoderProto& config):
Decoder(config)
{}
AgatDecoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ALL_PATTERNS);
}
{
return seekToPattern(ALL_PATTERNS);
}
void decodeSectorRecord() override
{
if (readRaw64() != SECTOR_ID)
return;
{
if (readRaw64() != SECTOR_ID)
return;
auto bytes = decodeFmMfm(readRawBits(64)).slice(0, 4);
if (bytes[3] != 0x5a)
return;
auto bytes = decodeFmMfm(readRawBits(64)).slice(0, 4);
if (bytes[3] != 0x5a)
return;
_sector->logicalTrack = bytes[1] >> 1;
_sector->logicalSector = bytes[2];
_sector->logicalSide = bytes[1] & 1;
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
_sector->logicalTrack = bytes[1] >> 1;
_sector->logicalSector = bytes[2];
_sector->logicalSide = bytes[1] & 1;
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord() override
{
if (readRaw64() != DATA_ID)
return;
void decodeDataRecord() override
{
if (readRaw64() != DATA_ID)
return;
Bytes bytes = decodeFmMfm(readRawBits((AGAT_SECTOR_SIZE+2)*16)).slice(0, AGAT_SECTOR_SIZE+2);
Bytes bytes = decodeFmMfm(readRawBits((AGAT_SECTOR_SIZE + 2) * 16))
.slice(0, AGAT_SECTOR_SIZE + 2);
if (bytes[AGAT_SECTOR_SIZE+1] != 0x5a)
return;
if (bytes[AGAT_SECTOR_SIZE + 1] != 0x5a)
return;
_sector->data = bytes.slice(0, AGAT_SECTOR_SIZE);
uint8_t wantChecksum = bytes[AGAT_SECTOR_SIZE];
uint8_t gotChecksum = agatChecksum(_sector->data);
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
_sector->data = bytes.slice(0, AGAT_SECTOR_SIZE);
uint8_t wantChecksum = bytes[AGAT_SECTOR_SIZE];
uint8_t gotChecksum = agatChecksum(_sector->data);
_sector->status =
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createAgatDecoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new AgatDecoder(config));
return std::unique_ptr<Decoder>(new AgatDecoder(config));
}

View File

@@ -95,7 +95,7 @@ public:
}
if (_cursor >= _bits.size())
Error() << "track data overrun";
error("track data overrun");
fillBitmapTo(_bits, _cursor, _bits.size(), {true, false});
auto fluxmap = std::make_unique<Fluxmap>();

View File

@@ -18,61 +18,61 @@ uint32_t amigaChecksum(const Bytes& bytes)
static uint8_t everyother(uint16_t x)
{
/* aabb ccdd eeff gghh */
x &= 0x6666; /* 0ab0 0cd0 0ef0 0gh0 */
x >>= 1; /* 00ab 00cd 00ef 00gh */
x |= x << 2; /* abab cdcd efef ghgh */
x &= 0x3c3c; /* 00ab cd00 00ef gh00 */
x >>= 2; /* 0000 abcd 0000 efgh */
x |= x >> 4; /* 0000 abcd abcd efgh */
return x;
/* aabb ccdd eeff gghh */
x &= 0x6666; /* 0ab0 0cd0 0ef0 0gh0 */
x >>= 1; /* 00ab 00cd 00ef 00gh */
x |= x << 2; /* abab cdcd efef ghgh */
x &= 0x3c3c; /* 00ab cd00 00ef gh00 */
x >>= 2; /* 0000 abcd 0000 efgh */
x |= x >> 4; /* 0000 abcd abcd efgh */
return x;
}
Bytes amigaInterleave(const Bytes& input)
{
Bytes output;
ByteWriter bw(output);
Bytes output;
ByteWriter bw(output);
/* Write all odd bits. (Numbering starts at 0...) */
/* Write all odd bits. (Numbering starts at 0...) */
{
ByteReader br(input);
while (!br.eof())
{
uint16_t x = br.read_be16();
x &= 0xaaaa; /* a0b0 c0d0 e0f0 g0h0 */
x |= x >> 1; /* aabb ccdd eeff gghh */
x = everyother(x); /* 0000 0000 abcd efgh */
bw.write_8(x);
}
}
{
ByteReader br(input);
while (!br.eof())
{
uint16_t x = br.read_be16();
x &= 0xaaaa; /* a0b0 c0d0 e0f0 g0h0 */
x |= x >> 1; /* aabb ccdd eeff gghh */
x = everyother(x); /* 0000 0000 abcd efgh */
bw.write_8(x);
}
}
/* Write all even bits. */
/* Write all even bits. */
{
ByteReader br(input);
while (!br.eof())
{
uint16_t x = br.read_be16();
x &= 0x5555; /* 0a0b 0c0d 0e0f 0g0h */
x |= x << 1; /* aabb ccdd eeff gghh */
x = everyother(x); /* 0000 0000 abcd efgh */
bw.write_8(x);
}
}
{
ByteReader br(input);
while (!br.eof())
{
uint16_t x = br.read_be16();
x &= 0x5555; /* 0a0b 0c0d 0e0f 0g0h */
x |= x << 1; /* aabb ccdd eeff gghh */
x = everyother(x); /* 0000 0000 abcd efgh */
bw.write_8(x);
}
}
return output;
return output;
}
Bytes amigaDeinterleave(const uint8_t*& input, size_t len)
{
assert(!(len & 1));
const uint8_t* odds = &input[0];
const uint8_t* evens = &input[len/2];
const uint8_t* evens = &input[len / 2];
Bytes output;
ByteWriter bw(output);
for (size_t i=0; i<len/2; i++)
for (size_t i = 0; i < len / 2; i++)
{
uint8_t o = *odds++;
uint8_t e = *evens++;
@@ -81,11 +81,15 @@ Bytes amigaDeinterleave(const uint8_t*& input, size_t len)
* http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
*/
uint16_t result =
(((e * 0x0101010101010101ULL & 0x8040201008040201ULL)
* 0x0102040810204081ULL >> 49) & 0x5555) |
(((o * 0x0101010101010101ULL & 0x8040201008040201ULL)
* 0x0102040810204081ULL >> 48) & 0xAAAA);
(((e * 0x0101010101010101ULL & 0x8040201008040201ULL) *
0x0102040810204081ULL >>
49) &
0x5555) |
(((o * 0x0101010101010101ULL & 0x8040201008040201ULL) *
0x0102040810204081ULL >>
48) &
0xAAAA);
bw.write_be16(result);
}
@@ -95,6 +99,6 @@ Bytes amigaDeinterleave(const uint8_t*& input, size_t len)
Bytes amigaDeinterleave(const Bytes& input)
{
const uint8_t* ptr = input.cbegin();
return amigaDeinterleave(ptr, input.size());
const uint8_t* ptr = input.cbegin();
return amigaDeinterleave(ptr, input.size());
}

View File

@@ -11,70 +11,74 @@
#include <string.h>
#include <algorithm>
/*
/*
* Amiga disks use MFM but it's not quite the same as IBM MFM. They only use
* a single type of record with a different marker byte.
*
*
* See the big comment in the IBM MFM decoder for the gruesome details of how
* MFM works.
*/
static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD);
class AmigaDecoder : public Decoder
{
public:
AmigaDecoder(const DecoderProto& config):
Decoder(config),
_config(config.amiga())
{}
AmigaDecoder(const DecoderProto& config):
Decoder(config),
_config(config.amiga())
{
}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(SECTOR_PATTERN);
}
{
return seekToPattern(SECTOR_PATTERN);
}
void decodeSectorRecord() override
{
if (readRaw48() != AMIGA_SECTOR_RECORD)
return;
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
if (rawbits.size() < (AMIGA_RECORD_SIZE*16))
return;
const auto& rawbytes = toBytes(rawbits).slice(0, AMIGA_RECORD_SIZE*2);
const auto& bytes = decodeFmMfm(rawbits).slice(0, AMIGA_RECORD_SIZE);
{
if (readRaw48() != AMIGA_SECTOR_RECORD)
return;
const uint8_t* ptr = bytes.begin();
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE * 16);
if (rawbits.size() < (AMIGA_RECORD_SIZE * 16))
return;
const auto& rawbytes = toBytes(rawbits).slice(0, AMIGA_RECORD_SIZE * 2);
const auto& bytes = decodeFmMfm(rawbits).slice(0, AMIGA_RECORD_SIZE);
Bytes header = amigaDeinterleave(ptr, 4);
Bytes recoveryinfo = amigaDeinterleave(ptr, 16);
const uint8_t* ptr = bytes.begin();
_sector->logicalTrack = header[1] >> 1;
_sector->logicalSide = header[1] & 1;
_sector->logicalSector = header[2];
Bytes header = amigaDeinterleave(ptr, 4);
Bytes recoveryinfo = amigaDeinterleave(ptr, 16);
uint32_t wantedheaderchecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(0, 40));
if (gotheaderchecksum != wantedheaderchecksum)
return;
_sector->logicalTrack = header[1] >> 1;
_sector->logicalSide = header[1] & 1;
_sector->logicalSector = header[2];
uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(56, 1024));
uint32_t wantedheaderchecksum =
amigaDeinterleave(ptr, 4).reader().read_be32();
uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(0, 40));
if (gotheaderchecksum != wantedheaderchecksum)
return;
Bytes data;
data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo);
_sector->data = data;
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
uint32_t wanteddatachecksum =
amigaDeinterleave(ptr, 4).reader().read_be32();
uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(56, 1024));
Bytes data;
data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo);
_sector->data = data;
_sector->status = (gotdatachecksum == wanteddatachecksum)
? Sector::OK
: Sector::BAD_CHECKSUM;
}
private:
const AmigaDecoderProto& _config;
nanoseconds_t _clock;
const AmigaDecoderProto& _config;
nanoseconds_t _clock;
};
std::unique_ptr<Decoder> createAmigaDecoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new AmigaDecoder(config));
return std::unique_ptr<Decoder>(new AmigaDecoder(config));
}

View File

@@ -59,7 +59,7 @@ static void write_sector(std::vector<bool>& bits,
const std::shared_ptr<const Sector>& sector)
{
if ((sector->data.size() != 512) && (sector->data.size() != 528))
Error() << "unsupported sector size --- you must pick 512 or 528";
error("unsupported sector size --- you must pick 512 or 528");
uint32_t checksum = 0;
@@ -114,7 +114,8 @@ public:
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
/* Number of bits for one nominal revolution of a real 200ms Amiga disk. */
/* Number of bits for one nominal revolution of a real 200ms Amiga disk.
*/
int bitsPerRevolution = 200e3 / _config.clock_rate_us();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
@@ -129,13 +130,12 @@ public:
write_sector(bits, cursor, sector);
if (cursor >= bits.size())
Error() << "track data overrun";
error("track data overrun");
fillBitmapTo(bits, cursor, bits.size(), {true, false});
auto fluxmap = std::make_unique<Fluxmap>();
fluxmap->appendBits(bits,
calculatePhysicalClockPeriod(
_config.clock_rate_us() * 1e3, 200e6));
calculatePhysicalClockPeriod(_config.clock_rate_us() * 1e3, 200e6));
return fluxmap;
}

View File

@@ -50,8 +50,7 @@ public:
writeSector(bits, cursor, *sector);
if (cursor >= bits.size())
Error() << fmt::format(
"track data overrun by {} bits", cursor - bits.size());
error("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), {true, false});
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
@@ -118,8 +117,7 @@ private:
// There is data to encode to disk.
if ((sector.data.size() != APPLE2_SECTOR_LENGTH))
Error() << fmt::format(
"unsupported sector size {} --- you must pick 256",
error("unsupported sector size {} --- you must pick 256",
sector.data.size());
// Write address syncing leader : A sequence of "FF40"s; 5 of them

View File

@@ -11,7 +11,8 @@
const FluxPattern SECTOR_RECORD_PATTERN(32, BROTHER_SECTOR_RECORD);
const FluxPattern DATA_RECORD_PATTERN(32, BROTHER_DATA_RECORD);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
const FluxMatchers ANY_RECORD_PATTERN(
{&SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN});
static std::vector<uint8_t> outputbuffer;
@@ -32,88 +33,89 @@ static int decode_data_gcr(uint8_t gcr)
{
switch (gcr)
{
#define GCR_ENTRY(gcr, data) \
case gcr: return data;
#include "data_gcr.h"
#undef GCR_ENTRY
#define GCR_ENTRY(gcr, data) \
case gcr: \
return data;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
static int decode_header_gcr(uint16_t word)
{
switch (word)
{
#define GCR_ENTRY(gcr, data) \
case gcr: return data;
#include "header_gcr.h"
#undef GCR_ENTRY
}
return -1;
switch (word)
{
#define GCR_ENTRY(gcr, data) \
case gcr: \
return data;
#include "header_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
class BrotherDecoder : public Decoder
{
public:
BrotherDecoder(const DecoderProto& config):
Decoder(config)
{}
BrotherDecoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
if (readRaw32() != BROTHER_SECTOR_RECORD)
return;
{
if (readRaw32() != BROTHER_SECTOR_RECORD)
return;
const auto& rawbits = readRawBits(32);
const auto& bytes = toBytes(rawbits).slice(0, 4);
const auto& rawbits = readRawBits(32);
const auto& bytes = toBytes(rawbits).slice(0, 4);
ByteReader br(bytes);
_sector->logicalTrack = decode_header_gcr(br.read_be16());
_sector->logicalSector = decode_header_gcr(br.read_be16());
ByteReader br(bytes);
_sector->logicalTrack = decode_header_gcr(br.read_be16());
_sector->logicalSector = decode_header_gcr(br.read_be16());
/* Sanity check the values read; there's no header checksum and
* occasionally we get garbage due to bit errors. */
if (_sector->logicalSector > 11)
return;
if (_sector->logicalTrack > 79)
return;
/* Sanity check the values read; there's no header checksum and
* occasionally we get garbage due to bit errors. */
if (_sector->logicalSector > 11)
return;
if (_sector->logicalTrack > 79)
return;
_sector->status = Sector::DATA_MISSING;
}
_sector->status = Sector::DATA_MISSING;
}
void decodeDataRecord() override
{
if (readRaw32() != BROTHER_DATA_RECORD)
return;
{
if (readRaw32() != BROTHER_DATA_RECORD)
return;
const auto& rawbits = readRawBits(BROTHER_DATA_RECORD_ENCODED_SIZE*8);
const auto& rawbytes = toBytes(rawbits).slice(0, BROTHER_DATA_RECORD_ENCODED_SIZE);
const auto& rawbits = readRawBits(BROTHER_DATA_RECORD_ENCODED_SIZE * 8);
const auto& rawbytes =
toBytes(rawbits).slice(0, BROTHER_DATA_RECORD_ENCODED_SIZE);
Bytes bytes;
ByteWriter bw(bytes);
BitWriter bitw(bw);
for (uint8_t b : rawbytes)
{
uint32_t nibble = decode_data_gcr(b);
bitw.push(nibble, 5);
}
bitw.flush();
Bytes bytes;
ByteWriter bw(bytes);
BitWriter bitw(bw);
for (uint8_t b : rawbytes)
{
uint32_t nibble = decode_data_gcr(b);
bitw.push(nibble, 5);
}
bitw.flush();
_sector->data = bytes.slice(0, BROTHER_DATA_RECORD_PAYLOAD);
uint32_t realCrc = crcbrother(_sector->data);
uint32_t wantCrc = bytes.reader().seek(BROTHER_DATA_RECORD_PAYLOAD).read_be24();
_sector->status = (realCrc == wantCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
}
_sector->data = bytes.slice(0, BROTHER_DATA_RECORD_PAYLOAD);
uint32_t realCrc = crcbrother(_sector->data);
uint32_t wantCrc =
bytes.reader().seek(BROTHER_DATA_RECORD_PAYLOAD).read_be24();
_sector->status =
(realCrc == wantCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createBrotherDecoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new BrotherDecoder(config));
return std::unique_ptr<Decoder>(new BrotherDecoder(config));
}

View File

@@ -67,7 +67,7 @@ static void write_sector_data(
int width = 0;
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
Error() << "unsupported sector size";
error("unsupported sector size");
auto write_byte = [&](uint8_t byte)
{
@@ -107,8 +107,7 @@ public:
}
public:
std::unique_ptr<Fluxmap> encode(
std::shared_ptr<const TrackInfo>& trackInfo,
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
@@ -116,8 +115,8 @@ public:
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
int sectorCount = 0;
for (const auto& sectorData : sectors)
int sectorCount = 0;
for (const auto& sectorData : sectors)
{
double headerMs = _config.post_index_gap_ms() +
sectorCount * _config.sector_spacing_ms();
@@ -126,16 +125,18 @@ public:
unsigned dataCursor = dataMs * 1e3 / _config.clock_rate_us();
fillBitmapTo(bits, cursor, headerCursor, {true, false});
write_sector_header(
bits, cursor, sectorData->logicalTrack, sectorData->logicalSector);
write_sector_header(bits,
cursor,
sectorData->logicalTrack,
sectorData->logicalSector);
fillBitmapTo(bits, cursor, dataCursor, {true, false});
write_sector_data(bits, cursor, sectorData->data);
sectorCount++;
sectorCount++;
}
if (cursor >= bits.size())
Error() << "track data overrun";
error("track data overrun");
fillBitmapTo(bits, cursor, bits.size(), {true, false});
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
@@ -147,8 +148,7 @@ private:
const BrotherEncoderProto& _config;
};
std::unique_ptr<Encoder> createBrotherEncoder(
const EncoderProto& config)
std::unique_ptr<Encoder> createBrotherEncoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new BrotherEncoder(config));
}

View File

@@ -2,17 +2,17 @@
#include "c64.h"
/*
* Track Sectors/track # Sectors Storage in Bytes Clock rate
* ----- ------------- --------- ---------------- ----------
* 1-17 21 357 7820 3.25
* 18-24 19 133 7170 3.5
* 25-30 18 108 6300 3.75
* 31-40(*) 17 85 6020 4
* ---
* 683 (for a 35 track image)
*
* The clock rate is normalised for a 200ms drive.
*/
* Track Sectors/track # Sectors Storage in Bytes Clock rate
* ----- ------------- --------- ---------------- ----------
* 1-17 21 357 7820 3.25
* 18-24 19 133 7170 3.5
* 25-30 18 108 6300 3.75
* 31-40(*) 17 85 6020 4
* ---
* 683 (for a 35 track image)
*
* The clock rate is normalised for a 200ms drive.
*/
nanoseconds_t clockPeriodForC64Track(unsigned track)
{

View File

@@ -96,8 +96,7 @@ public:
}
};
std::unique_ptr<Decoder> createCommodore64Decoder(
const DecoderProto& config)
std::unique_ptr<Decoder> createCommodore64Decoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new Commodore64Decoder(config));
}

View File

@@ -214,8 +214,7 @@ public:
writeSector(bits, cursor, sector);
if (cursor >= bits.size())
Error() << fmt::format(
"track data overrun by {} bits", cursor - bits.size());
error("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), {true, false});
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
@@ -243,8 +242,7 @@ private:
{
// There is data to encode to disk.
if ((sector->data.size() != C64_SECTOR_LENGTH))
Error() << fmt::format(
"unsupported sector size {} --- you must pick 256",
error("unsupported sector size {} --- you must pick 256",
sector->data.size());
// 1. Write header Sync (not GCR)

View File

@@ -13,16 +13,18 @@
const FluxPattern SECTOR_RECORD_PATTERN(24, F85_SECTOR_RECORD);
const FluxPattern DATA_RECORD_PATTERN(24, F85_DATA_RECORD);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
const FluxMatchers ANY_RECORD_PATTERN(
{&SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN});
static int decode_data_gcr(uint8_t gcr)
{
switch (gcr)
{
#define GCR_ENTRY(gcr, data) \
case gcr: return data;
#include "data_gcr.h"
#undef GCR_ENTRY
#define GCR_ENTRY(gcr, data) \
case gcr: \
return data;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
@@ -37,11 +39,11 @@ static Bytes decode(const std::vector<bool>& bits)
while (ii != bits.end())
{
uint8_t inputfifo = 0;
for (size_t i=0; i<5; i++)
for (size_t i = 0; i < 5; i++)
{
if (ii == bits.end())
break;
inputfifo = (inputfifo<<1) | *ii++;
inputfifo = (inputfifo << 1) | *ii++;
}
bitw.push(decode_data_gcr(inputfifo), 4);
@@ -54,56 +56,55 @@ static Bytes decode(const std::vector<bool>& bits)
class DurangoF85Decoder : public Decoder
{
public:
DurangoF85Decoder(const DecoderProto& config):
Decoder(config)
{}
DurangoF85Decoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
/* Skip sync bits and ID byte. */
{
/* Skip sync bits and ID byte. */
if (readRaw24() != F85_SECTOR_RECORD)
return;
if (readRaw24() != F85_SECTOR_RECORD)
return;
/* Read header. */
/* Read header. */
const auto& bytes = decode(readRawBits(6*10));
const auto& bytes = decode(readRawBits(6 * 10));
_sector->logicalSector = bytes[2];
_sector->logicalSide = 0;
_sector->logicalTrack = bytes[0];
_sector->logicalSector = bytes[2];
_sector->logicalSide = 0;
_sector->logicalTrack = bytes[0];
uint16_t wantChecksum = bytes.reader().seek(4).read_be16();
uint16_t gotChecksum = crc16(CCITT_POLY, 0xef21, bytes.slice(0, 4));
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
uint16_t wantChecksum = bytes.reader().seek(4).read_be16();
uint16_t gotChecksum = crc16(CCITT_POLY, 0xef21, bytes.slice(0, 4));
if (wantChecksum == gotChecksum)
_sector->status =
Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord() override
{
/* Skip sync bits ID byte. */
{
/* Skip sync bits ID byte. */
if (readRaw24() != F85_DATA_RECORD)
return;
if (readRaw24() != F85_DATA_RECORD)
return;
const auto& bytes = decode(readRawBits((F85_SECTOR_LENGTH+3)*10))
.slice(0, F85_SECTOR_LENGTH+3);
ByteReader br(bytes);
const auto& bytes = decode(readRawBits((F85_SECTOR_LENGTH + 3) * 10))
.slice(0, F85_SECTOR_LENGTH + 3);
ByteReader br(bytes);
_sector->data = br.read(F85_SECTOR_LENGTH);
uint16_t wantChecksum = br.read_be16();
uint16_t gotChecksum = crc16(CCITT_POLY, 0xbf84, _sector->data);
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
_sector->data = br.read(F85_SECTOR_LENGTH);
uint16_t wantChecksum = br.read_be16();
uint16_t gotChecksum = crc16(CCITT_POLY, 0xbf84, _sector->data);
_sector->status =
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createDurangoF85Decoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new DurangoF85Decoder(config));
return std::unique_ptr<Decoder>(new DurangoF85Decoder(config));
}

View File

@@ -14,10 +14,10 @@
const FluxPattern SECTOR_ID_PATTERN(16, 0xabaa);
/*
/*
* Reverse engineered from a dump of the floppy drive's ROM. I have no idea how
* it works.
*
*
* LF8BA:
* clra
* staa X00B0
@@ -100,45 +100,43 @@ static uint16_t checksum(const Bytes& bytes)
class Fb100Decoder : public Decoder
{
public:
Fb100Decoder(const DecoderProto& config):
Decoder(config)
{}
Fb100Decoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(SECTOR_ID_PATTERN);
}
{
return seekToPattern(SECTOR_ID_PATTERN);
}
void decodeSectorRecord() override
{
auto rawbits = readRawBits(FB100_RECORD_SIZE*16);
{
auto rawbits = readRawBits(FB100_RECORD_SIZE * 16);
const Bytes bytes = decodeFmMfm(rawbits).slice(0, FB100_RECORD_SIZE);
ByteReader br(bytes);
br.seek(1);
const Bytes id = br.read(FB100_ID_SIZE);
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);
const Bytes bytes = decodeFmMfm(rawbits).slice(0, FB100_RECORD_SIZE);
ByteReader br(bytes);
br.seek(1);
const Bytes id = br.read(FB100_ID_SIZE);
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);
if (wantIdCrc != gotIdCrc)
return;
if (wantIdCrc != gotIdCrc)
return;
uint8_t abssector = id[2];
_sector->logicalTrack = abssector >> 1;
_sector->logicalSide = 0;
_sector->logicalSector = abssector & 1;
_sector->data.writer().append(id.slice(5, 12)).append(payload);
uint8_t abssector = id[2];
_sector->logicalTrack = abssector >> 1;
_sector->logicalSide = 0;
_sector->logicalSector = abssector & 1;
_sector->data.writer().append(id.slice(5, 12)).append(payload);
_sector->status = (wantPayloadCrc == gotPayloadCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
}
_sector->status = (wantPayloadCrc == gotPayloadCrc)
? Sector::OK
: Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createFb100Decoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new Fb100Decoder(config));
return std::unique_ptr<Decoder>(new Fb100Decoder(config));
}

View File

@@ -112,10 +112,11 @@ public:
const Image& image) override
{
IbmEncoderProto::TrackdataProto trackdata;
getEncoderTrackData(trackdata, trackInfo->logicalTrack, trackInfo->logicalSide);
getEncoderTrackData(
trackdata, trackInfo->logicalTrack, trackInfo->logicalSide);
auto trackLayout =
Layout::getLayoutOfTrack(trackInfo->logicalTrack, trackInfo->logicalSide);
auto trackLayout = Layout::getLayoutOfTrack(
trackInfo->logicalTrack, trackInfo->logicalSide);
auto writeBytes = [&](const Bytes& bytes)
{
@@ -257,7 +258,7 @@ public:
}
if (_cursor >= _bits.size())
Error() << "track data overrun";
error("track data overrun");
while (_cursor < _bits.size())
writeFillerRawBytes(1, gapFill);

View File

@@ -12,22 +12,25 @@
const FluxPattern SECTOR_RECORD_PATTERN(24, MAC_SECTOR_RECORD);
const FluxPattern DATA_RECORD_PATTERN(24, MAC_DATA_RECORD);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
const FluxMatchers ANY_RECORD_PATTERN(
{&SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN});
static int decode_data_gcr(uint8_t gcr)
{
switch (gcr)
{
#define GCR_ENTRY(gcr, data) \
case gcr: return data;
#include "data_gcr.h"
#undef GCR_ENTRY
#define GCR_ENTRY(gcr, data) \
case gcr: \
return data;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
/* This is extremely inspired by the MESS implementation, written by Nathan Woods
* and R. Belmont: https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp
/* This is extremely inspired by the MESS implementation, written by Nathan
* Woods and R. Belmont:
* https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp
*/
static Bytes decode_crazy_data(const Bytes& input, Sector::Status& status)
{
@@ -41,7 +44,7 @@ static Bytes decode_crazy_data(const Bytes& input, Sector::Status& status)
uint8_t b2[LOOKUP_LEN + 1];
uint8_t b3[LOOKUP_LEN + 1];
for (int i=0; i<=LOOKUP_LEN; i++)
for (int i = 0; i <= LOOKUP_LEN; i++)
{
uint8_t w4 = br.read_8();
uint8_t w1 = br.read_8();
@@ -125,67 +128,68 @@ uint8_t decode_side(uint8_t side)
class MacintoshDecoder : public Decoder
{
public:
MacintoshDecoder(const DecoderProto& config):
Decoder(config)
{}
MacintoshDecoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
if (readRaw24() != MAC_SECTOR_RECORD)
return;
{
if (readRaw24() != MAC_SECTOR_RECORD)
return;
/* Read header. */
/* Read header. */
auto header = toBytes(readRawBits(7*8)).slice(0, 7);
uint8_t encodedTrack = decode_data_gcr(header[0]);
if (encodedTrack != (_sector->physicalTrack & 0x3f))
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]);
auto header = toBytes(readRawBits(7 * 8)).slice(0, 7);
if (encodedSector > 11)
return;
uint8_t encodedTrack = decode_data_gcr(header[0]);
if (encodedTrack != (_sector->physicalTrack & 0x3f))
return;
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalSide = decode_side(encodedSide);
_sector->logicalSector = encodedSector;
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
if (wantedsum == gotsum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
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]);
if (encodedSector > 11)
return;
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalSide = decode_side(encodedSide);
_sector->logicalSector = encodedSector;
uint8_t gotsum =
(encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
if (wantedsum == gotsum)
_sector->status =
Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord() override
{
if (readRaw24() != MAC_DATA_RECORD)
return;
{
if (readRaw24() != MAC_DATA_RECORD)
return;
/* Read data. */
/* Read data. */
readRawBits(8); /* skip spare byte */
auto inputbuffer = toBytes(readRawBits(MAC_ENCODED_SECTOR_LENGTH*8))
.slice(0, MAC_ENCODED_SECTOR_LENGTH);
readRawBits(8); /* skip spare byte */
auto inputbuffer = toBytes(readRawBits(MAC_ENCODED_SECTOR_LENGTH * 8))
.slice(0, MAC_ENCODED_SECTOR_LENGTH);
for (unsigned i=0; i<inputbuffer.size(); i++)
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
_sector->status = Sector::BAD_CHECKSUM;
Bytes userData = decode_crazy_data(inputbuffer, _sector->status);
_sector->data.clear();
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
}
for (unsigned i = 0; i < inputbuffer.size(); i++)
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
_sector->status = Sector::BAD_CHECKSUM;
Bytes userData = decode_crazy_data(inputbuffer, _sector->status);
_sector->data.clear();
_sector->data.writer()
.append(userData.slice(12, 512))
.append(userData.slice(0, 12));
}
};
std::unique_ptr<Decoder> createMacintoshDecoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new MacintoshDecoder(config));
return std::unique_ptr<Decoder>(new MacintoshDecoder(config));
}

View File

@@ -174,7 +174,7 @@ static void write_sector(std::vector<bool>& bits,
const std::shared_ptr<const Sector>& sector)
{
if ((sector->data.size() != 512) && (sector->data.size() != 524))
Error() << "unsupported sector size --- you must pick 512 or 524";
error("unsupported sector size --- you must pick 512 or 524");
write_bits(bits, cursor, 0xff, 1 * 8); /* pad byte */
for (int i = 0; i < 7; i++)
@@ -239,13 +239,12 @@ public:
write_sector(bits, cursor, sector);
if (cursor >= bits.size())
Error() << fmt::format(
"track data overrun by {} bits", cursor - bits.size());
error("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), {true, false});
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits,
calculatePhysicalClockPeriod(clockRateUs * 1e3, 200e6));
fluxmap->appendBits(
bits, calculatePhysicalClockPeriod(clockRateUs * 1e3, 200e6));
return fluxmap;
}
@@ -253,8 +252,7 @@ private:
const MacintoshEncoderProto& _config;
};
std::unique_ptr<Encoder> createMacintoshEncoder(
const EncoderProto& config)
std::unique_ptr<Encoder> createMacintoshEncoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new MacintoshEncoder(config));
}

View File

@@ -20,17 +20,20 @@ static const FluxPattern SECTOR_SYNC_PATTERN(64, 0xAAAAAAAAAAAA5555LL);
static const FluxPattern SECTOR_ADVANCE_PATTERN(64, 0xAAAAAAAAAAAAAAAALL);
/* Standard Micropolis checksum. Adds all bytes, with carry. */
uint8_t micropolisChecksum(const Bytes& bytes) {
ByteReader br(bytes);
uint16_t sum = 0;
while (!br.eof()) {
if (sum > 0xFF) {
sum -= 0x100 - 1;
}
sum += br.read_8();
}
/* The last carry is ignored */
return sum & 0xFF;
uint8_t micropolisChecksum(const Bytes& bytes)
{
ByteReader br(bytes);
uint16_t sum = 0;
while (!br.eof())
{
if (sum > 0xFF)
{
sum -= 0x100 - 1;
}
sum += br.read_8();
}
/* The last carry is ignored */
return sum & 0xFF;
}
/* Vector MZOS does not use the standard Micropolis checksum.
@@ -41,145 +44,164 @@ uint8_t micropolisChecksum(const Bytes& bytes) {
* Unlike the Micropolis checksum, this does not cover the 12-byte
* header (track, sector, 10 OS-specific bytes.)
*/
uint8_t mzosChecksum(const Bytes& bytes) {
ByteReader br(bytes);
uint8_t checksum = 0;
uint8_t databyte;
uint8_t mzosChecksum(const Bytes& bytes)
{
ByteReader br(bytes);
uint8_t checksum = 0;
uint8_t databyte;
while (!br.eof()) {
databyte = br.read_8();
checksum ^= ((databyte << 1) | (databyte >> 7));
}
while (!br.eof())
{
databyte = br.read_8();
checksum ^= ((databyte << 1) | (databyte >> 7));
}
return checksum;
return checksum;
}
class MicropolisDecoder : public Decoder
{
public:
MicropolisDecoder(const DecoderProto& config):
Decoder(config),
_config(config.micropolis())
{
_checksumType = _config.checksum_type();
}
MicropolisDecoder(const DecoderProto& config):
Decoder(config),
_config(config.micropolis())
{
_checksumType = _config.checksum_type();
}
nanoseconds_t advanceToNextRecord() override
{
nanoseconds_t now = tell().ns();
nanoseconds_t advanceToNextRecord() override
{
nanoseconds_t now = tell().ns();
/* For all but the first sector, seek to the next sector pulse.
* The first sector does not contain the sector pulse in the fluxmap.
*/
if (now != 0) {
seekToIndexMark();
now = tell().ns();
}
/* For all but the first sector, seek to the next sector pulse.
* The first sector does not contain the sector pulse in the fluxmap.
*/
if (now != 0)
{
seekToIndexMark();
now = tell().ns();
}
/* Discard a possible partial sector at the end of the track.
* This partial sector could be mistaken for a conflicted sector, if
* whatever data read happens to match the checksum of 0, which is
* rare, but has been observed on some disks.
*/
if (now > (getFluxmapDuration() - 12.5e6)) {
seekToIndexMark();
return 0;
}
/* Discard a possible partial sector at the end of the track.
* This partial sector could be mistaken for a conflicted sector, if
* whatever data read happens to match the checksum of 0, which is
* rare, but has been observed on some disks.
*/
if (now > (getFluxmapDuration() - 12.5e6))
{
seekToIndexMark();
return 0;
}
nanoseconds_t clock = seekToPattern(SECTOR_SYNC_PATTERN);
nanoseconds_t clock = seekToPattern(SECTOR_SYNC_PATTERN);
auto syncDelta = tell().ns() - now;
/* Due to the weak nature of the Micropolis SYNC patern,
* it's possible to detect a false SYNC during the gap
* between the sector pulse and the write gate. If the SYNC
* is detected less than 100uS after the sector pulse, search
* for another valid SYNC.
*
* Reference: Vector Micropolis Disk Controller Board Technical
* Information Manual, pp. 1-16.
*/
if ((syncDelta > 0) && (syncDelta < 100e3)) {
seekToPattern(SECTOR_ADVANCE_PATTERN);
clock = seekToPattern(SECTOR_SYNC_PATTERN);
}
auto syncDelta = tell().ns() - now;
/* Due to the weak nature of the Micropolis SYNC patern,
* it's possible to detect a false SYNC during the gap
* between the sector pulse and the write gate. If the SYNC
* is detected less than 100uS after the sector pulse, search
* for another valid SYNC.
*
* Reference: Vector Micropolis Disk Controller Board Technical
* Information Manual, pp. 1-16.
*/
if ((syncDelta > 0) && (syncDelta < 100e3))
{
seekToPattern(SECTOR_ADVANCE_PATTERN);
clock = seekToPattern(SECTOR_SYNC_PATTERN);
}
_sector->headerStartTime = tell().ns();
_sector->headerStartTime = tell().ns();
/* seekToPattern() can skip past the index hole, if this happens
* too close to the end of the Fluxmap, discard the sector.
*/
if (_sector->headerStartTime > (getFluxmapDuration() - 12.5e6)) {
return 0;
}
/* seekToPattern() can skip past the index hole, if this happens
* too close to the end of the Fluxmap, discard the sector.
*/
if (_sector->headerStartTime > (getFluxmapDuration() - 12.5e6))
{
return 0;
}
return clock;
}
return clock;
}
void decodeSectorRecord() override
{
readRawBits(48);
auto rawbits = readRawBits(MICROPOLIS_ENCODED_SECTOR_SIZE*16);
auto bytes = decodeFmMfm(rawbits).slice(0, MICROPOLIS_ENCODED_SECTOR_SIZE);
ByteReader br(bytes);
void decodeSectorRecord() override
{
readRawBits(48);
auto rawbits = readRawBits(MICROPOLIS_ENCODED_SECTOR_SIZE * 16);
auto bytes =
decodeFmMfm(rawbits).slice(0, MICROPOLIS_ENCODED_SECTOR_SIZE);
ByteReader br(bytes);
int syncByte = br.read_8(); /* sync */
if (syncByte != 0xFF)
return;
int syncByte = br.read_8(); /* sync */
if (syncByte != 0xFF)
return;
_sector->logicalTrack = br.read_8();
_sector->logicalSide = _sector->physicalSide;
_sector->logicalSector = br.read_8();
if (_sector->logicalSector > 15)
return;
if (_sector->logicalTrack > 76)
return;
if (_sector->logicalTrack != _sector->physicalTrack)
return;
_sector->logicalTrack = br.read_8();
_sector->logicalSide = _sector->physicalSide;
_sector->logicalSector = br.read_8();
if (_sector->logicalSector > 15)
return;
if (_sector->logicalTrack > 76)
return;
if (_sector->logicalTrack != _sector->physicalTrack)
return;
br.read(10); /* OS data or padding */
auto data = br.read(MICROPOLIS_PAYLOAD_SIZE);
uint8_t wantChecksum = br.read_8();
br.read(10); /* OS data or padding */
auto data = br.read(MICROPOLIS_PAYLOAD_SIZE);
uint8_t wantChecksum = br.read_8();
/* If not specified, automatically determine the checksum type.
* Once the checksum type is determined, it will be used for the
* entire disk.
*/
if (_checksumType == MicropolisDecoderProto::AUTO) {
/* Calculate both standard Micropolis (MDOS, CP/M, OASIS) and MZOS checksums */
if (wantChecksum == micropolisChecksum(bytes.slice(1, 2+266))) {
_checksumType = MicropolisDecoderProto::MICROPOLIS;
} else if (wantChecksum == mzosChecksum(bytes.slice(MICROPOLIS_HEADER_SIZE, MICROPOLIS_PAYLOAD_SIZE))) {
_checksumType = MicropolisDecoderProto::MZOS;
std::cout << "Note: MZOS checksum detected." << std::endl;
}
}
/* If not specified, automatically determine the checksum type.
* Once the checksum type is determined, it will be used for the
* entire disk.
*/
if (_checksumType == MicropolisDecoderProto::AUTO)
{
/* Calculate both standard Micropolis (MDOS, CP/M, OASIS) and MZOS
* checksums */
if (wantChecksum == micropolisChecksum(bytes.slice(1, 2 + 266)))
{
_checksumType = MicropolisDecoderProto::MICROPOLIS;
}
else if (wantChecksum ==
mzosChecksum(bytes.slice(
MICROPOLIS_HEADER_SIZE, MICROPOLIS_PAYLOAD_SIZE)))
{
_checksumType = MicropolisDecoderProto::MZOS;
std::cout << "Note: MZOS checksum detected." << std::endl;
}
}
uint8_t gotChecksum;
uint8_t gotChecksum;
if (_checksumType == MicropolisDecoderProto::MZOS) {
gotChecksum = mzosChecksum(bytes.slice(MICROPOLIS_HEADER_SIZE, MICROPOLIS_PAYLOAD_SIZE));
} else {
gotChecksum = micropolisChecksum(bytes.slice(1, 2+266));
}
if (_checksumType == MicropolisDecoderProto::MZOS)
{
gotChecksum = mzosChecksum(
bytes.slice(MICROPOLIS_HEADER_SIZE, MICROPOLIS_PAYLOAD_SIZE));
}
else
{
gotChecksum = micropolisChecksum(bytes.slice(1, 2 + 266));
}
br.read(5); /* 4 byte ECC and ECC-present flag */
br.read(5); /* 4 byte ECC and ECC-present flag */
if (_config.sector_output_size() == MICROPOLIS_PAYLOAD_SIZE)
_sector->data = data;
else if (_config.sector_output_size() == MICROPOLIS_ENCODED_SECTOR_SIZE)
_sector->data = bytes;
else
Error() << "Sector output size may only be 256 or 275";
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
if (_config.sector_output_size() == MICROPOLIS_PAYLOAD_SIZE)
_sector->data = data;
else if (_config.sector_output_size() == MICROPOLIS_ENCODED_SECTOR_SIZE)
_sector->data = bytes;
else
error("Sector output size may only be 256 or 275");
_sector->status =
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
private:
const MicropolisDecoderProto& _config;
MicropolisDecoderProto_ChecksumType _checksumType; /* -1 = auto, 1 = Micropolis, 2=MZOS */
const MicropolisDecoderProto& _config;
MicropolisDecoderProto_ChecksumType
_checksumType; /* -1 = auto, 1 = Micropolis, 2=MZOS */
};
std::unique_ptr<Decoder> createMicropolisDecoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new MicropolisDecoder(config));
return std::unique_ptr<Decoder>(new MicropolisDecoder(config));
}

View File

@@ -12,7 +12,7 @@ static void write_sector(std::vector<bool>& bits,
{
if ((sector->data.size() != 256) &&
(sector->data.size() != MICROPOLIS_ENCODED_SECTOR_SIZE))
Error() << "unsupported sector size --- you must pick 256 or 275";
error("unsupported sector size --- you must pick 256 or 275");
int fullSectorSize = 40 + MICROPOLIS_ENCODED_SECTOR_SIZE + 40 + 35;
auto fullSector = std::make_shared<std::vector<uint8_t>>();
@@ -24,8 +24,9 @@ static void write_sector(std::vector<bool>& bits,
if (sector->data.size() == MICROPOLIS_ENCODED_SECTOR_SIZE)
{
if (sector->data[0] != 0xFF)
Error() << "275 byte sector doesn't start with sync byte 0xFF. "
"Corrupted sector";
error(
"275 byte sector doesn't start with sync byte 0xFF. "
"Corrupted sector");
uint8_t wantChecksum = sector->data[1 + 2 + 266];
uint8_t gotChecksum =
micropolisChecksum(sector->data.slice(1, 2 + 266));
@@ -57,7 +58,7 @@ static void write_sector(std::vector<bool>& bits,
fullSector->push_back(0);
if (fullSector->size() != fullSectorSize)
Error() << "sector mismatched length";
error("sector mismatched length");
bool lastBit = false;
encodeMfm(bits, cursor, fullSector, lastBit);
/* filler */
@@ -91,12 +92,11 @@ public:
write_sector(bits, cursor, sectorData);
if (cursor != bits.size())
Error() << "track data mismatched length";
error("track data mismatched length");
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits,
calculatePhysicalClockPeriod(
_config.clock_period_us() * 1e3,
calculatePhysicalClockPeriod(_config.clock_period_us() * 1e3,
_config.rotational_period_ms() * 1e6));
return fluxmap;
}
@@ -105,8 +105,7 @@ private:
const MicropolisEncoderProto& _config;
};
std::unique_ptr<Encoder> createMicropolisEncoder(
const EncoderProto& config)
std::unique_ptr<Encoder> createMicropolisEncoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new MicropolisEncoder(config));
}

View File

@@ -26,52 +26,51 @@ const FluxPattern ID_PATTERN(32, 0xaaaaffaf);
class MxDecoder : public Decoder
{
public:
MxDecoder(const DecoderProto& config):
Decoder(config)
{}
MxDecoder(const DecoderProto& config): Decoder(config) {}
void beginTrack() override
{
_clock = _sector->clock = seekToPattern(ID_PATTERN);
_currentSector = 0;
}
{
_clock = _sector->clock = seekToPattern(ID_PATTERN);
_currentSector = 0;
}
nanoseconds_t advanceToNextRecord() override
{
if (_currentSector == 11)
{
/* That was the last sector on the disk. */
return 0;
}
else
return _clock;
}
{
if (_currentSector == 11)
{
/* That was the last sector on the disk. */
return 0;
}
else
return _clock;
}
void decodeSectorRecord() override
{
/* Skip the ID pattern and track word, which is only present on the
* first sector. We don't trust the track word because some driver
* don't write it correctly. */
{
/* Skip the ID pattern and track word, which is only present on the
* first sector. We don't trust the track word because some driver
* don't write it correctly. */
if (_currentSector == 0)
readRawBits(64);
if (_currentSector == 0)
readRawBits(64);
auto bits = readRawBits((SECTOR_SIZE+2)*16);
auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE+2);
auto bits = readRawBits((SECTOR_SIZE + 2) * 16);
auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE + 2);
uint16_t gotChecksum = 0;
ByteReader br(bytes);
for (int i=0; i<(SECTOR_SIZE/2); i++)
gotChecksum += br.read_be16();
uint16_t wantChecksum = br.read_be16();
uint16_t gotChecksum = 0;
ByteReader br(bytes);
for (int i = 0; i < (SECTOR_SIZE / 2); i++)
gotChecksum += br.read_be16();
uint16_t wantChecksum = br.read_be16();
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalSide = _sector->physicalSide;
_sector->logicalSector = _currentSector;
_sector->data = bytes.slice(0, SECTOR_SIZE).swab();
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
_currentSector++;
}
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalSide = _sector->physicalSide;
_sector->logicalSector = _currentSector;
_sector->data = bytes.slice(0, SECTOR_SIZE).swab();
_sector->status =
(gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
_currentSector++;
}
private:
nanoseconds_t _clock;
@@ -80,7 +79,5 @@ private:
std::unique_ptr<Decoder> createMxDecoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new MxDecoder(config));
return std::unique_ptr<Decoder>(new MxDecoder(config));
}

View File

@@ -22,7 +22,7 @@
#include "fmt/format.h"
#define MFM_ID 0xaaaaaaaaaaaa5545LL
#define FM_ID 0xaaaaaaaaaaaaffefLL
#define FM_ID 0xaaaaaaaaaaaaffefLL
/*
* MFM sectors have 32 bytes of 00's followed by two sync characters,
* specified in the North Star MDS manual as 0xFBFB.
@@ -44,133 +44,143 @@ static const FluxPattern MFM_PATTERN(64, MFM_ID);
*/
static const FluxPattern FM_PATTERN(64, FM_ID);
const FluxMatchers ANY_SECTOR_PATTERN(
{
&MFM_PATTERN,
&FM_PATTERN,
}
);
const FluxMatchers ANY_SECTOR_PATTERN({
&MFM_PATTERN,
&FM_PATTERN,
});
/* Checksum is initially 0.
* For each data byte, XOR with the current checksum.
* Rotate checksum left, carrying bit 7 to bit 0.
*/
uint8_t northstarChecksum(const Bytes& bytes) {
ByteReader br(bytes);
uint8_t checksum = 0;
uint8_t northstarChecksum(const Bytes& bytes)
{
ByteReader br(bytes);
uint8_t checksum = 0;
while (!br.eof()) {
checksum ^= br.read_8();
checksum = ((checksum << 1) | ((checksum >> 7)));
}
while (!br.eof())
{
checksum ^= br.read_8();
checksum = ((checksum << 1) | ((checksum >> 7)));
}
return checksum;
return checksum;
}
class NorthstarDecoder : public Decoder
{
public:
NorthstarDecoder(const DecoderProto& config):
Decoder(config),
_config(config.northstar())
{}
NorthstarDecoder(const DecoderProto& config):
Decoder(config),
_config(config.northstar())
{
}
/* Search for FM or MFM sector record */
nanoseconds_t advanceToNextRecord() override
{
nanoseconds_t now = tell().ns();
/* Search for FM or MFM sector record */
nanoseconds_t advanceToNextRecord() override
{
nanoseconds_t now = tell().ns();
/* For all but the first sector, seek to the next sector pulse.
* The first sector does not contain the sector pulse in the fluxmap.
*/
if (now != 0) {
seekToIndexMark();
now = tell().ns();
}
/* For all but the first sector, seek to the next sector pulse.
* The first sector does not contain the sector pulse in the fluxmap.
*/
if (now != 0)
{
seekToIndexMark();
now = tell().ns();
}
/* Discard a possible partial sector at the end of the track.
* This partial sector could be mistaken for a conflicted sector, if
* whatever data read happens to match the checksum of 0, which is
* rare, but has been observed on some disks.
*/
if (now > (getFluxmapDuration() - 21e6)) {
seekToIndexMark();
return 0;
}
/* Discard a possible partial sector at the end of the track.
* This partial sector could be mistaken for a conflicted sector, if
* whatever data read happens to match the checksum of 0, which is
* rare, but has been observed on some disks.
*/
if (now > (getFluxmapDuration() - 21e6))
{
seekToIndexMark();
return 0;
}
int msSinceIndex = std::round(now / 1e6);
int msSinceIndex = std::round(now / 1e6);
/* Note that the seekToPattern ignores the sector pulses, so if
* a sector is not found for some reason, the seek will advance
* past one or more sector pulses. For this reason, calculate
* _hardSectorId after the sector header is found.
*/
nanoseconds_t clock = seekToPattern(ANY_SECTOR_PATTERN);
_sector->headerStartTime = tell().ns();
/* Note that the seekToPattern ignores the sector pulses, so if
* a sector is not found for some reason, the seek will advance
* past one or more sector pulses. For this reason, calculate
* _hardSectorId after the sector header is found.
*/
nanoseconds_t clock = seekToPattern(ANY_SECTOR_PATTERN);
_sector->headerStartTime = tell().ns();
/* Discard a possible partial sector. */
if (_sector->headerStartTime > (getFluxmapDuration() - 21e6)) {
return 0;
}
/* Discard a possible partial sector. */
if (_sector->headerStartTime > (getFluxmapDuration() - 21e6))
{
return 0;
}
int sectorFoundTimeRaw = std::round(_sector->headerStartTime / 1e6);
int sectorFoundTime;
int sectorFoundTimeRaw = std::round(_sector->headerStartTime / 1e6);
int sectorFoundTime;
/* Round time to the nearest 20ms */
if ((sectorFoundTimeRaw % 20) < 10) {
sectorFoundTime = (sectorFoundTimeRaw / 20) * 20;
}
else {
sectorFoundTime = ((sectorFoundTimeRaw + 20) / 20) * 20;
}
/* Round time to the nearest 20ms */
if ((sectorFoundTimeRaw % 20) < 10)
{
sectorFoundTime = (sectorFoundTimeRaw / 20) * 20;
}
else
{
sectorFoundTime = ((sectorFoundTimeRaw + 20) / 20) * 20;
}
/* Calculate the sector ID based on time since the index */
_hardSectorId = (sectorFoundTime / 20) % 10;
/* Calculate the sector ID based on time since the index */
_hardSectorId = (sectorFoundTime / 20) % 10;
return clock;
}
return clock;
}
void decodeSectorRecord() override
{
uint64_t id = toBytes(readRawBits(64)).reader().read_be64();
unsigned recordSize, payloadSize, headerSize;
void decodeSectorRecord() override
{
uint64_t id = toBytes(readRawBits(64)).reader().read_be64();
unsigned recordSize, payloadSize, headerSize;
if (id == MFM_ID) {
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_DD;
payloadSize = NORTHSTAR_PAYLOAD_SIZE_DD;
headerSize = NORTHSTAR_HEADER_SIZE_DD;
}
else {
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_SD;
payloadSize = NORTHSTAR_PAYLOAD_SIZE_SD;
headerSize = NORTHSTAR_HEADER_SIZE_SD;
}
if (id == MFM_ID)
{
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_DD;
payloadSize = NORTHSTAR_PAYLOAD_SIZE_DD;
headerSize = NORTHSTAR_HEADER_SIZE_DD;
}
else
{
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_SD;
payloadSize = NORTHSTAR_PAYLOAD_SIZE_SD;
headerSize = NORTHSTAR_HEADER_SIZE_SD;
}
auto rawbits = readRawBits(recordSize * 16);
auto bytes = decodeFmMfm(rawbits).slice(0, recordSize);
ByteReader br(bytes);
auto rawbits = readRawBits(recordSize * 16);
auto bytes = decodeFmMfm(rawbits).slice(0, recordSize);
ByteReader br(bytes);
_sector->logicalSide = _sector->physicalSide;
_sector->logicalSector = _hardSectorId;
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalSide = _sector->physicalSide;
_sector->logicalSector = _hardSectorId;
_sector->logicalTrack = _sector->physicalTrack;
if (headerSize == NORTHSTAR_HEADER_SIZE_DD) {
br.read_8(); /* MFM second Sync char, usually 0xFB */
}
if (headerSize == NORTHSTAR_HEADER_SIZE_DD)
{
br.read_8(); /* MFM second Sync char, usually 0xFB */
}
_sector->data = br.read(payloadSize);
uint8_t wantChecksum = br.read_8();
uint8_t gotChecksum = northstarChecksum(bytes.slice(headerSize - 1, payloadSize));
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
_sector->data = br.read(payloadSize);
uint8_t wantChecksum = br.read_8();
uint8_t gotChecksum =
northstarChecksum(bytes.slice(headerSize - 1, payloadSize));
_sector->status =
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
private:
const NorthstarDecoderProto& _config;
uint8_t _hardSectorId;
const NorthstarDecoderProto& _config;
uint8_t _hardSectorId;
};
std::unique_ptr<Decoder> createNorthstarDecoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new NorthstarDecoder(config));
return std::unique_ptr<Decoder>(new NorthstarDecoder(config));
}

View File

@@ -49,7 +49,7 @@ static void write_sector(std::vector<bool>& bits,
doubleDensity = true;
break;
default:
Error() << "unsupported sector size --- you must pick 256 or 512";
error("unsupported sector size --- you must pick 256 or 512");
break;
}
@@ -96,9 +96,10 @@ static void write_sector(std::vector<bool>& bits,
fullSector->push_back(GAP2_FILL_BYTE);
if (fullSector->size() != fullSectorSize)
Error() << "sector mismatched length (" << sector->data.size()
<< ") expected: " << fullSector->size() << " got "
<< fullSectorSize;
error("sector mismatched length ({}); expected {}, got {}",
sector->data.size(),
fullSector->size(),
fullSectorSize);
}
else
{
@@ -148,7 +149,7 @@ public:
write_sector(bits, cursor, sectorData);
if (cursor > bits.size())
Error() << "track data overrun";
error("track data overrun");
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits,
@@ -161,8 +162,7 @@ private:
const NorthstarEncoderProto& _config;
};
std::unique_ptr<Encoder> createNorthstarEncoder(
const EncoderProto& config)
std::unique_ptr<Encoder> createNorthstarEncoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new NorthstarEncoder(config));
}

View File

@@ -29,28 +29,23 @@ static const FluxPattern SECTOR_PATTERN(64, 0xed55555555555555LL);
class RolandD20Decoder : public Decoder
{
public:
RolandD20Decoder(const DecoderProto& config):
Decoder(config)
{}
RolandD20Decoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(SECTOR_PATTERN);
}
{
return seekToPattern(SECTOR_PATTERN);
}
void decodeSectorRecord() override
{
auto rawbits = readRawBits(256);
const auto& bytes = decodeFmMfm(rawbits);
fmt::print("{} ", _sector->clock);
hexdump(std::cout, bytes);
}
{
auto rawbits = readRawBits(256);
const auto& bytes = decodeFmMfm(rawbits);
fmt::print("{} ", _sector->clock);
hexdump(std::cout, bytes);
}
};
std::unique_ptr<Decoder> createRolandD20Decoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new RolandD20Decoder(config));
return std::unique_ptr<Decoder>(new RolandD20Decoder(config));
}

View File

@@ -38,61 +38,63 @@ const FluxPattern SECTOR_RECORD_PATTERN(32, 0x11112244);
const uint16_t DATA_ID = 0x550b;
const FluxPattern DATA_RECORD_PATTERN(32, 0x11112245);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
const FluxMatchers ANY_RECORD_PATTERN(
{&SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN});
class Tids990Decoder : public Decoder
{
public:
Tids990Decoder(const DecoderProto& config):
Decoder(config)
{}
Tids990Decoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE*16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE);
{
auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE * 16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE);
ByteReader br(bytes);
if (br.read_be16() != SECTOR_ID)
return;
ByteReader br(bytes);
if (br.read_be16() != SECTOR_ID)
return;
uint16_t gotChecksum = crc16(CCITT_POLY, bytes.slice(1, TIDS990_SECTOR_RECORD_SIZE-3));
uint16_t gotChecksum =
crc16(CCITT_POLY, bytes.slice(1, TIDS990_SECTOR_RECORD_SIZE - 3));
_sector->logicalSide = br.read_8() >> 3;
_sector->logicalTrack = br.read_8();
br.read_8(); /* number of sectors per track */
_sector->logicalSector = br.read_8();
br.read_be16(); /* sector size */
uint16_t wantChecksum = br.read_be16();
_sector->logicalSide = br.read_8() >> 3;
_sector->logicalTrack = br.read_8();
br.read_8(); /* number of sectors per track */
_sector->logicalSector = br.read_8();
br.read_be16(); /* sector size */
uint16_t wantChecksum = br.read_be16();
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
}
if (wantChecksum == gotChecksum)
_sector->status =
Sector::DATA_MISSING; /* correct but unintuitive */
}
void decodeDataRecord() override
{
auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE*16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE);
void decodeDataRecord() override
{
auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE * 16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE);
ByteReader br(bytes);
if (br.read_be16() != DATA_ID)
return;
ByteReader br(bytes);
if (br.read_be16() != DATA_ID)
return;
uint16_t gotChecksum = crc16(CCITT_POLY, bytes.slice(1, TIDS990_DATA_RECORD_SIZE-3));
uint16_t gotChecksum =
crc16(CCITT_POLY, bytes.slice(1, TIDS990_DATA_RECORD_SIZE - 3));
_sector->data = br.read(TIDS990_PAYLOAD_SIZE);
uint16_t wantChecksum = br.read_be16();
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
_sector->data = br.read(TIDS990_PAYLOAD_SIZE);
uint16_t wantChecksum = br.read_be16();
_sector->status =
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createTids990Decoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new Tids990Decoder(config));
return std::unique_ptr<Decoder>(new Tids990Decoder(config));
}

View File

@@ -127,14 +127,14 @@ public:
}
if (_cursor >= _bits.size())
Error() << "track data overrun";
error("track data overrun");
while (_cursor < _bits.size())
writeBytes(1, 0x55);
auto fluxmap = std::make_unique<Fluxmap>();
fluxmap->appendBits(_bits,
calculatePhysicalClockPeriod(clockRateUs * 1e3,
_config.rotational_period_ms() * 1e6));
calculatePhysicalClockPeriod(
clockRateUs * 1e3, _config.rotational_period_ms() * 1e6));
return fluxmap;
}
@@ -145,8 +145,7 @@ private:
bool _lastBit;
};
std::unique_ptr<Encoder> createTids990Encoder(
const EncoderProto& config)
std::unique_ptr<Encoder> createTids990Encoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new Tids990Encoder(config));
}

View File

@@ -13,16 +13,18 @@
const FluxPattern SECTOR_RECORD_PATTERN(32, VICTOR9K_SECTOR_RECORD);
const FluxPattern DATA_RECORD_PATTERN(32, VICTOR9K_DATA_RECORD);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
const FluxMatchers ANY_RECORD_PATTERN(
{&SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN});
static int decode_data_gcr(uint8_t gcr)
{
switch (gcr)
{
#define GCR_ENTRY(gcr, data) \
case gcr: return data;
#include "data_gcr.h"
#undef GCR_ENTRY
#define GCR_ENTRY(gcr, data) \
case gcr: \
return data;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
@@ -37,11 +39,11 @@ static Bytes decode(const std::vector<bool>& bits)
while (ii != bits.end())
{
uint8_t inputfifo = 0;
for (size_t i=0; i<5; i++)
for (size_t i = 0; i < 5; i++)
{
if (ii == bits.end())
break;
inputfifo = (inputfifo<<1) | *ii++;
inputfifo = (inputfifo << 1) | *ii++;
}
uint8_t decoded = decode_data_gcr(inputfifo);
@@ -55,63 +57,62 @@ static Bytes decode(const std::vector<bool>& bits)
class Victor9kDecoder : public Decoder
{
public:
Victor9kDecoder(const DecoderProto& config):
Decoder(config)
{}
Victor9kDecoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
/* Check the ID. */
{
/* Check the ID. */
if (readRaw32() != VICTOR9K_SECTOR_RECORD)
return;
if (readRaw32() != VICTOR9K_SECTOR_RECORD)
return;
/* Read header. */
/* Read header. */
auto bytes = decode(readRawBits(3*10)).slice(0, 3);
auto bytes = decode(readRawBits(3 * 10)).slice(0, 3);
uint8_t rawTrack = bytes[0];
_sector->logicalSector = bytes[1];
uint8_t gotChecksum = bytes[2];
uint8_t rawTrack = bytes[0];
_sector->logicalSector = bytes[1];
uint8_t gotChecksum = bytes[2];
_sector->logicalTrack = rawTrack & 0x7f;
_sector->logicalSide = rawTrack >> 7;
uint8_t wantChecksum = bytes[0] + bytes[1];
if ((_sector->logicalSector > 20) || (_sector->logicalTrack > 85) || (_sector->logicalSide > 1))
return;
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
_sector->logicalTrack = rawTrack & 0x7f;
_sector->logicalSide = rawTrack >> 7;
uint8_t wantChecksum = bytes[0] + bytes[1];
if ((_sector->logicalSector > 20) || (_sector->logicalTrack > 85) ||
(_sector->logicalSide > 1))
return;
if (wantChecksum == gotChecksum)
_sector->status =
Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord() override
{
/* Check the ID. */
{
/* Check the ID. */
if (readRaw32() != VICTOR9K_DATA_RECORD)
return;
if (readRaw32() != VICTOR9K_DATA_RECORD)
return;
/* Read data. */
/* Read data. */
auto bytes = decode(readRawBits((VICTOR9K_SECTOR_LENGTH+4)*10))
.slice(0, VICTOR9K_SECTOR_LENGTH+4);
ByteReader br(bytes);
auto bytes = decode(readRawBits((VICTOR9K_SECTOR_LENGTH + 4) * 10))
.slice(0, VICTOR9K_SECTOR_LENGTH + 4);
ByteReader br(bytes);
_sector->data = br.read(VICTOR9K_SECTOR_LENGTH);
uint16_t gotChecksum = sumBytes(_sector->data);
uint16_t wantChecksum = br.read_le16();
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
_sector->data = br.read(VICTOR9K_SECTOR_LENGTH);
uint16_t gotChecksum = sumBytes(_sector->data);
uint16_t wantChecksum = br.read_le16();
_sector->status =
(gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createVictor9kDecoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new Victor9kDecoder(config));
return std::unique_ptr<Decoder>(new Victor9kDecoder(config));
}

View File

@@ -169,14 +169,15 @@ public:
const Image& image) override
{
Victor9kEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, trackInfo->logicalTrack, trackInfo->logicalSide);
getTrackFormat(
trackdata, trackInfo->logicalTrack, trackInfo->logicalSide);
unsigned bitsPerRevolution = (trackdata.rotational_period_ms() * 1e3) /
trackdata.clock_period_us();
std::vector<bool> bits(bitsPerRevolution);
nanoseconds_t clockPeriod = calculatePhysicalClockPeriod(
trackdata.clock_period_us() * 1e3,
trackdata.rotational_period_ms() * 1e6);
nanoseconds_t clockPeriod =
calculatePhysicalClockPeriod(trackdata.clock_period_us() * 1e3,
trackdata.rotational_period_ms() * 1e6);
unsigned cursor = 0;
fillBitmapTo(bits,
@@ -189,8 +190,7 @@ public:
write_sector(bits, cursor, trackdata, *sector);
if (cursor >= bits.size())
Error() << fmt::format(
"track data overrun by {} bits", cursor - bits.size());
error("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), {true, false});
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
@@ -202,8 +202,7 @@ private:
const Victor9kEncoderProto& _config;
};
std::unique_ptr<Encoder> createVictor9kEncoder(
const EncoderProto& config)
std::unique_ptr<Encoder> createVictor9kEncoder(const EncoderProto& config)
{
return std::unique_ptr<Encoder>(new Victor9kEncoder(config));
}

View File

@@ -16,42 +16,40 @@ static const FluxPattern SECTOR_START_PATTERN(16, 0xaaab);
class ZilogMczDecoder : public Decoder
{
public:
ZilogMczDecoder(const DecoderProto& config):
Decoder(config)
{}
ZilogMczDecoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
seekToIndexMark();
return seekToPattern(SECTOR_START_PATTERN);
}
{
seekToIndexMark();
return seekToPattern(SECTOR_START_PATTERN);
}
void decodeSectorRecord() override
{
readRawBits(14);
{
readRawBits(14);
auto rawbits = readRawBits(140*16);
auto bytes = decodeFmMfm(rawbits).slice(0, 140);
ByteReader br(bytes);
auto rawbits = readRawBits(140 * 16);
auto bytes = decodeFmMfm(rawbits).slice(0, 140);
ByteReader br(bytes);
_sector->logicalSector = br.read_8() & 0x1f;
_sector->logicalSide = 0;
_sector->logicalTrack = br.read_8() & 0x7f;
if (_sector->logicalSector > 31)
return;
if (_sector->logicalTrack > 80)
return;
_sector->logicalSector = br.read_8() & 0x1f;
_sector->logicalSide = 0;
_sector->logicalTrack = br.read_8() & 0x7f;
if (_sector->logicalSector > 31)
return;
if (_sector->logicalTrack > 80)
return;
_sector->data = br.read(132);
uint16_t wantChecksum = br.read_be16();
uint16_t gotChecksum = crc16(MODBUS_POLY, 0x0000, bytes.slice(0, 134));
_sector->data = br.read(132);
uint16_t wantChecksum = br.read_be16();
uint16_t gotChecksum = crc16(MODBUS_POLY, 0x0000, bytes.slice(0, 134));
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
_sector->status =
(wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createZilogMczDecoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new ZilogMczDecoder(config));
return std::unique_ptr<Decoder>(new ZilogMczDecoder(config));
}

View File

@@ -1,6 +1,5 @@
#include "globals.h"
#include "flags.h"
#include "fmt/format.h"
#include "dep/agg/include/agg2d.h"
#include "dep/stb/stb_image_write.h"
#include "utils.h"
@@ -13,30 +12,31 @@ Bitmap::Bitmap(const std::string filename, unsigned width, unsigned height):
width(width),
height(height),
initialised(true)
{}
{
}
Agg2D& Bitmap::painter()
{
if (!_painter)
{
_bitmap.resize(width * height * 4, 255);
_painter.reset(new Agg2D());
_painter->attach(&_bitmap[0], width, height, width*4);
}
return *_painter;
if (!_painter)
{
_bitmap.resize(width * height * 4, 255);
_painter.reset(new Agg2D());
_painter->attach(&_bitmap[0], width, height, width * 4);
}
return *_painter;
}
void Bitmap::save()
{
if (endsWith(filename, ".png"))
stbi_write_png(filename.c_str(), width, height, 4, &_bitmap[0], width*4);
else if (endsWith(filename, ".bmp"))
stbi_write_bmp(filename.c_str(), width, height, 4, &_bitmap[0]);
else if (endsWith(filename, ".tga"))
stbi_write_tga(filename.c_str(), width, height, 4, &_bitmap[0]);
else if (endsWith(filename, ".jpg"))
stbi_write_jpg(filename.c_str(), width, height, 4, &_bitmap[0], 80);
else
Error() << "don't know how to write that image format";
if (endsWith(filename, ".png"))
stbi_write_png(
filename.c_str(), width, height, 4, &_bitmap[0], width * 4);
else if (endsWith(filename, ".bmp"))
stbi_write_bmp(filename.c_str(), width, height, 4, &_bitmap[0]);
else if (endsWith(filename, ".tga"))
stbi_write_tga(filename.c_str(), width, height, 4, &_bitmap[0]);
else if (endsWith(filename, ".jpg"))
stbi_write_jpg(filename.c_str(), width, height, 4, &_bitmap[0], 80);
else
error("don't know how to write that image format");
}

View File

@@ -1,6 +1,5 @@
#include "globals.h"
#include "bytes.h"
#include "fmt/format.h"
#include <fstream>
#include <zlib.h>
@@ -9,69 +8,70 @@ static std::shared_ptr<std::vector<uint8_t>> createVector(unsigned size)
return std::make_shared<std::vector<uint8_t>>(size);
}
static std::shared_ptr<std::vector<uint8_t>> createVector(const uint8_t* ptr, unsigned size)
static std::shared_ptr<std::vector<uint8_t>> createVector(
const uint8_t* ptr, unsigned size)
{
auto vector = std::make_shared<std::vector<uint8_t>>(size);
std::uninitialized_copy(ptr, ptr+size, vector->begin());
std::uninitialized_copy(ptr, ptr + size, vector->begin());
return vector;
}
static std::shared_ptr<std::vector<uint8_t>> createVector(std::initializer_list<uint8_t> data)
static std::shared_ptr<std::vector<uint8_t>> createVector(
std::initializer_list<uint8_t> data)
{
auto vector = std::make_shared<std::vector<uint8_t>>(data.size());
std::uninitialized_copy(data.begin(), data.end(), vector->begin());
return vector;
}
Bytes::Bytes():
_data(createVector(0)),
_low(0),
_high(0)
{}
Bytes::Bytes(): _data(createVector(0)), _low(0), _high(0) {}
Bytes::Bytes(unsigned size):
_data(createVector(size)),
_low(0),
_high(size)
{}
Bytes::Bytes(unsigned size): _data(createVector(size)), _low(0), _high(size) {}
Bytes::Bytes(const uint8_t* ptr, size_t len):
_data(createVector(ptr, len)),
_low(0),
_high(len)
{}
{
}
Bytes::Bytes(const std::string& s):
_data(createVector((const uint8_t*)&s[0], s.size())),
_low(0),
_high(s.size())
{}
_data(createVector((const uint8_t*)&s[0], s.size())),
_low(0),
_high(s.size())
{
}
Bytes::Bytes(const char* s):
_data(createVector((const uint8_t*)s, strlen(s))),
_low(0),
_high(strlen(s))
{}
_data(createVector((const uint8_t*)s, strlen(s))),
_low(0),
_high(strlen(s))
{
}
Bytes::Bytes(std::initializer_list<uint8_t> data):
_data(createVector(data)),
_low(0),
_high(data.size())
{}
{
}
Bytes::Bytes(std::shared_ptr<std::vector<uint8_t>> data):
_data(data),
_low(0),
_high(data->size())
{}
{
}
Bytes::Bytes(std::shared_ptr<std::vector<uint8_t>> data, unsigned start, unsigned end):
Bytes::Bytes(
std::shared_ptr<std::vector<uint8_t>> data, unsigned start, unsigned end):
_data(data),
_low(start),
_high(end)
{}
{
}
Bytes* Bytes::operator = (const Bytes& other)
Bytes* Bytes::operator=(const Bytes& other)
{
_data = other._data;
_low = other._low;
@@ -102,7 +102,7 @@ void Bytes::adjustBounds(unsigned pos)
checkWritable();
if (pos >= _high)
{
_high = pos+1;
_high = pos + 1;
_data->resize(_high);
}
}
@@ -119,14 +119,14 @@ Bytes& Bytes::resize(unsigned size)
return *this;
}
const uint8_t& Bytes::operator [] (unsigned pos) const
const uint8_t& Bytes::operator[](unsigned pos) const
{
pos += _low;
boundsCheck(pos);
return (*_data)[pos];
}
uint8_t& Bytes::operator [] (unsigned pos)
uint8_t& Bytes::operator[](unsigned pos)
{
checkWritable();
pos += _low;
@@ -136,15 +136,15 @@ uint8_t& Bytes::operator [] (unsigned pos)
Bytes Bytes::readFromFile(const std::string& filename)
{
Bytes bytes;
ByteWriter bw(bytes);
Bytes bytes;
ByteWriter bw(bytes);
std::ifstream f(filename);
if (!f)
Error() << fmt::format("cannot open '{}': {}", filename, strerror(errno));
bw += f;
std::ifstream f(filename);
if (!f)
error("cannot open '{}': {}", filename, strerror(errno));
bw += f;
return bytes;
return bytes;
}
Bytes Bytes::slice(unsigned start, unsigned len) const
@@ -160,7 +160,8 @@ Bytes Bytes::slice(unsigned start, unsigned len) const
{
/* Can't share the buffer, as we need to zero-pad the end. */
Bytes b(len);
std::uninitialized_copy(_data->cbegin()+start, _data->cbegin()+_high, b._data->begin());
std::uninitialized_copy(
_data->cbegin() + start, _data->cbegin() + _high, b._data->begin());
return b;
}
else
@@ -173,42 +174,42 @@ Bytes Bytes::slice(unsigned start, unsigned len) const
Bytes Bytes::slice(unsigned start) const
{
int len = 0;
if (start < size())
len = size() - start;
return slice(start, len);
int len = 0;
if (start < size())
len = size() - start;
return slice(start, len);
}
std::vector<Bytes> Bytes::split(uint8_t separator) const
{
std::vector<Bytes> vector;
std::vector<Bytes> vector;
int lastEnd = 0;
for (int i=0; i<size(); i++)
{
if ((*this)[i] == separator)
{
vector.push_back(this->slice(lastEnd, i-lastEnd));
lastEnd = i + 1;
}
}
vector.push_back(this->slice(lastEnd));
int lastEnd = 0;
for (int i = 0; i < size(); i++)
{
if ((*this)[i] == separator)
{
vector.push_back(this->slice(lastEnd, i - lastEnd));
lastEnd = i + 1;
}
}
vector.push_back(this->slice(lastEnd));
return vector;
return vector;
}
std::vector<bool> Bytes::toBits() const
{
std::vector<bool> bits;
for (uint8_t byte : *this)
{
for (int i=0; i<8; i++)
{
bits.push_back(byte & 0x80);
byte <<= 1;
}
}
return bits;
std::vector<bool> bits;
for (uint8_t byte : *this)
{
for (int i = 0; i < 8; i++)
{
bits.push_back(byte & 0x80);
byte <<= 1;
}
}
return bits;
}
Bytes Bytes::reverseBits() const
@@ -221,7 +222,7 @@ Bytes Bytes::reverseBits() const
return output;
}
Bytes Bytes::operator + (const Bytes& other)
Bytes Bytes::operator+(const Bytes& other)
{
Bytes output;
ByteWriter bw(output);
@@ -230,7 +231,7 @@ Bytes Bytes::operator + (const Bytes& other)
return output;
}
Bytes Bytes::operator * (size_t count)
Bytes Bytes::operator*(size_t count)
{
Bytes output;
ByteWriter bw(output);
@@ -239,15 +240,13 @@ Bytes Bytes::operator * (size_t count)
return output;
}
uint8_t toByte(
std::vector<bool>::const_iterator start,
uint8_t toByte(std::vector<bool>::const_iterator start,
std::vector<bool>::const_iterator end)
{
return toBytes(start, end)[0];
}
Bytes toBytes(
std::vector<bool>::const_iterator start,
Bytes toBytes(std::vector<bool>::const_iterator start,
std::vector<bool>::const_iterator end)
{
Bytes bytes;
@@ -283,7 +282,7 @@ Bytes Bytes::compress() const
uLongf destsize = compressBound(size());
Bytes dest(destsize);
if (::compress(dest.begin(), &destsize, cbegin(), size()) != Z_OK)
Error() << "error compressing data";
error("error compressing data");
dest.resize(destsize);
return dest;
}
@@ -292,12 +291,12 @@ Bytes Bytes::decompress() const
{
Bytes output;
ByteWriter bw(output);
Bytes outputBuffer(1024*1024);
Bytes outputBuffer(1024 * 1024);
z_stream stream = {};
inflateInit(&stream);
stream.avail_in = size();
stream.next_in = (uint8_t*) cbegin();
stream.next_in = (uint8_t*)cbegin();
int ret;
do
@@ -308,12 +307,11 @@ Bytes Bytes::decompress() const
if (ret == Z_BUF_ERROR)
ret = Z_OK;
if ((ret != Z_OK) && (ret != Z_STREAM_END))
Error() << fmt::format(
"failed to decompress data: {}", stream.msg ? stream.msg : "(unknown error)");
error("failed to decompress data: {}",
stream.msg ? stream.msg : "(unknown error)");
bw += outputBuffer.slice(0, outputBuffer.size() - stream.avail_out);
}
while (ret != Z_STREAM_END);
} while (ret != Z_STREAM_END);
inflateEnd(&stream);
return output;
@@ -323,15 +321,15 @@ void Bytes::writeToFile(const std::string& filename) const
{
std::ofstream f(filename, std::ios::out | std::ios::binary);
if (!f.is_open())
Error() << fmt::format("cannot open output file '{}'", filename);
error("cannot open output file '{}'", filename);
f.write((const char*) cbegin(), size());
f.write((const char*)cbegin(), size());
f.close();
}
void Bytes::writeTo(std::ostream& stream) const
{
stream.write((const char*) cbegin(), size());
stream.write((const char*)cbegin(), size());
}
ByteReader Bytes::reader() const
@@ -346,19 +344,19 @@ ByteWriter Bytes::writer()
uint64_t ByteReader::read_be48()
{
return ((uint64_t)read_be16() << 32) | read_be32();
return ((uint64_t)read_be16() << 32) | read_be32();
}
uint64_t ByteReader::read_be64()
{
return ((uint64_t)read_be32() << 32) | read_be32();
return ((uint64_t)read_be32() << 32) | read_be32();
}
ByteWriter& ByteWriter::operator +=(std::istream& stream)
ByteWriter& ByteWriter::operator+=(std::istream& stream)
{
Bytes buffer(4096);
while (stream.read((char*) buffer.begin(), buffer.size()))
while (stream.read((char*)buffer.begin(), buffer.size()))
this->append(buffer);
this->append(buffer.slice(0, stream.gcount()));
return *this;
@@ -366,18 +364,18 @@ ByteWriter& ByteWriter::operator +=(std::istream& stream)
void BitWriter::push(uint32_t bits, size_t size)
{
bits <<= 32-size;
bits <<= 32 - size;
while (size-- != 0)
{
_fifo = (_fifo<<1) | (bits >> 31);
_fifo = (_fifo << 1) | (bits >> 31);
_bitcount++;
bits <<= 1;
if (_bitcount == 8)
{
_bw.write_8(_fifo);
_bitcount = 0;
_fifo = 0;
_fifo = 0;
}
}
}
@@ -393,18 +391,18 @@ void BitWriter::flush()
bool BitReader::eof()
{
return (_bitcount == 0) && _br.eof();
return (_bitcount == 0) && _br.eof();
}
bool BitReader::get()
{
if (_bitcount == 0)
_fifo = _br.read_8();
if (_bitcount == 0)
_fifo = _br.read_8();
bool bit = _fifo & 0x80;
_fifo <<= 1;
_bitcount = (_bitcount+1) & 7;
return bit;
bool bit = _fifo & 0x80;
_fifo <<= 1;
_bitcount = (_bitcount + 1) & 7;
return bit;
}
std::vector<bool> reverseBits(const std::vector<bool>& bits)

View File

@@ -3,108 +3,107 @@
#include "crc.h"
template <class T>
T reflect(T bin, unsigned width = sizeof(T)*8)
T reflect(T bin, unsigned width = sizeof(T) * 8)
{
T bout = 0;
while (width--)
{
bout <<= 1;
bout |= (bin & 1);
bin >>= 1;
}
return bout;
T bout = 0;
while (width--)
{
bout <<= 1;
bout |= (bin & 1);
bin >>= 1;
}
return bout;
}
uint64_t generic_crc(const struct crcspec& spec, const Bytes& bytes)
{
uint64_t crc = spec.init;
uint64_t top = 1LL << (spec.width-1);
uint64_t mask = (top<<1) - 1;
uint64_t crc = spec.init;
uint64_t top = 1LL << (spec.width - 1);
uint64_t mask = (top << 1) - 1;
for (uint8_t b : bytes)
{
if (spec.refin)
b = reflect(b);
for (uint8_t b : bytes)
{
if (spec.refin)
b = reflect(b);
for (uint8_t i = 0x80; i != 0; i >>= 1)
{
uint64_t bit = crc & top;
crc <<= 1;
if (b & i)
bit ^= top;
if (bit)
crc ^= spec.poly;
}
}
for (uint8_t i = 0x80; i != 0; i >>= 1)
{
uint64_t bit = crc & top;
crc <<= 1;
if (b & i)
bit ^= top;
if (bit)
crc ^= spec.poly;
}
}
if (spec.refout)
crc = reflect(crc, spec.width);
crc ^= spec.xorout;
return crc & mask;
if (spec.refout)
crc = reflect(crc, spec.width);
crc ^= spec.xorout;
return crc & mask;
}
uint16_t sumBytes(const Bytes& bytes)
{
ByteReader br(bytes);
ByteReader br(bytes);
uint16_t i = 0;
while (!br.eof())
i += br.read_8();
return i;
uint16_t i = 0;
while (!br.eof())
i += br.read_8();
return i;
}
uint8_t xorBytes(const Bytes& bytes)
{
ByteReader br(bytes);
ByteReader br(bytes);
uint8_t i = 0;
while (!br.eof())
i ^= br.read_8();
return i;
uint8_t i = 0;
while (!br.eof())
i ^= br.read_8();
return i;
}
uint16_t crc16(uint16_t poly, uint16_t crc, const Bytes& bytes)
{
ByteReader br(bytes);
ByteReader br(bytes);
while (!br.eof())
{
crc ^= br.read_8() << 8;
for (int i=0; i<8; i++)
crc = (crc & 0x8000) ? ((crc<<1)^poly) : (crc<<1);
}
while (!br.eof())
{
crc ^= br.read_8() << 8;
for (int i = 0; i < 8; i++)
crc = (crc & 0x8000) ? ((crc << 1) ^ poly) : (crc << 1);
}
return crc;
return crc;
}
uint16_t crc16ref(uint16_t poly, uint16_t crc, const Bytes& bytes)
{
ByteReader br(bytes);
ByteReader br(bytes);
while (!br.eof())
{
crc ^= br.read_8();
for (int i=0; i<8; i++)
crc = (crc & 0x0001) ? ((crc>>1)^poly) : (crc>>1);
}
while (!br.eof())
{
crc ^= br.read_8();
for (int i = 0; i < 8; i++)
crc = (crc & 0x0001) ? ((crc >> 1) ^ poly) : (crc >> 1);
}
return crc;
return crc;
}
/* Thanks to user202729 on StackOverflow for miraculously reverse engineering
* this. */
uint32_t crcbrother(const Bytes& bytes)
{
ByteReader br(bytes);
ByteReader br(bytes);
uint32_t crc = br.read_8();
while (!br.eof())
{
for (int i=0; i<8; i++)
crc = (crc & 0x800000) ? ((crc<<1)^BROTHER_POLY) : (crc<<1);
crc ^= br.read_8();
}
uint32_t crc = br.read_8();
while (!br.eof())
{
for (int i = 0; i < 8; i++)
crc = (crc & 0x800000) ? ((crc << 1) ^ BROTHER_POLY) : (crc << 1);
crc ^= br.read_8();
}
return crc & 0xFFFFFF;
return crc & 0xFFFFFF;
}

View File

@@ -4,78 +4,77 @@
std::vector<std::string> CsvReader::readLine()
{
enum state_t
{
UNQUOTED,
QUOTED,
QUOTEDQUOTE
};
enum state_t
{
UNQUOTED,
QUOTED,
QUOTEDQUOTE
};
std::vector<std::string> results;
std::string row;
std::getline(_istream, row);
if (_istream.bad() || _istream.fail())
return results;
std::vector<std::string> results;
std::string row;
std::getline(_istream, row);
if (_istream.bad() || _istream.fail())
return results;
results.push_back("");
state_t state = UNQUOTED;
size_t index = 0;
for (char c : row)
{
switch (state)
{
case UNQUOTED:
switch (c)
{
case ',':
results.push_back("");
index++;
break;
results.push_back("");
state_t state = UNQUOTED;
size_t index = 0;
for (char c : row)
{
switch (state)
{
case UNQUOTED:
switch (c)
{
case ',':
results.push_back("");
index++;
break;
case '"':
state = QUOTED;
break;
case '"':
state = QUOTED;
break;
default:
results[index].push_back(c);
break;
}
break;
default:
results[index].push_back(c);
break;
}
break;
case QUOTED:
switch (c)
{
case '"':
state = QUOTEDQUOTE;
break;
case QUOTED:
switch (c)
{
case '"':
state = QUOTEDQUOTE;
break;
default:
results[index].push_back(c);
break;
}
break;
default:
results[index].push_back(c);
break;
}
break;
case QUOTEDQUOTE:
switch (c)
{
case ',':
results.push_back("");
index++;
state = UNQUOTED;
break;
case QUOTEDQUOTE:
switch (c)
{
case ',':
results.push_back("");
index++;
state = UNQUOTED;
break;
case '"':
results[index].push_back('"');
state = QUOTED;
break;
case '"':
results[index].push_back('"');
state = QUOTED;
break;
default:
state = UNQUOTED;
break;
}
break;
}
}
default:
state = UNQUOTED;
break;
}
break;
}
}
return results;
}

View File

@@ -29,7 +29,6 @@
#include "image.h"
#include "lib/decoders/decoders.pb.h"
#include "lib/layout.h"
#include "fmt/format.h"
#include <numeric>
std::unique_ptr<Decoder> Decoder::create(const DecoderProto& config)
@@ -59,7 +58,7 @@ std::unique_ptr<Decoder> Decoder::create(const DecoderProto& config)
auto decoder = decoders.find(config.format_case());
if (decoder == decoders.end())
Error() << "no decoder specified";
error("no decoder specified");
return (decoder->second)(config);
}

View File

@@ -12,81 +12,82 @@
* than my code.
*/
FluxDecoder::FluxDecoder(FluxmapReader* fmr, nanoseconds_t bitcell,
const DecoderProto& config):
_fmr(fmr),
_pll_phase(config.pll_phase()),
_pll_adjust(config.pll_adjust()),
_flux_scale(config.flux_scale()),
_clock(bitcell),
_clock_centre(bitcell),
_clock_min(bitcell * (1.0 - _pll_adjust)),
_clock_max(bitcell * (1.0 + _pll_adjust)),
_flux(0),
_leading_zeroes(fmr->tell().zeroes)
{}
FluxDecoder::FluxDecoder(
FluxmapReader* fmr, nanoseconds_t bitcell, const DecoderProto& config):
_fmr(fmr),
_pll_phase(config.pll_phase()),
_pll_adjust(config.pll_adjust()),
_flux_scale(config.flux_scale()),
_clock(bitcell),
_clock_centre(bitcell),
_clock_min(bitcell * (1.0 - _pll_adjust)),
_clock_max(bitcell * (1.0 + _pll_adjust)),
_flux(0),
_leading_zeroes(fmr->tell().zeroes)
{
}
bool FluxDecoder::readBit()
{
if (_leading_zeroes > 0)
{
_leading_zeroes--;
return false;
}
else if (_leading_zeroes == 0)
{
_leading_zeroes--;
return true;
}
if (_leading_zeroes > 0)
{
_leading_zeroes--;
return false;
}
else if (_leading_zeroes == 0)
{
_leading_zeroes--;
return true;
}
while (!_fmr->eof() && (_flux < (_clock/2)))
{
_flux += nextFlux() * _flux_scale;;
_clocked_zeroes = 0;
}
while (!_fmr->eof() && (_flux < (_clock / 2)))
{
_flux += nextFlux() * _flux_scale;
;
_clocked_zeroes = 0;
}
_flux -= _clock;
if (_flux >= (_clock/2))
{
_clocked_zeroes++;
_goodbits++;
return false;
}
_flux -= _clock;
if (_flux >= (_clock / 2))
{
_clocked_zeroes++;
_goodbits++;
return false;
}
/* PLL adjustment: change the clock frequency according to the phase
* mismatch */
if (_clocked_zeroes <= 3)
{
/* In sync: adjust base clock */
_clock += _flux * _pll_adjust;
}
else
{
/* Out of sync: adjust the base clock back towards the centre */
/* PLL adjustment: change the clock frequency according to the phase
* mismatch */
if (_clocked_zeroes <= 3)
{
/* In sync: adjust base clock */
_clock += (_clock_centre - _clock) * _pll_adjust;
_clock += _flux * _pll_adjust;
}
else
{
/* Out of sync: adjust the base clock back towards the centre */
/* We require 256 good bits before reporting another sync loss event. */
_clock += (_clock_centre - _clock) * _pll_adjust;
if (_goodbits >= 256)
_sync_lost = true;
_goodbits = 0;
}
/* We require 256 good bits before reporting another sync loss event. */
/* Clamp the clock's adjustment range. */
if (_goodbits >= 256)
_sync_lost = true;
_goodbits = 0;
}
_clock = std::min(std::max(_clock_min, _clock), _clock_max);
/* Clamp the clock's adjustment range. */
/* I'm not sure what this does, but the original comment is:
_clock = std::min(std::max(_clock_min, _clock), _clock_max);
/* I'm not sure what this does, but the original comment is:
* Authentic PLL: Do not snap the timing window to each flux transition
*/
*/
_flux = _flux * (1.0 - _pll_phase);
_goodbits++;
return true;
_goodbits++;
return true;
}
std::vector<bool> FluxDecoder::readBits(unsigned count)
@@ -113,5 +114,5 @@ std::vector<bool> FluxDecoder::readBits(const Fluxmap::Position& until)
nanoseconds_t FluxDecoder::nextFlux()
{
return _fmr->readInterval(_clock_centre) * NS_PER_TICK;
return _fmr->readInterval(_clock_centre) * NS_PER_TICK;
}

View File

@@ -4,18 +4,17 @@
#include "flags.h"
#include "protocol.h"
#include "proto.h"
#include "fmt/format.h"
#include <numeric>
#include <math.h>
#include <strings.h>
FluxmapReader::FluxmapReader(const Fluxmap& fluxmap):
_fluxmap(fluxmap),
_bytes(fluxmap.ptr()),
_size(fluxmap.bytes()),
_config(config.decoder())
_fluxmap(fluxmap),
_bytes(fluxmap.ptr()),
_size(fluxmap.bytes()),
_config(config.decoder())
{
rewind();
rewind();
}
void FluxmapReader::getNextEvent(int& event, unsigned& ticks)
@@ -25,23 +24,23 @@ void FluxmapReader::getNextEvent(int& event, unsigned& ticks)
while (!eof())
{
uint8_t b = _bytes[_pos.bytes++];
ticks += b & 0x3f;
if (!b || (b & (F_BIT_PULSE|F_BIT_INDEX)))
ticks += b & 0x3f;
if (!b || (b & (F_BIT_PULSE | F_BIT_INDEX)))
{
_pos.ticks += ticks;
event = b & 0xc0;
event = b & 0xc0;
return;
}
}
_pos.ticks += ticks;
event = F_EOF;
event = F_EOF;
}
void FluxmapReader::skipToEvent(int event)
{
unsigned ticks;
findEvent(event, ticks);
unsigned ticks;
findEvent(event, ticks);
}
bool FluxmapReader::findEvent(int event, unsigned& ticks)
@@ -51,29 +50,30 @@ bool FluxmapReader::findEvent(int event, unsigned& ticks)
for (;;)
{
unsigned thisTicks;
int thisEvent;
getNextEvent(thisEvent, thisTicks);
ticks += thisTicks;
int thisEvent;
getNextEvent(thisEvent, thisTicks);
ticks += thisTicks;
if (thisEvent == F_EOF)
return false;
if (thisEvent == F_EOF)
return false;
if (eof())
return false;
if ((event == thisEvent) || (event & thisEvent))
return true;
if ((event == thisEvent) || (event & thisEvent))
return true;
}
}
unsigned FluxmapReader::readInterval(nanoseconds_t clock)
{
unsigned thresholdTicks = (clock * _config.pulse_debounce_threshold()) / NS_PER_TICK;
unsigned thresholdTicks =
(clock * _config.pulse_debounce_threshold()) / NS_PER_TICK;
unsigned ticks = 0;
while (ticks <= thresholdTicks)
{
unsigned thisTicks;
if (!findEvent(F_BIT_PULSE, thisTicks))
if (!findEvent(F_BIT_PULSE, thisTicks))
break;
ticks += thisTicks;
}
@@ -93,14 +93,13 @@ static int findLowestSetBit(uint64_t value)
return bit;
}
FluxPattern::FluxPattern(unsigned bits, uint64_t pattern):
_bits(bits)
FluxPattern::FluxPattern(unsigned bits, uint64_t pattern): _bits(bits)
{
const uint64_t TOPBIT = 1ULL << 63;
assert(pattern != 0);
unsigned lowbit = findLowestSetBit(pattern)-1;
unsigned lowbit = findLowestSetBit(pattern) - 1;
pattern <<= 64 - bits;
_highzeroes = 0;
@@ -118,8 +117,7 @@ FluxPattern::FluxPattern(unsigned bits, uint64_t pattern):
{
pattern <<= 1;
interval++;
}
while (!(pattern & TOPBIT));
} while (!(pattern & TOPBIT));
_intervals.push_back(interval);
_length += interval;
}
@@ -134,7 +132,7 @@ FluxPattern::FluxPattern(unsigned bits, uint64_t pattern):
bool FluxPattern::matches(const unsigned* end, FluxMatch& match) const
{
const double clockDecodeThreshold = config.decoder().bit_error_threshold();
const double clockDecodeThreshold = config.decoder().bit_error_threshold();
const unsigned* start = end - _intervals.size();
unsigned candidatelength = std::accumulate(start, end - _lowzero, 0);
if (!candidatelength)
@@ -142,7 +140,7 @@ bool FluxPattern::matches(const unsigned* end, FluxMatch& match) const
match.clock = (double)candidatelength / (double)_length;
unsigned exactIntervals = _intervals.size() - _lowzero;
for (unsigned i=0; i<exactIntervals; i++)
for (unsigned i = 0; i < exactIntervals; i++)
{
double ii = match.clock * (double)_intervals[i];
double ci = (double)start[i];
@@ -166,7 +164,8 @@ bool FluxPattern::matches(const unsigned* end, FluxMatch& match) const
return true;
}
FluxMatchers::FluxMatchers(const std::initializer_list<const FluxMatcher*> matchers):
FluxMatchers::FluxMatchers(
const std::initializer_list<const FluxMatcher*> matchers):
_matchers(matchers)
{
_intervals = 0;
@@ -195,7 +194,7 @@ void FluxmapReader::seek(nanoseconds_t ns)
while (!eof() && (_pos.ticks < ticks))
{
int e;
int e;
unsigned t;
getNextEvent(e, t);
}
@@ -204,7 +203,7 @@ void FluxmapReader::seek(nanoseconds_t ns)
void FluxmapReader::seekToByte(unsigned b)
{
if (b < _pos.bytes)
if (b < _pos.bytes)
{
_pos.ticks = 0;
_pos.bytes = 0;
@@ -212,7 +211,7 @@ void FluxmapReader::seekToByte(unsigned b)
while (!eof() && (_pos.bytes < b))
{
int e;
int e;
unsigned t;
getNextEvent(e, t);
}
@@ -225,13 +224,14 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern)
return seekToPattern(pattern, unused);
}
nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const FluxMatcher*& matching)
nanoseconds_t FluxmapReader::seekToPattern(
const FluxMatcher& pattern, const FluxMatcher*& matching)
{
unsigned intervalCount = pattern.intervals();
std::vector<unsigned> candidates(intervalCount+1);
std::vector<Fluxmap::Position> positions(intervalCount+1);
std::vector<unsigned> candidates(intervalCount + 1);
std::vector<Fluxmap::Position> positions(intervalCount + 1);
for (unsigned i=0; i<=intervalCount; i++)
for (unsigned i = 0; i <= intervalCount; i++)
{
positions[i] = tell();
candidates[i] = 0;
@@ -242,22 +242,21 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu
FluxMatch match;
if (pattern.matches(&*candidates.end(), match))
{
seek(positions[intervalCount-match.intervals]);
seek(positions[intervalCount - match.intervals]);
_pos.zeroes = match.zeroes;
matching = match.matcher;
nanoseconds_t detectedClock = match.clock * NS_PER_TICK;
if (detectedClock > (_config.minimum_clock_us()*1000))
if (detectedClock > (_config.minimum_clock_us() * 1000))
return match.clock * NS_PER_TICK;
}
for (unsigned i=0; i<intervalCount; i++)
for (unsigned i = 0; i < intervalCount; i++)
{
positions[i] = positions[i+1];
candidates[i] = candidates[i+1];
positions[i] = positions[i + 1];
candidates[i] = candidates[i + 1];
}
findEvent(F_BIT_PULSE, candidates[intervalCount]);
findEvent(F_BIT_PULSE, candidates[intervalCount]);
positions[intervalCount] = tell();
}
matching = NULL;
@@ -269,4 +268,3 @@ void FluxmapReader::seekToIndexMark()
skipToEvent(F_BIT_INDEX);
_pos.zeroes = 0;
}

View File

@@ -2,20 +2,20 @@
#include "decoders/decoders.h"
Bytes decodeFmMfm(
std::vector<bool>::const_iterator ii, std::vector<bool>::const_iterator end)
std::vector<bool>::const_iterator ii, std::vector<bool>::const_iterator end)
{
/*
* FM is dumb as rocks, consisting on regular clock pulses with data pulses in
* the gaps. 0x00 is:
*
/*
* FM is dumb as rocks, consisting on regular clock pulses with data pulses
* in the gaps. 0x00 is:
*
* X-X-X-X-X-X-X-X-
*
*
* 0xff is:
*
*
* XXXXXXXXXXXXXXXX
*
* So we just need to extract all the odd bits.
*
*
* MFM and M2FM are slightly more complicated, where the first bit of each
* pair can be either 0 or 1... but the second bit is always the data bit,
* and at this point we simply don't care what the first bit is, so
@@ -33,7 +33,7 @@ Bytes decodeFmMfm(
ii++; /* skip clock bit */
if (ii == end)
break;
fifo = (fifo<<1) | *ii++;
fifo = (fifo << 1) | *ii++;
bitcount++;
if (bitcount == 8)
@@ -45,7 +45,7 @@ Bytes decodeFmMfm(
if (bitcount != 0)
{
fifo <<= 8-bitcount;
fifo <<= 8 - bitcount;
bw.write_8(fifo);
}
@@ -54,42 +54,45 @@ Bytes decodeFmMfm(
void encodeFm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input)
{
if (bits.size() == 0)
return;
unsigned len = bits.size()-1;
if (bits.size() == 0)
return;
unsigned len = bits.size() - 1;
for (uint8_t b : input)
{
for (int i=0; i<8; i++)
for (int i = 0; i < 8; i++)
{
bool bit = b & 0x80;
b <<= 1;
if (cursor >= len)
return;
bits[cursor++] = true;
bits[cursor++] = bit;
}
}
}
void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input, bool& lastBit)
void encodeMfm(std::vector<bool>& bits,
unsigned& cursor,
const Bytes& input,
bool& lastBit)
{
if (bits.size() == 0)
return;
unsigned len = bits.size()-1;
if (bits.size() == 0)
return;
unsigned len = bits.size() - 1;
for (uint8_t b : input)
{
for (int i=0; i<8; i++)
for (int i = 0; i < 8; i++)
{
bool bit = b & 0x80;
b <<= 1;
if (cursor >= len)
return;
bits[cursor++] = !lastBit && !bit;
bits[cursor++] = bit;
lastBit = bit;
@@ -99,22 +102,21 @@ void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input, bo
Bytes encodeMfm(const Bytes& input, bool& lastBit)
{
ByteReader br(input);
BitReader bitr(br);
Bytes b;
ByteWriter bw(b);
BitWriter bitw(bw);
ByteReader br(input);
BitReader bitr(br);
Bytes b;
ByteWriter bw(b);
BitWriter bitw(bw);
while (!bitr.eof())
{
uint8_t bit = bitr.get();
bitw.push(!lastBit && !bit);
bitw.push(bit);
lastBit = bit;
while (!bitr.eof())
{
uint8_t bit = bitr.get();
bitw.push(!lastBit && !bit);
bitw.push(bit);
lastBit = bit;
}
bitw.flush();
return b;
bitw.flush();
return b;
}

View File

@@ -19,14 +19,13 @@
#include "lib/image.h"
#include "protocol.h"
std::unique_ptr<Encoder> Encoder::create(
const EncoderProto& config)
std::unique_ptr<Encoder> Encoder::create(const EncoderProto& config)
{
static const std::map<int,
std::function<std::unique_ptr<Encoder>(const EncoderProto&)>>
encoders = {
{EncoderProto::kAmiga, createAmigaEncoder },
{EncoderProto::kAgat, createAgatEncoder },
{EncoderProto::kAgat, createAgatEncoder },
{EncoderProto::kApple2, createApple2Encoder },
{EncoderProto::kBrother, createBrotherEncoder },
{EncoderProto::kC64, createCommodore64Encoder},
@@ -40,7 +39,7 @@ std::unique_ptr<Encoder> Encoder::create(
auto encoder = encoders.find(config.format_case());
if (encoder == encoders.end())
Error() << "no encoder specified";
error("no encoder specified");
return (encoder->second)(config);
}
@@ -51,15 +50,18 @@ nanoseconds_t Encoder::calculatePhysicalClockPeriod(
nanoseconds_t currentRotationalPeriod =
config.drive().rotational_period_ms() * 1e6;
if (currentRotationalPeriod == 0)
Error() << "you must set --drive.rotational_period_ms as it can't be "
"autodetected";
error(
"you must set --drive.rotational_period_ms as it can't be "
"autodetected");
return targetClockPeriod *
(currentRotationalPeriod / targetRotationalPeriod);
}
std::shared_ptr<const Sector> Encoder::getSector(
std::shared_ptr<const TrackInfo>& trackInfo, const Image& image, unsigned sectorId)
std::shared_ptr<const TrackInfo>& trackInfo,
const Image& image,
unsigned sectorId)
{
return image.get(trackInfo->logicalTrack, trackInfo->logicalSide, sectorId);
}
@@ -73,7 +75,7 @@ std::vector<std::shared_ptr<const Sector>> Encoder::collectSectors(
{
const auto& sector = getSector(trackLayout, image, sectorId);
if (!sector)
Error() << fmt::format("sector {}.{}.{} is missing from the image",
error("sector {}.{}.{} is missing from the image",
trackLayout->logicalTrack,
trackLayout->logicalSide,
sectorId);

View File

@@ -1,7 +1,6 @@
#include "globals.h"
#include "proto.h"
#include "fluxmap.h"
#include "fmt/format.h"
#include "lib/fl2.pb.h"
#include <fstream>
@@ -27,7 +26,7 @@ static void upgradeFluxFile(FluxFileProto& proto)
proto.set_version(FluxFileVersion::VERSION_2);
}
if (proto.version() > FluxFileVersion::VERSION_2)
Error() << fmt::format(
error(
"this is a version {} flux file, but this build of the client can "
"only handle up to version {} --- please upgrade",
proto.version(),
@@ -38,19 +37,19 @@ FluxFileProto loadFl2File(const std::string filename)
{
std::ifstream ifs(filename, std::ios::in | std::ios::binary);
if (!ifs.is_open())
Error() << fmt::format(
"cannot open input file '{}': {}", filename, strerror(errno));
error("cannot open input file '{}': {}", filename, strerror(errno));
char buffer[16];
ifs.read(buffer, sizeof(buffer));
ifs.read(buffer, sizeof(buffer));
if (strncmp(buffer, "SQLite format 3", 16) == 0)
Error() << "this flux file is too old; please use the "
"upgrade-flux-file tool to upgrade it";
error(
"this flux file is too old; please use the upgrade-flux-file tool "
"to upgrade it");
FluxFileProto proto;
ifs.seekg(0);
ifs.seekg(0);
if (!proto.ParseFromIstream(&ifs))
Error() << fmt::format("unable to read input file '{}'", filename);
error("unable to read input file '{}'", filename);
upgradeFluxFile(proto);
return proto;
}
@@ -62,8 +61,8 @@ void saveFl2File(const std::string filename, FluxFileProto& proto)
std::ofstream of(filename, std::ios::out | std::ios::binary);
if (!proto.SerializeToOstream(&of))
Error() << fmt::format("unable to write output file '{}'", filename);
error("unable to write output file '{}'", filename);
of.close();
if (of.fail())
Error() << "FL2 write I/O error: " << strerror(errno);
error("FL2 write I/O error: {}", strerror(errno));
}

View File

@@ -3,7 +3,6 @@
#include "proto.h"
#include "utils.h"
#include "logger.h"
#include "fmt/format.h"
#include <google/protobuf/text_format.h>
#include <regex>
#include <fstream>
@@ -44,17 +43,13 @@ void FlagGroup::addFlag(Flag* flag)
void FlagGroup::applyOption(const OptionProto& option)
{
if (option.config().option_size() > 0)
Error() << fmt::format(
"option '{}' has an option inside it, which isn't "
"allowed",
error("option '{}' has an option inside it, which isn't allowed",
option.name());
if (option.config().option_group_size() > 0)
Error() << fmt::format(
"option '{}' has an option group inside it, which isn't "
"allowed",
error("option '{}' has an option group inside it, which isn't allowed",
option.name());
Logger() << fmt::format("OPTION: {}",
log("OPTION: {}",
option.has_message() ? option.message() : option.comment());
config.MergeFrom(option.config());
@@ -112,7 +107,7 @@ std::vector<std::string> FlagGroup::parseFlagsWithFilenames(int argc,
for (const auto& name : flag->names())
{
if (flags_by_name.find(name) != flags_by_name.end())
Error() << "two flags use the name '" << name << "'";
error("two flags use the name '{}'", name);
flags_by_name[name] = flag;
}
@@ -203,7 +198,7 @@ std::vector<std::string> FlagGroup::parseFlagsWithFilenames(int argc,
options.insert(path);
}
else
Error() << "unrecognised flag; try --help";
error("unrecognised flag; try --help");
}
else
{
@@ -235,20 +230,19 @@ std::vector<std::string> FlagGroup::parseFlagsWithFilenames(int argc,
FlagGroup::applyOption(*defaultOption);
}
/* Next, any standalone options. */
/* Next, any standalone options. */
for (auto& option : config.option())
{
if (options.find(option.name()) != options.end())
{
FlagGroup::applyOption(option);
options.erase(option.name());
}
}
for (auto& option : config.option())
{
if (options.find(option.name()) != options.end())
{
FlagGroup::applyOption(option);
options.erase(option.name());
}
}
if (!options.empty())
Error() << fmt::format(
"--{} is not a known flag or format option; try --help",
error("--{} is not a known flag or format option; try --help",
*options.begin());
/* Now apply any value overrides (in order). */
@@ -268,8 +262,8 @@ void FlagGroup::parseFlags(int argc,
{
auto filenames = parseFlagsWithFilenames(argc, argv, callback);
if (!filenames.empty())
Error() << "non-option parameter " << *filenames.begin()
<< " seen (try --help)";
error(
"non-option parameter '{}' seen (try --help)", *filenames.begin());
}
void FlagGroup::parseFlagsWithConfigFiles(int argc,
@@ -290,20 +284,19 @@ ConfigProto FlagGroup::parseSingleConfigFile(const std::string& filename,
{
const auto& it = configFiles.find(filename);
if (it != configFiles.end())
return *it->second;
return *it->second;
else
{
std::ifstream f(filename, std::ios::out);
if (f.fail())
Error() << fmt::format(
"Cannot open '{}': {}", filename, strerror(errno));
error("Cannot open '{}': {}", filename, strerror(errno));
std::ostringstream ss;
ss << f.rdbuf();
ConfigProto config;
if (!google::protobuf::TextFormat::MergeFromString(ss.str(), &config))
Error() << "couldn't load external config proto";
error("couldn't load external config proto");
return config;
}
}
@@ -326,7 +319,7 @@ Flag::Flag(const std::vector<std::string>& names, const std::string helptext):
_helptext(helptext)
{
if (!currentFlagGroup)
Error() << "no flag group defined for " << *names.begin();
error("no flag group defined for {}", *names.begin());
_group.addFlag(this);
}
@@ -337,7 +330,7 @@ void BoolFlag::set(const std::string& value)
else if ((value == "false") || (value == "n"))
_value = false;
else
Error() << "can't parse '" << value << "'; try 'true' or 'false'";
error("can't parse '{}'; try 'true' or 'false'", value);
_callback(_value);
_isSet = true;
}

View File

@@ -8,217 +8,217 @@
#include "lib/fluxsink/fluxsink.pb.h"
#include "lib/logger.h"
#include "proto.h"
#include "fmt/format.h"
#include "fmt/chrono.h"
#include "fluxmap.h"
#include "a2r.h"
#include <fstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <fmt/chrono.h>
namespace
{
uint32_t ticks_to_a2r(uint32_t ticks)
{
return ticks * NS_PER_TICK / A2R_NS_PER_TICK;
}
bool singlesided(void)
{
return config.heads().start() == config.heads().end();
}
class A2RFluxSink : public FluxSink
{
public:
A2RFluxSink(const A2RFluxSinkProto& lconfig):
_config(lconfig),
_bytes{},
_writer{_bytes.writer()}
uint32_t ticks_to_a2r(uint32_t ticks)
{
Logger() << fmt::format(
"A2R: writing A2R {} file containing {} tracks\n",
singlesided() ? "single sided" : "double sided",
config.tracks().end() - config.tracks().start() + 1);
time_t now{std::time(nullptr)};
auto t = gmtime(&now);
_metadata["image_date"] = fmt::format("{:%FT%TZ}", *t);
return ticks * NS_PER_TICK / A2R_NS_PER_TICK;
}
~A2RFluxSink()
bool singlesided(void)
{
writeHeader();
writeInfo();
writeStream();
writeMeta();
Logger() << "A2R: writing output file...\n";
std::ofstream of(_config.filename(), std::ios::out | std::ios::binary);
if (!of.is_open())
Error() << "cannot open output file";
_bytes.writeTo(of);
of.close();
return config.heads().start() == config.heads().end();
}
private:
void writeChunkAndData(uint32_t chunk_id, const Bytes& data)
class A2RFluxSink : public FluxSink
{
_writer.write_le32(chunk_id);
_writer.write_le32(data.size());
_writer += data;
}
void writeHeader()
{
static const uint8_t a2r2_fileheader[] = {'A', '2', 'R', '2', 0xff, 0x0a, 0x0d, 0x0a};
_writer += Bytes(a2r2_fileheader, sizeof(a2r2_fileheader));
}
void writeInfo()
{
Bytes info;
auto writer = info.writer();
writer.write_8(A2R_INFO_CHUNK_VERSION);
auto version_str_padded = fmt::format("{: <32}", "Fluxengine");
assert(version_str_padded.size() == 32);
writer.append(version_str_padded);
writer.write_8(singlesided() ? A2R_DISK_525 : A2R_DISK_35);
writer.write_8(1); // write protected
writer.write_8(1); // synchronized
writeChunkAndData(A2R_CHUNK_INFO, info);
}
void writeMeta()
{
Bytes meta;
auto writer = meta.writer();
for (auto& i : _metadata)
public:
A2RFluxSink(const A2RFluxSinkProto& lconfig):
_config(lconfig),
_bytes{},
_writer{_bytes.writer()}
{
writer.append(i.first);
writer.write_8('\t');
writer.append(i.second);
writer.write_8('\n');
}
writeChunkAndData(A2R_CHUNK_META, meta);
}
void writeStream()
{
// A STRM always ends with a 255, even though this could ALSO indicate
// the first byte of a multi-byte sequence
_strmWriter.write_8(255);
log("A2R: writing A2R {} file containing {} tracks\n",
singlesided() ? "single sided" : "double sided",
config.tracks().end() - config.tracks().start() + 1);
writeChunkAndData(A2R_CHUNK_STRM, _strmBytes);
}
void writeFlux(int cylinder, int head, const Fluxmap& fluxmap) override
{
if (!fluxmap.bytes())
{
return;
time_t now{std::time(nullptr)};
auto t = gmtime(&now);
_metadata["image_date"] = fmt::format("{:%FT%TZ}", *t);
}
// Writing from an image (as opposed to from a floppy) will contain
// exactly one revolution and no index events.
auto is_image = [](auto& fluxmap)
~A2RFluxSink()
{
writeHeader();
writeInfo();
writeStream();
writeMeta();
log("A2R: writing output file...\n");
std::ofstream of(
_config.filename(), std::ios::out | std::ios::binary);
if (!of.is_open())
error("cannot open output file");
_bytes.writeTo(of);
of.close();
}
private:
void writeChunkAndData(uint32_t chunk_id, const Bytes& data)
{
_writer.write_le32(chunk_id);
_writer.write_le32(data.size());
_writer += data;
}
void writeHeader()
{
static const uint8_t a2r2_fileheader[] = {
'A', '2', 'R', '2', 0xff, 0x0a, 0x0d, 0x0a};
_writer += Bytes(a2r2_fileheader, sizeof(a2r2_fileheader));
}
void writeInfo()
{
Bytes info;
auto writer = info.writer();
writer.write_8(A2R_INFO_CHUNK_VERSION);
auto version_str_padded = fmt::format("{: <32}", "Fluxengine");
assert(version_str_padded.size() == 32);
writer.append(version_str_padded);
writer.write_8(singlesided() ? A2R_DISK_525 : A2R_DISK_35);
writer.write_8(1); // write protected
writer.write_8(1); // synchronized
writeChunkAndData(A2R_CHUNK_INFO, info);
}
void writeMeta()
{
Bytes meta;
auto writer = meta.writer();
for (auto& i : _metadata)
{
writer.append(i.first);
writer.write_8('\t');
writer.append(i.second);
writer.write_8('\n');
}
writeChunkAndData(A2R_CHUNK_META, meta);
}
void writeStream()
{
// A STRM always ends with a 255, even though this could ALSO
// indicate the first byte of a multi-byte sequence
_strmWriter.write_8(255);
writeChunkAndData(A2R_CHUNK_STRM, _strmBytes);
}
void writeFlux(int cylinder, int head, const Fluxmap& fluxmap) override
{
if (!fluxmap.bytes())
{
return;
}
// Writing from an image (as opposed to from a floppy) will contain
// exactly one revolution and no index events.
auto is_image = [](auto& fluxmap)
{
FluxmapReader fmr(fluxmap);
fmr.skipToEvent(F_BIT_INDEX);
// but maybe there is no index, if we're writing from an image
// to an a2r
return fmr.eof();
};
// Write the flux data into its own Bytes
Bytes trackBytes;
auto trackWriter = trackBytes.writer();
auto write_one_flux = [&](unsigned ticks)
{
auto value = ticks_to_a2r(ticks);
while (value > 254)
{
trackWriter.write_8(255);
value -= 255;
}
trackWriter.write_8(value);
};
int revolution = 0;
uint32_t loopPoint = 0;
uint32_t totalTicks = 0;
FluxmapReader fmr(fluxmap);
fmr.skipToEvent(F_BIT_INDEX);
// but maybe there is no index, if we're writing from an image to an
// a2r
return fmr.eof();
};
// Write the flux data into its own Bytes
Bytes trackBytes;
auto trackWriter = trackBytes.writer();
auto write_one_flux = [&](unsigned ticks)
{
auto value = ticks_to_a2r(ticks);
while (value > 254)
auto write_flux = [&](unsigned maxTicks = ~0u)
{
trackWriter.write_8(255);
value -= 255;
}
trackWriter.write_8(value);
};
unsigned ticksSinceLastPulse = 0;
int revolution = 0;
uint32_t loopPoint = 0;
uint32_t totalTicks = 0;
FluxmapReader fmr(fluxmap);
while (!fmr.eof() && totalTicks < maxTicks)
{
unsigned ticks;
int event;
fmr.getNextEvent(event, ticks);
auto write_flux = [&](unsigned maxTicks = ~0u)
{
unsigned ticksSinceLastPulse = 0;
ticksSinceLastPulse += ticks;
totalTicks += ticks;
while (!fmr.eof() && totalTicks < maxTicks)
if (event & F_BIT_PULSE)
{
write_one_flux(ticksSinceLastPulse);
ticksSinceLastPulse = 0;
}
if (event & F_BIT_INDEX && revolution == 0)
{
loopPoint = totalTicks;
revolution += 1;
}
}
};
if (is_image(fluxmap))
{
unsigned ticks;
int event;
fmr.getNextEvent(event, ticks);
ticksSinceLastPulse += ticks;
totalTicks += ticks;
if (event & F_BIT_PULSE)
{
write_one_flux(ticksSinceLastPulse);
ticksSinceLastPulse = 0;
}
if (event & F_BIT_INDEX && revolution == 0)
{
loopPoint = totalTicks;
revolution += 1;
}
// A timing stream with no index represents exactly one
// revolution with no index. However, a2r nominally contains 450
// degress of rotation, 250ms at 300rpm.
write_flux();
loopPoint = totalTicks;
fmr.rewind();
revolution += 1;
write_flux(totalTicks * 5 / 4);
}
else
{
// We have an index, so this is real from a floppy and should be
// "one revolution plus a bit"
fmr.skipToEvent(F_BIT_INDEX);
write_flux();
}
};
if (is_image(fluxmap))
{
// A timing stream with no index represents exactly one
// revolution with no index. However, a2r nominally contains 450
// degress of rotation, 250ms at 300rpm.
write_flux();
loopPoint = totalTicks;
fmr.rewind();
revolution += 1;
write_flux(totalTicks * 5 / 4);
}
else
{
// We have an index, so this is real from a floppy and should be
// "one revolution plus a bit"
fmr.skipToEvent(F_BIT_INDEX);
write_flux();
uint32_t chunk_size = 10 + trackBytes.size();
_strmWriter.write_8(cylinder);
_strmWriter.write_8(A2R_TIMING);
_strmWriter.write_le32(trackBytes.size());
_strmWriter.write_le32(ticks_to_a2r(loopPoint));
_strmWriter += trackBytes;
}
uint32_t chunk_size = 10 + trackBytes.size();
operator std::string() const override
{
return fmt::format("a2r({})", _config.filename());
}
_strmWriter.write_8(cylinder);
_strmWriter.write_8(A2R_TIMING);
_strmWriter.write_le32(trackBytes.size());
_strmWriter.write_le32(ticks_to_a2r(loopPoint));
_strmWriter += trackBytes;
}
operator std::string() const override
{
return fmt::format("a2r({})", _config.filename());
}
private:
const A2RFluxSinkProto& _config;
Bytes _bytes;
ByteWriter _writer;
Bytes _strmBytes;
ByteWriter _strmWriter{_strmBytes.writer()};
std::map<std::string, std::string> _metadata;
};
private:
const A2RFluxSinkProto& _config;
Bytes _bytes;
ByteWriter _writer;
Bytes _strmBytes;
ByteWriter _strmWriter{_strmBytes.writer()};
std::map<std::string, std::string> _metadata;
};
} // namespace
std::unique_ptr<FluxSink> FluxSink::createA2RFluxSink(
@@ -226,4 +226,3 @@ std::unique_ptr<FluxSink> FluxSink::createA2RFluxSink(
{
return std::unique_ptr<FluxSink>(new A2RFluxSink(config));
}

View File

@@ -7,7 +7,6 @@
#include "decoders/fluxmapreader.h"
#include "lib/fluxsink/fluxsink.pb.h"
#include "proto.h"
#include "fmt/format.h"
#include <fstream>
#include <sys/stat.h>
#include <sys/types.h>
@@ -15,87 +14,84 @@
class AuFluxSink : public FluxSink
{
public:
AuFluxSink(const AuFluxSinkProto& config):
_config(config)
{}
AuFluxSink(const AuFluxSinkProto& config): _config(config) {}
~AuFluxSink()
{
std::cerr << "Warning: do not play these files, or you will break your speakers"
" and/or ears!\n";
}
~AuFluxSink()
{
std::cerr << "Warning: do not play these files, or you will break your "
"speakers and/or ears!\n";
}
public:
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
{
unsigned totalTicks = fluxmap.ticks() + 2;
unsigned channels = _config.index_markers() ? 2 : 1;
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
{
unsigned totalTicks = fluxmap.ticks() + 2;
unsigned channels = _config.index_markers() ? 2 : 1;
mkdir(_config.directory().c_str(), 0744);
std::ofstream of(
fmt::format("{}/c{:02d}.h{:01d}.au", _config.directory(), track, head),
std::ios::out | std::ios::binary);
if (!of.is_open())
Error() << "cannot open output file";
mkdir(_config.directory().c_str(), 0744);
std::ofstream of(
fmt::format(
"{}/c{:02d}.h{:01d}.au", _config.directory(), track, head),
std::ios::out | std::ios::binary);
if (!of.is_open())
error("cannot open output file");
/* Write header */
/* Write header */
{
Bytes header;
header.resize(24);
ByteWriter bw(header);
{
Bytes header;
header.resize(24);
ByteWriter bw(header);
bw.write_be32(0x2e736e64);
bw.write_be32(24);
bw.write_be32(totalTicks * channels);
bw.write_be32(2); /* 8-bit PCM */
bw.write_be32(TICK_FREQUENCY);
bw.write_be32(channels); /* channels */
bw.write_be32(0x2e736e64);
bw.write_be32(24);
bw.write_be32(totalTicks * channels);
bw.write_be32(2); /* 8-bit PCM */
bw.write_be32(TICK_FREQUENCY);
bw.write_be32(channels); /* channels */
of.write((const char*) header.cbegin(), header.size());
}
of.write((const char*)header.cbegin(), header.size());
}
/* Write data */
/* Write data */
{
Bytes data;
data.resize(totalTicks * channels);
memset(data.begin(), 0x80, data.size());
{
Bytes data;
data.resize(totalTicks * channels);
memset(data.begin(), 0x80, data.size());
FluxmapReader fmr(fluxmap);
unsigned timestamp = 0;
while (!fmr.eof())
{
unsigned ticks;
int event;
fmr.getNextEvent(event, ticks);
if (fmr.eof())
break;
timestamp += ticks;
FluxmapReader fmr(fluxmap);
unsigned timestamp = 0;
while (!fmr.eof())
{
unsigned ticks;
int event;
fmr.getNextEvent(event, ticks);
if (fmr.eof())
break;
timestamp += ticks;
if (event & F_BIT_PULSE)
data[timestamp*channels + 0] = 0x7f;
if (_config.index_markers() && (event & F_BIT_INDEX))
data[timestamp*channels + 1] = 0x7f;
}
if (event & F_BIT_PULSE)
data[timestamp * channels + 0] = 0x7f;
if (_config.index_markers() && (event & F_BIT_INDEX))
data[timestamp * channels + 1] = 0x7f;
}
of.write((const char*) data.cbegin(), data.size());
}
of.write((const char*)data.cbegin(), data.size());
}
}
}
operator std::string () const override
{
return fmt::format("au({})", _config.directory());
}
operator std::string() const override
{
return fmt::format("au({})", _config.directory());
}
private:
const AuFluxSinkProto& _config;
const AuFluxSinkProto& _config;
};
std::unique_ptr<FluxSink> FluxSink::createAuFluxSink(const AuFluxSinkProto& config)
std::unique_ptr<FluxSink> FluxSink::createAuFluxSink(
const AuFluxSinkProto& config)
{
return std::unique_ptr<FluxSink>(new AuFluxSink(config));
}

View File

@@ -7,7 +7,6 @@
#include "decoders/fluxmapreader.h"
#include "lib/fluxsink/fluxsink.pb.h"
#include "proto.h"
#include "fmt/format.h"
#include "lib/fl2.pb.h"
#include "fl2.h"
#include <fstream>
@@ -27,7 +26,7 @@ public:
{
std::ofstream of(filename);
if (!of.is_open())
Error() << "cannot open output file";
error("cannot open output file");
of.close();
std::filesystem::remove(filename);
}

View File

@@ -4,7 +4,6 @@
#include "lib/config.pb.h"
#include "proto.h"
#include "utils.h"
#include "fmt/format.h"
#include <regex>
std::unique_ptr<FluxSink> FluxSink::create(const FluxSinkProto& config)
@@ -30,7 +29,7 @@ std::unique_ptr<FluxSink> FluxSink::create(const FluxSinkProto& config)
return createFl2FluxSink(config.fl2());
default:
Error() << "bad output disk config";
error("bad output disk config");
return std::unique_ptr<FluxSink>();
}
}
@@ -89,5 +88,5 @@ void FluxSink::updateConfigForFilename(
}
}
Error() << fmt::format("unrecognised flux filename '{}'", filename);
error("unrecognised flux filename '{}'", filename);
}

View File

@@ -7,54 +7,50 @@
#include "fluxsink/fluxsink.h"
#include "lib/fluxsink/fluxsink.pb.h"
#include "lib/readerwriter.cc"
#include "fmt/format.h"
class HardwareFluxSink : public FluxSink
{
public:
HardwareFluxSink(const HardwareFluxSinkProto& conf):
_config(conf)
HardwareFluxSink(const HardwareFluxSinkProto& conf): _config(conf)
{
nanoseconds_t oneRevolution;
measureDiskRotation(oneRevolution, _hardSectorThreshold);
nanoseconds_t oneRevolution;
measureDiskRotation(oneRevolution, _hardSectorThreshold);
}
~HardwareFluxSink()
{
}
~HardwareFluxSink() {}
public:
void writeFlux(int track, int side, const Fluxmap& fluxmap) override
{
usbSetDrive(config.drive().drive(), config.drive().high_density(), config.drive().index_mode());
#if 0
usbSetDrive(config.drive().drive(),
config.drive().high_density(),
config.drive().index_mode());
#if 0
if (fluxSourceSinkFortyTrack)
{
if (track & 1)
Error() << "cannot write to odd physical tracks in 40-track mode";
error("cannot write to odd physical tracks in 40-track mode");
usbSeek(track / 2);
}
else
#endif
usbSeek(track);
#endif
usbSeek(track);
return usbWrite(side, fluxmap.rawBytes(), _hardSectorThreshold);
}
operator std::string () const
{
return fmt::format("drive {}", config.drive().drive());
}
operator std::string() const
{
return fmt::format("drive {}", config.drive().drive());
}
private:
const HardwareFluxSinkProto& _config;
nanoseconds_t _hardSectorThreshold;
};
std::unique_ptr<FluxSink> FluxSink::createHardwareFluxSink(const HardwareFluxSinkProto& config)
std::unique_ptr<FluxSink> FluxSink::createHardwareFluxSink(
const HardwareFluxSinkProto& config)
{
return std::unique_ptr<FluxSink>(new HardwareFluxSink(config));
}

View File

@@ -7,10 +7,10 @@
#include "decoders/fluxmapreader.h"
#include "lib/fluxsink/fluxsink.pb.h"
#include "proto.h"
#include "fmt/format.h"
#include "fluxmap.h"
#include "layout.h"
#include "scp.h"
#include "lib/logger.h"
#include <fstream>
#include <sys/stat.h>
#include <sys/types.h>
@@ -58,15 +58,14 @@ public:
if (config.tpi() != 48)
_fileheader.flags |= SCP_FLAG_96TPI;
_fileheader.cell_width = 0;
if ((minSide == 0) && (maxSide == 0))
_fileheader.heads = 1;
else if ((minSide == 1) && (maxSide == 1))
_fileheader.heads = 2;
else
_fileheader.heads = 0;
if ((minSide == 0) && (maxSide == 0))
_fileheader.heads = 1;
else if ((minSide == 1) && (maxSide == 1))
_fileheader.heads = 2;
else
_fileheader.heads = 0;
std::cout << fmt::format(
"SCP: writing 96 tpi {} file containing {} tracks\n",
log("SCP: writing 96 tpi {} file containing {} tracks",
(minSide == maxSide) ? "single sided" : "double sided",
_fileheader.end_track - _fileheader.start_track + 1);
}
@@ -80,10 +79,10 @@ public:
appendChecksum(checksum, _trackdata);
write_le32(_fileheader.checksum, checksum);
std::cout << "SCP: writing output file...\n";
log("SCP: writing output file");
std::ofstream of(_config.filename(), std::ios::out | std::ios::binary);
if (!of.is_open())
Error() << "cannot open output file";
error("cannot open output file");
of.write((const char*)&_fileheader, sizeof(_fileheader));
_trackdata.writeTo(of);
of.close();
@@ -98,9 +97,8 @@ public:
if (strack >= std::size(_fileheader.track))
{
std::cout << fmt::format(
"SCP: cannot write track {} head {}, "
"there are not not enough Track Data Headers.\n",
log("SCP: cannot write track {} head {}, there are not not enough "
"Track Data Headers.",
track,
head);
return;

View File

@@ -7,7 +7,6 @@
#include "decoders/fluxmapreader.h"
#include "lib/fluxsink/fluxsink.pb.h"
#include "proto.h"
#include "fmt/format.h"
#include <fstream>
#include <sys/stat.h>
#include <sys/types.h>
@@ -15,67 +14,67 @@
class VcdFluxSink : public FluxSink
{
public:
VcdFluxSink(const VcdFluxSinkProto& config):
_config(config)
{}
VcdFluxSink(const VcdFluxSinkProto& config): _config(config) {}
public:
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
{
mkdir(_config.directory().c_str(), 0744);
std::ofstream of(
fmt::format("{}/c{:02d}.h{:01d}.vcd", _config.directory(), track, head),
std::ios::out | std::ios::binary);
if (!of.is_open())
Error() << "cannot open output file";
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
{
mkdir(_config.directory().c_str(), 0744);
std::ofstream of(
fmt::format(
"{}/c{:02d}.h{:01d}.vcd", _config.directory(), track, head),
std::ios::out | std::ios::binary);
if (!of.is_open())
error("cannot open output file");
of << "$timescale 1ns $end\n"
<< "$var wire 1 i index $end\n"
<< "$var wire 1 p pulse $end\n"
<< "$upscope $end\n"
<< "$enddefinitions $end\n"
<< "$dumpvars 0i 0p $end\n";
of << "$timescale 1ns $end\n"
<< "$var wire 1 i index $end\n"
<< "$var wire 1 p pulse $end\n"
<< "$upscope $end\n"
<< "$enddefinitions $end\n"
<< "$dumpvars 0i 0p $end\n";
FluxmapReader fmr(fluxmap);
unsigned timestamp = 0;
unsigned lasttimestamp = 0;
while (!fmr.eof())
{
unsigned ticks;
int event;
fmr.getNextEvent(event, ticks);
if (fmr.eof())
break;
FluxmapReader fmr(fluxmap);
unsigned timestamp = 0;
unsigned lasttimestamp = 0;
while (!fmr.eof())
{
unsigned ticks;
int event;
fmr.getNextEvent(event, ticks);
if (fmr.eof())
break;
unsigned newtimestamp = timestamp + ticks;
if (newtimestamp != lasttimestamp)
{
of << fmt::format("\n#{} 0i 0p\n", (uint64_t)((lasttimestamp+1) * NS_PER_TICK));
timestamp = newtimestamp;
of << fmt::format("#{} ", (uint64_t)(timestamp * NS_PER_TICK));
}
unsigned newtimestamp = timestamp + ticks;
if (newtimestamp != lasttimestamp)
{
of << fmt::format("\n#{} 0i 0p\n",
(uint64_t)((lasttimestamp + 1) * NS_PER_TICK));
timestamp = newtimestamp;
of << fmt::format("#{} ", (uint64_t)(timestamp * NS_PER_TICK));
}
if (event & F_BIT_PULSE)
of << "1p ";
if (event & F_BIT_INDEX)
of << "1i ";
if (event & F_BIT_PULSE)
of << "1p ";
if (event & F_BIT_INDEX)
of << "1i ";
lasttimestamp = timestamp;
}
of << "\n";
}
lasttimestamp = timestamp;
}
of << "\n";
}
operator std::string () const
{
return fmt::format("vcd({})", _config.directory());
}
operator std::string() const
{
return fmt::format("vcd({})", _config.directory());
}
private:
const VcdFluxSinkProto& _config;
const VcdFluxSinkProto& _config;
};
std::unique_ptr<FluxSink> FluxSink::createVcdFluxSink(const VcdFluxSinkProto& config)
std::unique_ptr<FluxSink> FluxSink::createVcdFluxSink(
const VcdFluxSinkProto& config)
{
return std::unique_ptr<FluxSink>(new VcdFluxSink(config));
}

View File

@@ -3,7 +3,6 @@
#include "lib/fluxsource/fluxsource.pb.h"
#include "fluxsource/fluxsource.h"
#include "proto.h"
#include "fmt/format.h"
#include <fstream>
struct A2Rv2Flux
@@ -121,7 +120,7 @@ public:
}
default:
Error() << "unsupported A2R version";
error("unsupported A2R version");
}
}
@@ -141,7 +140,7 @@ public:
}
default:
Error() << "unsupported A2R version";
error("unsupported A2R version");
}
}
@@ -164,7 +163,7 @@ private:
offset += br.read_le32() + 8;
}
Error() << "A2R file missing chunk";
error("A2R file missing chunk");
}
private:

View File

@@ -3,7 +3,6 @@
#include "lib/fluxsource/fluxsource.pb.h"
#include "fluxsource/fluxsource.h"
#include "proto.h"
#include "fmt/format.h"
#include <fstream>
struct CwfHeader
@@ -14,7 +13,7 @@ struct CwfHeader
uint8_t version; // version of this file
uint8_t clock_rate; // clock rate used: 0, 1, 2 (2 = 28MHz)
uint8_t drive_type; // type of drive
uint8_t tracks; // number of tracks
uint8_t tracks; // number of tracks
uint8_t sides; // number of sides
uint8_t index_mark; // nonzero if index marks are included
uint8_t step; // track stepping interval
@@ -24,7 +23,7 @@ struct CwfHeader
struct CwfTrack
{
uint8_t track; // sequential
uint8_t track; // sequential
uint8_t side;
uint8_t unused[2];
uint8_t length[4]; // little-endian
@@ -33,109 +32,120 @@ struct CwfTrack
class CwfFluxSource : public TrivialFluxSource
{
public:
CwfFluxSource(const CwfFluxSourceProto& config):
_config(config)
CwfFluxSource(const CwfFluxSourceProto& config): _config(config)
{
_if.open(_config.filename(), std::ios::in | std::ios::binary);
if (!_if.is_open())
Error() << fmt::format("cannot open input file '{}': {}",
_config.filename(), strerror(errno));
_if.open(_config.filename(), std::ios::in | std::ios::binary);
if (!_if.is_open())
error("cannot open input file '{}': {}",
_config.filename(),
strerror(errno));
_if.read((char*) &_header, sizeof(_header));
check_for_error();
_if.read((char*)&_header, sizeof(_header));
check_for_error();
if ((_header.file_id[0] != 'C')
|| (_header.file_id[1] != 'W')
|| (_header.file_id[2] != 'S')
|| (_header.file_id[3] != 'F'))
Error() << "input not a CWF file";
if ((_header.file_id[0] != 'C') || (_header.file_id[1] != 'W') ||
(_header.file_id[2] != 'S') || (_header.file_id[3] != 'F'))
error("input not a CWF file");
switch (_header.clock_rate)
{
case 1: _clockRate = 14161000.0; break;
case 2: _clockRate = 28322000.0; break;
default:
Error() << "unsupported clock rate";
}
switch (_header.clock_rate)
{
case 1:
_clockRate = 14161000.0;
break;
case 2:
_clockRate = 28322000.0;
break;
default:
error("unsupported clock rate");
}
std::cout << fmt::format("CWF {}x{} = {} tracks, {} sides\n",
_header.tracks, _header.step, _header.tracks*_header.step, _header.sides);
std::cout << fmt::format("CWF sample clock rate: {} MHz\n", _clockRate / 1e6);
std::cout << fmt::format("CWF {}x{} = {} tracks, {} sides\n",
_header.tracks,
_header.step,
_header.tracks * _header.step,
_header.sides);
std::cout << fmt::format(
"CWF sample clock rate: {} MHz\n", _clockRate / 1e6);
int tracks = _header.tracks*_header.sides;
for (int i=0; i<tracks; i++)
{
CwfTrack trackheader;
_if.read((char*) &trackheader, sizeof(trackheader));
check_for_error();
int tracks = _header.tracks * _header.sides;
for (int i = 0; i < tracks; i++)
{
CwfTrack trackheader;
_if.read((char*)&trackheader, sizeof(trackheader));
check_for_error();
uint32_t length = Bytes(trackheader.length, 4).reader().read_le32() - sizeof(CwfTrack);
unsigned track_number = trackheader.track * _header.step;
uint32_t length =
Bytes(trackheader.length, 4).reader().read_le32() -
sizeof(CwfTrack);
unsigned track_number = trackheader.track * _header.step;
off_t pos = _if.tellg();
_trackOffsets[std::make_pair(track_number, trackheader.side)] = std::make_pair(pos, length);
_if.seekg(pos + length);
}
}
off_t pos = _if.tellg();
_trackOffsets[std::make_pair(track_number, trackheader.side)] =
std::make_pair(pos, length);
_if.seekg(pos + length);
}
}
public:
std::unique_ptr<const Fluxmap> readSingleFlux(int track, int side)
{
const auto& p = _trackOffsets.find(std::make_pair(track, side));
if (p == _trackOffsets.end())
return std::make_unique<const Fluxmap>();
const auto& p = _trackOffsets.find(std::make_pair(track, side));
if (p == _trackOffsets.end())
return std::make_unique<const Fluxmap>();
off_t pos = p->second.first;;
size_t length = p->second.second;
_if.seekg(pos);
off_t pos = p->second.first;
;
size_t length = p->second.second;
_if.seekg(pos);
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
uint32_t pending = 0;
bool oldindex = true;
for (size_t cursor = 0; cursor < length; cursor++)
{
uint32_t b = _if.get();
bool index = !!(b & 0x80);
b &= 0x7f;
if (b == 0x7f)
{
pending += 0x7f;
continue;
}
b += pending;
pending = 0;
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
uint32_t pending = 0;
bool oldindex = true;
for (size_t cursor = 0; cursor < length; cursor++)
{
uint32_t b = _if.get();
bool index = !!(b & 0x80);
b &= 0x7f;
if (b == 0x7f)
{
pending += 0x7f;
continue;
}
b += pending;
pending = 0;
double interval_us = b * (1e6/_clockRate);
fluxmap->appendInterval(interval_us / US_PER_TICK);
fluxmap->appendPulse();
double interval_us = b * (1e6 / _clockRate);
fluxmap->appendInterval(interval_us / US_PER_TICK);
fluxmap->appendPulse();
if (index && !oldindex)
fluxmap->appendIndex();
oldindex = index;
}
check_for_error();
if (index && !oldindex)
fluxmap->appendIndex();
oldindex = index;
}
check_for_error();
return fluxmap;
return fluxmap;
}
void recalibrate() {}
private:
void check_for_error()
{
if (_if.fail())
Error() << fmt::format("SCP read I/O error: {}", strerror(errno));
}
void check_for_error()
{
if (_if.fail())
error("SCP read I/O error: {}", strerror(errno));
}
private:
const CwfFluxSourceProto& _config;
std::ifstream _if;
CwfHeader _header;
nanoseconds_t _clockRate;
std::map<std::pair<int, int>, std::pair<off_t, size_t>> _trackOffsets;
std::ifstream _if;
CwfHeader _header;
nanoseconds_t _clockRate;
std::map<std::pair<int, int>, std::pair<off_t, size_t>> _trackOffsets;
};
std::unique_ptr<FluxSource> FluxSource::createCwfFluxSource(const CwfFluxSourceProto& config)
std::unique_ptr<FluxSource> FluxSource::createCwfFluxSource(
const CwfFluxSourceProto& config)
{
return std::unique_ptr<FluxSource>(new CwfFluxSource(config));
}

View File

@@ -2,7 +2,6 @@
#include "fluxmap.h"
#include "fluxsource/fluxsource.h"
#include "lib/fluxsource/fluxsource.pb.h"
#include "fmt/format.h"
class EraseFluxSource : public TrivialFluxSource
{
@@ -13,16 +12,14 @@ public:
public:
std::unique_ptr<const Fluxmap> readSingleFlux(int track, int side) override
{
return std::unique_ptr<const Fluxmap>();
return std::unique_ptr<const Fluxmap>();
}
void recalibrate() {}
};
std::unique_ptr<FluxSource> FluxSource::createEraseFluxSource(const EraseFluxSourceProto& config)
std::unique_ptr<FluxSource> FluxSource::createEraseFluxSource(
const EraseFluxSourceProto& config)
{
return std::make_unique<EraseFluxSource>(config);
}

View File

@@ -5,7 +5,6 @@
#include "fluxsource/fluxsource.h"
#include "proto.h"
#include "fl2.h"
#include "fmt/format.h"
#include "fluxmap.h"
#include <fstream>
@@ -57,7 +56,7 @@ private:
void check_for_error(std::ifstream& ifs)
{
if (ifs.fail())
Error() << fmt::format("FL2 read I/O error: {}", strerror(errno));
error("FL2 read I/O error: {}", strerror(errno));
}
private:

View File

@@ -5,7 +5,6 @@
#include "lib/config.pb.h"
#include "proto.h"
#include "utils.h"
#include "fmt/format.h"
#include <regex>
static bool ends_with(const std::string& value, const std::string& ending)
@@ -47,7 +46,7 @@ std::unique_ptr<FluxSource> FluxSource::create(const FluxSourceProto& config)
return createFlxFluxSource(config.flx());
default:
Error() << "bad input disk configuration";
error("bad input disk configuration");
return std::unique_ptr<FluxSource>();
}
}
@@ -123,7 +122,7 @@ void FluxSource::updateConfigForFilename(
}
}
Error() << fmt::format("unrecognised flux filename '{}'", filename);
error("unrecognised flux filename '{}'", filename);
}
class TrivialFluxSourceIterator : public FluxSourceIterator

View File

@@ -79,7 +79,7 @@ class EmptyFluxSourceIterator : public FluxSourceIterator
std::unique_ptr<const Fluxmap> next() override
{
Error() << "no flux to read";
error("no flux to read");
}
};

View File

@@ -3,7 +3,6 @@
#include "kryoflux.h"
#include "protocol.h"
#include "lib/fluxsource/flx.h"
#include "fmt/format.h"
std::unique_ptr<Fluxmap> readFlxBytes(const Bytes& bytes)
{
@@ -14,7 +13,7 @@ std::unique_ptr<Fluxmap> readFlxBytes(const Bytes& bytes)
for (;;)
{
if (br.eof())
Error() << fmt::format("malformed FLX stream");
error("malformed FLX stream");
uint8_t b = br.read_8();
if (b == 0)
break;
@@ -36,7 +35,7 @@ std::unique_ptr<Fluxmap> readFlxBytes(const Bytes& bytes)
default:
{
if (b < 32)
Error() << fmt::format("unknown FLX opcode 0x{:2x}", b);
error("unknown FLX opcode 0x{:2x}", b);
nanoseconds_t interval = b * FLX_TICK_NS;
fluxmap->appendInterval(interval / NS_PER_TICK);
fluxmap->appendPulse();

View File

@@ -7,7 +7,6 @@
#include "fluxsource/fluxsource.h"
#include "lib/fluxsource/fluxsource.pb.h"
#include "lib/readerwriter.h"
#include "fmt/format.h"
class HardwareFluxSource : public FluxSource
{

View File

@@ -2,7 +2,6 @@
#include "fluxmap.h"
#include "kryoflux.h"
#include "protocol.h"
#include "fmt/format.h"
#include <fstream>
#include <sys/types.h>
#include <dirent.h>
@@ -17,17 +16,20 @@ static bool has_suffix(const std::string& haystack, const std::string& needle)
{
if (needle.length() > haystack.length())
return false;
return haystack.compare(haystack.length() - needle.length(), needle.length(), needle) == 0;
return haystack.compare(haystack.length() - needle.length(),
needle.length(),
needle) == 0;
}
std::unique_ptr<Fluxmap> readStream(const std::string& dir, unsigned track, unsigned side)
std::unique_ptr<Fluxmap> readStream(
const std::string& dir, unsigned track, unsigned side)
{
std::string suffix = fmt::format("{:02}.{}.raw", track, side);
DIR* dirp = opendir(dir.c_str());
if (!dirp)
Error() << fmt::format("cannot access path '{}'", dir);
error("cannot access path '{}'", dir);
std::string filename;
for (;;)
@@ -35,18 +37,18 @@ std::unique_ptr<Fluxmap> readStream(const std::string& dir, unsigned track, unsi
struct dirent* de = readdir(dirp);
if (!de)
break;
if (has_suffix(de->d_name, suffix))
{
if (!filename.empty())
Error() << fmt::format("data is ambiguous --- multiple files end in {}", suffix);
error("data is ambiguous --- multiple files end in {}", suffix);
filename = fmt::format("{}/{}", dir, de->d_name);
}
}
closedir(dirp);
if (filename.empty())
Error() << fmt::format("failed to find track {} side {} in {}", track, side, dir);
error("failed to find track {} side {} in {}", track, side, dir);
return readStream(filename);
}
@@ -55,7 +57,7 @@ std::unique_ptr<Fluxmap> readStream(const std::string& filename)
{
std::ifstream f(filename, std::ios::in | std::ios::binary);
if (!f.is_open())
Error() << fmt::format("cannot open input file '{}'", filename);
error("cannot open input file '{}'", filename);
Bytes bytes;
ByteWriter bw(bytes);
@@ -69,7 +71,7 @@ std::unique_ptr<Fluxmap> readStream(const Bytes& bytes)
ByteReader br(bytes);
/* Pass 1: scan the stream looking for index marks. */
std::set<uint32_t> indexmarks;
br.seek(0);
while (!br.eof())
@@ -119,7 +121,8 @@ std::unique_ptr<Fluxmap> readStream(const Bytes& bytes)
}
else if (b == 0x0b)
{
/* Ovl16: the next block is 0x10000 sclks longer than normal. */
/* Ovl16: the next block is 0x10000 sclks longer than
* normal. */
len = 0;
}
else if (b == 0x0c)
@@ -133,8 +136,9 @@ std::unique_ptr<Fluxmap> readStream(const Bytes& bytes)
len = 0;
}
else
Error() << fmt::format(
"unknown stream block byte 0x{:02x} at 0x{:08x}", b, (uint64_t)br.pos-1);
error("unknown stream block byte 0x{:02x} at 0x{:08x}",
b,
(uint64_t)br.pos - 1);
}
}
@@ -197,7 +201,7 @@ finished_pass_1:
if ((b >= 0x00) && (b <= 0x07))
{
/* Flux2: double byte value */
b = (b<<8) | br.read_8();
b = (b << 8) | br.read_8();
writeFlux(extrasclks + b);
extrasclks = 0;
}
@@ -217,7 +221,8 @@ finished_pass_1:
}
else if (b == 0x0b)
{
/* Ovl16: the next block is 0x10000 sclks longer than normal. */
/* Ovl16: the next block is 0x10000 sclks longer than
* normal. */
extrasclks += 0x10000;
}
else if (b == 0x0c)
@@ -234,14 +239,15 @@ finished_pass_1:
extrasclks = 0;
}
else
Error() << fmt::format(
"unknown stream block byte 0x{:02x} at 0x{:08x}", b, (uint64_t)br.pos-1);
error("unknown stream block byte 0x{:02x} at 0x{:08x}",
b,
(uint64_t)br.pos - 1);
}
}
}
finished_pass_2:
if (!br.eof())
Error() << "I/O error reading stream";
error("I/O error reading stream");
return fluxmap;
}

View File

@@ -9,7 +9,8 @@ class KryofluxFluxSource : public TrivialFluxSource
public:
KryofluxFluxSource(const KryofluxFluxSourceProto& config):
_path(config.directory())
{}
{
}
public:
std::unique_ptr<const Fluxmap> readSingleFlux(int track, int side) override
@@ -23,7 +24,8 @@ private:
const std::string _path;
};
std::unique_ptr<FluxSource> FluxSource::createKryofluxFluxSource(const KryofluxFluxSourceProto& config)
std::unique_ptr<FluxSource> FluxSource::createKryofluxFluxSource(
const KryofluxFluxSourceProto& config)
{
return std::make_unique<KryofluxFluxSource>(config);
}

View File

@@ -4,47 +4,44 @@
#include "lib/fluxsource/fluxsource.h"
#include "lib/fluxmap.h"
#include "lib/layout.h"
#include <fmt/format.h>
#include <fstream>
class MemoryFluxSourceIterator : public FluxSourceIterator
{
public:
MemoryFluxSourceIterator(const TrackFlux& track):
_track(track)
{}
MemoryFluxSourceIterator(const TrackFlux& track): _track(track) {}
bool hasNext() const override
{
return _count < _track.trackDatas.size();
}
bool hasNext() const override
{
return _count < _track.trackDatas.size();
}
std::unique_ptr<const Fluxmap> next() override
{
auto bytes = _track.trackDatas[_count]->fluxmap->rawBytes();
_count++;
return std::make_unique<Fluxmap>(bytes);
}
std::unique_ptr<const Fluxmap> next() override
{
auto bytes = _track.trackDatas[_count]->fluxmap->rawBytes();
_count++;
return std::make_unique<Fluxmap>(bytes);
}
private:
const TrackFlux& _track;
int _count = 0;
const TrackFlux& _track;
int _count = 0;
};
class MemoryFluxSource : public FluxSource
{
public:
MemoryFluxSource(const DiskFlux& flux): _flux(flux)
{
}
MemoryFluxSource(const DiskFlux& flux): _flux(flux) {}
public:
std::unique_ptr<FluxSourceIterator> readFlux(int physicalTrack, int physicalSide) override
std::unique_ptr<FluxSourceIterator> readFlux(
int physicalTrack, int physicalSide) override
{
for (const auto& trackFlux : _flux.tracks)
{
if ((trackFlux->trackInfo->physicalTrack == physicalTrack) && (trackFlux->trackInfo->physicalSide == physicalSide))
return std::make_unique<MemoryFluxSourceIterator>(*trackFlux);
if ((trackFlux->trackInfo->physicalTrack == physicalTrack) &&
(trackFlux->trackInfo->physicalSide == physicalSide))
return std::make_unique<MemoryFluxSourceIterator>(*trackFlux);
}
return std::make_unique<EmptyFluxSourceIterator>();
@@ -53,11 +50,11 @@ public:
void recalibrate() {}
private:
const DiskFlux& _flux;
const DiskFlux& _flux;
};
std::unique_ptr<FluxSource> FluxSource::createMemoryFluxSource(const DiskFlux& flux)
std::unique_ptr<FluxSource> FluxSource::createMemoryFluxSource(
const DiskFlux& flux)
{
return std::make_unique<MemoryFluxSource>(flux);
return std::make_unique<MemoryFluxSource>(flux);
}

View File

@@ -6,7 +6,7 @@
#include "fluxsource/fluxsource.h"
#include "scp.h"
#include "proto.h"
#include "fmt/format.h"
#include "lib/logger.h"
#include <fstream>
static int trackno(int strack)
@@ -31,7 +31,7 @@ public:
{
_if.open(_config.filename(), std::ios::in | std::ios::binary);
if (!_if.is_open())
Error() << fmt::format("cannot open input file '{}': {}",
error("cannot open input file '{}': {}",
_config.filename(),
strerror(errno));
@@ -40,9 +40,9 @@ public:
if ((_header.file_id[0] != 'S') || (_header.file_id[1] != 'C') ||
(_header.file_id[2] != 'P'))
Error() << "input not a SCP file";
error("input not a SCP file");
int tpi = (_header.flags & SCP_FLAG_96TPI) ? 96 : 48;
int tpi = (_header.flags & SCP_FLAG_96TPI) ? 96 : 48;
::config.set_tpi(tpi);
_resolution = 25 * (_header.resolution + 1);
@@ -50,14 +50,14 @@ public:
int endSide = (_header.heads == 1) ? 0 : 1;
if ((_header.cell_width != 0) && (_header.cell_width != 16))
Error() << "currently only 16-bit cells in SCP files are supported";
error("currently only 16-bit cells in SCP files are supported");
std::cout << fmt::format("SCP tracks {}-{}, heads {}-{}\n",
log("SCP tracks {}-{}, heads {}-{}",
trackno(_header.start_track),
trackno(_header.end_track),
startSide,
endSide);
std::cout << fmt::format("SCP sample resolution: {} ns\n", _resolution);
log("SCP sample resolution: {} ns", _resolution);
}
public:
@@ -78,7 +78,7 @@ public:
if ((trackheader.track_id[0] != 'T') ||
(trackheader.track_id[1] != 'R') ||
(trackheader.track_id[2] != 'K'))
Error() << "corrupt SCP file";
error("corrupt SCP file");
std::vector<ScpTrackRevolution> revs(_header.revolutions);
for (int revolution = 0; revolution < _header.revolutions; revolution++)
@@ -134,7 +134,7 @@ private:
void check_for_error()
{
if (_if.fail())
Error() << fmt::format("SCP read I/O error: {}", strerror(errno));
error("SCP read I/O error: {}", strerror(errno));
}
private:

View File

@@ -2,40 +2,39 @@
#include "fluxmap.h"
#include "fluxsource/fluxsource.h"
#include "lib/fluxsource/fluxsource.pb.h"
#include "fmt/format.h"
class TestPatternFluxSource : public TrivialFluxSource
{
public:
TestPatternFluxSource(const TestPatternFluxSourceProto& config):
_config(config)
{}
_config(config)
{
}
~TestPatternFluxSource() {}
public:
std::unique_ptr<const Fluxmap> readSingleFlux(int track, int side) override
{
auto fluxmap = std::make_unique<Fluxmap>();
auto fluxmap = std::make_unique<Fluxmap>();
while (fluxmap->duration() < (_config.sequence_length_us()*1000000.0))
{
fluxmap->appendInterval(_config.interval_us());
fluxmap->appendPulse();
}
while (fluxmap->duration() < (_config.sequence_length_us() * 1000000.0))
{
fluxmap->appendInterval(_config.interval_us());
fluxmap->appendPulse();
}
return fluxmap;
return fluxmap;
}
void recalibrate() {}
private:
const TestPatternFluxSourceProto& _config;
const TestPatternFluxSourceProto& _config;
};
std::unique_ptr<FluxSource> FluxSource::createTestPatternFluxSource(const TestPatternFluxSourceProto& config)
std::unique_ptr<FluxSource> FluxSource::createTestPatternFluxSource(
const TestPatternFluxSourceProto& config)
{
return std::make_unique<TestPatternFluxSource>(config);
}

View File

@@ -4,8 +4,8 @@
double getCurrentTime(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
struct timeval tv;
gettimeofday(&tv, NULL);
return double(tv.tv_sec) + tv.tv_usec/1000000.0;
return double(tv.tv_sec) + tv.tv_usec / 1000000.0;
}

View File

@@ -15,6 +15,7 @@
#include <climits>
#include <variant>
#include <optional>
#include <fmt/format.h>
#if defined(_WIN32) || defined(__WIN32__)
#include <direct.h>
@@ -41,29 +42,12 @@ struct ErrorException
void print() const;
};
class Error
template <typename... Args>
[[ noreturn ]]
inline void error(fmt::string_view fstr, const Args&... args)
{
public:
Error()
{
_stream << "Error: ";
}
[[ noreturn ]] ~Error() noexcept(false)
{
throw ErrorException { _stream.str() };
}
template <typename T>
Error& operator<<(T&& t)
{
_stream << t;
return *this;
}
private:
std::stringstream _stream;
};
throw ErrorException { fmt::format(fstr, args...) };
}
template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

View File

@@ -1,44 +1,41 @@
#include "globals.h"
#include "bytes.h"
#include "fmt/format.h"
void hexdump(std::ostream& stream, const Bytes& buffer)
{
size_t pos = 0;
size_t pos = 0;
while (pos < buffer.size())
{
stream << fmt::format("{:05x} : ", pos);
for (int i=0; i<16; i++)
{
if ((pos+i) < buffer.size())
stream << fmt::format("{:02x} ", buffer[pos+i]);
else
stream << "-- ";
}
stream << " : ";
for (int i=0; i<16; i++)
{
if ((pos+i) >= buffer.size())
break;
while (pos < buffer.size())
{
stream << fmt::format("{:05x} : ", pos);
for (int i = 0; i < 16; i++)
{
if ((pos + i) < buffer.size())
stream << fmt::format("{:02x} ", buffer[pos + i]);
else
stream << "-- ";
}
stream << " : ";
for (int i = 0; i < 16; i++)
{
if ((pos + i) >= buffer.size())
break;
uint8_t c = buffer[pos+i];
if ((c >= 32) && (c <= 126))
stream << (char)c;
else
stream << '.';
}
stream << std::endl;
uint8_t c = buffer[pos + i];
if ((c >= 32) && (c <= 126))
stream << (char)c;
else
stream << '.';
}
stream << std::endl;
pos += 16;
}
pos += 16;
}
}
void hexdumpForSrp16(std::ostream& stream, const Bytes& buffer)
{
for (uint8_t byte : buffer)
stream << fmt::format("{:02x}", byte);
stream << std::endl;
for (uint8_t byte : buffer)
stream << fmt::format("{:02x}", byte);
stream << std::endl;
}

View File

@@ -2,7 +2,6 @@
#include "flags.h"
#include "sector.h"
#include "imagereader/imagereader.h"
#include "fmt/format.h"
#include "image.h"
#include "logger.h"
#include "proto.h"
@@ -20,7 +19,7 @@ public:
std::ifstream inputFile(
_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
error("cannot open input file");
inputFile.seekg(0, inputFile.beg);
uint32_t begin = inputFile.tellg();
@@ -36,7 +35,7 @@ public:
unsigned numHeads = 1;
unsigned numSectors = 0;
Logger() << fmt::format("D64: reading image with {} tracks, {} heads", numTracks, numHeads);
log("D64: reading image with {} tracks, {} heads", numTracks, numHeads);
uint32_t offset = 0;

View File

@@ -6,7 +6,6 @@
#include "proto.h"
#include "logger.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
@@ -24,7 +23,7 @@ public:
std::ifstream inputFile(
_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
error("cannot open input file");
Bytes header(0x24); // read first entry of track table as well
inputFile.read((char*)header.begin(), header.size());
@@ -35,7 +34,7 @@ public:
std::string diskName = header.slice(0, 0x16);
if (diskName[0])
Logger() << fmt::format("D88: disk name: {}", diskName);
log("D88: disk name: {}", diskName);
ByteReader headerReader(header);
@@ -47,7 +46,7 @@ public:
int diskSize = headerReader.seek(0x1c).read_le32();
if (diskSize > fileSize)
Logger() << "D88: found multiple disk images. Only using first";
log("D88: found multiple disk images. Only using first");
int trackTableEnd = headerReader.seek(0x20).read_le32();
int trackTableSize = trackTableEnd - 0x20;
@@ -59,25 +58,25 @@ public:
if (config.encoder().format_case() !=
EncoderProto::FormatCase::FORMAT_NOT_SET)
Logger() << "D88: overriding configured format";
log("D88: overriding configured format");
auto ibm = config.mutable_encoder()->mutable_ibm();
int clockRate = 500;
if (mediaFlag == 0x20)
{
Logger() << "D88: forcing high density mode";
config.mutable_drive()->set_high_density(true);
config.set_tpi(96);
log("D88: forcing high density mode");
config.mutable_drive()->set_high_density(true);
config.set_tpi(96);
}
else
{
Logger() << "D88: forcing single/double density mode";
log("D88: forcing single/double density mode");
clockRate = 300;
config.mutable_drive()->set_high_density(false);
config.set_tpi(48);
config.mutable_drive()->set_high_density(false);
config.set_tpi(48);
}
auto layout = config.mutable_layout();
auto layout = config.mutable_layout();
std::unique_ptr<Image> image(new Image);
for (int track = 0; track < trackTableSize / 4; track++)
{
@@ -96,7 +95,7 @@ public:
trackdata->set_target_clock_period_us(1e3 / clockRate);
trackdata->set_target_rotational_period_ms(167);
auto layoutdata = layout->add_layoutdata();
auto layoutdata = layout->add_layoutdata();
auto physical = layoutdata->mutable_physical();
for (int sectorInTrack = 0; sectorInTrack < currentSectorsInTrack;
@@ -116,26 +115,27 @@ public:
int fddStatusCode = sectorHeaderReader.seek(8).read_8();
int rpm = sectorHeaderReader.seek(13).read_8();
int dataLength = sectorHeaderReader.seek(14).read_le16();
if (dataLength < sectorSize) {
if (dataLength < sectorSize)
{
dataLength = sectorSize;
}
// D88 provides much more sector information that is currently
// ignored
if (ddam != 0)
Error() << "D88: nonzero ddam currently unsupported";
error("D88: nonzero ddam currently unsupported");
if (rpm != 0)
Error()
<< "D88: 1.44MB 300rpm formats currently unsupported";
error("D88: 1.44MB 300rpm formats currently unsupported");
if (fddStatusCode != 0)
Error() << "D88: nonzero fdd status codes are currently "
"unsupported";
error(
"D88: nonzero fdd status codes are currently "
"unsupported");
if (currentSectorsInTrack == 0xffff)
{
currentSectorsInTrack = sectorsInTrack;
}
else if (currentSectorsInTrack != sectorsInTrack)
{
Error() << "D88: mismatched number of sectors in track";
error("D88: mismatched number of sectors in track");
}
if (currentTrackTrack < 0)
{
@@ -143,8 +143,9 @@ public:
}
else if (currentTrackTrack != track)
{
Error() << "D88: all sectors in a track must belong to the "
"same track";
error(
"D88: all sectors in a track must belong to the same "
"track");
}
if (trackSectorSize < 0)
{
@@ -166,14 +167,17 @@ public:
trackdata->set_dam_byte(0xf56f);
}
// create timings to approximately match N88-BASIC
if (clockRate == 300) {
if (clockRate == 300)
{
if (sectorSize <= 256)
{
trackdata->set_gap0(0x1b);
trackdata->set_gap2(0x14);
trackdata->set_gap3(0x1b);
}
} else {
}
else
{
if (sectorSize <= 128)
{
trackdata->set_gap0(0x1b);
@@ -189,12 +193,13 @@ public:
}
else if (trackSectorSize != sectorSize)
{
Error() << "D88: multiple sector sizes per track are "
"currently unsupported";
error(
"D88: multiple sector sizes per track are currently "
"unsupported");
}
Bytes data(sectorSize);
inputFile.read((char*)data.begin(), data.size());
inputFile.seekg(dataLength-sectorSize, std::ios_base::cur);
inputFile.seekg(dataLength - sectorSize, std::ios_base::cur);
physical->add_sector(sectorId);
const auto& sector = image->put(track, head, sectorId);
sector->status = Sector::OK;
@@ -211,12 +216,12 @@ public:
image->calculateSize();
const Geometry& geometry = image->getGeometry();
Logger() << fmt::format("D88: read {} tracks, {} sides",
log("D88: read {} tracks, {} sides",
geometry.numTracks,
geometry.numSides);
layout->set_tracks(geometry.numTracks);
layout->set_sides(geometry.numSides);
layout->set_tracks(geometry.numTracks);
layout->set_sides(geometry.numSides);
return image;
}

View File

@@ -6,7 +6,6 @@
#include "logger.h"
#include "proto.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
@@ -24,12 +23,12 @@ public:
std::ifstream inputFile(
_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
error("cannot open input file");
Bytes header(256);
inputFile.read((char*)header.begin(), header.size());
if (header.slice(0xAB, 13) != Bytes("DIFC HEADER "))
Error() << "DIM: could not find DIM header, is this a DIM file?";
error("DIM: could not find DIM header, is this a DIM file?");
// the DIM header technically has a bit field for sectors present,
// however it is currently ignored by this reader
@@ -61,7 +60,7 @@ public:
sectorSize = 512;
break;
default:
Error() << "DIM: unsupported media byte";
error("DIM: unsupported media byte");
break;
}
@@ -92,7 +91,7 @@ public:
trackCount++;
}
auto layout = config.mutable_layout();
auto layout = config.mutable_layout();
if (config.encoder().format_case() ==
EncoderProto::FormatCase::FORMAT_NOT_SET)
{
@@ -100,36 +99,36 @@ public:
auto trackdata = ibm->add_trackdata();
trackdata->set_target_clock_period_us(2);
auto layoutdata = layout->add_layoutdata();
auto layoutdata = layout->add_layoutdata();
auto physical = layoutdata->mutable_physical();
switch (mediaByte)
{
case 0x00:
Logger() << "DIM: automatically setting format to 1.2MB "
"(1024 byte sectors)";
log("DIM: automatically setting format to 1.2MB "
"(1024 byte sectors)");
trackdata->set_target_rotational_period_ms(167);
layoutdata->set_sector_size(1024);
for (int i = 0; i < 9; i++)
physical->add_sector(i);
break;
case 0x02:
Logger() << "DIM: automatically setting format to 1.2MB "
"(512 byte sectors)";
log("DIM: automatically setting format to 1.2MB "
"(512 byte sectors)");
trackdata->set_target_rotational_period_ms(167);
layoutdata->set_sector_size(512);
for (int i = 0; i < 15; i++)
physical->add_sector(i);
break;
case 0x03:
Logger() << "DIM: automatically setting format to 1.44MB";
log("DIM: automatically setting format to 1.44MB");
trackdata->set_target_rotational_period_ms(200);
layoutdata->set_sector_size(512);
for (int i = 0; i < 18; i++)
physical->add_sector(i);
break;
default:
Error() << fmt::format(
"DIM: unknown media byte 0x%02x, could not determine "
error(
"DIM: unknown media byte 0x{:02x}, could not determine "
"write profile automatically",
mediaByte);
break;
@@ -140,13 +139,13 @@ public:
image->calculateSize();
const Geometry& geometry = image->getGeometry();
Logger() << fmt::format("DIM: read {} tracks, {} sides, {} kB total",
log("DIM: read {} tracks, {} sides, {} kB total",
geometry.numTracks,
geometry.numSides,
((int)inputFile.tellg() - 256) / 1024);
layout->set_tracks(geometry.numTracks);
layout->set_sides(geometry.numSides);
layout->set_tracks(geometry.numTracks);
layout->set_sides(geometry.numSides);
return image;
}

View File

@@ -5,7 +5,6 @@
#include "image.h"
#include "logger.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
@@ -20,7 +19,7 @@ public:
std::ifstream inputFile(
_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
error("cannot open input file");
Bytes data;
data.writer() += inputFile;
@@ -61,12 +60,10 @@ public:
break;
default:
Error() << fmt::format(
"don't understand DiskCopy disks of type {}", encoding);
error("don't understand DiskCopy disks of type {}", encoding);
}
Logger() << fmt::format(
"DC42: reading image with {} tracks, {} heads; {}; {}",
log("DC42: reading image with {} tracks, {} heads; {}; {}",
numTracks,
numHeads,
mfm ? "MFM" : "GCR",

View File

@@ -6,7 +6,6 @@
#include "proto.h"
#include "logger.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
@@ -24,13 +23,13 @@ public:
std::ifstream inputFile(
_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
error("cannot open input file");
Bytes header(32);
inputFile.read((char*)header.begin(), header.size());
ByteReader headerReader(header);
if (headerReader.seek(0).read_le32() != 0)
Error() << "FDI: could not find FDI header, is this a FDI file?";
error("FDI: could not find FDI header, is this a FDI file?");
// we currently don't use fddType but it could be used to automatically
// select profile parameters in the future
@@ -62,8 +61,7 @@ public:
Bytes data(sectorSize);
inputFile.read((char*)data.begin(), data.size());
const auto& sector =
image->put(track, side, sectorId);
const auto& sector = image->put(track, side, sectorId);
sector->status = Sector::OK;
sector->data = data;
}
@@ -72,7 +70,7 @@ public:
trackCount++;
}
auto layout = config.mutable_layout();
auto layout = config.mutable_layout();
if (config.encoder().format_case() ==
EncoderProto::FormatCase::FORMAT_NOT_SET)
{
@@ -80,13 +78,13 @@ public:
auto trackdata = ibm->add_trackdata();
trackdata->set_target_clock_period_us(2);
auto layoutdata = layout->add_layoutdata();
auto layoutdata = layout->add_layoutdata();
auto physical = layoutdata->mutable_physical();
switch (fddType)
{
case 0x90:
Logger() << "FDI: automatically setting format to 1.2MB "
"(1024 byte sectors)";
log("FDI: automatically setting format to 1.2MB (1024 byte "
"sectors)");
trackdata->set_target_rotational_period_ms(167);
layoutdata->set_sector_size(1024);
for (int i = 0; i < 9; i++)
@@ -94,7 +92,7 @@ public:
break;
case 0x30:
Logger() << "FDI: automatically setting format to 1.44MB";
log("FDI: automatically setting format to 1.44MB");
trackdata->set_target_rotational_period_ms(200);
layoutdata->set_sector_size(512);
for (int i = 0; i < 18; i++)
@@ -102,7 +100,7 @@ public:
break;
default:
Error() << fmt::format(
error(
"FDI: unknown fdd type 0x{:2x}, could not determine "
"write profile automatically",
fddType);
@@ -112,13 +110,13 @@ public:
image->calculateSize();
const Geometry& geometry = image->getGeometry();
Logger() << fmt::format("FDI: read {} tracks, {} sides, {} kB total",
log("FDI: read {} tracks, {} sides, {} kB total",
geometry.numTracks,
geometry.numSides,
((int)inputFile.tellg() - headerSize) / 1024);
layout->set_tracks(geometry.numTracks);
layout->set_sides(geometry.numSides);
layout->set_tracks(geometry.numTracks);
layout->set_sides(geometry.numSides);
return image;
}

View File

@@ -3,7 +3,6 @@
#include "sector.h"
#include "imagereader/imagereader.h"
#include "utils.h"
#include "fmt/format.h"
#include "proto.h"
#include "image.h"
#include "lib/layout.h"
@@ -50,7 +49,7 @@ std::unique_ptr<ImageReader> ImageReader::create(const ImageReaderProto& config)
return ImageReader::createTd0ImageReader(config);
default:
Error() << "bad input file config";
error("bad input file config");
return std::unique_ptr<ImageReader>();
}
}
@@ -91,7 +90,7 @@ void ImageReader::updateConfigForFilename(
}
}
Error() << fmt::format("unrecognised image filename '{}'", filename);
error("unrecognised image filename '{}'", filename);
}
ImageReader::ImageReader(const ImageReaderProto& config): _config(config) {}
@@ -103,7 +102,7 @@ std::unique_ptr<Image> ImageReader::readMappedImage()
if (!_config.filesystem_sector_order())
return rawImage;
Logger() << "READER: converting from filesystem sector order to disk order";
log("READER: converting from filesystem sector order to disk order");
std::set<std::shared_ptr<const Sector>> sectors;
for (const auto& e : *rawImage)
{

View File

@@ -7,51 +7,53 @@
#include "logger.h"
#include "layout.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
static unsigned getModulationandSpeed(uint8_t flags, bool *fm)
static unsigned getModulationandSpeed(uint8_t flags, bool* fm)
{
switch (flags)
{
case 0: /* 500 kbps FM */
//clockRateKhz.setDefaultValue(250);
*fm = true;
return 500;
break;
{
case 0: /* 500 kbps FM */
// clockRateKhz.setDefaultValue(250);
*fm = true;
return 500;
break;
case 1: /* 300 kbps FM */
*fm = true;
return 300;
break;
case 1: /* 300 kbps FM */
*fm = true;
return 300;
case 2: /* 250 kbps FM */
*fm = true;
return 250;
break;
break;
case 3: /* 500 kbps MFM */
*fm = false;
return 500;
break;
case 2: /* 250 kbps FM */
*fm = true;
return 250;
break;
case 4: /* 300 kbps MFM */
*fm = false;
return 300;
break;
case 3: /* 500 kbps MFM */
*fm = false;
return 500;
break;
case 5: /* 250 kbps MFM */
*fm = false;
return 250;
break;
case 4: /* 300 kbps MFM */
*fm = false;
return 300;
break;
default:
Error() << fmt::format("IMD: don't understand IMD disks with this modulation and speed {}", flags);
throw 0;
}
case 5: /* 250 kbps MFM */
*fm = false;
return 250;
break;
default:
error(
"IMD: don't understand IMD disks with this modulation and "
"speed {}",
flags);
throw 0;
}
}
struct TrackHeader
@@ -67,73 +69,86 @@ static unsigned getSectorSize(uint8_t flags)
{
switch (flags)
{
case 0:
return 128;
break;
case 1:
return 256;
break;
case 2:
return 512;
break;
case 3:
return 1024;
break;
case 4:
return 2048;
break;
case 5:
return 4096;
break;
case 6:
return 8192;
break;
default:
Error() << "not reachable";
throw 0;
}
case 0:
return 128;
break;
case 1:
return 256;
break;
case 2:
return 512;
break;
case 3:
return 1024;
break;
case 4:
return 2048;
break;
case 5:
return 4096;
break;
case 6:
return 8192;
break;
default:
error("not reachable");
throw 0;
}
}
#define SEC_CYL_MAP_FLAG 0x80
#define SEC_CYL_MAP_FLAG 0x80
#define SEC_HEAD_MAP_FLAG 0x40
#define HEAD_MASK 0x3F
#define END_OF_FILE 0x1A
#define HEAD_MASK 0x3F
#define END_OF_FILE 0x1A
class IMDImageReader : public ImageReader
{
public:
IMDImageReader(const ImageReaderProto& config): ImageReader(config) {}
std::unique_ptr<Image> readImage()
// clang-format off
/*
IMAGE FILE FORMAT
The overall layout of an ImageDisk .IMD image file is:
IMD v.vv: dd/mm/yyyy hh:mm:ss
Comment (ASCII only - unlimited size)
1A byte - ASCII EOF character
- For each track on the disk:
1 byte Mode value see getModulationspeed for definition
1 byte Track
1 byte Head
1 byte number of sectors in track
1 byte sector size see getsectorsize for definition
sector numbering map
sector track map (optional) definied in high byte of head (since head is 0 or 1)
sector head map (optional) definied in high byte of head (since head is 0 or 1)
sector data records
<End of file>
*/
* IMAGE FILE FORMAT
* The overall layout of an ImageDisk .IMD image file is:
* IMD v.vv: dd/mm/yyyy hh:mm:ss
* Comment (ASCII only - unlimited size)
* 1A byte - ASCII EOF character
* - For each track on the disk:
* 1 byte Mode value (0-5) see getModulationspeed for definition
* 1 byte Cylinder (0-n)
* 1 byte Head (0-1)
* 1 byte number of sectors in track (1-n)
* 1 byte sector size (0-6) see getsectorsize for definition
* sector numbering map IMD start numbering sectors with 1.
* sector cylinder map (optional) definied in high byte of head (since head is 0 or 1)
* sector head map (optional) definied in high byte of head (since head is 0 or 1)
* sector data records For each data record:
* 1 byte Sector status
* 0: Sector data unavailable - could not be read
* 1: Normal data: (Sector Size) bytes follow
* 2: Compressed: All bytes in sector have same value (xx)
* 3: Normal data with "Deleted-Data address mark"
* 4: Compressed with "Deleted-Data address mark"
* 5: Normal data read with data error
* 6: Compressed read with data error"
* 7: Deleted data read with data error"
* 8: Compressed, Deleted read with data error"
* sector size of Sector data
* <End of file>
*/
// clang-format on
std::unique_ptr<Image> readImage()
{
//Read File
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
// Read File
std::ifstream inputFile(
_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "IMD: cannot open input file";
//define some variables
bool fm = false; //define coding just to show in comment for setting the right write parameters
error("IMD: cannot open input file");
// define some variables
bool fm = false; // define coding just to show in comment for setting
// the right write parameters
inputFile.seekg(0, inputFile.end);
int inputFileSize = inputFile.tellg(); // determine filesize
int inputFileSize = inputFile.tellg(); // determine filesize
inputFile.seekg(0, inputFile.beg);
Bytes data;
data.writer() += inputFile;
@@ -148,32 +163,30 @@ public:
unsigned headerPtr = 0;
unsigned Modulation_Speed = 0;
unsigned sectorSize = 0;
std::string sector_skew;
std::string sector_skew;
int b;
int b;
std::string comment;
bool blnOptionalCylinderMap = false;
bool blnOptionalHeadMap = false;
int trackSectorSize = -1;
// Read comment
comment.clear();
while ((b = br.read_8()) != EOF && b != END_OF_FILE)
{
comment.push_back(b);
n++;
}
headerPtr = n; //set pointer to after comment
Logger() << "Comment in IMD file:"
<< fmt::format("{}",
comment);
bool blnOptionalCylinderMap = false;
bool blnOptionalHeadMap = false;
int trackSectorSize = -1;
// Read comment
comment.clear();
while ((b = br.read_8()) != EOF && b != END_OF_FILE)
{
comment.push_back(b);
n++;
}
headerPtr = n; // set pointer to after comment
log("Comment in IMD file: {}", comment);
for (;;)
{
if (headerPtr >= inputFileSize-1)
if (headerPtr >= inputFileSize - 1)
{
break;
}
//first read header
// first read header
header.ModeValue = br.read_8();
headerPtr++;
Modulation_Speed = getModulationandSpeed(header.ModeValue, &fm);
@@ -187,237 +200,272 @@ public:
headerPtr++;
sectorSize = getSectorSize(header.SectorSize);
unsigned optionalsector_map[header.numSectors];
//The Sector Cylinder Map has one entry for each sector, and contains the logical Cylinder ID for the corresponding sector in the Sector Numbering Map.
if (header.Head & SEC_CYL_MAP_FLAG)
{
//Read optional cylinder map
for (b = 0; b < header.numSectors; b++)
{
optionalsector_map[b] = br.read_8();
headerPtr++;
}
blnOptionalCylinderMap = true; //set bool so we know there is an optional cylinder map
header.Head = header.Head^SEC_CYL_MAP_FLAG; //remove flag 10000001 ^ 10000000 = 00000001 and 10000000 ^ 10000000 = 00000000
}
//Read optional sector head map
//The Sector Head Map has one entry for each sector, and contains the logical Head ID for the corresponding sector in the Sector Numbering Map.
unsigned optionalhead_map[header.numSectors];
if (header.Head & SEC_HEAD_MAP_FLAG)
{
//Read optional sector head map
for (b = 0; b < header.numSectors; b++)
{
optionalhead_map[b] = br.read_8();
headerPtr++;
}
blnOptionalHeadMap = true; //set bool so we know there is an optional head map
header.Head = header.Head^SEC_HEAD_MAP_FLAG; //remove flag 01000001 ^ 01000001 = 00000001 and 01000000 ^ 0100000 = 00000000 for writing sector head later
}
//read sector numbering map
sector_skew.clear();
bool blnBase0 = false; //check what first start number of the sector is. Fluxengine expects 1.
for (b = 0; b < header.numSectors; b++)
{
uint8_t t;
t = br.read_8();
if (t == 0x00) blnBase0 = true;
if (blnBase0)
{
t=t+1;
}
sector_skew.push_back(t);
headerPtr++;
unsigned optionalsector_map[header.numSectors];
// The Sector Cylinder Map has one entry for each sector, and
// contains the logical Cylinder ID for the corresponding sector in
// the Sector Numbering Map.
if (header.Head & SEC_CYL_MAP_FLAG)
{
// Read optional cylinder map
for (b = 0; b < header.numSectors; b++)
{
optionalsector_map[b] = br.read_8();
headerPtr++;
}
blnOptionalCylinderMap = true; // set bool so we know there is
// an optional cylinder map
header.Head =
header.Head ^
SEC_CYL_MAP_FLAG; // remove flag 10000001 ^ 10000000 =
// 00000001 and 10000000 ^ 10000000 =
// 00000000
}
// Read optional sector head map
// The Sector Head Map has one entry for each sector, and contains
// the logical Head ID for the corresponding sector in the Sector
// Numbering Map.
unsigned optionalhead_map[header.numSectors];
if (header.Head & SEC_HEAD_MAP_FLAG)
{
// Read optional sector head map
for (b = 0; b < header.numSectors; b++)
{
optionalhead_map[b] = br.read_8();
headerPtr++;
}
blnOptionalHeadMap =
true; // set bool so we know there is an optional head map
header.Head =
header.Head ^
SEC_HEAD_MAP_FLAG; // remove flag 01000001 ^ 01000001 =
// 00000001 and 01000000 ^ 0100000 =
// 00000000 for writing sector head later
}
// read sector numbering map
sector_skew.clear();
bool blnBase0 = false; // check what first start number of the
// sector is. Fluxengine expects 1.
for (b = 0; b < header.numSectors; b++)
{
uint8_t t;
t = br.read_8();
if (t == 0x00)
blnBase0 = true;
if (blnBase0)
{
t = t + 1;
}
sector_skew.push_back(t);
headerPtr++;
}
auto ibm = config.mutable_encoder()->mutable_ibm();
auto ibm = config.mutable_encoder()->mutable_ibm();
auto trackdata = ibm->add_trackdata();
auto layoutdata = layout->add_layoutdata();
trackdata->set_target_clock_period_us(1e3 / Modulation_Speed);
trackdata->set_target_rotational_period_ms(200);
if (trackSectorSize < 0)
{
trackSectorSize = sectorSize;
// this is the first sector we've read, use it settings for
// per-track data
trackdata->set_track(header.track);
trackdata->set_head(header.Head);
trackdata->set_use_fm(fm);
if (trackSectorSize < 0)
{
trackSectorSize = sectorSize;
// this is the first sector we've read, use it settings for
// per-track data
trackdata->set_track(header.track);
trackdata->set_head(header.Head);
trackdata->set_use_fm(fm);
layoutdata->set_track(header.track);
layoutdata->set_side(header.Head);
layoutdata->set_sector_size(sectorSize);
}
else if (trackSectorSize != sectorSize)
{
Error() << "IMD: multiple sector sizes per track are "
"currently unsupported";
}
//read the sectors
layoutdata->set_track(header.track);
layoutdata->set_side(header.Head);
layoutdata->set_sector_size(sectorSize);
}
else if (trackSectorSize != sectorSize)
{
error(
"IMD: multiple sector sizes per track are "
"currently unsupported");
}
// read the sectors
for (int s = 0; s < header.numSectors; s++)
{
Bytes sectordata;
Bytes compressed(sectorSize);
int SectorID;
SectorID = sector_skew[s];
const auto& sector = image->put(header.track, header.Head, SectorID);
//read the status of the sector
Bytes compressed(sectorSize);
int SectorID;
SectorID = sector_skew[s];
const auto& sector =
image->put(header.track, header.Head, SectorID);
// read the status of the sector
unsigned int Status_Sector = br.read_8();
headerPtr++;
switch (Status_Sector)
{
/*fluxengine knows of a few sector statussen but not all of the statussen in IMD.
* // the statussen are in sector.h. Translation to fluxengine is as follows:
* Statussen fluxengine | Status IMD
*--------------------------------------------------------------------------------------------------------------------
* OK, | 1, 2 (Normal data: (Sector Size) of (compressed) bytes follow)
* BAD_CHECKSUM, | 5, 6, 7, 8
* MISSING, sector not found | 0 (Sector data unavailable - could not be read)
* DATA_MISSING, sector present but no data found | 3, 4
* CONFLICT, |
* INTERNAL_ERROR |
*/
case 0: /* Sector data unavailable - could not be read */
// clang-format off
/* fluxengine knows of a few sector statussen but not all of the statussen in IMD.
* // the statussen are in sector.h. Translation to fluxengine is as follows:
* Statussen fluxengine | Status IMD
*--------------------------------------------------------------------------------------------------------------------
* OK, | 1, 2 (Normal data: (Sector Size) of (compressed) bytes follow)
* BAD_CHECKSUM, | 5, 6, 7, 8
* MISSING, sector not found | 0 (Sector data unavailable - could not be read)
* DATA_MISSING, sector present but no data found | 3, 4
* CONFLICT, |
* INTERNAL_ERROR |
*/
// clang-format on
case 0: /* Sector data unavailable - could not be read */
sector->status = Sector::MISSING;
break;
sector->status = Sector::MISSING;
break;
case 1: /* Normal data: (Sector Size) bytes follow */
sectordata = br.read(sectorSize);
headerPtr += sectorSize;
sector->data.writer().append(sectordata);
sector->status = Sector::OK;
break;
case 1: /* Normal data: (Sector Size) bytes follow */
sectordata = br.read(sectorSize);
headerPtr += sectorSize;
sector->data.writer().append(sectordata);
sector->status = Sector::OK;
break;
case 2: /* Compressed: All bytes in sector have same value (xx) */
compressed[0] = br.read_8();
headerPtr++;
for (int k = 1; k < sectorSize; k++)
{
//fill data till sector is full
br.seek(headerPtr);
compressed[k] = br.read_8();
}
sector->data.writer().append(compressed);
sector->status = Sector::OK;
break;
case 2: /* Compressed: All bytes in sector have same value
(xx) */
compressed[0] = br.read_8();
headerPtr++;
for (int k = 1; k < sectorSize; k++)
{
// fill data till sector is full
br.seek(headerPtr);
compressed[k] = br.read_8();
}
sector->data.writer().append(compressed);
sector->status = Sector::OK;
break;
case 3: /* Normal data with "Deleted-Data address mark" */
sector->status = Sector::DATA_MISSING;
sectordata = br.read(sectorSize);
headerPtr += sectorSize;
sector->data.writer().append(sectordata);
break;
case 3: /* Normal data with "Deleted-Data address mark" */
sector->status = Sector::DATA_MISSING;
sectordata = br.read(sectorSize);
headerPtr += sectorSize;
sector->data.writer().append(sectordata);
break;
case 4: /* Compressed with "Deleted-Data address mark"*/
compressed[0] = br.read_8();
headerPtr++;
for (int k = 1; k < sectorSize; k++)
{
//fill data till sector is full
br.seek(headerPtr);
compressed[k] = br.read_8();
}
sector->data.writer().append(compressed);
sector->status = Sector::DATA_MISSING;
break;
case 4: /* Compressed with "Deleted-Data address mark"*/
compressed[0] = br.read_8();
headerPtr++;
for (int k = 1; k < sectorSize; k++)
{
// fill data till sector is full
br.seek(headerPtr);
compressed[k] = br.read_8();
}
sector->data.writer().append(compressed);
sector->status = Sector::DATA_MISSING;
break;
case 5: /* Normal data read with data error*/
sectordata = br.read(sectorSize);
headerPtr += sectorSize;
sector->status = Sector::BAD_CHECKSUM;
sector->data.writer().append(sectordata);
break;
case 5: /* Normal data read with data error*/
sectordata = br.read(sectorSize);
headerPtr += sectorSize;
sector->status = Sector::BAD_CHECKSUM;
sector->data.writer().append(sectordata);
break;
case 6: /* Compressed read with data error*/
compressed[0] = br.read_8();
headerPtr++;
for (int k = 1; k < sectorSize; k++)
{
//fill data till sector is full
br.seek(headerPtr);
compressed[k] = br.read_8();
}
sector->data.writer().append(compressed);
sector->status = Sector::BAD_CHECKSUM;
break;
case 6: /* Compressed read with data error*/
compressed[0] = br.read_8();
headerPtr++;
for (int k = 1; k < sectorSize; k++)
{
// fill data till sector is full
br.seek(headerPtr);
compressed[k] = br.read_8();
}
sector->data.writer().append(compressed);
sector->status = Sector::BAD_CHECKSUM;
break;
case 7: /* Deleted data read with data error*/
sectordata = br.read(sectorSize);
headerPtr += sectorSize;
sector->status = Sector::BAD_CHECKSUM;
sector->data.writer().append(sectordata);
break;
case 7: /* Deleted data read with data error*/
sectordata = br.read(sectorSize);
headerPtr += sectorSize;
sector->status = Sector::BAD_CHECKSUM;
sector->data.writer().append(sectordata);
break;
case 8: /* Compressed, Deleted read with data error*/
compressed[0] = br.read_8();
headerPtr++;
for (int k = 1; k < sectorSize; k++)
{
//fill data till sector is full
br.seek(headerPtr);
compressed[k] = br.read_8();
}
sector->data.writer().append(compressed);
sector->status = Sector::BAD_CHECKSUM;
break;
case 8: /* Compressed, Deleted read with data error*/
compressed[0] = br.read_8();
headerPtr++;
for (int k = 1; k < sectorSize; k++)
{
// fill data till sector is full
br.seek(headerPtr);
compressed[k] = br.read_8();
}
sector->data.writer().append(compressed);
sector->status = Sector::BAD_CHECKSUM;
break;
default:
Error() << fmt::format("IMD: Don't understand IMD files with sector status {}, track {}, sector {}", Status_Sector, header.track, s);
}
if (blnOptionalCylinderMap) //there was een optional cylinder map. write it to the sector
//The Sector Cylinder Map has one entry for each sector, and contains the logical Cylinder ID for the corresponding sector in the Sector Numbering Map.
{
sector->physicalTrack = Layout::remapTrackLogicalToPhysical(header.track);
sector->logicalTrack = optionalsector_map[s];
blnOptionalCylinderMap = false;
}
else
{
sector->logicalTrack = header.track;
sector->physicalTrack = Layout::remapTrackLogicalToPhysical(header.track);
}
if (blnOptionalHeadMap) //there was een optional head map. write it to the sector
//The Sector Head Map has one entry for each sector, and contains the logical Head ID for the corresponding sector in the Sector Numbering Map.
{
sector->physicalSide = header.Head;
sector->logicalSide = optionalhead_map[s];
blnOptionalHeadMap = false;
}
else
{
sector->logicalSide = header.Head;
default:
error(
"IMD: Don't understand IMD files with sector "
"status {}, track {}, sector {}",
Status_Sector,
header.track,
s);
}
if (blnOptionalCylinderMap) // there was een optional cylinder
// map. write it to the sector
// The Sector Cylinder Map has one entry for each sector, and
// contains the logical Cylinder ID for the corresponding sector
// in the Sector Numbering Map.
{
sector->physicalTrack =
Layout::remapTrackLogicalToPhysical(header.track);
sector->logicalTrack = optionalsector_map[s];
blnOptionalCylinderMap = false;
}
else
{
sector->logicalTrack = header.track;
sector->physicalTrack =
Layout::remapTrackLogicalToPhysical(header.track);
}
if (blnOptionalHeadMap) // there was een optional head map.
// write it to the sector
// The Sector Head Map has one entry for each sector, and
// contains the logical Head ID for the corresponding sector in
// the Sector Numbering Map.
{
sector->physicalSide = header.Head;
}
sector->logicalSide = optionalhead_map[s];
blnOptionalHeadMap = false;
}
else
{
sector->logicalSide = header.Head;
sector->physicalSide = header.Head;
}
}
}
if (config.encoder().format_case() != EncoderProto::FormatCase::FORMAT_NOT_SET)
Logger() << "IMD: overriding configured format";
if (config.encoder().format_case() !=
EncoderProto::FormatCase::FORMAT_NOT_SET)
log("IMD: overriding configured format");
image->calculateSize();
const Geometry& geometry = image->getGeometry();
size_t headSize = ((header.numSectors) * (sectorSize));
size_t trackSize = (headSize * (header.Head + 1));
size_t headSize = ((header.numSectors) * (sectorSize));
size_t trackSize = (headSize * (header.Head + 1));
Logger() << "IMD: read "
<< fmt::format("{} tracks, {} heads; {}; {} kbps; {} sectors; sectorsize {}; {} kB total.",
header.track + 1, header.Head + 1,
fm ? "FM" : "MFM",
Modulation_Speed, header.numSectors, sectorSize, (header.track+1) * trackSize / 1024);
log("IMD: read {} tracks, {} heads; {}; {} kbps; {} sectors; "
"sectorsize {}; {} kB total.",
header.track + 1,
header.Head + 1,
fm ? "FM" : "MFM",
Modulation_Speed,
header.numSectors,
sectorSize,
(header.track + 1) * trackSize / 1024);
layout->set_tracks(geometry.numTracks);
layout->set_sides(geometry.numSides);
return image;
}
};

View File

@@ -8,7 +8,6 @@
#include "lib/layout.pb.h"
#include "lib/proto.h"
#include "lib/layout.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
@@ -23,12 +22,13 @@ public:
std::ifstream inputFile(
_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
error("cannot open input file");
auto layout = config.layout();
if (!layout.tracks() || !layout.sides())
Error() << "IMG: bad configuration; did you remember to set the "
"tracks, sides and trackdata fields in the layout?";
error(
"IMG: bad configuration; did you remember to set the "
"tracks, sides and trackdata fields in the layout?");
std::unique_ptr<Image> image(new Image);
for (const auto& p : Layout::getTrackOrdering())
@@ -53,11 +53,11 @@ public:
image->calculateSize();
const Geometry& geometry = image->getGeometry();
Logger() << fmt::format("IMG: read {} tracks, {} sides, {} kB total from {}",
log("IMG: read {} tracks, {} sides, {} kB total from {}",
geometry.numTracks,
geometry.numSides,
inputFile.tellg() / 1024,
_config.filename());
_config.filename());
return image;
}
};

View File

@@ -4,7 +4,6 @@
#include "imagereader/imagereader.h"
#include "image.h"
#include "logger.h"
#include "fmt/format.h"
#include "lib/config.pb.h"
#include <algorithm>
#include <iostream>
@@ -82,7 +81,7 @@ static unsigned getSectorSize(uint8_t flags)
return 512;
}
}
Error() << "not reachable";
error("not reachable");
}
class Jv3ImageReader : public ImageReader
@@ -95,7 +94,7 @@ public:
std::ifstream inputFile(
_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
error("cannot open input file");
inputFile.seekg(0, std::ios::end);
unsigned inputFileSize = inputFile.tellg();

View File

@@ -6,7 +6,6 @@
#include "proto.h"
#include "logger.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
@@ -24,18 +23,18 @@ public:
std::ifstream inputFile(
_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
error("cannot open input file");
Bytes fileId(14); // read first entry of track table as well
inputFile.read((char*)fileId.begin(), fileId.size());
if (fileId == Bytes("T98FDDIMAGE.R1"))
{
Error() << "NFD: r1 images are not currently supported";
error("NFD: r1 images are not currently supported");
}
if (fileId != Bytes("T98FDDIMAGE.R0"))
{
Error() << "NFD: could not find NFD header";
error("NFD: could not find NFD header");
}
Bytes header(0x10a10);
@@ -47,17 +46,17 @@ public:
char heads = headerReader.seek(0x115).read_8();
if (heads != 2)
{
Error() << "NFD: unsupported number of heads";
error("NFD: unsupported number of heads");
}
if (config.encoder().format_case() !=
EncoderProto::FormatCase::FORMAT_NOT_SET)
Logger() << "NFD: overriding configured format";
log("NFD: overriding configured format");
auto ibm = config.mutable_encoder()->mutable_ibm();
auto layout = config.mutable_layout();
Logger() << "NFD: HD 1.2MB mode";
Logger() << "NFD: forcing hign density mode";
auto layout = config.mutable_layout();
log("NFD: HD 1.2MB mode");
log("NFD: forcing hign density mode");
config.mutable_drive()->set_high_density(true);
config.set_tpi(96);
@@ -68,7 +67,7 @@ public:
trackdata->set_target_clock_period_us(2);
trackdata->set_target_rotational_period_ms(167);
auto layoutdata = layout->add_layoutdata();
auto layoutdata = layout->add_layoutdata();
auto physical = layoutdata->mutable_physical();
int currentTrackTrack = -1;
int currentTrackHead = -1;
@@ -88,10 +87,11 @@ public:
if (track == 0xFF)
continue;
if (ddam != 0)
Error() << "NFD: nonzero ddam currently unsupported";
error("NFD: nonzero ddam currently unsupported");
if (status != 0)
Error() << "NFD: nonzero fdd status codes are currently "
"unsupported";
error(
"NFD: nonzero fdd status codes are currently "
"unsupported");
if (currentTrackTrack < 0)
{
currentTrackTrack = track;
@@ -99,13 +99,15 @@ public:
}
else if (currentTrackTrack != track)
{
Error() << "NFD: all sectors in a track must belong to the "
"same track";
error(
"NFD: all sectors in a track must belong to the same "
"track");
}
else if (currentTrackHead != head)
{
Error() << "NFD: all sectors in a track must belong to the "
"same head";
error(
"NFD: all sectors in a track must belong to the same "
"head");
}
if (trackSectorSize < 0)
{
@@ -114,8 +116,8 @@ public:
// per-track data
trackdata->set_track(track);
trackdata->set_head(head);
layoutdata->set_track(track);
layoutdata->set_side(head);
layoutdata->set_track(track);
layoutdata->set_side(head);
layoutdata->set_sector_size(sectorSize);
trackdata->set_use_fm(!mfm);
if (!mfm)
@@ -139,8 +141,9 @@ public:
}
else if (trackSectorSize != sectorSize)
{
Error() << "NFD: multiple sector sizes per track are "
"currently unsupported";
error(
"NFD: multiple sector sizes per track are currently "
"unsupported");
}
Bytes data(sectorSize);
inputFile.read((char*)data.begin(), data.size());
@@ -153,7 +156,7 @@ public:
image->calculateSize();
const Geometry& geometry = image->getGeometry();
Logger() << fmt::format("NFD: read {} tracks, {} sides",
log("NFD: read {} tracks, {} sides",
geometry.numTracks,
geometry.numSides);
return image;

View File

@@ -5,7 +5,6 @@
#include "sector.h"
#include "imagereader/imagereader.h"
#include "image.h"
#include "fmt/format.h"
#include "logger.h"
#include "lib/imagereader/imagereader.pb.h"
#include <algorithm>
@@ -22,15 +21,14 @@ public:
std::ifstream inputFile(
_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
error("cannot open input file");
const auto begin = inputFile.tellg();
inputFile.seekg(0, std::ios::end);
const auto end = inputFile.tellg();
const auto fsize = (end - begin);
Logger() << fmt::format(
"NSI: Autodetecting geometry based on file size: {}", fsize);
log("NSI: Autodetecting geometry based on file size: {}", fsize);
unsigned numTracks = 35;
unsigned numSectors = 10;
@@ -55,13 +53,12 @@ public:
break;
default:
Error() << "NSI: unknown file size";
error("NSI: unknown file size");
}
size_t trackSize = numSectors * sectorSize;
Logger() << fmt::format(
"reading {} tracks, {} heads, {} sectors, {} bytes per sector, {} "
log("reading {} tracks, {} heads, {} sectors, {} bytes per sector, {} "
"kB total",
numTracks,
numHeads,

View File

@@ -5,9 +5,7 @@
#include "image.h"
#include "crc.h"
#include "logger.h"
#include "fmt/format.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
@@ -54,7 +52,7 @@ public:
std::ifstream inputFile(
_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
error("cannot open input file");
Bytes input;
input.writer() += inputFile;
@@ -71,10 +69,11 @@ public:
uint16_t gotCrc = crc16(0xa097, 0, input.slice(0, 10));
if (gotCrc != headerCrc)
Error() << "TD0: header checksum mismatch";
error("TD0: header checksum mismatch");
if (signature != 0x5444)
Error() << "TD0: unsupported file type (only uncompressed files "
"are supported for now)";
error(
"TD0: unsupported file type (only uncompressed files "
"are supported for now)");
std::string comment = "(no comment)";
if (stepping & 0x80)
@@ -98,8 +97,7 @@ public:
comment.erase(nl.base(), comment.end());
}
Logger() << fmt::format(
"TD0: TeleDisk {}.{}: {}", version / 10, version % 10, comment);
log("TD0: TeleDisk {}.{}: {}", version / 10, version % 10, comment);
unsigned totalSize = 0;
std::unique_ptr<Image> image(new Image);
@@ -194,8 +192,7 @@ public:
image->calculateSize();
const Geometry& geometry = image->getGeometry();
Logger() << fmt::format(
"TD0: found {} tracks, {} sides, {} sectors, {} bytes per sector, "
log("TD0: found {} tracks, {} sides, {} sectors, {} bytes per sector, "
"{} kB total",
geometry.numTracks,
geometry.numSides,

View File

@@ -2,7 +2,6 @@
#include "flags.h"
#include "sector.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
#include "image.h"
#include "ldbs.h"
#include "logger.h"
@@ -25,21 +24,20 @@ static int sectors_per_track(int track)
class D64ImageWriter : public ImageWriter
{
public:
D64ImageWriter(const ImageWriterProto& config):
ImageWriter(config)
{}
D64ImageWriter(const ImageWriterProto& config): ImageWriter(config) {}
void writeImage(const Image& image)
{
Logger() << "D64: writing triangular image";
void writeImage(const Image& image)
{
log("D64: writing triangular image");
std::ofstream outputFile(_config.filename(), std::ios::out | std::ios::binary);
if (!outputFile.is_open())
Error() << "cannot open output file";
std::ofstream outputFile(
_config.filename(), std::ios::out | std::ios::binary);
if (!outputFile.is_open())
error("cannot open output file");
uint32_t offset = 0;
for (int track = 0; track < 40; track++)
{
for (int track = 0; track < 40; track++)
{
int sectorCount = sectors_per_track(track);
for (int sectorId = 0; sectorId < sectorCount; sectorId++)
{
@@ -47,17 +45,18 @@ public:
if (sector)
{
outputFile.seekp(offset);
outputFile.write((const char*) sector->data.cbegin(), sector->data.size());
outputFile.write((const char*)sector->data.cbegin(),
sector->data.size());
}
offset += 256;
}
}
}
}
};
std::unique_ptr<ImageWriter> ImageWriter::createD64ImageWriter(const ImageWriterProto& config)
std::unique_ptr<ImageWriter> ImageWriter::createD64ImageWriter(
const ImageWriterProto& config)
{
return std::unique_ptr<ImageWriter>(new D64ImageWriter(config));
}

View File

@@ -5,7 +5,6 @@
#include "image.h"
#include "lib/config.pb.h"
#include "lib/layout.h"
#include "fmt/format.h"
#include "logger.h"
#include <algorithm>
#include <iostream>
@@ -13,112 +12,129 @@
static int countl_zero(uint32_t value)
{
int count = 0;
while (!(value & 0x80000000))
{
value <<= 1;
count++;
}
return count;
int count = 0;
while (!(value & 0x80000000))
{
value <<= 1;
count++;
}
return count;
}
class D88ImageWriter : public ImageWriter
{
public:
D88ImageWriter(const ImageWriterProto& config):
ImageWriter(config)
{}
D88ImageWriter(const ImageWriterProto& config): ImageWriter(config) {}
void writeImage(const Image& image)
{
const Geometry geometry = image.getGeometry();
void writeImage(const Image& image)
{
const Geometry geometry = image.getGeometry();
int tracks = geometry.numTracks;
int sides = geometry.numSides;
int tracks = geometry.numTracks;
int sides = geometry.numSides;
std::ofstream outputFile(_config.filename(), std::ios::out | std::ios::binary);
if (!outputFile.is_open())
Error() << "cannot open output file";
std::ofstream outputFile(
_config.filename(), std::ios::out | std::ios::binary);
if (!outputFile.is_open())
error("cannot open output file");
Bytes header;
ByteWriter headerWriter(header);
for(int i = 0; i < 26; i++) {
headerWriter.write_8(0x0); // image name + reserved bytes
}
headerWriter.write_8(0x00); // not write protected
if (geometry.numTracks > 42) {
headerWriter.write_8(0x20); // 2HD
} else {
headerWriter.write_8(0x00); // 2D
}
headerWriter.write_le32(0); // disk size (will be overridden at the end of writing)
for (int i = 0; i < 164; i++) {
headerWriter.write_le32(0); // track pointer (will be overridden in track loop)
}
header.writeTo(outputFile);
Bytes header;
ByteWriter headerWriter(header);
for (int i = 0; i < 26; i++)
{
headerWriter.write_8(0x0); // image name + reserved bytes
}
headerWriter.write_8(0x00); // not write protected
if (geometry.numTracks > 42)
{
headerWriter.write_8(0x20); // 2HD
}
else
{
headerWriter.write_8(0x00); // 2D
}
headerWriter.write_le32(
0); // disk size (will be overridden at the end of writing)
for (int i = 0; i < 164; i++)
{
headerWriter.write_le32(
0); // track pointer (will be overridden in track loop)
}
header.writeTo(outputFile);
uint32_t trackOffset = 688;
uint32_t trackOffset = 688;
for (int track = 0; track < geometry.numTracks * geometry.numSides; track++)
{
headerWriter.seek(0x20 + 4 * track);
headerWriter.write_le32(trackOffset);
int side = track & 1;
std::vector<std::shared_ptr<const Sector>> sectors;
for (int sectorId = geometry.firstSector; sectorId <= geometry.numSectors; sectorId++)
{
const auto& sector = image.get(track >> 1, side, sectorId);
if (sector)
{
sectors.push_back(sector);
}
}
std::sort(begin(sectors),
end(sectors),
[](std::shared_ptr<const Sector> a, std::shared_ptr<const Sector> b) { return a->position < b->position; });
for (auto& sector : sectors)
{
Bytes sectorBytes;
ByteWriter sectorWriter(sectorBytes);
sectorWriter.write_8(sector->logicalTrack);
sectorWriter.write_8(sector->logicalSide);
sectorWriter.write_8(sector->logicalSector);
sectorWriter.write_8(24 - countl_zero(uint32_t(sector->data.size())));
sectorWriter.write_le16(sectors.size());
sectorWriter.write_8(0x00); // always write mfm
sectorWriter.write_8(0x00); // always write not deleted data
if (sector->status == Sector::Status::BAD_CHECKSUM) {
sectorWriter.write_8(0xB0);
} else {
sectorWriter.write_8(0x00);
}
sectorWriter.write_8(0x00); // reserved
sectorWriter.write_8(0x00);
sectorWriter.write_8(0x00);
sectorWriter.write_8(0x00);
sectorWriter.write_8(0x00);
sectorWriter.write_le16(sector->data.size());
sectorBytes.writeTo(outputFile);
sector->data.writeTo(outputFile);
trackOffset += sectorBytes.size();
trackOffset += sector->data.size();
}
}
for (int track = 0; track < geometry.numTracks * geometry.numSides;
track++)
{
headerWriter.seek(0x20 + 4 * track);
headerWriter.write_le32(trackOffset);
int side = track & 1;
std::vector<std::shared_ptr<const Sector>> sectors;
for (int sectorId = geometry.firstSector;
sectorId <= geometry.numSectors;
sectorId++)
{
const auto& sector = image.get(track >> 1, side, sectorId);
if (sector)
{
sectors.push_back(sector);
}
}
std::sort(begin(sectors),
end(sectors),
[](std::shared_ptr<const Sector> a,
std::shared_ptr<const Sector> b)
{
return a->position < b->position;
});
for (auto& sector : sectors)
{
Bytes sectorBytes;
ByteWriter sectorWriter(sectorBytes);
sectorWriter.write_8(sector->logicalTrack);
sectorWriter.write_8(sector->logicalSide);
sectorWriter.write_8(sector->logicalSector);
sectorWriter.write_8(
24 - countl_zero(uint32_t(sector->data.size())));
sectorWriter.write_le16(sectors.size());
sectorWriter.write_8(0x00); // always write mfm
sectorWriter.write_8(0x00); // always write not deleted data
if (sector->status == Sector::Status::BAD_CHECKSUM)
{
sectorWriter.write_8(0xB0);
}
else
{
sectorWriter.write_8(0x00);
}
sectorWriter.write_8(0x00); // reserved
sectorWriter.write_8(0x00);
sectorWriter.write_8(0x00);
sectorWriter.write_8(0x00);
sectorWriter.write_8(0x00);
sectorWriter.write_le16(sector->data.size());
sectorBytes.writeTo(outputFile);
sector->data.writeTo(outputFile);
trackOffset += sectorBytes.size();
trackOffset += sector->data.size();
}
}
headerWriter.seek(0x1c);
headerWriter.write_le32(outputFile.tellp());
outputFile.seekp(0);
header.writeTo(outputFile);
Logger() << fmt::format("D88: wrote {} tracks, {} sides, {} kB total",
tracks, sides,
outputFile.tellp() / 1024);
}
headerWriter.seek(0x1c);
headerWriter.write_le32(outputFile.tellp());
outputFile.seekp(0);
header.writeTo(outputFile);
log("D88: wrote {} tracks, {} sides, {} kB total",
tracks,
sides,
outputFile.tellp() / 1024);
}
};
std::unique_ptr<ImageWriter> ImageWriter::createD88ImageWriter(
const ImageWriterProto& config)
const ImageWriterProto& config)
{
return std::unique_ptr<ImageWriter>(new D88ImageWriter(config));
}

View File

@@ -2,7 +2,6 @@
#include "flags.h"
#include "sector.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
#include "ldbs.h"
#include "image.h"
#include "logger.h"
@@ -13,162 +12,166 @@
static const char LABEL[] = "FluxEngine image";
static void write_and_update_checksum(ByteWriter& bw, uint32_t& checksum, const Bytes& data)
static void write_and_update_checksum(
ByteWriter& bw, uint32_t& checksum, const Bytes& data)
{
ByteReader br(data);
while (!br.eof())
{
uint32_t i = br.read_be16();
checksum += i;
checksum = (checksum >> 1) | (checksum << 31);
bw.write_be16(i);
}
ByteReader br(data);
while (!br.eof())
{
uint32_t i = br.read_be16();
checksum += i;
checksum = (checksum >> 1) | (checksum << 31);
bw.write_be16(i);
}
}
class DiskCopyImageWriter : public ImageWriter
{
public:
DiskCopyImageWriter(const ImageWriterProto& config):
ImageWriter(config)
{}
DiskCopyImageWriter(const ImageWriterProto& config): ImageWriter(config) {}
void writeImage(const Image& image)
{
const Geometry& geometry = image.getGeometry();
void writeImage(const Image& image)
{
const Geometry& geometry = image.getGeometry();
bool mfm = false;
bool mfm = false;
switch (geometry.sectorSize)
{
case 524:
/* GCR disk */
break;
switch (geometry.sectorSize)
{
case 524:
/* GCR disk */
break;
case 512:
/* MFM disk */
mfm = true;
break;
case 512:
/* MFM disk */
mfm = true;
break;
default:
Error() << "this image is not compatible with the DiskCopy 4.2 format";
}
default:
error(
"this image is not compatible with the DiskCopy 4.2 "
"format");
}
Logger() << "DC42: writing DiskCopy 4.2 image"
<< fmt::format("DC42: {} tracks, {} sides, {} sectors, {} bytes per sector; {}",
geometry.numTracks, geometry.numSides, geometry.numSectors, geometry.sectorSize,
mfm ? "MFM" : "GCR");
log("DC42: writing DiskCopy 4.2 image");
log("DC42: {} tracks, {} sides, {} sectors, {} bytes per sector; {}",
geometry.numTracks,
geometry.numSides,
geometry.numSectors,
geometry.sectorSize,
mfm ? "MFM" : "GCR");
auto sectors_per_track = [&](int track) -> int
{
if (mfm)
return geometry.numSectors;
auto sectors_per_track = [&](int track) -> int
{
if (mfm)
return geometry.numSectors;
if (track < 16)
return 12;
if (track < 32)
return 11;
if (track < 48)
return 10;
if (track < 64)
return 9;
return 8;
};
if (track < 16)
return 12;
if (track < 32)
return 11;
if (track < 48)
return 10;
if (track < 64)
return 9;
return 8;
};
Bytes data;
ByteWriter bw(data);
Bytes data;
ByteWriter bw(data);
/* Write the actual sectr data. */
/* Write the actual sectr data. */
uint32_t dataChecksum = 0;
uint32_t tagChecksum = 0;
uint32_t offset = 0x54;
uint32_t sectorDataStart = offset;
for (int track = 0; track < geometry.numTracks; track++)
{
for (int side = 0; side < geometry.numSides; side++)
{
int sectorCount = sectors_per_track(track);
for (int sectorId = 0; sectorId < sectorCount; sectorId++)
{
const auto& sector = image.get(track, side, sectorId);
if (sector)
{
bw.seek(offset);
write_and_update_checksum(bw, dataChecksum, sector->data.slice(0, 512));
}
offset += 512;
}
}
}
uint32_t sectorDataEnd = offset;
if (!mfm)
{
for (int track = 0; track < geometry.numTracks; track++)
{
for (int side = 0; side < geometry.numSides; side++)
{
int sectorCount = sectors_per_track(track);
for (int sectorId = 0; sectorId < sectorCount; sectorId++)
{
const auto& sector = image.get(track, side, sectorId);
if (sector)
{
bw.seek(offset);
write_and_update_checksum(bw, tagChecksum, sector->data.slice(512, 12));
}
offset += 12;
}
}
}
}
uint32_t tagDataEnd = offset;
uint32_t dataChecksum = 0;
uint32_t tagChecksum = 0;
uint32_t offset = 0x54;
uint32_t sectorDataStart = offset;
for (int track = 0; track < geometry.numTracks; track++)
{
for (int side = 0; side < geometry.numSides; side++)
{
int sectorCount = sectors_per_track(track);
for (int sectorId = 0; sectorId < sectorCount; sectorId++)
{
const auto& sector = image.get(track, side, sectorId);
if (sector)
{
bw.seek(offset);
write_and_update_checksum(
bw, dataChecksum, sector->data.slice(0, 512));
}
offset += 512;
}
}
}
uint32_t sectorDataEnd = offset;
if (!mfm)
{
for (int track = 0; track < geometry.numTracks; track++)
{
for (int side = 0; side < geometry.numSides; side++)
{
int sectorCount = sectors_per_track(track);
for (int sectorId = 0; sectorId < sectorCount; sectorId++)
{
const auto& sector = image.get(track, side, sectorId);
if (sector)
{
bw.seek(offset);
write_and_update_checksum(
bw, tagChecksum, sector->data.slice(512, 12));
}
offset += 12;
}
}
}
}
uint32_t tagDataEnd = offset;
/* Write the header. */
/* Write the header. */
uint8_t encoding;
uint8_t format;
if (mfm)
{
format = 0x22;
if (geometry.numSectors == 18)
encoding = 3;
else
encoding = 2;
}
else
{
if (geometry.numSides == 2)
{
encoding = 1;
format = 0x22;
}
else
{
encoding = 0;
format = 0x02;
}
}
uint8_t encoding;
uint8_t format;
if (mfm)
{
format = 0x22;
if (geometry.numSectors == 18)
encoding = 3;
else
encoding = 2;
}
else
{
if (geometry.numSides == 2)
{
encoding = 1;
format = 0x22;
}
else
{
encoding = 0;
format = 0x02;
}
}
bw.seek(0);
bw.write_8(sizeof(LABEL));
bw.append(LABEL);
bw.seek(0x40);
bw.write_be32(sectorDataEnd - sectorDataStart); /* data size */
bw.write_be32(tagDataEnd - sectorDataEnd); /* tag size */
bw.write_be32(dataChecksum); /* data checksum */
bw.write_be32(tagChecksum); /* tag checksum */
bw.write_8(encoding); /* encoding */
bw.write_8(format); /* format byte */
bw.write_be16(0x0100); /* magic number */
bw.seek(0);
bw.write_8(sizeof(LABEL));
bw.append(LABEL);
bw.seek(0x40);
bw.write_be32(sectorDataEnd - sectorDataStart); /* data size */
bw.write_be32(tagDataEnd - sectorDataEnd); /* tag size */
bw.write_be32(dataChecksum); /* data checksum */
bw.write_be32(tagChecksum); /* tag checksum */
bw.write_8(encoding); /* encoding */
bw.write_8(format); /* format byte */
bw.write_be16(0x0100); /* magic number */
data.writeToFile(_config.filename());
data.writeToFile(_config.filename());
}
};
std::unique_ptr<ImageWriter> ImageWriter::createDiskCopyImageWriter(
const ImageWriterProto& config)
const ImageWriterProto& config)
{
return std::unique_ptr<ImageWriter>(new DiskCopyImageWriter(config));
}

View File

@@ -8,7 +8,6 @@
#include "proto.h"
#include "lib/layout.h"
#include "lib/logger.h"
#include "fmt/format.h"
#include <iostream>
#include <fstream>
@@ -41,7 +40,7 @@ std::unique_ptr<ImageWriter> ImageWriter::create(const ImageWriterProto& config)
return ImageWriter::createImdImageWriter(config);
default:
Error() << "bad output image config";
error("bad output image config");
return std::unique_ptr<ImageWriter>();
}
}
@@ -79,7 +78,7 @@ void ImageWriter::updateConfigForFilename(
}
}
Error() << fmt::format("unrecognised image filename '{}'", filename);
error("unrecognised image filename '{}'", filename);
}
ImageWriter::ImageWriter(const ImageWriterProto& config): _config(config) {}
@@ -88,7 +87,7 @@ void ImageWriter::writeCsv(const Image& image, const std::string& filename)
{
std::ofstream f(filename, std::ios::out);
if (!f.is_open())
Error() << "cannot open CSV report file";
error("cannot open CSV report file");
f << "\"Physical track\","
"\"Physical side\","
@@ -207,8 +206,7 @@ void ImageWriter::writeMappedImage(const Image& image)
{
if (_config.filesystem_sector_order())
{
Logger()
<< "WRITER: converting from disk sector order to filesystem order";
log("WRITER: converting from disk sector order to filesystem order");
std::set<std::shared_ptr<const Sector>> sectors;
for (const auto& e : image)

View File

@@ -5,7 +5,6 @@
#include "image.h"
#include "lib/config.pb.h"
#include "lib/layout.h"
#include "fmt/format.h"
#include "logger.h"
#include <algorithm>
#include <iostream>
@@ -14,426 +13,520 @@
#include <ctime>
/*
* Where to get the type of encoding FM or MFM? Now solved with options in proto config
*
*/
static const char LABEL[] = "IMD archive by fluxengine on"; //22 karakters
static uint8_t getModulationandSpeed(int flags, ImdOutputProto::RecordingMode mode)
* Where to get the type of encoding FM or MFM? Now solved with options in
* proto config
*/
static const char LABEL[] = "IMD archive by fluxengine on"; // 22 karakters
static uint8_t getModulationandSpeed(
int flags, ImdOutputProto::RecordingMode mode)
{
if (flags == 0)
{
Error() << fmt::format("Can't write IMD files with this speed {}, and modulation {}. Did you read a real disk?", flags, false);
}
else{
flags = 1000000.0 / flags;
}
if (flags == 0)
{
error(
"Can't write IMD files with this speed {}, and modulation {}. Did "
"you read a real disk?",
flags,
false);
}
else
{
flags = 1000000.0 / flags;
}
if ((flags>950) and (flags<1050)) //HD disk 5% discrepency is ok 1000*5% = 50 1 us
{
/* 500 kbps */
if (mode == ImdOutputProto::RECMODE_FM)
{
return 0;
}
else
{
return 3;
}
} else if ((flags>1475) and (flags<1575))//SD disk
/* 300 kbps*/
{
if (mode == ImdOutputProto::RECMODE_FM)
{
return 1;
}
else
{
return 4;
}
} else if ((flags>1900) and (flags<2100)) //DD disk
/* 250 kbps */
{
if (mode == ImdOutputProto::RECMODE_FM)
{
return 2;
}
else
{
return 5;
}
} else {
Error() << fmt::format("IMD: Can't write IMD files with this speed {}, and modulation {}. Try another format.", flags, false);
}
if ((flags > 950) and
(flags < 1050)) // HD disk 5% discrepency is ok 1000*5% = 50 1 us
{
/* 500 kbps */
if (mode == ImdOutputProto::RECMODE_FM)
{
return 0;
}
else
{
return 3;
}
}
else if ((flags > 1475) and (flags < 1575)) // SD disk
{
/* 300 kbps*/
if (mode == ImdOutputProto::RECMODE_FM)
{
return 1;
}
else
{
return 4;
}
}
else if ((flags > 1900) and (flags < 2100)) // DD disk
{
/* 250 kbps */
if (mode == ImdOutputProto::RECMODE_FM)
{
return 2;
}
else
{
return 5;
}
}
else
{
error(
"IMD: Can't write IMD files with this speed {}, and modulation {}. "
"Try another format.",
flags,
false);
}
}
struct TrackHeader
{
uint8_t ModeValue;
uint8_t track;
uint8_t Head;
uint8_t numSectors;
uint8_t SectorSize;
uint8_t ModeValue;
uint8_t track;
uint8_t Head;
uint8_t numSectors;
uint8_t SectorSize;
};
static uint8_t setSectorSize(int flags)
{
switch (flags)
{
case 128: return 0;
case 256: return 1;
case 512: return 2;
case 1024: return 3;
case 2048: return 4;
case 4096: return 5;
case 8192: return 6;
}
Error() << fmt::format("IMD: Sector size {} not in standard range (128, 256, 512, 1024, 2048, 4096, 8192).", flags);
switch (flags)
{
case 128:
return 0;
case 256:
return 1;
case 512:
return 2;
case 1024:
return 3;
case 2048:
return 4;
case 4096:
return 5;
case 8192:
return 6;
}
error(
"IMD: Sector size {} not in standard range (128, 256, 512, 1024, 2048, "
"4096, 8192).",
flags);
}
#define SEC_CYL_MAP_FLAG 0x80
#define SEC_CYL_MAP_FLAG 0x80
#define SEC_HEAD_MAP_FLAG 0x40
#define HEAD_MASK 0x3F
#define END_OF_FILE 0x1A
/*
*IMAGE FILE FORMAT
*The overall layout of an ImageDisk .IMD image file is:
*IMD v.vv: dd/mm/yyyy hh:mm:ss
*Comment (ASCII only - unlimited size)
*1A byte - ASCII EOF character
*- For each track on the disk:
*1 byte Mode value (0-5) see getModulationspeed for definition
*1 byte Cylinder (0-n)
*1 byte Head (0-1)
*1 byte number of sectors in track (1-n)
*1 byte sector size (0-6) see getsectorsize for definition
*sector numbering map IMD start numbering sectors with 1.
*sector cylinder map (optional) definied in high byte of head (since head is 0 or 1)
*sector head map (optional) definied in high byte of head (since head is 0 or 1)
*sector data records For each data record:
* 1 byte Sector status
* 0: Sector data unavailable - could not be read
* 1: Normal data: (Sector Size) bytes follow
* 2: Compressed: All bytes in sector have same value (xx)
* 3: Normal data with "Deleted-Data address mark"
* 4: Compressed with "Deleted-Data address mark"
* 5: Normal data read with data error
* 6: Compressed read with data error"
* 7: Deleted data read with data error"
* 8: Compressed, Deleted read with data error"
* sector size of Sector data
*<End of file>
*/
#define HEAD_MASK 0x3F
#define END_OF_FILE 0x1A
// clang-format off
/*
* IMAGE FILE FORMAT
* The overall layout of an ImageDisk .IMD image file is:
* IMD v.vv: dd/mm/yyyy hh:mm:ss
* Comment (ASCII only - unlimited size)
* 1A byte - ASCII EOF character
* - For each track on the disk:
* 1 byte Mode value (0-5) see getModulationspeed for definition
* 1 byte Cylinder (0-n)
* 1 byte Head (0-1)
* 1 byte number of sectors in track (1-n)
* 1 byte sector size (0-6) see getsectorsize for definition
* sector numbering map IMD start numbering sectors with 1.
* sector cylinder map (optional) definied in high byte of head (since head is 0 or 1)
* sector head map (optional) definied in high byte of head (since head is 0 or 1)
* sector data records For each data record:
* 1 byte Sector status
* 0: Sector data unavailable - could not be read
* 1: Normal data: (Sector Size) bytes follow
* 2: Compressed: All bytes in sector have same value (xx)
* 3: Normal data with "Deleted-Data address mark"
* 4: Compressed with "Deleted-Data address mark"
* 5: Normal data read with data error
* 6: Compressed read with data error"
* 7: Deleted data read with data error"
* 8: Compressed, Deleted read with data error"
* sector size of Sector data
* <End of file>
*/
// clang-format on
class ImdImageWriter : public ImageWriter
{
public:
ImdImageWriter(const ImageWriterProto& config):
ImageWriter(config)
{}
ImdImageWriter(const ImageWriterProto& config): ImageWriter(config) {}
void writeImage(const Image& image)
{
const Geometry& geometry = image.getGeometry();
unsigned numHeads;
unsigned numSectors;
unsigned numBytes;
std::ofstream outputFile(_config.filename(), std::ios::out | std::ios::binary);
if (!outputFile.is_open())
Error() << "IMD: cannot open output file";
unsigned numSectorsinTrack = 0;
void writeImage(const Image& image)
{
const Geometry& geometry = image.getGeometry();
unsigned numHeads;
unsigned numSectors;
unsigned numBytes;
std::ofstream outputFile(
_config.filename(), std::ios::out | std::ios::binary);
if (!outputFile.is_open())
error("IMD: cannot open output file");
unsigned numSectorsinTrack = 0;
numHeads = geometry.numSides;
numSectors = geometry.numSectors;
numBytes = geometry.sectorSize;
numHeads = geometry.numSides;
numSectors = geometry.numSectors;
numBytes = geometry.sectorSize;
Bytes imagenew;
ByteWriter bw(imagenew);
Bytes imagenew;
ByteWriter bw(imagenew);
ImdOutputProto::DataRate dataRate = _config.imd().data_rate();
if (dataRate == ImdOutputProto::RATE_GUESS)
{
dataRate = (geometry.numSectors > 10) ? ImdOutputProto::RATE_HD : ImdOutputProto::RATE_DD;
if (geometry.sectorSize <= 256)
dataRate = ImdOutputProto::RATE_SD;
Logger() << fmt::format("IMD: guessing data rate as {}", ImdOutputProto::DataRate_Name(dataRate));
}
ImdOutputProto::DataRate dataRate = _config.imd().data_rate();
if (dataRate == ImdOutputProto::RATE_GUESS)
{
dataRate = (geometry.numSectors > 10) ? ImdOutputProto::RATE_HD
: ImdOutputProto::RATE_DD;
if (geometry.sectorSize <= 256)
dataRate = ImdOutputProto::RATE_SD;
log("IMD: guessing data rate as {}",
ImdOutputProto::DataRate_Name(dataRate));
}
ImdOutputProto::RecordingMode recordingMode = _config.imd().recording_mode();
if (recordingMode == ImdOutputProto::RECMODE_GUESS)
{
recordingMode = ImdOutputProto::RECMODE_MFM;
Logger() << fmt::format("IMD: guessing recording mode as {}", ImdOutputProto::RecordingMode_Name(recordingMode));
}
ImdOutputProto::RecordingMode recordingMode =
_config.imd().recording_mode();
if (recordingMode == ImdOutputProto::RECMODE_GUESS)
{
recordingMode = ImdOutputProto::RECMODE_MFM;
log("IMD: guessing recording mode as {}",
ImdOutputProto::RecordingMode_Name(recordingMode));
}
// Give the user a option to give a comment in the IMD file for archive
// purposes.
auto start = std::chrono::system_clock::now();
std::time_t time = std::chrono::system_clock::to_time_t(start);
//Give the user a option to give a comment in the IMD file for archive purposes.
auto start = std::chrono::system_clock::now();
std::time_t time = std::chrono::system_clock::to_time_t(start);
std::string comment = _config.imd().comment();
if (comment.size() == 0)
{
comment = LABEL;
comment.append(" date: ");
comment.append(std::ctime(&time));
}
else
{
comment.insert(0, "IMD ");
}
bw.seek(0);
std::string comment = _config.imd().comment();
if (comment.size() == 0)
{
comment = LABEL ;
comment.append(" date: ");
comment.append(std::ctime(&time));
} else
{
comment.insert(0,"IMD ");
}
bw.seek(0);
bw.append(comment);
bw.write_8(END_OF_FILE);
std::string sector_skew;
sector_skew.clear();
unsigned Status_Sector = 1;
bool blnOptionalCylinderMap = false;
bool blnOptionalHeadMap = false;
bw.append(comment);
bw.write_8(END_OF_FILE);
std::string sector_skew;
sector_skew.clear();
unsigned Status_Sector = 1;
bool blnOptionalCylinderMap = false;
bool blnOptionalHeadMap = false;
/* Write the actual sector data. */
for (int track = 0; track < geometry.numTracks; track++)
{
for (int head = 0; head < numHeads; head++)
{
unsigned sectorIdBase = 1; // IMD starts sector numbering with
// 1;
unsigned sectorId = 0;
TrackHeader header = {0,
0,
0,
0,
0}; // define something to hold the header values
const auto& sector = image.get(track, head, sectorId + 1);
if (!sector)
{ // sector 0 doesnt exist exit with error
// this track, head has no sectors
Status_Sector = 0;
log("IMD: sector {} not found on track {}, head {}\n",
sectorId + 1,
track,
head);
break;
}
else
{
/* Get the header information */
numBytes =
sector->data.size(); // number of bytes can change per
// sector per track
header.track = track;
header.Head = head;
header.SectorSize = setSectorSize(numBytes);
sector_skew.clear();
numSectorsinTrack = 0;
nanoseconds_t RATE = 0;
if (sector->clock > 0)
{
RATE = 1000000.0 / sector->clock;
}
else
{
switch (dataRate)
{
case ImdOutputProto::RATE_HD:
RATE = 1000;
break;
case ImdOutputProto::RATE_SD:
RATE = 1500;
break;
case ImdOutputProto::RATE_DD:
RATE = 2000;
break;
}
}
header.ModeValue =
getModulationandSpeed(RATE, recordingMode);
}
// determine number of sectors in track
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
const auto& sector = image.get(track, head, sectorId + 1);
if (!sector)
{
break;
}
else
{
numSectorsinTrack++;
}
}
// determine sector skew and if there are optional cylindermaps
// or headermaps
for (int sectorId = 0; sectorId < numSectorsinTrack; sectorId++)
{
const auto& sector = image.get(track, head, sectorId + 1);
if (!sector)
{
break;
}
else
{
sector_skew.push_back(
(sectorId + sectorIdBase) +
'0'); // fill sectorskew start with 1
if ((sector->physicalTrack) !=
(sector->logicalTrack)) // different physicaltrack
// fromn logicaltrack
{
blnOptionalCylinderMap = true;
}
if (sector->logicalSide !=
sector->physicalSide) // different physicalside
// fromn logicalside
{
blnOptionalHeadMap = true;
}
}
}
bw.write_8(header.ModeValue); // 1 byte ModeValue
bw.write_8(track); // 1 byte Cylinder
// are there optional cylinder or head maps?
if (blnOptionalCylinderMap)
{
header.Head = header.Head ^
SEC_CYL_MAP_FLAG; // if head was 0 (00000000)
// it becomes (10000000)
}
if (blnOptionalHeadMap)
{
header.Head = header.Head ^
SEC_HEAD_MAP_FLAG; // if head was 1 (00000001)
// it becomes (01000001)
}
bw.write_8(head); // 1 byte Head
bw.write_8(
numSectorsinTrack); // 1 byte number of sectors in track
bw.write_8(header.SectorSize); // 1 byte sector size
for (int sectorId = 0; sectorId < numSectorsinTrack; sectorId++)
{
bw.write_8(
(sectorId + sectorIdBase)); // sector numbering map
}
// Write optional cylinder map
// The Sector Cylinder Map has one entry for each sector, and
// contains the logical Cylinder ID for the corresponding sector
// in the Sector Numbering Map.
if (blnOptionalCylinderMap)
{
// determine how the optional cylinder map looks like
// write the corresponding logical ID
for (int sectorId = 0; sectorId < numSectorsinTrack;
sectorId++)
{
// const auto& sector = sectors.get(track, head,
// sectorId);
bw.write_8(sector->logicalTrack); // 1 byte logical
// track
}
}
/* Write the actual sector data. */
for (int track = 0; track < geometry.numTracks; track++)
{
for (int head = 0; head < numHeads; head++)
{
unsigned sectorIdBase = 1; //IMD starts sector numbering with 1;
unsigned sectorId = 0;
TrackHeader header = {0, 0, 0, 0, 0}; //define something to hold the header values
const auto& sector = image.get(track, head, sectorId+1);
if (!sector)
{//sector 0 doesnt exist exit with error
//this track, head has no sectors
Status_Sector = 0;
Logger() << fmt::format("IMD: sector {} not found on track {}, head {}\n", sectorId+1, track, head);
break;
} else
{
/* Get the header information */
numBytes = sector->data.size(); //number of bytes can change per sector per track
header.track = track;
header.Head = head;
header.SectorSize = setSectorSize(numBytes);
sector_skew.clear();
numSectorsinTrack = 0;
nanoseconds_t RATE = 0;
if (sector->clock > 0)
{
RATE = 1000000.0 / sector->clock;
} else
{
switch (dataRate)
{
case ImdOutputProto::RATE_HD :
RATE = 1000;
break;
case ImdOutputProto::RATE_SD :
RATE = 1500;
break;
case ImdOutputProto::RATE_DD :
RATE = 2000;
break;
}
}
header.ModeValue = getModulationandSpeed(RATE, recordingMode);
}
//determine number of sectors in track
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
const auto& sector = image.get(track, head, sectorId+1);
if (!sector)
{
break;
} else
{
numSectorsinTrack++;
}
}
//determine sector skew and if there are optional cylindermaps or headermaps
for (int sectorId = 0; sectorId < numSectorsinTrack; sectorId++)
{
const auto& sector = image.get(track, head, sectorId+1);
if (!sector)
{
break;
} else
{
sector_skew.push_back((sectorId + sectorIdBase) + '0'); //fill sectorskew start with 1
if ((sector->physicalTrack) != (sector->logicalTrack)) //different physicaltrack fromn logicaltrack
{
blnOptionalCylinderMap = true;
}
if (sector->logicalSide != sector->physicalSide) //different physicalside fromn logicalside
{
blnOptionalHeadMap = true;
}
}
}
bw.write_8(header.ModeValue); //1 byte ModeValue
bw.write_8(track); //1 byte Cylinder
//are there optional cylinder or head maps?
if (blnOptionalCylinderMap)
{
header.Head = header.Head^SEC_CYL_MAP_FLAG; //if head was 0 (00000000) it becomes (10000000)
}
if (blnOptionalHeadMap)
{
header.Head = header.Head^SEC_HEAD_MAP_FLAG; //if head was 1 (00000001) it becomes (01000001)
}
bw.write_8(head); //1 byte Head
bw.write_8(numSectorsinTrack); //1 byte number of sectors in track
bw.write_8(header.SectorSize); //1 byte sector size
for (int sectorId = 0; sectorId < numSectorsinTrack; sectorId++)
{
bw.write_8((sectorId+ sectorIdBase)); //sector numbering map
}
//Write optional cylinder map
//The Sector Cylinder Map has one entry for each sector, and contains the logical Cylinder ID for the corresponding sector in the Sector Numbering Map.
if (blnOptionalCylinderMap)
{
//determine how the optional cylinder map looks like
//write the corresponding logical ID
for (int sectorId = 0; sectorId < numSectorsinTrack; sectorId++)
{
//const auto& sector = sectors.get(track, head, sectorId);
bw.write_8(sector->logicalTrack); //1 byte logical track
}
}
// Write optional sector head map
// The Sector Head Map has one entry for each sector, and
// contains the logical Head ID for the corresponding sector in
// the Sector Numbering Map.
if (blnOptionalHeadMap)
{
// determine how the optional head map looks like
// write the corresponding logical ID
for (int sectorId = 0; sectorId < numSectorsinTrack;
sectorId++)
{
// const auto& sector = sectors.get(track, head,
// sectorId);
bw.write_8(sector->logicalSide); // 1 byte logical side
}
}
// Now read data and write to file
for (int sectorId = 0; sectorId < numSectorsinTrack; sectorId++)
{
// clang-format off
/* For each data record:
* 1 byte Sector status
* 0: Sector data unavailable - could not be read
* 1: Normal data: (Sector Size) bytes follow
* 2: Compressed: All bytes in sector have same value (xx)
* 3: Normal data with "Deleted-Data address mark"
* 4: Compressed with "Deleted-Data address mark"
* 5: Normal data read with data error
* 6: Compressed read with data error"
* 7: Deleted data read with data error"
* 8: Compressed, Deleted read with data error"
* sector size of Sector data
*/
// clang-format on
// read sector
const auto& sector = image.get(track, head, sectorId + 1);
bool blnCompressable =
false; // Consists the sector of 1 value? if yes then
// compresses IMD this to 1 value
Bytes sectordata(numBytes); // define the sectordata with
// the size of the sectorsize
Bytes compressed(
1); // reserve 1 byte for comressed sectordata
uint8_t byte; // value read
uint8_t
byte_previous; // previous value read (to determine if
// all bytes are equel in this sector)
if (!sector)
{
Status_Sector = 0;
break;
}
else
{
ByteReader br(sector->data); // read the sector data
int i;
// determine if all bytes are the same -> compress and
// sector status = 2
for (i = 0; i < numBytes; i++)
{
byte = br.read_8();
if (i == 0)
{
byte_previous = byte;
}
if (byte_previous == byte)
{
blnCompressable = true;
}
else
{
blnCompressable = false;
break;
}
}
switch (sector->status)
{
// clang-format off
/* fluxengine knows of a few sector statussen but not all of the statussen in IMD.
* // the statussen are in sector.h. Translation to fluxengine is as follows:
* Statussen fluxengine | Status IMD
*--------------------------------------------------------------------------------------------------------------------
* OK, | 1, 2 (Normal data: (Sector Size) of (compressed) bytes follow)
* BAD_CHECKSUM, | 5, 6, 7, 8
* MISSING, sector not found | 0 (Sector data unavailable - could not be read)
* DATA_MISSING, sector present but no data found | 3, 4
* CONFLICT, |
* INTERNAL_ERROR |
*/
// clang-format on
case Sector::MISSING: /* Sector data unavailable -
could not be read */
//Write optional sector head map
//The Sector Head Map has one entry for each sector, and contains the logical Head ID for the corresponding sector in the Sector Numbering Map.
if (blnOptionalHeadMap)
{
//determine how the optional head map looks like
//write the corresponding logical ID
for (int sectorId = 0; sectorId < numSectorsinTrack; sectorId++)
{
// const auto& sector = sectors.get(track, head, sectorId);
bw.write_8(sector->logicalSide); //1 byte logical side
}
}
//Now read data and write to file
for (int sectorId = 0; sectorId < numSectorsinTrack; sectorId++)
{
/* For each data record:
* 1 byte Sector status
* 0: Sector data unavailable - could not be read
* 1: Normal data: (Sector Size) bytes follow
* 2: Compressed: All bytes in sector have same value (xx)
* 3: Normal data with "Deleted-Data address mark"
* 4: Compressed with "Deleted-Data address mark"
* 5: Normal data read with data error
* 6: Compressed read with data error"
* 7: Deleted data read with data error"
* 8: Compressed, Deleted read with data error"
* sector size of Sector data
*/
//read sector
const auto& sector = image.get(track, head, sectorId+1);
bool blnCompressable = false; //Consists the sector of 1 value? if yes then compresses IMD this to 1 value
Bytes sectordata(numBytes); //define the sectordata with the size of the sectorsize
Bytes compressed(1); //reserve 1 byte for comressed sectordata
uint8_t byte; //value read
uint8_t byte_previous; //previous value read (to determine if all bytes are equel in this sector)
if (!sector)
{
Status_Sector = 0;
break;
} else
{
ByteReader br(sector->data); //read the sector data
int i;
//determine if all bytes are the same -> compress and sector status = 2
for (i=0 ; i<numBytes ; i++)
{
byte = br.read_8();
if (i == 0)
{
byte_previous = byte;
}
if (byte_previous == byte)
{
blnCompressable = true;
} else
{
blnCompressable = false;
break;
}
}
switch (sector->status)
{
/*fluxengine knows of a few sector statussen but not all of the statussen in IMD.
* // the statussen are in sector.h. Translation to fluxengine is as follows:
* Statussen fluxengine | Status IMD
*--------------------------------------------------------------------------------------------------------------------
* OK, | 1, 2 (Normal data: (Sector Size) of (compressed) bytes follow)
* BAD_CHECKSUM, | 5, 6, 7, 8
* MISSING, sector not found | 0 (Sector data unavailable - could not be read)
* DATA_MISSING, sector present but no data found | 3, 4
* CONFLICT, |
* INTERNAL_ERROR |
*/
case Sector::MISSING: /* Sector data unavailable - could not be read */
Status_Sector = 0;
break;
Status_Sector = 0;
break;
case Sector::OK: /* Normal data: (Sector Size) bytes
follow */
if (blnCompressable) // data is compressable
{
Status_Sector = 2;
}
else
{
Status_Sector = 1;
}
break;
case Sector::DATA_MISSING:
Status_Sector =
3; // we misuse normal data with
// deleted-data addres mark for this
// missing data option
break;
// IMD recognizes all of these cases. but fluxengine
// doesnt support them. case 2: /* Compressed: All
// bytes in sector have same value (xx) */ case 3:
// /* Normal data with "Deleted-Data address mark"
// */ case 4: /* Compressed with "Deleted-Data
// address mark"*/
case Sector::BAD_CHECKSUM:
// case 5: /* Normal data read with data error -
// could not be read*/
Status_Sector = 5;
break;
// case 6: /* Compressed read with data error -
// could not be read */ case 7: /* Deleted data
// read with data error - could not be read */
// case 8: /* Compressed, Deleted read with data
// error - could not be read */
case Sector::OK: /* Normal data: (Sector Size) bytes follow */
if (blnCompressable) //data is compressable
{
Status_Sector = 2;
} else
{
Status_Sector = 1;
}
break;
case Sector::DATA_MISSING:
Status_Sector = 3; //we misuse normal data with deleted-data addres mark for this missing data option
break;
// IMD recognizes all of these cases. but fluxengine doesnt support them.
// case 2: /* Compressed: All bytes in sector have same value (xx) */
// case 3: /* Normal data with "Deleted-Data address mark" */
// case 4: /* Compressed with "Deleted-Data address mark"*/
case Sector::BAD_CHECKSUM:
// case 5: /* Normal data read with data error - could not be read*/
Status_Sector = 5;
break;
// case 6: /* Compressed read with data error - could not be read */
// case 7: /* Deleted data read with data error - could not be read */
// case 8: /* Compressed, Deleted read with data error - could not be read */
default:
Error() << fmt::format("IMD: Don't understand IMD files with sector status {}", Status_Sector);
}
bw.write_8(Status_Sector); //1 byte status sector
if (blnCompressable)
{
bw.write_8(byte);
blnCompressable = false;
} else
{
bw.append(sector->data);
}
numSectors = numSectorsinTrack;
}
blnOptionalCylinderMap = false;
blnOptionalHeadMap = false;
}
}
}
imagenew.writeTo(outputFile);
Logger() << fmt::format("IMD: Written {} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total",
geometry.numTracks, numHeads,
numSectors, numBytes,
outputFile.tellp() / 1024);
}
default:
error(
"IMD: Don't understand IMD files with "
"sector status {}",
Status_Sector);
}
bw.write_8(Status_Sector); // 1 byte status sector
if (blnCompressable)
{
bw.write_8(byte);
blnCompressable = false;
}
else
{
bw.append(sector->data);
}
numSectors = numSectorsinTrack;
}
blnOptionalCylinderMap = false;
blnOptionalHeadMap = false;
}
}
}
imagenew.writeTo(outputFile);
log("IMD: Written {} tracks, {} heads, {} sectors, {} bytes per "
"sector, {} kB total",
geometry.numTracks,
numHeads,
numSectors,
numBytes,
outputFile.tellp() / 1024);
}
};
std::unique_ptr<ImageWriter> ImageWriter::createImdImageWriter(
const ImageWriterProto& config)
const ImageWriterProto& config)
{
return std::unique_ptr<ImageWriter>(new ImdImageWriter(config));
}

View File

@@ -7,7 +7,6 @@
#include "lib/config.pb.h"
#include "lib/layout.h"
#include "lib/layout.pb.h"
#include "fmt/format.h"
#include "logger.h"
#include <algorithm>
#include <iostream>
@@ -29,7 +28,7 @@ public:
std::ofstream outputFile(
_config.filename(), std::ios::out | std::ios::binary);
if (!outputFile.is_open())
Error() << "cannot open output file";
error("cannot open output file");
for (const auto& p : Layout::getTrackOrdering(tracks, sides))
{
@@ -41,17 +40,18 @@ public:
{
const auto& sector = image.get(track, side, sectorId);
if (sector)
sector->data.slice(0, trackLayout->sectorSize).writeTo(outputFile);
sector->data.slice(0, trackLayout->sectorSize)
.writeTo(outputFile);
else
outputFile.seekp(trackLayout->sectorSize, std::ios::cur);
}
}
Logger() << fmt::format("IMG: wrote {} tracks, {} sides, {} kB total to {}",
log("IMG: wrote {} tracks, {} sides, {} kB total to {}",
tracks,
sides,
outputFile.tellp() / 1024,
_config.filename());
_config.filename());
}
};

View File

@@ -2,7 +2,6 @@
#include "flags.h"
#include "sector.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
#include "ldbs.h"
#include "image.h"
#include "logger.h"
@@ -14,106 +13,125 @@
class LDBSImageWriter : public ImageWriter
{
public:
LDBSImageWriter(const ImageWriterProto& config):
ImageWriter(config)
{}
LDBSImageWriter(const ImageWriterProto& config): ImageWriter(config) {}
void writeImage(const Image& image)
{
void writeImage(const Image& image)
{
LDBS ldbs;
const Geometry geometry = image.getGeometry();
const Geometry geometry = image.getGeometry();
Logger() << fmt::format("LDBS: writing {} tracks, {} sides, {} sectors, {} bytes per sector",
geometry.numTracks, geometry.numSides, geometry.numSectors,
geometry.sectorSize);
log("LDBS: writing {} tracks, {} sides, {} sectors, {} bytes per "
"sector",
geometry.numTracks,
geometry.numSides,
geometry.numSectors,
geometry.sectorSize);
Bytes trackDirectory;
ByteWriter trackDirectoryWriter(trackDirectory);
int trackDirectorySize = 0;
trackDirectoryWriter.write_le16(0);
LDBSOutputProto::DataRate dataRate = _config.ldbs().data_rate();
if (dataRate == LDBSOutputProto::RATE_GUESS)
{
dataRate = (geometry.numSectors > 10) ? LDBSOutputProto::RATE_HD : LDBSOutputProto::RATE_DD;
if (geometry.sectorSize <= 256)
dataRate = LDBSOutputProto::RATE_SD;
Logger() << fmt::format("LDBS: guessing data rate as {}", LDBSOutputProto::DataRate_Name(dataRate));
}
LDBSOutputProto::DataRate dataRate = _config.ldbs().data_rate();
if (dataRate == LDBSOutputProto::RATE_GUESS)
{
dataRate = (geometry.numSectors > 10) ? LDBSOutputProto::RATE_HD
: LDBSOutputProto::RATE_DD;
if (geometry.sectorSize <= 256)
dataRate = LDBSOutputProto::RATE_SD;
log("LDBS: guessing data rate as {}",
LDBSOutputProto::DataRate_Name(dataRate));
}
LDBSOutputProto::RecordingMode recordingMode = _config.ldbs().recording_mode();
if (recordingMode == LDBSOutputProto::RECMODE_GUESS)
{
recordingMode = LDBSOutputProto::RECMODE_MFM;
Logger() << fmt::format("LDBS: guessing recording mode as {}", LDBSOutputProto::RecordingMode_Name(recordingMode));
}
LDBSOutputProto::RecordingMode recordingMode =
_config.ldbs().recording_mode();
if (recordingMode == LDBSOutputProto::RECMODE_GUESS)
{
recordingMode = LDBSOutputProto::RECMODE_MFM;
log("LDBS: guessing recording mode as {}",
LDBSOutputProto::RecordingMode_Name(recordingMode));
}
for (int track = 0; track < geometry.numTracks; track++)
{
for (int side = 0; side < geometry.numSides; side++)
{
for (int track = 0; track < geometry.numTracks; track++)
{
for (int side = 0; side < geometry.numSides; side++)
{
Bytes trackHeader;
ByteWriter trackHeaderWriter(trackHeader);
int actualSectors = 0;
for (int sectorId = 0; sectorId < geometry.numSectors; sectorId++)
{
const auto& sector = image.get(track, side, sectorId);
if (sector)
for (int sectorId = 0; sectorId < geometry.numSectors;
sectorId++)
{
const auto& sector = image.get(track, side, sectorId);
if (sector)
actualSectors++;
}
trackHeaderWriter.write_le16(0x000C); /* offset of sector sideers */
trackHeaderWriter.write_le16(0x0012); /* length of each sector descriptor */
trackHeaderWriter.write_le16(
0x000C); /* offset of sector sideers */
trackHeaderWriter.write_le16(
0x0012); /* length of each sector descriptor */
trackHeaderWriter.write_le16(actualSectors);
trackHeaderWriter.write_8(dataRate);
trackHeaderWriter.write_8(recordingMode);
trackHeaderWriter.write_8(0); /* format gap length */
trackHeaderWriter.write_8(0); /* filler byte */
trackHeaderWriter.write_8(0); /* format gap length */
trackHeaderWriter.write_8(0); /* filler byte */
trackHeaderWriter.write_le16(0); /* approximate track length */
for (int sectorId = 0; sectorId < geometry.numSectors; sectorId++)
{
const auto& sector = image.get(track, side, sectorId);
if (sector)
{
uint32_t sectorLabel = (('S') << 24) | ((track & 0xff) << 16) | (side << 8) | sectorId;
uint32_t sectorAddress = ldbs.put(sector->data, sectorLabel);
for (int sectorId = 0; sectorId < geometry.numSectors;
sectorId++)
{
const auto& sector = image.get(track, side, sectorId);
if (sector)
{
uint32_t sectorLabel = (('S') << 24) |
((track & 0xff) << 16) |
(side << 8) | sectorId;
uint32_t sectorAddress =
ldbs.put(sector->data, sectorLabel);
trackHeaderWriter.write_8(track);
trackHeaderWriter.write_8(side);
trackHeaderWriter.write_8(sectorId);
trackHeaderWriter.write_8(0); /* power-of-two size */
trackHeaderWriter.write_8((sector->status == Sector::OK) ? 0x00 : 0x20); /* 8272 status 1 */
trackHeaderWriter.write_8(
(sector->status == Sector::OK)
? 0x00
: 0x20); /* 8272 status 1 */
trackHeaderWriter.write_8(0); /* 8272 status 2 */
trackHeaderWriter.write_8(1); /* number of copies */
trackHeaderWriter.write_8(0); /* filler byte */
trackHeaderWriter.write_le32(sectorAddress);
trackHeaderWriter.write_le16(0); /* trailing bytes */
trackHeaderWriter.write_le16(0); /* approximate offset */
trackHeaderWriter.write_le16(
0); /* approximate offset */
trackHeaderWriter.write_le16(sector->data.size());
}
}
}
}
uint32_t trackLabel = (('T') << 24) | ((track & 0xff) << 16) | ((track >> 8) << 8) | side;
uint32_t trackLabel = (('T') << 24) | ((track & 0xff) << 16) |
((track >> 8) << 8) | side;
uint32_t trackHeaderAddress = ldbs.put(trackHeader, trackLabel);
trackDirectoryWriter.write_be32(trackLabel);
trackDirectoryWriter.write_le32(trackHeaderAddress);
trackDirectorySize++;
}
}
}
}
trackDirectoryWriter.seek(0);
trackDirectoryWriter.write_le16(trackDirectorySize);
uint32_t trackDirectoryAddress = ldbs.put(trackDirectory, LDBS_TRACK_BLOCK);
uint32_t trackDirectoryAddress =
ldbs.put(trackDirectory, LDBS_TRACK_BLOCK);
Bytes data = ldbs.write(trackDirectoryAddress);
data.writeToFile(_config.filename());
}
};
std::unique_ptr<ImageWriter> ImageWriter::createLDBSImageWriter(const ImageWriterProto& config)
std::unique_ptr<ImageWriter> ImageWriter::createLDBSImageWriter(
const ImageWriterProto& config)
{
return std::unique_ptr<ImageWriter>(new LDBSImageWriter(config));
}

View File

@@ -2,7 +2,6 @@
#include "flags.h"
#include "sector.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
#include "decoders/decoders.h"
#include "image.h"
#include "logger.h"
@@ -15,76 +14,101 @@
class NsiImageWriter : public ImageWriter
{
public:
NsiImageWriter(const ImageWriterProto& config):
ImageWriter(config)
{}
NsiImageWriter(const ImageWriterProto& config): ImageWriter(config) {}
void writeImage(const Image& image)
{
const Geometry& geometry = image.getGeometry();
bool mixedDensity = false;
void writeImage(const Image& image)
{
const Geometry& geometry = image.getGeometry();
bool mixedDensity = false;
size_t trackSize = geometry.numSectors * geometry.sectorSize;
size_t trackSize = geometry.numSectors * geometry.sectorSize;
if (geometry.numTracks * trackSize == 0) {
Logger() << "No sectors in output; skipping .nsi image file generation.";
return;
}
if (geometry.numTracks * trackSize == 0)
{
log("No sectors in output; skipping .nsi image file generation.");
return;
}
Logger() << fmt::format("Writing {} tracks, {} sides, {} sectors, {} ({} bytes/sector), {} kB total",
geometry.numTracks, geometry.numSides,
geometry.numSectors, geometry.sectorSize == 256 ? "SD" : "DD", geometry.sectorSize,
geometry.numTracks * geometry.numSides * geometry.numSectors * geometry.sectorSize / 1024);
log("Writing {} tracks, {} sides, {} sectors, {} ({} bytes/sector), {} "
"kB total",
geometry.numTracks,
geometry.numSides,
geometry.numSectors,
geometry.sectorSize == 256 ? "SD" : "DD",
geometry.sectorSize,
geometry.numTracks * geometry.numSides * geometry.numSectors *
geometry.sectorSize / 1024);
std::ofstream outputFile(_config.filename(), std::ios::out | std::ios::binary);
if (!outputFile.is_open())
Error() << "cannot open output file";
std::ofstream outputFile(
_config.filename(), std::ios::out | std::ios::binary);
if (!outputFile.is_open())
error("cannot open output file");
unsigned sectorFileOffset;
for (int track = 0; track < geometry.numTracks * geometry.numSides; track++)
{
int side = (track < geometry.numTracks) ? 0 : 1;
for (int sectorId = 0; sectorId < geometry.numSectors; sectorId++)
{
const auto& sector = image.get(track % geometry.numTracks, side, sectorId);
if (sector)
{
if (side == 0) { /* Side 0 is from track 0-34 */
sectorFileOffset = track * trackSize + sectorId * geometry.sectorSize;
}
else { /* Side 1 is from track 70-35 */
sectorFileOffset = (geometry.sectorSize * geometry.numSectors * geometry.numTracks) + /* Skip over side 0 */
((geometry.numTracks - 1) - (track % geometry.numTracks)) * (geometry.sectorSize * geometry.numSectors) +
(sectorId * geometry.sectorSize); /* Sector offset from beginning of track. */
}
outputFile.seekp(sectorFileOffset, std::ios::beg);
if ((geometry.sectorSize == 512) && (sector->data.size() == 256)) {
/* North Star DOS provided an upgrade path for disks formatted as single-
* density to hold double-density data without reformatting. In this
* case, the four directory blocks will be single-density but other areas
* of the disk are double-density. This cannot be accurately represented
* using a .nsi file, so in these cases, we pad the sector to 512-bytes,
* filling with spaces.
*/
char fill[256];
memset(fill, ' ', sizeof(fill));
if (mixedDensity == false) {
Logger() << "Warning: Disk contains mixed single/double-density sectors.";
}
mixedDensity = true;
sector->data.slice(0, 256).writeTo(outputFile);
outputFile.write(fill, sizeof(fill));
} else {
sector->data.slice(0, geometry.sectorSize).writeTo(outputFile);
}
}
}
}
}
unsigned sectorFileOffset;
for (int track = 0; track < geometry.numTracks * geometry.numSides;
track++)
{
int side = (track < geometry.numTracks) ? 0 : 1;
for (int sectorId = 0; sectorId < geometry.numSectors; sectorId++)
{
const auto& sector =
image.get(track % geometry.numTracks, side, sectorId);
if (sector)
{
if (side == 0)
{ /* Side 0 is from track 0-34 */
sectorFileOffset =
track * trackSize + sectorId * geometry.sectorSize;
}
else
{ /* Side 1 is from track 70-35 */
sectorFileOffset =
(geometry.sectorSize * geometry.numSectors *
geometry.numTracks) + /* Skip over side 0 */
((geometry.numTracks - 1) -
(track % geometry.numTracks)) *
(geometry.sectorSize * geometry.numSectors) +
(sectorId *
geometry.sectorSize); /* Sector offset from
beginning of track. */
}
outputFile.seekp(sectorFileOffset, std::ios::beg);
if ((geometry.sectorSize == 512) &&
(sector->data.size() == 256))
{
/* North Star DOS provided an upgrade path for disks
* formatted as single- density to hold double-density
* data without reformatting. In this case, the four
* directory blocks will be single-density but other
* areas of the disk are double-density. This cannot be
* accurately represented using a .nsi file, so in these
* cases, we pad the sector to 512-bytes, filling with
* spaces.
*/
char fill[256];
memset(fill, ' ', sizeof(fill));
if (mixedDensity == false)
{
log("Warning: Disk contains mixed "
"single/double-density sectors.");
}
mixedDensity = true;
sector->data.slice(0, 256).writeTo(outputFile);
outputFile.write(fill, sizeof(fill));
}
else
{
sector->data.slice(0, geometry.sectorSize)
.writeTo(outputFile);
}
}
}
}
}
};
std::unique_ptr<ImageWriter> ImageWriter::createNsiImageWriter(
const ImageWriterProto& config)
const ImageWriterProto& config)
{
return std::unique_ptr<ImageWriter>(new NsiImageWriter(config));
}

View File

@@ -2,7 +2,6 @@
#include "flags.h"
#include "sector.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
#include "decoders/decoders.h"
#include "image.h"
#include "logger.h"
@@ -15,60 +14,65 @@
class RawImageWriter : public ImageWriter
{
public:
RawImageWriter(const ImageWriterProto& config):
ImageWriter(config)
{}
RawImageWriter(const ImageWriterProto& config): ImageWriter(config) {}
void writeImage(const Image& image)
{
const Geometry& geometry = image.getGeometry();
void writeImage(const Image& image)
{
const Geometry& geometry = image.getGeometry();
size_t trackSize = geometry.numSectors * geometry.sectorSize;
size_t trackSize = geometry.numSectors * geometry.sectorSize;
if (geometry.numTracks * trackSize == 0) {
Logger() << "RAW: no sectors in output; skipping image file generation.";
return;
}
if (geometry.numTracks * trackSize == 0)
{
log("RAW: no sectors in output; skipping image file generation.");
return;
}
Logger() << fmt::format("RAW: writing {} tracks, {} sides",
geometry.numTracks, geometry.numSides);
log("RAW: writing {} tracks, {} sides",
geometry.numTracks,
geometry.numSides);
std::ofstream outputFile(_config.filename(), std::ios::out | std::ios::binary);
if (!outputFile.is_open())
Error() << "RAW: cannot open output file";
std::ofstream outputFile(
_config.filename(), std::ios::out | std::ios::binary);
if (!outputFile.is_open())
error("RAW: cannot open output file");
unsigned sectorFileOffset;
for (int track = 0; track < geometry.numTracks * geometry.numSides; track++)
{
int side = (track < geometry.numTracks) ? 0 : 1;
unsigned sectorFileOffset;
for (int track = 0; track < geometry.numTracks * geometry.numSides;
track++)
{
int side = (track < geometry.numTracks) ? 0 : 1;
std::vector<std::shared_ptr<Record>> records;
for (int sectorId = 0; sectorId < geometry.numSectors; sectorId++)
{
const auto& sector = image.get(track % geometry.numTracks, side, sectorId);
if (sector)
records.insert(records.end(), sector->records.begin(), sector->records.end());
}
std::sort(records.begin(), records.end(),
[&](std::shared_ptr<Record> left, std::shared_ptr<Record> right) {
return left->startTime < right->startTime;
});
std::vector<std::shared_ptr<Record>> records;
for (int sectorId = 0; sectorId < geometry.numSectors; sectorId++)
{
const auto& sector =
image.get(track % geometry.numTracks, side, sectorId);
if (sector)
records.insert(records.end(),
sector->records.begin(),
sector->records.end());
}
for (const auto& record : records)
{
record->rawData.writeTo(outputFile);
Bytes(3).writeTo(outputFile);
}
Bytes(1).writeTo(outputFile);
}
}
std::sort(records.begin(),
records.end(),
[&](std::shared_ptr<Record> left, std::shared_ptr<Record> right)
{
return left->startTime < right->startTime;
});
for (const auto& record : records)
{
record->rawData.writeTo(outputFile);
Bytes(3).writeTo(outputFile);
}
Bytes(1).writeTo(outputFile);
}
}
};
std::unique_ptr<ImageWriter> ImageWriter::createRawImageWriter(
const ImageWriterProto& config)
const ImageWriterProto& config)
{
return std::unique_ptr<ImageWriter>(new RawImageWriter(config));
}

View File

@@ -2,7 +2,6 @@
#include "lib/layout.h"
#include "lib/proto.h"
#include "lib/environment.h"
#include <fmt/format.h>
static unsigned getTrackStep()
{
@@ -10,8 +9,7 @@ static unsigned getTrackStep()
(config.tpi() == 0) ? 1 : (config.drive().tpi() / config.tpi());
if (track_step == 0)
Error()
<< "this drive can't write this image, because the head is too big";
error("this drive can't write this image, because the head is too big");
return track_step;
}
@@ -108,7 +106,7 @@ std::vector<std::pair<int, int>> Layout::getTrackOrdering(
}
default:
Error() << "LAYOUT: invalid track ordering";
error("LAYOUT: invalid track ordering");
}
return ordering;
@@ -122,8 +120,9 @@ std::vector<unsigned> Layout::expandSectorList(
if (sectorsProto.has_count())
{
if (sectorsProto.sector_size() != 0)
Error() << "LAYOUT: if you use a sector count, you can't use an "
"explicit sector list";
error(
"LAYOUT: if you use a sector count, you can't use an "
"explicit sector list");
std::set<unsigned> sectorset;
int id = sectorsProto.start_sector();
@@ -150,7 +149,7 @@ std::vector<unsigned> Layout::expandSectorList(
sectors.push_back(sectorId);
}
else
Error() << "LAYOUT: no sectors in sector definition!";
error("LAYOUT: no sectors in sector definition!");
return sectors;
}
@@ -194,8 +193,9 @@ std::shared_ptr<const TrackInfo> Layout::getLayoutOfTrack(
trackInfo->filesystemSectorOrder =
expandSectorList(layoutdata.filesystem());
if (trackInfo->filesystemSectorOrder.size() != trackInfo->numSectors)
Error() << "filesystem sector order list doesn't contain the right "
"number of sectors";
error(
"filesystem sector order list doesn't contain the right "
"number of sectors");
}
else
trackInfo->filesystemSectorOrder = trackInfo->naturalSectorOrder;

View File

@@ -2,10 +2,8 @@
#include <string.h>
#include "bytes.h"
#include "ldbs.h"
#include "fmt/format.h"
LDBS::LDBS()
{}
LDBS::LDBS() {}
uint32_t LDBS::put(const Bytes& data, uint32_t type)
{
@@ -23,9 +21,9 @@ uint32_t LDBS::read(const Bytes& data)
ByteReader br(data);
br.seek(0);
if ((br.read_be32() != LDBS_FILE_MAGIC)
|| (br.read_be32() != LDBS_FILE_TYPE))
Error() << "not a valid LDBS file";
if ((br.read_be32() != LDBS_FILE_MAGIC) ||
(br.read_be32() != LDBS_FILE_TYPE))
error("not a valid LDBS file");
uint32_t address = br.read_le32();
br.skip(4);
@@ -35,7 +33,7 @@ uint32_t LDBS::read(const Bytes& data)
{
br.seek(address);
if (br.read_be32() != LDBS_BLOCK_MAGIC)
Error() << fmt::format("invalid block at address 0x{:x}", address);
error("invalid block at address 0x{:x}", address);
Block& block = blocks[address];
block.type = br.read_be32();
@@ -46,7 +44,7 @@ uint32_t LDBS::read(const Bytes& data)
block.data.writer().append(br.read(size));
}
top = data.size();
return trackDirectory;
}
@@ -76,6 +74,6 @@ const Bytes LDBS::write(uint32_t trackDirectory)
bw.write_le32(previous);
bw.write_le32(0);
bw.write_le32(trackDirectory);
return data;
}

View File

@@ -3,17 +3,19 @@
#include "fluxmap.h"
#include "sector.h"
#include "flux.h"
#include "fmt/format.h"
#include "logger.h"
static bool indented = false;
static std::function<void(std::shared_ptr<const AnyLogMessage>)> loggerImpl =
Logger::textLogger;
Logger& Logger::operator<<(std::shared_ptr<const AnyLogMessage> message)
static std::function<void(std::shared_ptr<const AnyLogMessage>)> loggerImpl =
[](auto message)
{
std::cout << Logger::toString(*message) << std::flush;
};
void log(std::shared_ptr<const AnyLogMessage> message)
{
loggerImpl(message);
return *this;
}
void Logger::setLogger(
@@ -22,11 +24,6 @@ void Logger::setLogger(
loggerImpl = cb;
}
void Logger::textLogger(std::shared_ptr<const AnyLogMessage> message)
{
std::cout << toString(*message) << std::flush;
}
std::string Logger::toString(const AnyLogMessage& message)
{
std::stringstream stream;

View File

@@ -91,22 +91,26 @@ typedef std::variant<std::string,
OperationProgressLogMessage>
AnyLogMessage;
class Logger
template <class T>
inline void log(const T& message)
{
public:
Logger& operator<<(std::shared_ptr<const AnyLogMessage> message);
log(std::make_shared<const AnyLogMessage>(message));
}
template <class T>
Logger& operator<<(const T& message)
{
return *this << std::make_shared<const AnyLogMessage>(message);
}
extern void log(std::shared_ptr<const AnyLogMessage> message);
static void setLogger(
template <typename... Args>
inline void log(fmt::string_view fstr, const Args&... args)
{
log(fmt::format(fstr, args...));
}
namespace Logger
{
extern void setLogger(
std::function<void(std::shared_ptr<const AnyLogMessage>)> cb);
static std::string toString(const AnyLogMessage&);
static void textLogger(std::shared_ptr<const AnyLogMessage>);
};
extern std::string toString(const AnyLogMessage&);
}
#endif

View File

@@ -1,7 +1,6 @@
#include "globals.h"
#include "proto.h"
#include "lib/common.pb.h"
#include "fmt/format.h"
#include <regex>
ConfigProto config = []()
@@ -17,7 +16,7 @@ static double toDouble(const std::string& value)
size_t idx;
double d = std::stod(value, &idx);
if (value[idx] != '\0')
Error() << fmt::format("invalid number '{}'", value);
error("invalid number '{}'", value);
return d;
}
@@ -26,7 +25,7 @@ static int64_t toInt64(const std::string& value)
size_t idx;
int64_t d = std::stoll(value, &idx);
if (value[idx] != '\0')
Error() << fmt::format("invalid number '{}'", value);
error("invalid number '{}'", value);
return d;
}
@@ -35,7 +34,7 @@ static uint64_t toUint64(const std::string& value)
size_t idx;
uint64_t d = std::stoull(value, &idx);
if (value[idx] != '\0')
Error() << fmt::format("invalid number '{}'", value);
error("invalid number '{}'", value);
return d;
}
@@ -46,7 +45,7 @@ void setRange(RangeProto* range, const std::string& data)
std::smatch dmatch;
if (!std::regex_match(data, dmatch, DATA_REGEX))
Error() << "invalid range '" << data << "'";
error("invalid range '{}'", data);
int start = std::stoi(dmatch[1]);
range->set_start(start);
@@ -76,11 +75,9 @@ ProtoField resolveProtoPath(
{
const auto* field = descriptor->FindFieldByName(item);
if (!field)
Error() << fmt::format(
"no such config field '{}' in '{}'", item, path);
error("no such config field '{}' in '{}'", item, path);
if (field->type() != google::protobuf::FieldDescriptor::TYPE_MESSAGE)
Error() << fmt::format(
"config field '{}' in '{}' is not a message", item, path);
error("config field '{}' in '{}' is not a message", item, path);
const auto* reflection = message->GetReflection();
switch (field->label())
@@ -98,7 +95,7 @@ ProtoField resolveProtoPath(
break;
default:
Error() << "bad proto label " << field->label();
error("bad proto label {}", field->label());
}
descriptor = message->GetDescriptor();
@@ -106,8 +103,7 @@ ProtoField resolveProtoPath(
const auto* field = descriptor->FindFieldByName(trailing);
if (!field)
Error() << fmt::format(
"no such config field '{}' in '{}'", trailing, path);
error("no such config field '{}' in '{}'", trailing, path);
return std::make_pair(message, field);
}
@@ -161,7 +157,7 @@ void setProtoFieldFromString(ProtoField& protoField, const std::string& value)
const auto& it = boolvalues.find(value);
if (it == boolvalues.end())
Error() << "invalid boolean value";
error("invalid boolean value");
reflection->SetBool(message, field, it->second);
break;
}
@@ -171,7 +167,7 @@ void setProtoFieldFromString(ProtoField& protoField, const std::string& value)
const auto* enumfield = field->enum_type();
const auto* enumvalue = enumfield->FindValueByName(value);
if (!enumvalue)
Error() << fmt::format("unrecognised enum value '{}'", value);
error("unrecognised enum value '{}'", value);
reflection->SetEnum(message, field, enumvalue);
break;
@@ -192,7 +188,7 @@ void setProtoFieldFromString(ProtoField& protoField, const std::string& value)
}
/* fall through */
default:
Error() << "can't set this config value type";
error("can't set this config value type");
}
}
@@ -249,9 +245,8 @@ findAllProtoFields(google::protobuf::Message* message)
ConfigProto parseConfigBytes(const std::string_view& data)
{
ConfigProto proto;
if (!proto.ParseFromArray(data.begin(), data.size()))
Error() << "invalid internal config data";
return proto;
ConfigProto proto;
if (!proto.ParseFromArray(data.begin(), data.size()))
error("invalid internal config data");
return proto;
}

View File

@@ -10,7 +10,6 @@
#include "fluxsink/fluxsink.h"
#include "imagereader/imagereader.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
#include "sector.h"
#include "image.h"
#include "logger.h"
@@ -59,7 +58,7 @@ private:
void measureDiskRotation(
nanoseconds_t& oneRevolution, nanoseconds_t& hardSectorThreshold)
{
Logger() << BeginSpeedOperationLogMessage();
log(BeginSpeedOperationLogMessage());
int retries = 5;
usbSetDrive(config.drive().drive(),
@@ -74,8 +73,7 @@ void measureDiskRotation(
if (oneRevolution == 0)
{
Logger() << BeginOperationLogMessage{
"Measuring drive rotational speed"};
log(BeginOperationLogMessage{"Measuring drive rotational speed"});
do
{
oneRevolution =
@@ -87,13 +85,13 @@ void measureDiskRotation(
retries--;
} while ((oneRevolution == 0) && (retries > 0));
config.mutable_drive()->set_rotational_period_ms(oneRevolution / 1e6);
Logger() << EndOperationLogMessage{};
log(EndOperationLogMessage{});
}
if (oneRevolution == 0)
Error() << "Failed\nIs a disk in the drive?";
error("Failed\nIs a disk in the drive?");
Logger() << EndSpeedOperationLogMessage{oneRevolution};
log(EndSpeedOperationLogMessage{oneRevolution});
}
/* Given a set of sectors, deduplicates them sensibly (e.g. if there is a good
@@ -224,15 +222,15 @@ ReadResult readGroup(FluxSourceIteratorHolder& fluxSourceIteratorHolder,
if (!fluxSourceIterator.hasNext())
continue;
Logger() << BeginReadOperationLogMessage{
trackInfo->physicalTrack + offset, trackInfo->physicalSide};
log(BeginReadOperationLogMessage{
trackInfo->physicalTrack + offset, trackInfo->physicalSide});
std::shared_ptr<const Fluxmap> fluxmap = fluxSourceIterator.next();
// ->rescale(
// 1.0 / config.flux_source().rescale());
Logger() << EndReadOperationLogMessage()
<< fmt::format("{0} ms in {1} bytes",
(int)(fluxmap->duration() / 1e6),
fluxmap->bytes());
log(EndReadOperationLogMessage());
log("{0} ms in {1} bytes",
(int)(fluxmap->duration() / 1e6),
fluxmap->bytes());
auto trackdataflux = decoder.decodeToSectors(fluxmap, trackInfo);
trackFlux.trackDatas.push_back(trackdataflux);
@@ -256,13 +254,13 @@ void writeTracks(FluxSink& fluxSink,
std::function<bool(std::shared_ptr<const TrackInfo>& trackInfo)> verifier,
std::vector<std::shared_ptr<const TrackInfo>>& trackInfos)
{
Logger() << BeginOperationLogMessage{"Encoding and writing to disk"};
log(BeginOperationLogMessage{"Encoding and writing to disk"});
int index = 0;
for (auto& trackInfo : trackInfos)
{
Logger() << OperationProgressLogMessage{
index * 100 / (unsigned)trackInfos.size()};
log(OperationProgressLogMessage{
index * 100 / (unsigned)trackInfos.size()});
index++;
testForEmergencyStop();
@@ -275,8 +273,8 @@ void writeTracks(FluxSink& fluxSink,
{
unsigned physicalTrack = trackInfo->physicalTrack + offset;
Logger() << BeginWriteOperationLogMessage{
physicalTrack, trackInfo->physicalSide};
log(BeginWriteOperationLogMessage{
physicalTrack, trackInfo->physicalSide});
if (offset == config.drive().group_offset())
{
@@ -286,7 +284,7 @@ void writeTracks(FluxSink& fluxSink,
fluxSink.writeFlux(
physicalTrack, trackInfo->physicalSide, *fluxmap);
Logger() << fmt::format("writing {0} ms in {1} bytes",
log("writing {0} ms in {1} bytes",
int(fluxmap->duration() / 1e6),
fluxmap->bytes());
}
@@ -298,25 +296,24 @@ void writeTracks(FluxSink& fluxSink,
Fluxmap blank;
fluxSink.writeFlux(
physicalTrack, trackInfo->physicalSide, blank);
Logger() << "erased";
log("erased");
}
Logger() << EndWriteOperationLogMessage();
log(EndWriteOperationLogMessage());
}
if (verifier(trackInfo))
break;
if (retriesRemaining == 0)
Error() << "fatal error on write";
error("fatal error on write");
Logger() << fmt::format(
"retrying; {} retries remaining", retriesRemaining);
log("retrying; {} retries remaining", retriesRemaining);
retriesRemaining--;
}
}
Logger() << EndOperationLogMessage{"Write complete"};
log(EndOperationLogMessage{"Write complete"});
}
void writeTracks(FluxSink& fluxSink,
@@ -359,12 +356,12 @@ void writeTracksAndVerify(FluxSink& fluxSink,
FluxSourceIteratorHolder fluxSourceIteratorHolder(fluxSource);
auto result = readGroup(
fluxSourceIteratorHolder, trackInfo, *trackFlux, decoder);
Logger() << TrackReadLogMessage{trackFlux};
log(TrackReadLogMessage{trackFlux});
if (result != GOOD_READ)
{
adjustTrackOnError(fluxSource, trackInfo->physicalTrack);
Logger() << "bad read";
log("bad read");
return false;
}
@@ -383,12 +380,12 @@ void writeTracksAndVerify(FluxSink& fluxSink,
sector->logicalSector);
if (!s)
{
Logger() << "spurious sector on verify";
log("spurious sector on verify");
return false;
}
if (s->data != sector->data.slice(0, s->data.size()))
{
Logger() << "data mismatch on verify";
log("data mismatch on verify");
return false;
}
wanted.erase(sector->logicalTrack,
@@ -397,7 +394,7 @@ void writeTracksAndVerify(FluxSink& fluxSink,
}
if (!wanted.empty())
{
Logger() << "missing sector on verify";
log("missing sector on verify");
return false;
}
return true;
@@ -464,21 +461,20 @@ std::shared_ptr<TrackFlux> readAndDecodeTrack(FluxSource& fluxSource,
break;
if (result == BAD_AND_CAN_NOT_RETRY)
{
Logger() << fmt::format("no more data; giving up");
log("no more data; giving up");
break;
}
if (retriesRemaining == 0)
{
Logger() << fmt::format("giving up");
log("giving up");
break;
}
if (fluxSource.isHardware())
{
adjustTrackOnError(fluxSource, trackInfo->physicalTrack);
Logger() << fmt::format(
"retrying; {} retries remaining", retriesRemaining);
log("retrying; {} retries remaining", retriesRemaining);
retriesRemaining--;
}
}
@@ -495,13 +491,13 @@ std::shared_ptr<const DiskFlux> readDiskCommand(
auto diskflux = std::make_shared<DiskFlux>();
Logger() << BeginOperationLogMessage{"Reading and decoding disk"};
log(BeginOperationLogMessage{"Reading and decoding disk"});
auto locations = Layout::computeLocations();
unsigned index = 0;
for (auto& trackInfo : locations)
{
Logger() << OperationProgressLogMessage{
index * 100 / (unsigned)locations.size()};
log(OperationProgressLogMessage{
index * 100 / (unsigned)locations.size()});
index++;
testForEmergencyStop();
@@ -574,7 +570,7 @@ std::shared_ptr<const DiskFlux> readDiskCommand(
}
/* track can't be modified below this point. */
Logger() << TrackReadLogMessage{trackFlux};
log(TrackReadLogMessage{trackFlux});
}
std::set<std::shared_ptr<const Sector>> all_sectors;
@@ -585,8 +581,8 @@ std::shared_ptr<const DiskFlux> readDiskCommand(
diskflux->image = std::make_shared<Image>(all_sectors);
/* diskflux can't be modified below this point. */
Logger() << DiskReadLogMessage{diskflux};
Logger() << EndOperationLogMessage{"Read complete"};
log(DiskReadLogMessage{diskflux});
log(EndOperationLogMessage{"Read complete"});
return diskflux;
}
@@ -603,33 +599,32 @@ void readDiskCommand(
void rawReadDiskCommand(FluxSource& fluxsource, FluxSink& fluxsink)
{
Logger() << BeginOperationLogMessage{"Performing raw read of disk"};
log(BeginOperationLogMessage{"Performing raw read of disk"});
auto locations = Layout::computeLocations();
unsigned index = 0;
for (auto& trackInfo : locations)
{
Logger() << OperationProgressLogMessage{
index * 100 / (int)locations.size()};
log(OperationProgressLogMessage{index * 100 / (int)locations.size()});
index++;
testForEmergencyStop();
auto fluxSourceIterator = fluxsource.readFlux(
trackInfo->physicalTrack, trackInfo->physicalSide);
Logger() << BeginReadOperationLogMessage{
trackInfo->physicalTrack, trackInfo->physicalSide};
log(BeginReadOperationLogMessage{
trackInfo->physicalTrack, trackInfo->physicalSide});
auto fluxmap = fluxSourceIterator->next();
Logger() << EndReadOperationLogMessage()
<< fmt::format("{0} ms in {1} bytes",
(int)(fluxmap->duration() / 1e6),
fluxmap->bytes());
log(EndReadOperationLogMessage());
log("{0} ms in {1} bytes",
(int)(fluxmap->duration() / 1e6),
fluxmap->bytes());
fluxsink.writeFlux(
trackInfo->physicalTrack, trackInfo->physicalSide, *fluxmap);
}
Logger() << EndOperationLogMessage{"Raw read complete"};
log(EndOperationLogMessage{"Raw read complete"});
}
void fillBitmapTo(std::vector<bool>& bitmap,

View File

@@ -2,19 +2,20 @@
#include "flux.h"
#include "sector.h"
#include "layout.h"
#include "fmt/format.h"
Sector::Sector(const LogicalLocation& location):
LogicalLocation(location),
physicalTrack(Layout::remapTrackLogicalToPhysical(location.logicalTrack)),
physicalSide(Layout::remapSideLogicalToPhysical(location.logicalSide))
{}
LogicalLocation(location),
physicalTrack(Layout::remapTrackLogicalToPhysical(location.logicalTrack)),
physicalSide(Layout::remapSideLogicalToPhysical(location.logicalSide))
{
}
Sector::Sector(std::shared_ptr<const TrackInfo>& layout, unsigned sectorId):
LogicalLocation({ layout->logicalTrack, layout->logicalSide, sectorId }),
LogicalLocation({layout->logicalTrack, layout->logicalSide, sectorId}),
physicalTrack(layout->physicalTrack),
physicalSide(layout->physicalSide)
{}
{
}
std::string Sector::statusToString(Status status)
{

View File

@@ -5,332 +5,352 @@
#include "bytes.h"
#include "libusbp_config.h"
#include "libusbp.hpp"
#include "fmt/format.h"
#define MAX_TRANSFER (32*1024)
#define MAX_TRANSFER (32 * 1024)
/* Hacky: the board always operates in little-endian mode. */
static uint16_t read_short_from_usb(uint16_t usb)
{
uint8_t* p = (uint8_t*)&usb;
return p[0] | (p[1] << 8);
uint8_t* p = (uint8_t*)&usb;
return p[0] | (p[1] << 8);
}
class FluxEngineUsb : public USB
{
private:
uint8_t _buffer[FRAME_SIZE];
uint8_t _buffer[FRAME_SIZE];
void usb_cmd_send(void* ptr, size_t len)
{
size_t rlen;
_handle.write_pipe(FLUXENGINE_CMD_OUT_EP, ptr, len, &rlen);
}
void usb_cmd_send(void* ptr, size_t len)
{
size_t rlen;
_handle.write_pipe(FLUXENGINE_CMD_OUT_EP, ptr, len, &rlen);
}
void usb_cmd_recv(void* ptr, size_t len)
{
size_t rlen;
_handle.read_pipe(FLUXENGINE_CMD_IN_EP, ptr, len, &rlen);
}
void usb_cmd_recv(void* ptr, size_t len)
{
size_t rlen;
_handle.read_pipe(FLUXENGINE_CMD_IN_EP, ptr, len, &rlen);
}
void usb_data_send(const Bytes& bytes)
{
size_t ptr = 0;
while (ptr < bytes.size())
{
size_t rlen = bytes.size() - ptr;
if (rlen > MAX_TRANSFER)
rlen = MAX_TRANSFER;
_handle.write_pipe(FLUXENGINE_DATA_OUT_EP, bytes.cbegin() + ptr, rlen, &rlen);
ptr += rlen;
}
}
void usb_data_send(const Bytes& bytes)
{
size_t ptr = 0;
while (ptr < bytes.size())
{
size_t rlen = bytes.size() - ptr;
if (rlen > MAX_TRANSFER)
rlen = MAX_TRANSFER;
_handle.write_pipe(
FLUXENGINE_DATA_OUT_EP, bytes.cbegin() + ptr, rlen, &rlen);
ptr += rlen;
}
}
void usb_data_recv(Bytes& bytes)
{
size_t ptr = 0;
while (ptr < bytes.size())
{
size_t rlen = bytes.size() - ptr;
if (rlen > MAX_TRANSFER)
rlen = MAX_TRANSFER;
_handle.read_pipe(FLUXENGINE_DATA_IN_EP, bytes.begin() + ptr, rlen, &rlen);
ptr += rlen;
if (rlen < MAX_TRANSFER)
break;
}
bytes.resize(ptr);
}
void usb_data_recv(Bytes& bytes)
{
size_t ptr = 0;
while (ptr < bytes.size())
{
size_t rlen = bytes.size() - ptr;
if (rlen > MAX_TRANSFER)
rlen = MAX_TRANSFER;
_handle.read_pipe(
FLUXENGINE_DATA_IN_EP, bytes.begin() + ptr, rlen, &rlen);
ptr += rlen;
if (rlen < MAX_TRANSFER)
break;
}
bytes.resize(ptr);
}
public:
FluxEngineUsb(libusbp::device& device):
_device(device),
_interface(_device, 0, false),
_handle(_interface)
{
int version = getVersion();
if (version != FLUXENGINE_PROTOCOL_VERSION)
Error() << "your FluxEngine firmware is at version " << version
<< " but the client is for version " << FLUXENGINE_PROTOCOL_VERSION
<< "; please upgrade";
}
FluxEngineUsb(libusbp::device& device):
_device(device),
_interface(_device, 0, false),
_handle(_interface)
{
int version = getVersion();
if (version != FLUXENGINE_PROTOCOL_VERSION)
error(
"your FluxEngine firmware is at version {} but the client is "
"for version {}; please upgrade",
version,
FLUXENGINE_PROTOCOL_VERSION);
}
private:
libusbp::device _device;
libusbp::generic_interface _interface;
libusbp::generic_handle _handle;
libusbp::device _device;
libusbp::generic_interface _interface;
libusbp::generic_handle _handle;
private:
void bad_reply(void)
{
struct error_frame* f = (struct error_frame*) _buffer;
if (f->f.type != F_FRAME_ERROR)
Error() << fmt::format("bad USB reply 0x{:2x}", f->f.type);
switch (f->error)
{
case F_ERROR_BAD_COMMAND:
Error() << "device did not understand command";
void bad_reply(void)
{
struct error_frame* f = (struct error_frame*)_buffer;
if (f->f.type != F_FRAME_ERROR)
error("bad USB reply 0x{:2x}", f->f.type);
switch (f->error)
{
case F_ERROR_BAD_COMMAND:
error("device did not understand command");
case F_ERROR_UNDERRUN:
Error() << "USB underrun (not enough bandwidth)";
default:
Error() << fmt::format("unknown device error {}", f->error);
}
}
case F_ERROR_UNDERRUN:
error("USB underrun (not enough bandwidth)");
template <typename T>
T* await_reply(int desired)
{
for (;;)
{
usb_cmd_recv(_buffer, sizeof(_buffer));
struct any_frame* r = (struct any_frame*) _buffer;
if (r->f.type == F_FRAME_DEBUG)
{
std::cout << "dev: " << ((struct debug_frame*)r)->payload << std::endl;
continue;
}
if (r->f.type != desired)
bad_reply();
return (T*) r;
}
}
default:
error("unknown device error {}", f->error);
}
}
template <typename T>
T* await_reply(int desired)
{
for (;;)
{
usb_cmd_recv(_buffer, sizeof(_buffer));
struct any_frame* r = (struct any_frame*)_buffer;
if (r->f.type == F_FRAME_DEBUG)
{
std::cout << "dev: " << ((struct debug_frame*)r)->payload
<< std::endl;
continue;
}
if (r->f.type != desired)
bad_reply();
return (T*)r;
}
}
public:
int getVersion()
{
struct any_frame f = { .f = {.type = F_FRAME_GET_VERSION_CMD, .size = sizeof(f)} };
usb_cmd_send(&f, f.f.size);
auto r = await_reply<struct version_frame>(F_FRAME_GET_VERSION_REPLY);
return r->version;
}
int getVersion()
{
struct any_frame f = {
.f = {.type = F_FRAME_GET_VERSION_CMD, .size = sizeof(f)}
};
usb_cmd_send(&f, f.f.size);
auto r = await_reply<struct version_frame>(F_FRAME_GET_VERSION_REPLY);
return r->version;
}
void seek(int track)
{
struct seek_frame f = {
.f = { .type = F_FRAME_SEEK_CMD, .size = sizeof(f) },
.track = (uint8_t) track
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_SEEK_REPLY);
}
void seek(int track)
{
struct seek_frame f = {
.f = {.type = F_FRAME_SEEK_CMD, .size = sizeof(f)},
.track = (uint8_t)track
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_SEEK_REPLY);
}
void recalibrate()
{
struct any_frame f = {
.f = { .type = F_FRAME_RECALIBRATE_CMD, .size = sizeof(f) },
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_RECALIBRATE_REPLY);
}
void recalibrate()
{
struct any_frame f = {
.f = {.type = F_FRAME_RECALIBRATE_CMD, .size = sizeof(f)},
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_RECALIBRATE_REPLY);
}
nanoseconds_t getRotationalPeriod(int hardSectorCount)
{
struct measurespeed_frame f = {
.f = {.type = F_FRAME_MEASURE_SPEED_CMD, .size = sizeof(f)},
.hard_sector_count = (uint8_t) hardSectorCount,
};
usb_cmd_send(&f, f.f.size);
nanoseconds_t getRotationalPeriod(int hardSectorCount)
{
struct measurespeed_frame f = {
.f = {.type = F_FRAME_MEASURE_SPEED_CMD, .size = sizeof(f)},
.hard_sector_count = (uint8_t)hardSectorCount,
};
usb_cmd_send(&f, f.f.size);
auto r = await_reply<struct speed_frame>(F_FRAME_MEASURE_SPEED_REPLY);
return r->period_ms * 1000000;
}
auto r = await_reply<struct speed_frame>(F_FRAME_MEASURE_SPEED_REPLY);
return r->period_ms * 1000000;
}
void testBulkWrite()
{
struct any_frame f = { .f = {.type = F_FRAME_BULK_WRITE_TEST_CMD, .size = sizeof(f)} };
usb_cmd_send(&f, f.f.size);
void testBulkWrite()
{
struct any_frame f = {
.f = {.type = F_FRAME_BULK_WRITE_TEST_CMD, .size = sizeof(f)}
};
usb_cmd_send(&f, f.f.size);
/* These must match the device. */
const int XSIZE = 64;
const int YSIZE = 256;
const int ZSIZE = 64;
/* These must match the device. */
const int XSIZE = 64;
const int YSIZE = 256;
const int ZSIZE = 64;
std::cout << "Reading data: " << std::flush;
Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
double start_time = getCurrentTime();
usb_data_recv(bulk_buffer);
double elapsed_time = getCurrentTime() - start_time;
std::cout << "Reading data: " << std::flush;
Bytes bulk_buffer(XSIZE * YSIZE * ZSIZE);
double start_time = getCurrentTime();
usb_data_recv(bulk_buffer);
double elapsed_time = getCurrentTime() - start_time;
std::cout << "transferred "
<< bulk_buffer.size()
<< " bytes from device -> PC in "
<< int(elapsed_time * 1000.0)
<< " ms ("
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
<< " kB/s)"
<< std::endl;
std::cout << "transferred " << bulk_buffer.size()
<< " bytes from device -> PC in "
<< int(elapsed_time * 1000.0) << " ms ("
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
<< " kB/s)" << std::endl;
for (int x=0; x<XSIZE; x++)
for (int y=0; y<YSIZE; y++)
for (int z=0; z<ZSIZE; z++)
{
int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
if (bulk_buffer[offset] != uint8_t(x+y+z))
Error() << "data transfer corrupted at 0x"
<< std::hex << offset << std::dec
<< " "
<< x << '.' << y << '.' << z << '.';
}
for (int x = 0; x < XSIZE; x++)
for (int y = 0; y < YSIZE; y++)
for (int z = 0; z < ZSIZE; z++)
{
int offset = x * XSIZE * YSIZE + y * ZSIZE + z;
if (bulk_buffer[offset] != uint8_t(x + y + z))
error("data transfer corrupted at 0x{:x} {}.{}.{}",
offset,
x,
y,
z);
}
await_reply<struct any_frame>(F_FRAME_BULK_WRITE_TEST_REPLY);
}
await_reply<struct any_frame>(F_FRAME_BULK_WRITE_TEST_REPLY);
}
void testBulkRead()
{
struct any_frame f = { .f = {.type = F_FRAME_BULK_READ_TEST_CMD, .size = sizeof(f)} };
usb_cmd_send(&f, f.f.size);
void testBulkRead()
{
struct any_frame f = {
.f = {.type = F_FRAME_BULK_READ_TEST_CMD, .size = sizeof(f)}
};
usb_cmd_send(&f, f.f.size);
/* These must match the device. */
const int XSIZE = 64;
const int YSIZE = 256;
const int ZSIZE = 64;
/* These must match the device. */
const int XSIZE = 64;
const int YSIZE = 256;
const int ZSIZE = 64;
Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
for (int x=0; x<XSIZE; x++)
for (int y=0; y<YSIZE; y++)
for (int z=0; z<ZSIZE; z++)
{
int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
bulk_buffer[offset] = uint8_t(x+y+z);
}
Bytes bulk_buffer(XSIZE * YSIZE * ZSIZE);
for (int x = 0; x < XSIZE; x++)
for (int y = 0; y < YSIZE; y++)
for (int z = 0; z < ZSIZE; z++)
{
int offset = x * XSIZE * YSIZE + y * ZSIZE + z;
bulk_buffer[offset] = uint8_t(x + y + z);
}
std::cout << "Writing data: " << std::flush;
double start_time = getCurrentTime();
usb_data_send(bulk_buffer);
double elapsed_time = getCurrentTime() - start_time;
std::cout << "Writing data: " << std::flush;
double start_time = getCurrentTime();
usb_data_send(bulk_buffer);
double elapsed_time = getCurrentTime() - start_time;
std::cout << "transferred "
<< bulk_buffer.size()
<< " bytes from PC -> device in "
<< int(elapsed_time * 1000.0)
<< " ms ("
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
<< " kB/s)"
<< std::endl;
std::cout << "transferred " << bulk_buffer.size()
<< " bytes from PC -> device in "
<< int(elapsed_time * 1000.0) << " ms ("
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
<< " kB/s)" << std::endl;
await_reply<struct any_frame>(F_FRAME_BULK_READ_TEST_REPLY);
}
await_reply<struct any_frame>(F_FRAME_BULK_READ_TEST_REPLY);
}
Bytes read(int side, bool synced, nanoseconds_t readTime,
nanoseconds_t hardSectorThreshold)
{
struct read_frame f = {
.f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
.synced = (uint8_t) synced,
};
f.hardsec_threshold_ms = (hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
uint16_t milliseconds = readTime / 1e6;
((uint8_t*)&f.milliseconds)[0] = milliseconds;
((uint8_t*)&f.milliseconds)[1] = milliseconds >> 8;
usb_cmd_send(&f, f.f.size);
Bytes read(int side,
bool synced,
nanoseconds_t readTime,
nanoseconds_t hardSectorThreshold)
{
struct read_frame f = {
.f = {.type = F_FRAME_READ_CMD, .size = sizeof(f)},
.side = (uint8_t)side,
.synced = (uint8_t)synced,
};
f.hardsec_threshold_ms =
(hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
uint16_t milliseconds = readTime / 1e6;
((uint8_t*)&f.milliseconds)[0] = milliseconds;
((uint8_t*)&f.milliseconds)[1] = milliseconds >> 8;
usb_cmd_send(&f, f.f.size);
auto fluxmap = std::unique_ptr<Fluxmap>(new Fluxmap);
auto fluxmap = std::unique_ptr<Fluxmap>(new Fluxmap);
Bytes buffer(1024*1024);
usb_data_recv(buffer);
Bytes buffer(1024 * 1024);
usb_data_recv(buffer);
await_reply<struct any_frame>(F_FRAME_READ_REPLY);
return buffer;
}
await_reply<struct any_frame>(F_FRAME_READ_REPLY);
return buffer;
}
void write(int side, const Bytes& bytes, nanoseconds_t hardSectorThreshold)
{
unsigned safelen = bytes.size() & ~(FRAME_SIZE-1);
Bytes safeBytes = bytes.slice(0, safelen);
void write(int side, const Bytes& bytes, nanoseconds_t hardSectorThreshold)
{
unsigned safelen = bytes.size() & ~(FRAME_SIZE - 1);
Bytes safeBytes = bytes.slice(0, safelen);
struct write_frame f = {
.f = { .type = F_FRAME_WRITE_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
};
f.hardsec_threshold_ms = (hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
((uint8_t*)&f.bytes_to_write)[0] = safelen;
((uint8_t*)&f.bytes_to_write)[1] = safelen >> 8;
((uint8_t*)&f.bytes_to_write)[2] = safelen >> 16;
((uint8_t*)&f.bytes_to_write)[3] = safelen >> 24;
struct write_frame f = {
.f = {.type = F_FRAME_WRITE_CMD, .size = sizeof(f)},
.side = (uint8_t)side,
};
f.hardsec_threshold_ms =
(hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
((uint8_t*)&f.bytes_to_write)[0] = safelen;
((uint8_t*)&f.bytes_to_write)[1] = safelen >> 8;
((uint8_t*)&f.bytes_to_write)[2] = safelen >> 16;
((uint8_t*)&f.bytes_to_write)[3] = safelen >> 24;
usb_cmd_send(&f, f.f.size);
usb_data_send(safeBytes);
await_reply<struct any_frame>(F_FRAME_WRITE_REPLY);
}
usb_cmd_send(&f, f.f.size);
usb_data_send(safeBytes);
void erase(int side, nanoseconds_t hardSectorThreshold)
{
struct erase_frame f = {
.f = { .type = F_FRAME_ERASE_CMD, .size = sizeof(f) },
.side = (uint8_t) side,
};
f.hardsec_threshold_ms = (hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_WRITE_REPLY);
}
await_reply<struct any_frame>(F_FRAME_ERASE_REPLY);
}
void erase(int side, nanoseconds_t hardSectorThreshold)
{
struct erase_frame f = {
.f = {.type = F_FRAME_ERASE_CMD, .size = sizeof(f)},
.side = (uint8_t)side,
};
f.hardsec_threshold_ms =
(hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
usb_cmd_send(&f, f.f.size);
void setDrive(int drive, bool high_density, int index_mode)
{
struct set_drive_frame f = {
.f = { .type = F_FRAME_SET_DRIVE_CMD, .size = sizeof(f) },
.drive = (uint8_t) drive,
.high_density = high_density,
.index_mode = (uint8_t) index_mode
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_SET_DRIVE_REPLY);
}
await_reply<struct any_frame>(F_FRAME_ERASE_REPLY);
}
void measureVoltages(struct voltages_frame* voltages)
{
struct any_frame f = {
{ .type = F_FRAME_MEASURE_VOLTAGES_CMD, .size = sizeof(f) },
};
usb_cmd_send(&f, f.f.size);
void setDrive(int drive, bool high_density, int index_mode)
{
struct set_drive_frame f = {
.f = {.type = F_FRAME_SET_DRIVE_CMD, .size = sizeof(f)},
.drive = (uint8_t)drive,
.high_density = high_density,
.index_mode = (uint8_t)index_mode
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_SET_DRIVE_REPLY);
}
auto convert_voltages_from_usb = [&](const struct voltages& vin, struct voltages& vout)
{
vout.logic0_mv = read_short_from_usb(vin.logic0_mv);
vout.logic1_mv = read_short_from_usb(vin.logic1_mv);
};
void measureVoltages(struct voltages_frame* voltages)
{
struct any_frame f = {
{.type = F_FRAME_MEASURE_VOLTAGES_CMD, .size = sizeof(f)},
};
usb_cmd_send(&f, f.f.size);
struct voltages_frame* r = await_reply<struct voltages_frame>(F_FRAME_MEASURE_VOLTAGES_REPLY);
convert_voltages_from_usb(r->input_both_off, voltages->input_both_off);
convert_voltages_from_usb(r->input_drive_0_selected, voltages->input_drive_0_selected);
convert_voltages_from_usb(r->input_drive_1_selected, voltages->input_drive_1_selected);
convert_voltages_from_usb(r->input_drive_0_running, voltages->input_drive_0_running);
convert_voltages_from_usb(r->input_drive_1_running, voltages->input_drive_1_running);
convert_voltages_from_usb(r->output_both_off, voltages->output_both_off);
convert_voltages_from_usb(r->output_drive_0_selected, voltages->output_drive_0_selected);
convert_voltages_from_usb(r->output_drive_1_selected, voltages->output_drive_1_selected);
convert_voltages_from_usb(r->output_drive_0_running, voltages->output_drive_0_running);
convert_voltages_from_usb(r->output_drive_1_running, voltages->output_drive_1_running);
}
auto convert_voltages_from_usb =
[&](const struct voltages& vin, struct voltages& vout)
{
vout.logic0_mv = read_short_from_usb(vin.logic0_mv);
vout.logic1_mv = read_short_from_usb(vin.logic1_mv);
};
struct voltages_frame* r =
await_reply<struct voltages_frame>(F_FRAME_MEASURE_VOLTAGES_REPLY);
convert_voltages_from_usb(r->input_both_off, voltages->input_both_off);
convert_voltages_from_usb(
r->input_drive_0_selected, voltages->input_drive_0_selected);
convert_voltages_from_usb(
r->input_drive_1_selected, voltages->input_drive_1_selected);
convert_voltages_from_usb(
r->input_drive_0_running, voltages->input_drive_0_running);
convert_voltages_from_usb(
r->input_drive_1_running, voltages->input_drive_1_running);
convert_voltages_from_usb(
r->output_both_off, voltages->output_both_off);
convert_voltages_from_usb(
r->output_drive_0_selected, voltages->output_drive_0_selected);
convert_voltages_from_usb(
r->output_drive_1_selected, voltages->output_drive_1_selected);
convert_voltages_from_usb(
r->output_drive_0_running, voltages->output_drive_0_running);
convert_voltages_from_usb(
r->output_drive_1_running, voltages->output_drive_1_running);
}
};
USB* createFluxengineUsb(libusbp::device& device)
{
return new FluxEngineUsb(device);
return new FluxEngineUsb(device);
}

View File

@@ -2,155 +2,153 @@
#include "usb.h"
#include "protocol.h"
#include "bytes.h"
#include "fmt/format.h"
#include "greaseweazle.h"
Bytes fluxEngineToGreaseWeazle(const Bytes& fldata, nanoseconds_t clock)
{
Bytes gwdata;
ByteWriter bw(gwdata);
ByteReader br(fldata);
uint32_t ticks_fl = 0;
uint32_t ticks_gw = 0;
Bytes gwdata;
ByteWriter bw(gwdata);
ByteReader br(fldata);
uint32_t ticks_fl = 0;
uint32_t ticks_gw = 0;
auto write_28 = [&](uint32_t val) {
bw.write_8(1 | (val<<1) & 0xff);
bw.write_8(1 | (val>>6) & 0xff);
bw.write_8(1 | (val>>13) & 0xff);
bw.write_8(1 | (val>>20) & 0xff);
};
auto write_28 = [&](uint32_t val)
{
bw.write_8(1 | (val << 1) & 0xff);
bw.write_8(1 | (val >> 6) & 0xff);
bw.write_8(1 | (val >> 13) & 0xff);
bw.write_8(1 | (val >> 20) & 0xff);
};
while (!br.eof())
{
uint8_t b = br.read_8();
ticks_fl += b & 0x3f;
if (b & F_BIT_PULSE)
{
uint32_t newticks_gw = ticks_fl * NS_PER_TICK / clock;
uint32_t delta = newticks_gw - ticks_gw;
if (delta < 250)
bw.write_8(delta);
else
{
int high = (delta-250) / 255;
if (high < 5)
{
bw.write_8(250 + high);
bw.write_8(1 + (delta-250) % 255);
}
else
{
bw.write_8(255);
bw.write_8(FLUXOP_SPACE);
write_28(delta - 249);
bw.write_8(249);
}
}
ticks_gw = newticks_gw;
}
}
bw.write_8(0); /* end of stream */
return gwdata;
while (!br.eof())
{
uint8_t b = br.read_8();
ticks_fl += b & 0x3f;
if (b & F_BIT_PULSE)
{
uint32_t newticks_gw = ticks_fl * NS_PER_TICK / clock;
uint32_t delta = newticks_gw - ticks_gw;
if (delta < 250)
bw.write_8(delta);
else
{
int high = (delta - 250) / 255;
if (high < 5)
{
bw.write_8(250 + high);
bw.write_8(1 + (delta - 250) % 255);
}
else
{
bw.write_8(255);
bw.write_8(FLUXOP_SPACE);
write_28(delta - 249);
bw.write_8(249);
}
}
ticks_gw = newticks_gw;
}
}
bw.write_8(0); /* end of stream */
return gwdata;
}
Bytes greaseWeazleToFluxEngine(const Bytes& gwdata, nanoseconds_t clock)
{
Bytes fldata;
ByteReader br(gwdata);
ByteWriter bw(fldata);
Bytes fldata;
ByteReader br(gwdata);
ByteWriter bw(fldata);
auto read_28 = [&]() {
return ((br.read_8() & 0xfe) >> 1)
| ((br.read_8() & 0xfe) << 6)
| ((br.read_8() & 0xfe) << 13)
| ((br.read_8() & 0xfe) << 20);
auto read_28 = [&]()
{
return ((br.read_8() & 0xfe) >> 1) | ((br.read_8() & 0xfe) << 6) |
((br.read_8() & 0xfe) << 13) | ((br.read_8() & 0xfe) << 20);
};
uint32_t ticks_gw = 0;
uint32_t lastevent_fl = 0;
uint32_t index_gw = ~0;
uint32_t ticks_gw = 0;
uint32_t lastevent_fl = 0;
uint32_t index_gw = ~0;
while (!br.eof())
{
uint8_t b = br.read_8();
if (!b)
break;
while (!br.eof())
{
uint8_t b = br.read_8();
if (!b)
break;
uint8_t event = 0;
if (b == 255)
{
switch (br.read_8())
{
case FLUXOP_INDEX:
index_gw = ticks_gw + read_28();
break;
uint8_t event = 0;
if (b == 255)
{
switch (br.read_8())
{
case FLUXOP_INDEX:
index_gw = ticks_gw + read_28();
break;
case FLUXOP_SPACE:
ticks_gw += read_28();
break;
case FLUXOP_SPACE:
ticks_gw += read_28();
break;
default:
Error() << "bad opcode in GreaseWeazle stream";
}
}
else
{
if (b < 250)
ticks_gw += b;
else
{
int delta = 250 + (b-250)*255 + br.read_8() - 1;
ticks_gw += delta;
}
event = F_BIT_PULSE;
}
default:
error("bad opcode in GreaseWeazle stream");
}
}
else
{
if (b < 250)
ticks_gw += b;
else
{
int delta = 250 + (b - 250) * 255 + br.read_8() - 1;
ticks_gw += delta;
}
event = F_BIT_PULSE;
}
if (event)
{
uint32_t index_fl = round((index_gw * clock) / NS_PER_TICK);
uint32_t ticks_fl = round((ticks_gw * clock) / NS_PER_TICK);
if (index_gw != ~0)
{
if (index_fl < ticks_fl)
{
uint32_t delta_fl = index_fl - lastevent_fl;
while (delta_fl > 0x3f)
{
bw.write_8(0x3f);
delta_fl -= 0x3f;
}
bw.write_8(delta_fl | F_BIT_INDEX);
lastevent_fl = index_fl;
index_gw = ~0;
}
else if (index_fl == ticks_fl)
event |= F_BIT_INDEX;
}
if (event)
{
uint32_t index_fl = round((index_gw * clock) / NS_PER_TICK);
uint32_t ticks_fl = round((ticks_gw * clock) / NS_PER_TICK);
if (index_gw != ~0)
{
if (index_fl < ticks_fl)
{
uint32_t delta_fl = index_fl - lastevent_fl;
while (delta_fl > 0x3f)
{
bw.write_8(0x3f);
delta_fl -= 0x3f;
}
bw.write_8(delta_fl | F_BIT_INDEX);
lastevent_fl = index_fl;
index_gw = ~0;
}
else if (index_fl == ticks_fl)
event |= F_BIT_INDEX;
}
uint32_t delta_fl = ticks_fl - lastevent_fl;
while (delta_fl > 0x3f)
{
bw.write_8(0x3f);
delta_fl -= 0x3f;
}
bw.write_8(delta_fl | event);
lastevent_fl = ticks_fl;
}
}
uint32_t delta_fl = ticks_fl - lastevent_fl;
while (delta_fl > 0x3f)
{
bw.write_8(0x3f);
delta_fl -= 0x3f;
}
bw.write_8(delta_fl | event);
lastevent_fl = ticks_fl;
}
}
return fldata;
return fldata;
}
/* Left-truncates at the first index mark, so the resulting data as aligned at
* the index. */
Bytes stripPartialRotation(const Bytes& fldata)
{
for (unsigned i=0; i<fldata.size(); i++)
{
uint8_t b = fldata[i];
if (b & F_BIT_INDEX)
return fldata.slice(i);
}
return fldata;
for (unsigned i = 0; i < fldata.size(); i++)
{
uint8_t b = fldata[i];
if (b & F_BIT_INDEX)
return fldata.slice(i);
}
return fldata;
}

View File

@@ -3,7 +3,6 @@
#include "protocol.h"
#include "fluxmap.h"
#include "bytes.h"
#include "fmt/format.h"
#include "lib/usb/usb.pb.h"
#include "greaseweazle.h"
#include "serial.h"
@@ -12,25 +11,38 @@ static const char* gw_error(int e)
{
switch (e)
{
case ACK_OKAY: return "OK";
case ACK_BAD_COMMAND: return "Bad command";
case ACK_NO_INDEX: return "No index";
case ACK_NO_TRK0: return "No track 0";
case ACK_FLUX_OVERFLOW: return "Overflow";
case ACK_FLUX_UNDERFLOW: return "Underflow";
case ACK_WRPROT: return "Write protected";
case ACK_NO_UNIT: return "No unit";
case ACK_NO_BUS: return "No bus";
case ACK_BAD_UNIT: return "Invalid unit";
case ACK_BAD_PIN: return "Invalid pin";
case ACK_BAD_CYLINDER: return "Invalid track";
default: return "Unknown error";
case ACK_OKAY:
return "OK";
case ACK_BAD_COMMAND:
return "Bad command";
case ACK_NO_INDEX:
return "No index";
case ACK_NO_TRK0:
return "No track 0";
case ACK_FLUX_OVERFLOW:
return "Overflow";
case ACK_FLUX_UNDERFLOW:
return "Underflow";
case ACK_WRPROT:
return "Write protected";
case ACK_NO_UNIT:
return "No unit";
case ACK_NO_BUS:
return "No bus";
case ACK_BAD_UNIT:
return "Invalid unit";
case ACK_BAD_PIN:
return "Invalid pin";
case ACK_BAD_CYLINDER:
return "Invalid track";
default:
return "Unknown error";
}
}
static uint32_t ss_rand_next(uint32_t x)
{
return (x&1) ? (x>>1) ^ 0x80000062 : x>>1;
return (x & 1) ? (x >> 1) ^ 0x80000062 : x >> 1;
}
class GreaseWeazleUsb : public USB
@@ -41,10 +53,8 @@ private:
uint8_t buffer[4];
_serial->read(buffer, sizeof(buffer));
return ((buffer[0] & 0xfe) >> 1)
| ((buffer[1] & 0xfe) << 6)
| ((buffer[2] & 0xfe) << 13)
| ((buffer[3] & 0xfe) << 20);
return ((buffer[0] & 0xfe) >> 1) | ((buffer[1] & 0xfe) << 6) |
((buffer[2] & 0xfe) << 13) | ((buffer[3] & 0xfe) << 20);
}
void do_command(const Bytes& command)
@@ -55,16 +65,20 @@ private:
_serial->read(buffer, sizeof(buffer));
if (buffer[0] != command[0])
Error() << fmt::format("command returned garbage (0x{:x} != 0x{:x} with status 0x{:x})",
buffer[0], command[0], buffer[1]);
error(
"command returned garbage (0x{:x} != 0x{:x} with status "
"0x{:x})",
buffer[0],
command[0],
buffer[1]);
if (buffer[1])
Error() << fmt::format("GreaseWeazle error: {}", gw_error(buffer[1]));
error("GreaseWeazle error: {}", gw_error(buffer[1]));
}
public:
GreaseWeazleUsb(const std::string& port, const GreaseWeazleProto& config):
_serial(SerialPort::openSerialPort(port)),
_config(config)
_serial(SerialPort::openSerialPort(port)),
_config(config)
{
int version = getVersion();
if (version >= 29)
@@ -75,18 +89,21 @@ public:
_version = V22;
else
{
Error() << "only GreaseWeazle firmware versions 22 and 24 or above are currently "
<< "supported, but you have version " << version << ". Please file a bug.";
error(
"only GreaseWeazle firmware versions 22 and 24 or above are "
"currently "
"supported, but you have version {}. Please file a bug.",
version);
}
/* Configure the hardware. */
do_command({ CMD_SET_BUS_TYPE, 3, (uint8_t)config.bus_type() });
do_command({CMD_SET_BUS_TYPE, 3, (uint8_t)config.bus_type()});
}
int getVersion()
{
do_command({ CMD_GET_INFO, 3, GETINFO_FIRMWARE });
do_command({CMD_GET_INFO, 3, GETINFO_FIRMWARE});
Bytes response = _serial->readBytes(32);
ByteReader br(response);
@@ -103,16 +120,16 @@ public:
{
seek(0);
}
void seek(int track)
{
do_command({ CMD_SEEK, 3, (uint8_t)track });
do_command({CMD_SEEK, 3, (uint8_t)track});
}
nanoseconds_t getRotationalPeriod(int hardSectorCount)
{
if (hardSectorCount != 0)
Error() << "hard sectors are currently unsupported on the GreaseWeazel";
error("hard sectors are currently unsupported on the GreaseWeazel");
/* The GreaseWeazle doesn't have a command to fetch the period directly,
* so we have to do a flux read. */
@@ -120,7 +137,7 @@ public:
switch (_version)
{
case V22:
do_command({ CMD_READ_FLUX, 2 });
do_command({CMD_READ_FLUX, 2});
break;
case V24:
@@ -130,8 +147,8 @@ public:
cmd.writer()
.write_8(CMD_READ_FLUX)
.write_8(cmd.size())
.write_le32(0) //ticks default value (guessed)
.write_le16(2);//revolutions
.write_le32(0) // ticks default value (guessed)
.write_le16(2); // revolutions
do_command(cmd);
}
}
@@ -164,7 +181,7 @@ public:
break;
default:
Error() << "bad opcode in GreaseWeazle stream";
error("bad opcode in GreaseWeazle stream");
}
}
else
@@ -173,24 +190,26 @@ public:
ticks_gw += b;
else
{
int delta = 250 + (b-250)*255 + _serial->readByte() - 1;
int delta = 250 + (b - 250) * 255 + _serial->readByte() - 1;
ticks_gw += delta;
}
}
}
if (secondindex == ~0)
Error() << "unable to determine disk rotational period (is a disk in the drive?)";
do_command({ CMD_GET_FLUX_STATUS, 2 });
error(
"unable to determine disk rotational period (is a disk in the "
"drive?)");
do_command({CMD_GET_FLUX_STATUS, 2});
_revolutions = (nanoseconds_t)(secondindex - firstindex) * _clock;
return _revolutions;
}
void testBulkWrite()
{
std::cout << "Writing data: " << std::flush;
const int LEN = 10*1024*1024;
const int LEN = 10 * 1024 * 1024;
Bytes cmd;
switch (_version)
{
@@ -220,24 +239,27 @@ public:
Bytes junk(LEN);
uint32_t seed = 0;
for (int i=0; i<LEN; i++)
for (int i = 0; i < LEN; i++)
{
junk[i] = seed;
seed = ss_rand_next(seed);
}
double start_time = getCurrentTime();
double start_time = getCurrentTime();
_serial->write(junk);
_serial->readBytes(1);
double elapsed_time = getCurrentTime() - start_time;
double elapsed_time = getCurrentTime() - start_time;
std::cout << fmt::format("transferred {} bytes from PC -> device in {} ms ({} kb/s)\n",
LEN, int(elapsed_time * 1000.0), int((LEN / 1024.0) / elapsed_time));
std::cout << fmt::format(
"transferred {} bytes from PC -> device in {} ms ({} kb/s)\n",
LEN,
int(elapsed_time * 1000.0),
int((LEN / 1024.0) / elapsed_time));
}
void testBulkRead()
{
std::cout << "Reading data: " << std::flush;
const int LEN = 10*1024*1024;
const int LEN = 10 * 1024 * 1024;
Bytes cmd;
switch (_version)
{
@@ -265,26 +287,32 @@ public:
}
do_command(cmd);
double start_time = getCurrentTime();
double start_time = getCurrentTime();
_serial->readBytes(LEN);
double elapsed_time = getCurrentTime() - start_time;
double elapsed_time = getCurrentTime() - start_time;
std::cout << fmt::format("transferred {} bytes from device -> PC in {} ms ({} kb/s)\n",
LEN, int(elapsed_time * 1000.0), int((LEN / 1024.0) / elapsed_time));
std::cout << fmt::format(
"transferred {} bytes from device -> PC in {} ms ({} kb/s)\n",
LEN,
int(elapsed_time * 1000.0),
int((LEN / 1024.0) / elapsed_time));
}
Bytes read(int side, bool synced, nanoseconds_t readTime, nanoseconds_t hardSectorThreshold)
Bytes read(int side,
bool synced,
nanoseconds_t readTime,
nanoseconds_t hardSectorThreshold)
{
if (hardSectorThreshold != 0)
Error() << "hard sectors are currently unsupported on the GreaseWeazel";
error("hard sectors are currently unsupported on the GreaseWeazel");
do_command({ CMD_HEAD, 3, (uint8_t)side });
do_command({CMD_HEAD, 3, (uint8_t)side});
switch (_version)
{
case V22:
{
int revolutions = (readTime+_revolutions-1) / _revolutions;
int revolutions = (readTime + _revolutions - 1) / _revolutions;
Bytes cmd(4);
cmd.writer()
.write_8(CMD_READ_FLUX)
@@ -301,13 +329,14 @@ public:
cmd.writer()
.write_8(CMD_READ_FLUX)
.write_8(cmd.size())
.write_le32((readTime + (synced ? _revolutions : 0)) / _clock)
.write_le32(
(readTime + (synced ? _revolutions : 0)) / _clock)
.write_le16(0);
do_command(cmd);
}
}
Bytes buffer;
Bytes buffer;
ByteWriter bw(buffer);
for (;;)
{
@@ -317,7 +346,7 @@ public:
bw.write_8(b);
}
do_command({ CMD_GET_FLUX_STATUS, 2 });
do_command({CMD_GET_FLUX_STATUS, 2});
Bytes fldata = greaseWeazleToFluxEngine(buffer, _clock);
if (synced)
@@ -328,32 +357,32 @@ public:
void write(int side, const Bytes& fldata, nanoseconds_t hardSectorThreshold)
{
if (hardSectorThreshold != 0)
Error() << "hard sectors are currently unsupported on the GreaseWeazel";
error("hard sectors are currently unsupported on the GreaseWeazel");
do_command({ CMD_HEAD, 3, (uint8_t)side });
do_command({CMD_HEAD, 3, (uint8_t)side});
switch (_version)
{
case V22:
do_command({ CMD_WRITE_FLUX, 3, 1 });
do_command({CMD_WRITE_FLUX, 3, 1});
break;
case V24:
case V29:
do_command({ CMD_WRITE_FLUX, 4, 1, 1 });
do_command({CMD_WRITE_FLUX, 4, 1, 1});
break;
}
_serial->write(fluxEngineToGreaseWeazle(fldata, _clock));
_serial->readByte(); /* synchronise */
do_command({ CMD_GET_FLUX_STATUS, 2 });
do_command({CMD_GET_FLUX_STATUS, 2});
}
void erase(int side, nanoseconds_t hardSectorThreshold)
{
if (hardSectorThreshold != 0)
Error() << "hard sectors are currently unsupported on the GreaseWeazel";
error("hard sectors are currently unsupported on the GreaseWeazel");
do_command({ CMD_HEAD, 3, (uint8_t)side });
do_command({CMD_HEAD, 3, (uint8_t)side});
Bytes cmd(6);
ByteWriter bw(cmd);
@@ -363,18 +392,20 @@ public:
do_command(cmd);
_serial->readByte(); /* synchronise */
do_command({ CMD_GET_FLUX_STATUS, 2 });
do_command({CMD_GET_FLUX_STATUS, 2});
}
void setDrive(int drive, bool high_density, int index_mode)
{
do_command({ CMD_SELECT, 3, (uint8_t)drive });
do_command({ CMD_MOTOR, 4, (uint8_t)drive, 1 });
do_command({ CMD_SET_PIN, 4, 2, (uint8_t)(high_density ? 1 : 0) });
do_command({CMD_SELECT, 3, (uint8_t)drive});
do_command({CMD_MOTOR, 4, (uint8_t)drive, 1});
do_command({CMD_SET_PIN, 4, 2, (uint8_t)(high_density ? 1 : 0)});
}
void measureVoltages(struct voltages_frame* voltages)
{ Error() << "unsupported operation on the GreaseWeazle"; }
{
error("unsupported operation on the GreaseWeazle");
}
private:
enum
@@ -383,7 +414,7 @@ private:
V24,
V29
};
std::unique_ptr<SerialPort> _serial;
const GreaseWeazleProto& _config;
int _version;
@@ -391,7 +422,8 @@ private:
nanoseconds_t _revolutions;
};
USB* createGreaseWeazleUsb(const std::string& port, const GreaseWeazleProto& config)
USB* createGreaseWeazleUsb(
const std::string& port, const GreaseWeazleProto& config)
{
return new GreaseWeazleUsb(port, config);
}

View File

@@ -3,7 +3,6 @@
#include "protocol.h"
#include "fluxmap.h"
#include "bytes.h"
#include "fmt/format.h"
#include "serial.h"
#include <unistd.h>
#include <sys/types.h>
@@ -11,230 +10,226 @@
#include <fcntl.h>
#if defined __WIN32__
#include <windows.h>
#include <windows.h>
class SerialPortImpl : public SerialPort
{
public:
SerialPortImpl(const std::string& name)
{
std::string dos_name = "\\\\.\\" + name;
_handle = CreateFileA(
dos_name.c_str(),
/* dwDesiredAccess= */ GENERIC_READ|GENERIC_WRITE,
/* dwShareMode= */ 0,
/* lpSecurityAttribues= */ nullptr,
/* dwCreationDisposition= */ OPEN_EXISTING,
/* dwFlagsAndAttributes= */ FILE_ATTRIBUTE_NORMAL,
/* hTemplateFile= */ nullptr);
if (_handle == INVALID_HANDLE_VALUE)
Error() << fmt::format("cannot open serial port '{}': {}",
name, get_last_error_string());
DCB dcb =
{
.DCBlength = sizeof(DCB),
.BaudRate = CBR_9600,
.fBinary = true,
.ByteSize = 8,
.Parity = NOPARITY,
.StopBits = ONESTOPBIT
};
SetCommState(_handle, &dcb);
class SerialPortImpl : public SerialPort
{
public:
SerialPortImpl(const std::string& name)
{
std::string dos_name = "\\\\.\\" + name;
_handle = CreateFileA(dos_name.c_str(),
/* dwDesiredAccess= */ GENERIC_READ | GENERIC_WRITE,
/* dwShareMode= */ 0,
/* lpSecurityAttribues= */ nullptr,
/* dwCreationDisposition= */ OPEN_EXISTING,
/* dwFlagsAndAttributes= */ FILE_ATTRIBUTE_NORMAL,
/* hTemplateFile= */ nullptr);
if (_handle == INVALID_HANDLE_VALUE)
error("cannot open serial port '{}': {}",
name,
get_last_error_string());
COMMTIMEOUTS commtimeouts = {0};
commtimeouts.ReadIntervalTimeout = 100;
SetCommTimeouts(_handle, &commtimeouts);
if (!EscapeCommFunction(_handle, CLRDTR))
Error() << fmt::format("Couldn't clear DTR: {}",
get_last_error_string());
Sleep(200);
if (!EscapeCommFunction(_handle, SETDTR))
Error() << fmt::format("Couldn't set DTR: {}",
get_last_error_string());
DCB dcb = {.DCBlength = sizeof(DCB),
.BaudRate = CBR_9600,
.fBinary = true,
.ByteSize = 8,
.Parity = NOPARITY,
.StopBits = ONESTOPBIT};
SetCommState(_handle, &dcb);
PurgeComm(_handle, PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR);
}
COMMTIMEOUTS commtimeouts = {0};
commtimeouts.ReadIntervalTimeout = 100;
SetCommTimeouts(_handle, &commtimeouts);
~SerialPortImpl() override
{
CloseHandle(_handle);
}
if (!EscapeCommFunction(_handle, CLRDTR))
error("Couldn't clear DTR: {}", get_last_error_string());
Sleep(200);
if (!EscapeCommFunction(_handle, SETDTR))
error("Couldn't set DTR: {}", get_last_error_string());
public:
ssize_t readImpl(uint8_t* buffer, size_t len) override
{
DWORD rlen;
bool r = ReadFile(
/* hFile= */ _handle,
/* lpBuffer= */ buffer,
/* nNumberOfBytesToRead= */ len,
/* lpNumberOfBytesRead= */ &rlen,
/* lpOverlapped= */ nullptr);
if (!r)
Error() << fmt::format("serial read I/O error: {}", get_last_error_string());
return rlen;
}
PurgeComm(_handle,
PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
}
ssize_t write(const uint8_t* buffer, size_t len) override
{
DWORD wlen;
/* Windows gets unhappy if we try to transfer too much... */
len = std::min(4096U, len);
bool r = WriteFile(
/* hFile= */ _handle,
/* lpBuffer= */ buffer,
/* nNumberOfBytesToWrite= */ len,
/* lpNumberOfBytesWritten= */ &wlen,
/* lpOverlapped= */ nullptr);
if (!r)
Error() << fmt::format("serial write I/O error: {}", get_last_error_string());
return wlen;
}
~SerialPortImpl() override
{
CloseHandle(_handle);
}
private:
static std::string get_last_error_string()
{
DWORD error = GetLastError();
if (error == 0)
return "OK";
public:
ssize_t readImpl(uint8_t* buffer, size_t len) override
{
DWORD rlen;
bool r = ReadFile(
/* hFile= */ _handle,
/* lpBuffer= */ buffer,
/* nNumberOfBytesToRead= */ len,
/* lpNumberOfBytesRead= */ &rlen,
/* lpOverlapped= */ nullptr);
if (!r)
error("serial read I/O error: {}", get_last_error_string());
return rlen;
}
LPSTR buffer = nullptr;
size_t size = FormatMessageA(
/* dwFlags= */ FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
/* lpSource= */ nullptr,
/* dwMessageId= */ error,
/* dwLanguageId= */ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
/* lpBuffer= */ (LPSTR) &buffer,
/* nSize= */ 0,
/* Arguments= */ nullptr);
std::string message(buffer, size);
LocalFree(buffer);
return message;
}
ssize_t write(const uint8_t* buffer, size_t len) override
{
DWORD wlen;
/* Windows gets unhappy if we try to transfer too much... */
len = std::min(4096U, len);
bool r = WriteFile(
/* hFile= */ _handle,
/* lpBuffer= */ buffer,
/* nNumberOfBytesToWrite= */ len,
/* lpNumberOfBytesWritten= */ &wlen,
/* lpOverlapped= */ nullptr);
if (!r)
error("serial write I/O error: {}", get_last_error_string());
return wlen;
}
private:
HANDLE _handle;
};
private:
static std::string get_last_error_string()
{
DWORD error = GetLastError();
if (error == 0)
return "OK";
LPSTR buffer = nullptr;
size_t size = FormatMessageA(
/* dwFlags= */ FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
/* lpSource= */ nullptr,
/* dwMessageId= */ error,
/* dwLanguageId= */ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
/* lpBuffer= */ (LPSTR)&buffer,
/* nSize= */ 0,
/* Arguments= */ nullptr);
std::string message(buffer, size);
LocalFree(buffer);
return message;
}
private:
HANDLE _handle;
};
#else
#include <termios.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <sys/ioctl.h>
class SerialPortImpl : public SerialPort
{
public:
SerialPortImpl(const std::string& path)
{
#ifdef __APPLE__
if (path.find("/dev/tty.") != std::string::npos)
std::cerr << "Warning: you probably want to be using a /dev/cu.* device\n";
#endif
_fd = open(path.c_str(), O_RDWR);
if (_fd == -1)
Error() << fmt::format("cannot open serial port '{}': {}",
path, strerror(errno));
struct termios t;
tcgetattr(_fd, &t);
t.c_iflag = 0;
t.c_oflag = 0;
t.c_cflag = CREAD;
t.c_lflag = 0;
t.c_cc[VMIN] = 1;
cfsetspeed(&t, 9600);
tcsetattr(_fd, TCSANOW, &t);
/* Toggle DTR to reset the device. */
int flag = TIOCM_DTR;
if (ioctl(_fd, TIOCMBIC, &flag) == -1)
Error() << fmt::format("cannot clear DTR on serial port: {}", strerror(errno));
usleep(200000);
if (ioctl(_fd, TIOCMBIS, &flag) == -1)
Error() << fmt::format("cannot set DTR on serial port: {}", strerror(errno));
/* Flush pending input from a generic greaseweazel device */
tcsetattr(_fd, TCSAFLUSH, &t);
}
~SerialPortImpl() override
{
close(_fd);
}
public:
ssize_t readImpl(uint8_t* buffer, size_t len) override
{
ssize_t rlen = ::read(_fd, buffer, len);
if (rlen == 0)
Error() << "serial read returned no data (device removed?)";
if (rlen == -1)
Error() << fmt::format("serial read I/O error: {}", strerror(errno));
return rlen;
}
ssize_t write(const uint8_t* buffer, size_t len) override
{
ssize_t wlen = ::write(_fd, buffer, len);
if (wlen == -1)
Error() << fmt::format("serial write I/O error: {}", strerror(errno));
return wlen;
}
private:
int _fd;
};
class SerialPortImpl : public SerialPort
{
public:
SerialPortImpl(const std::string& path)
{
#ifdef __APPLE__
if (path.find("/dev/tty.") != std::string::npos)
std::cerr << "Warning: you probably want to be using a /dev/cu.* "
"device\n";
#endif
SerialPort::~SerialPort()
{}
_fd = open(path.c_str(), O_RDWR);
if (_fd == -1)
error("cannot open serial port '{}': {}", path, strerror(errno));
struct termios t;
tcgetattr(_fd, &t);
t.c_iflag = 0;
t.c_oflag = 0;
t.c_cflag = CREAD;
t.c_lflag = 0;
t.c_cc[VMIN] = 1;
cfsetspeed(&t, 9600);
tcsetattr(_fd, TCSANOW, &t);
/* Toggle DTR to reset the device. */
int flag = TIOCM_DTR;
if (ioctl(_fd, TIOCMBIC, &flag) == -1)
error("cannot clear DTR on serial port: {}", strerror(errno));
usleep(200000);
if (ioctl(_fd, TIOCMBIS, &flag) == -1)
error("cannot set DTR on serial port: {}", strerror(errno));
/* Flush pending input from a generic greaseweazel device */
tcsetattr(_fd, TCSAFLUSH, &t);
}
~SerialPortImpl() override
{
close(_fd);
}
public:
ssize_t readImpl(uint8_t* buffer, size_t len) override
{
ssize_t rlen = ::read(_fd, buffer, len);
if (rlen == 0)
error("serial read returned no data (device removed?)");
if (rlen == -1)
error("serial read I/O error: {}", strerror(errno));
return rlen;
}
ssize_t write(const uint8_t* buffer, size_t len) override
{
ssize_t wlen = ::write(_fd, buffer, len);
if (wlen == -1)
error("serial write I/O error: {}", strerror(errno));
return wlen;
}
private:
int _fd;
};
#endif
SerialPort::~SerialPort() {}
void SerialPort::read(uint8_t* buffer, size_t len)
{
while (len != 0)
{
//std::cout << "want " << len << " " << std::flush;
size_t rlen = this->readImpl(buffer, len);
//std::cout << "got " << rlen << "\n" << std::flush;
buffer += rlen;
len -= rlen;
}
while (len != 0)
{
// std::cout << "want " << len << " " << std::flush;
size_t rlen = this->readImpl(buffer, len);
// std::cout << "got " << rlen << "\n" << std::flush;
buffer += rlen;
len -= rlen;
}
}
void SerialPort::read(Bytes& bytes)
{
this->read(bytes.begin(), bytes.size());
this->read(bytes.begin(), bytes.size());
}
Bytes SerialPort::readBytes(size_t len)
{
Bytes b(len);
this->read(b);
return b;
Bytes b(len);
this->read(b);
return b;
}
uint8_t SerialPort::readByte()
{
uint8_t b;
this->read(&b, 1);
return b;
uint8_t b;
this->read(&b, 1);
return b;
}
void SerialPort::write(const Bytes& bytes)
{
int ptr = 0;
while (ptr < bytes.size())
{
ssize_t wlen = this->write(bytes.cbegin() + ptr, bytes.size() - ptr);
ptr += wlen;
}
int ptr = 0;
while (ptr < bytes.size())
{
ssize_t wlen = this->write(bytes.cbegin() + ptr, bytes.size() - ptr);
ptr += wlen;
}
}
std::unique_ptr<SerialPort> SerialPort::openSerialPort(const std::string& path)
{
return std::make_unique<SerialPortImpl>(path);
return std::make_unique<SerialPortImpl>(path);
}

View File

@@ -10,7 +10,6 @@
#include "usbfinder.h"
#include "logger.h"
#include "greaseweazle.h"
#include "fmt/format.h"
static USB* usb = NULL;
@@ -20,8 +19,9 @@ static std::shared_ptr<CandidateDevice> selectDevice()
{
auto candidates = findUsbDevices();
if (candidates.size() == 0)
Error() << "no devices found (is one plugged in? Do you have the "
"appropriate permissions?";
error(
"no devices found (is one plugged in? Do you have the "
"appropriate permissions?");
if (config.usb().has_serial())
{
@@ -31,8 +31,9 @@ static std::shared_ptr<CandidateDevice> selectDevice()
if (c->serial == wantedSerial)
return c;
}
Error() << "serial number not found (try without one to list or "
"autodetect devices)";
error(
"serial number not found (try without one to list or "
"autodetect devices)");
}
if (candidates.size() == 1)
@@ -66,8 +67,7 @@ USB* get_usb_impl()
config.usb().greaseweazle().has_port())
{
const auto& conf = config.usb().greaseweazle();
Logger() << fmt::format(
"Using GreaseWeazle on serial port {}", conf.port());
log("Using GreaseWeazle on serial port {}", conf.port());
return createGreaseWeazleUsb(conf.port(), conf);
}
@@ -77,18 +77,18 @@ USB* get_usb_impl()
switch (candidate->id)
{
case FLUXENGINE_ID:
Logger() << fmt::format(
"Using FluxEngine {}", candidate->serial);
log("Using FluxEngine {}", candidate->serial);
return createFluxengineUsb(candidate->device);
case GREASEWEAZLE_ID:
Logger() << fmt::format("Using GreaseWeazle {} on {}",
log("Using GreaseWeazle {} on {}",
candidate->serial,
candidate->serialPort);
return createGreaseWeazleUsb(
candidate->serialPort, config.usb().greaseweazle());
default: Error() << "internal";
default:
error("internal");
}
}

View File

@@ -2,13 +2,13 @@
#include "flags.h"
#include "usb.h"
#include "bytes.h"
#include "fmt/format.h"
#include "usbfinder.h"
#include "greaseweazle.h"
#include "protocol.h"
#include "libusbp.hpp"
static const std::set<uint32_t> VALID_DEVICES = { GREASEWEAZLE_ID, FLUXENGINE_ID };
static const std::set<uint32_t> VALID_DEVICES = {
GREASEWEAZLE_ID, FLUXENGINE_ID};
static const std::string get_serial_number(const libusbp::device& device)
{
@@ -42,10 +42,10 @@ std::vector<std::shared_ptr<CandidateDevice>> findUsbDevices()
{
libusbp::serial_port port(candidate->device);
candidate->serialPort = port.get_name();
candidate->type = DEVICE_GREASEWEAZLE;
candidate->type = DEVICE_GREASEWEAZLE;
}
else if (id == FLUXENGINE_ID)
candidate->type = DEVICE_FLUXENGINE;
else if (id == FLUXENGINE_ID)
candidate->type = DEVICE_FLUXENGINE;
candidates.push_back(std::move(candidate));
}
@@ -56,16 +56,15 @@ std::vector<std::shared_ptr<CandidateDevice>> findUsbDevices()
std::string getDeviceName(DeviceType type)
{
switch (type)
{
case DEVICE_GREASEWEAZLE:
return "Greaseweazle";
switch (type)
{
case DEVICE_GREASEWEAZLE:
return "Greaseweazle";
case DEVICE_FLUXENGINE:
return "FluxEngine";
case DEVICE_FLUXENGINE:
return "FluxEngine";
default:
return "unknown";
}
default:
return "unknown";
}
}

View File

@@ -1,7 +1,6 @@
#include "globals.h"
#include "utils.h"
#include "lib/bytes.h"
#include <fmt/format.h>
#include <iomanip>
#include <fstream>

View File

@@ -2,7 +2,6 @@
#include "lib/vfs/vfs.h"
#include "lib/config.pb.h"
#include "lib/utils.h"
#include <fmt/format.h>
class AcornDfsFilesystem;

View File

@@ -4,7 +4,6 @@
#include "lib/proto.h"
#include "lib/layout.h"
#include "lib/logger.h"
#include <fmt/format.h>
#include "adflib.h"
#include "adf_blk.h"
@@ -420,7 +419,7 @@ private:
static void onAdfWarning(char* message)
{
Logger() << message;
log(message);
}
static void onAdfError(char* message)

Some files were not shown because too many files have changed in this diff Show More