Massive refactor to work in logical cylinders everywhere. The logical/physical

mapping is now done at the last stage and should, ideally, be automatic. I'm
sure there are bugs everywhere.
This commit is contained in:
David Given
2022-03-25 00:22:28 +01:00
parent aaccd648b3
commit d303067deb
47 changed files with 2149 additions and 1643 deletions

View File

@@ -68,7 +68,7 @@ public:
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
std::set<unsigned> requiredSectors(const Location&) const override
{
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
return sectors;

View File

@@ -12,152 +12,156 @@ static bool lastBit;
static int charToInt(char c)
{
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
{
for (bool bit : src)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = bit;
}
for (bool bit : src)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = bit;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
{
cursor += width;
lastBit = data & 1;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
cursor += width;
lastBit = data & 1;
for (int i = 0; i < width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
{
ByteReader br(bytes);
BitReader bitr(br);
ByteReader br(bytes);
BitReader bitr(br);
while (!bitr.eof())
{
if (cursor < bits.size())
bits[cursor++] = bitr.get();
}
while (!bitr.eof())
{
if (cursor < bits.size())
bits[cursor++] = bitr.get();
}
}
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<const Sector>& sector)
static void write_sector(std::vector<bool>& bits,
unsigned& cursor,
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";
if ((sector->data.size() != 512) && (sector->data.size() != 528))
Error() << "unsupported sector size --- you must pick 512 or 528";
uint32_t checksum = 0;
uint32_t checksum = 0;
auto write_interleaved_bytes = [&](const Bytes& bytes)
{
Bytes interleaved = amigaInterleave(bytes);
Bytes mfm = encodeMfm(interleaved, lastBit);
checksum ^= amigaChecksum(mfm);
checksum &= 0x55555555;
write_bits(bits, cursor, mfm);
};
auto write_interleaved_bytes = [&](const Bytes& bytes)
{
Bytes interleaved = amigaInterleave(bytes);
Bytes mfm = encodeMfm(interleaved, lastBit);
checksum ^= amigaChecksum(mfm);
checksum &= 0x55555555;
write_bits(bits, cursor, mfm);
};
auto write_interleaved_word = [&](uint32_t word)
{
Bytes b(4);
b.writer().write_be32(word);
write_interleaved_bytes(b);
};
auto write_interleaved_word = [&](uint32_t word)
{
Bytes b(4);
b.writer().write_be32(word);
write_interleaved_bytes(b);
};
write_bits(bits, cursor, 0xaaaa, 2*8);
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6*8);
write_bits(bits, cursor, 0xaaaa, 2 * 8);
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6 * 8);
checksum = 0;
Bytes header =
{
0xff, /* Amiga 1.0 format byte */
(uint8_t) ((sector->logicalTrack<<1) | sector->logicalSide),
(uint8_t) sector->logicalSector,
(uint8_t) (AMIGA_SECTORS_PER_TRACK - sector->logicalSector)
};
write_interleaved_bytes(header);
Bytes recoveryInfo(16);
if (sector->data.size() == 528)
recoveryInfo = sector->data.slice(512, 16);
write_interleaved_bytes(recoveryInfo);
write_interleaved_word(checksum);
checksum = 0;
Bytes header = {0xff, /* Amiga 1.0 format byte */
(uint8_t)((sector->logicalTrack << 1) | sector->logicalSide),
(uint8_t)sector->logicalSector,
(uint8_t)(AMIGA_SECTORS_PER_TRACK - sector->logicalSector)};
write_interleaved_bytes(header);
Bytes recoveryInfo(16);
if (sector->data.size() == 528)
recoveryInfo = sector->data.slice(512, 16);
write_interleaved_bytes(recoveryInfo);
write_interleaved_word(checksum);
Bytes data = sector->data.slice(0, 512);
write_interleaved_word(amigaChecksum(encodeMfm(amigaInterleave(data), lastBit)));
write_interleaved_bytes(data);
Bytes data = sector->data.slice(0, 512);
write_interleaved_word(
amigaChecksum(encodeMfm(amigaInterleave(data), lastBit)));
write_interleaved_bytes(data);
}
class AmigaEncoder : public AbstractEncoder
{
public:
AmigaEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.amiga()) {}
AmigaEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.amiga())
{
}
public:
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
std::vector<std::shared_ptr<const Sector>> collectSectors(
const Location& location, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
if ((physicalTrack >= 0) && (physicalTrack < AMIGA_TRACKS_PER_DISK))
{
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
{
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
if (sector)
sectors.push_back(sector);
}
}
if ((location.logicalCylinder >= 0) &&
(location.logicalCylinder < AMIGA_TRACKS_PER_DISK))
{
for (int sectorId = 0; sectorId < AMIGA_SECTORS_PER_TRACK;
sectorId++)
{
const auto& sector = image.get(
location.logicalCylinder, location.head, sectorId);
if (sector)
sectors.push_back(sector);
}
}
return sectors;
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
{
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
std::unique_ptr<Fluxmap> encode(const Location& location,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits,
cursor,
_config.post_index_gap_ms() * 1000 / _config.clock_rate_us(),
{true, false});
lastBit = false;
fillBitmapTo(bits, cursor, _config.post_index_gap_ms() * 1000 / _config.clock_rate_us(), { true, false });
lastBit = false;
for (const auto& sector : sectors)
write_sector(bits, cursor, sector);
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
{
const auto& sectorData = image.get(physicalTrack, physicalSide, sectorId);
if (sectorData)
write_sector(bits, cursor, sectorData);
}
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), {true, false});
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, _config.clock_rate_us()*1e3);
return fluxmap;
}
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, _config.clock_rate_us() * 1e3);
return fluxmap;
}
private:
const AmigaEncoderProto& _config;
const AmigaEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createAmigaEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new AmigaEncoder(config));
return std::unique_ptr<AbstractEncoder>(new AmigaEncoder(config));
}

View File

@@ -10,13 +10,15 @@
#include <ctype.h>
#include "bytes.h"
static int encode_data_gcr(uint8_t data) {
switch(data)
static int encode_data_gcr(uint8_t data)
{
switch (data)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
#define GCR_ENTRY(gcr, data) \
case data: \
return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
@@ -24,162 +26,178 @@ static int encode_data_gcr(uint8_t data) {
class Apple2Encoder : public AbstractEncoder
{
public:
Apple2Encoder(const EncoderProto& config):
AbstractEncoder(config)
{}
Apple2Encoder(const EncoderProto& config): AbstractEncoder(config) {}
public:
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
std::vector<std::shared_ptr<const Sector>> collectSectors(
const Location& location, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
constexpr auto numSectors = 16;
if (physicalSide == 0)
{
int logicalTrack = physicalTrack / 2;
unsigned numSectors = 16;
for (int sectorId=0; sectorId<numSectors; sectorId++)
{
const auto& sector = image.get(logicalTrack, 0, sectorId);
if (sector)
sectors.push_back(sector);
}
}
std::vector<std::shared_ptr<const Sector>> sectors;
constexpr auto numSectors = 16;
if (location.head == 0)
{
unsigned numSectors = 16;
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
const auto& sector = image.get(location.logicalCylinder, 0, sectorId);
if (sector)
sectors.push_back(sector);
}
}
return sectors;
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
std::unique_ptr<Fluxmap> encode(
const Location& location,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
if (physicalSide != 0)
return std::unique_ptr<Fluxmap>();
double clockRateUs = 4.;
int logicalTrack = physicalTrack / 2;
double clockRateUs = 4.;
// apple2 drives spin at 300rpm. 300/minute in us = 200000.
int bitsPerRevolution = 200000.0 / clockRateUs;
// apple2 drives spin at 300rpm. 300/minute in us = 200000.
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (const auto& sector : sectors)
writeSector(bits, cursor, *sector);
for (const auto& sector : sectors) {
if(sector) {
writeSector(bits, cursor, *sector);
}
}
if (cursor >= bits.size())
Error() << fmt::format("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, clockRateUs*1e3);
return fluxmap;
if (cursor >= bits.size())
Error() << fmt::format(
"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, clockRateUs * 1e3);
return fluxmap;
}
private:
uint8_t volume_id = 254;
/* This is extremely inspired by the MESS implementation, written by Nathan Woods
* and R. Belmont: https://github.com/mamedev/mame/blob/7914a6083a3b3a8c243ae6c3b8cb50b023f21e0e/src/lib/formats/ap2_dsk.cpp
/* This is extremely inspired by the MESS implementation, written by Nathan
* Woods and R. Belmont:
* https://github.com/mamedev/mame/blob/7914a6083a3b3a8c243ae6c3b8cb50b023f21e0e/src/lib/formats/ap2_dsk.cpp
* as well as Understanding the Apple II (1983) Chapter 9
* https://archive.org/details/Understanding_the_Apple_II_1983_Quality_Software/page/n230/mode/1up?view=theater
*/
void writeSector(std::vector<bool>& bits, unsigned& cursor, const Sector& sector) const
void writeSector(
std::vector<bool>& bits, unsigned& cursor, const Sector& sector) const
{
if ((sector.status == Sector::OK) or (sector.status == Sector::BAD_CHECKSUM))
{
auto write_bit = [&](bool val) {
if(cursor <= bits.size()) { bits[cursor] = val; }
cursor++;
};
if ((sector.status == Sector::OK) or
(sector.status == Sector::BAD_CHECKSUM))
{
auto write_bit = [&](bool val)
{
if (cursor <= bits.size())
{
bits[cursor] = val;
}
cursor++;
};
auto write_bits = [&](uint32_t bits, int width) {
for(int i=width; i--;) {
write_bit(bits & (1u << i));
}
};
auto write_bits = [&](uint32_t bits, int width)
{
for (int i = width; i--;)
{
write_bit(bits & (1u << i));
}
};
auto write_gcr44 = [&](uint8_t value) {
write_bits((value << 7) | value | 0xaaaa, 16);
};
auto write_gcr44 = [&](uint8_t value)
{
write_bits((value << 7) | value | 0xaaaa, 16);
};
auto write_gcr6 = [&](uint8_t value) {
write_bits(encode_data_gcr(value), 8);
};
auto write_gcr6 = [&](uint8_t value)
{
write_bits(encode_data_gcr(value), 8);
};
// The special "FF40" sequence is used to synchronize the receiving
// shift register. It's written as "1111 1111 00"; FF indicates the
// 8 consecutive 1-bits, while "40" indicates the total number of
// microseconds.
auto write_ff40 = [&](int n=1) {
for(;n--;) {
write_bits(0xff << 2, 10);
}
};
// The special "FF40" sequence is used to synchronize the receiving
// shift register. It's written as "1111 1111 00"; FF indicates the
// 8 consecutive 1-bits, while "40" indicates the total number of
// microseconds.
auto write_ff40 = [&](int n = 1)
{
for (; n--;)
{
write_bits(0xff << 2, 10);
}
};
// There is data to encode to disk.
if ((sector.data.size() != APPLE2_SECTOR_LENGTH))
Error() << fmt::format("unsupported sector size {} --- you must pick 256", sector.data.size());
// There is data to encode to disk.
if ((sector.data.size() != APPLE2_SECTOR_LENGTH))
Error() << fmt::format(
"unsupported sector size {} --- you must pick 256",
sector.data.size());
// Write address syncing leader : A sequence of "FF40"s; 5 of them
// are said to suffice to synchronize the decoder.
// "FF40" indicates that the actual data written is "1111
// 1111 00" i.e., 8 1s and a total of 40 microseconds
//
// In standard formatting, the first logical sector apparently gets
// extra padding.
write_ff40(sector.logicalSector == 0 ? 32 : 8);
// Write address syncing leader : A sequence of "FF40"s; 5 of them
// are said to suffice to synchronize the decoder.
// "FF40" indicates that the actual data written is "1111
// 1111 00" i.e., 8 1s and a total of 40 microseconds
//
// In standard formatting, the first logical sector apparently gets
// extra padding.
write_ff40(sector.logicalSector == 0 ? 32 : 8);
// Write address field: APPLE2_SECTOR_RECORD + sector identifier + DE AA EB
write_bits(APPLE2_SECTOR_RECORD, 24);
write_gcr44(volume_id);
write_gcr44(sector.logicalTrack);
write_gcr44(sector.logicalSector);
write_gcr44(volume_id ^ sector.logicalTrack ^ sector.logicalSector);
write_bits(0xDEAAEB, 24);
// Write address field: APPLE2_SECTOR_RECORD + sector identifier +
// DE AA EB
write_bits(APPLE2_SECTOR_RECORD, 24);
write_gcr44(volume_id);
write_gcr44(sector.logicalTrack);
write_gcr44(sector.logicalSector);
write_gcr44(volume_id ^ sector.logicalTrack ^ sector.logicalSector);
write_bits(0xDEAAEB, 24);
// Write data syncing leader: FF40 + APPLE2_DATA_RECORD + sector data + sum + DE AA EB (+ mystery bits cut off of the scan?)
write_ff40(8);
write_bits(APPLE2_DATA_RECORD, 24);
// Write data syncing leader: FF40 + APPLE2_DATA_RECORD + sector
// data + sum + DE AA EB (+ mystery bits cut off of the scan?)
write_ff40(8);
write_bits(APPLE2_DATA_RECORD, 24);
// Convert the sector data to GCR, append the checksum, and write it out
constexpr auto TWOBIT_COUNT = 0x56; // Size of the 'twobit' area at the start of the GCR data
uint8_t checksum = 0;
for(int i=0; i<APPLE2_ENCODED_SECTOR_LENGTH; i++) {
int value;
if(i >= TWOBIT_COUNT) {
value = sector.data[i - TWOBIT_COUNT] >> 2;
} else {
uint8_t tmp = sector.data[i];
value = ((tmp & 1) << 1) | ((tmp & 2) >> 1);
// Convert the sector data to GCR, append the checksum, and write it
// out
constexpr auto TWOBIT_COUNT =
0x56; // Size of the 'twobit' area at the start of the GCR data
uint8_t checksum = 0;
for (int i = 0; i < APPLE2_ENCODED_SECTOR_LENGTH; i++)
{
int value;
if (i >= TWOBIT_COUNT)
{
value = sector.data[i - TWOBIT_COUNT] >> 2;
}
else
{
uint8_t tmp = sector.data[i];
value = ((tmp & 1) << 1) | ((tmp & 2) >> 1);
tmp = sector.data[i + TWOBIT_COUNT];
value |= ((tmp & 1) << 3) | ((tmp & 2) << 1);
tmp = sector.data[i + TWOBIT_COUNT];
value |= ((tmp & 1) << 3) | ((tmp & 2) << 1);
if(i + 2*TWOBIT_COUNT < APPLE2_SECTOR_LENGTH) {
tmp = sector.data[i + 2*TWOBIT_COUNT];
value |= ((tmp & 1) << 5) | ((tmp & 2) << 3);
}
}
checksum ^= value;
// assert(checksum & ~0x3f == 0);
write_gcr6(checksum);
checksum = value;
}
if(sector.status == Sector::BAD_CHECKSUM) checksum ^= 0x3f;
write_gcr6(checksum);
write_bits(0xDEAAEB, 24);
}
if (i + 2 * TWOBIT_COUNT < APPLE2_SECTOR_LENGTH)
{
tmp = sector.data[i + 2 * TWOBIT_COUNT];
value |= ((tmp & 1) << 5) | ((tmp & 2) << 3);
}
}
checksum ^= value;
// assert(checksum & ~0x3f == 0);
write_gcr6(checksum);
checksum = value;
}
if (sector.status == Sector::BAD_CHECKSUM)
checksum ^= 0x3f;
write_gcr6(checksum);
write_bits(0xDEAAEB, 24);
}
}
};
std::unique_ptr<AbstractEncoder> createApple2Encoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new Apple2Encoder(config));
return std::unique_ptr<AbstractEncoder>(new Apple2Encoder(config));
}

View File

@@ -15,6 +15,5 @@ message BrotherEncoderProto {
optional string sector_skew = 5 [default = "05a3816b4927"];
optional BrotherFormat format = 6 [default = BROTHER240];
optional int32 bias = 7 [default = 0];
}

View File

@@ -10,84 +10,88 @@
static int encode_header_gcr(uint16_t word)
{
switch (word)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "header_gcr.h"
#undef GCR_ENTRY
}
return -1;
switch (word)
{
#define GCR_ENTRY(gcr, data) \
case data: \
return gcr;
#include "header_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
static int encode_data_gcr(uint8_t data)
{
switch (data)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
switch (data)
{
#define GCR_ENTRY(gcr, data) \
case data: \
return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width)
{
cursor += width;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
cursor += width;
for (int i = 0; i < width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
static void write_sector_header(std::vector<bool>& bits, unsigned& cursor,
int track, int sector)
static void write_sector_header(
std::vector<bool>& bits, unsigned& cursor, int track, int sector)
{
write_bits(bits, cursor, 0xffffffff, 31);
write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32);
write_bits(bits, cursor, encode_header_gcr(track), 16);
write_bits(bits, cursor, encode_header_gcr(sector), 16);
write_bits(bits, cursor, encode_header_gcr(0x2f), 16);
write_bits(bits, cursor, 0xffffffff, 31);
write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32);
write_bits(bits, cursor, encode_header_gcr(track), 16);
write_bits(bits, cursor, encode_header_gcr(sector), 16);
write_bits(bits, cursor, encode_header_gcr(0x2f), 16);
}
static void write_sector_data(std::vector<bool>& bits, unsigned& cursor, const Bytes& data)
static void write_sector_data(
std::vector<bool>& bits, unsigned& cursor, const Bytes& data)
{
write_bits(bits, cursor, 0xffffffff, 32);
write_bits(bits, cursor, BROTHER_DATA_RECORD, 32);
write_bits(bits, cursor, 0xffffffff, 32);
write_bits(bits, cursor, BROTHER_DATA_RECORD, 32);
uint16_t fifo = 0;
int width = 0;
uint16_t fifo = 0;
int width = 0;
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
Error() << "unsupported sector size";
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
Error() << "unsupported sector size";
auto write_byte = [&](uint8_t byte)
{
fifo |= (byte << (8 - width));
width += 8;
auto write_byte = [&](uint8_t byte)
{
fifo |= (byte << (8 - width));
width += 8;
while (width >= 5)
{
uint8_t quintet = fifo >> 11;
fifo <<= 5;
width -= 5;
while (width >= 5)
{
uint8_t quintet = fifo >> 11;
fifo <<= 5;
width -= 5;
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
}
};
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
}
};
for (uint8_t byte : data)
write_byte(byte);
for (uint8_t byte : data)
write_byte(byte);
uint32_t realCrc = crcbrother(data);
write_byte(realCrc>>16);
write_byte(realCrc>>8);
write_byte(realCrc);
write_byte(0x58); /* magic */
uint32_t realCrc = crcbrother(data);
write_byte(realCrc >> 16);
write_byte(realCrc >> 8);
write_byte(realCrc);
write_byte(0x58); /* magic */
write_byte(0xd4);
while (width != 0)
write_byte(0);
@@ -95,112 +99,98 @@ static void write_sector_data(std::vector<bool>& bits, unsigned& cursor, const B
static int charToInt(char c)
{
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
}
class BrotherEncoder : public AbstractEncoder
{
public:
BrotherEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.brother())
{}
BrotherEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.brother())
{
}
public:
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
std::vector<std::shared_ptr<const Sector>> collectSectors(
const Location& location,
const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
if (physicalSide != 0)
return sectors;
physicalTrack -= _config.bias();
switch (_config.format())
{
case BROTHER120:
if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2))
|| (physicalTrack & 1))
return sectors;
break;
if (location.head != 0)
return sectors;
case BROTHER240:
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
return sectors;
break;
}
for (int sectorId=0; sectorId<BROTHER_SECTORS_PER_TRACK; sectorId++)
switch (_config.format())
{
const auto& sector = image.get(physicalTrack, 0, sectorId);
case BROTHER120:
if (location.logicalCylinder >= BROTHER_TRACKS_PER_120KB_DISK)
return sectors;
break;
case BROTHER240:
if (location.logicalCylinder >= BROTHER_TRACKS_PER_240KB_DISK)
return sectors;
break;
}
const std::string& skew = _config.sector_skew();
for (int sectorCount = 0; sectorCount < BROTHER_SECTORS_PER_TRACK;
sectorCount++)
{
int sectorId = charToInt(skew.at(sectorCount));
const auto& sector = image.get(location.logicalCylinder, 0, sectorId);
if (sector)
sectors.push_back(sector);
}
}
return sectors;
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
{
if (physicalSide != 0)
return std::unique_ptr<Fluxmap>();
physicalTrack -= _config.bias();
switch (_config.format())
{
case BROTHER120:
if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2))
|| (physicalTrack & 1))
return std::unique_ptr<Fluxmap>();
break;
std::unique_ptr<Fluxmap> encode(
const Location& location,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
case BROTHER240:
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
return std::unique_ptr<Fluxmap>();
break;
}
int sectorCount = 0;
for (const auto& sectorData : sectors)
{
double headerMs = _config.post_index_gap_ms() +
sectorCount * _config.sector_spacing_ms();
unsigned headerCursor = headerMs * 1e3 / _config.clock_rate_us();
double dataMs = headerMs + _config.post_header_spacing_ms();
unsigned dataCursor = dataMs * 1e3 / _config.clock_rate_us();
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
const std::string& skew = _config.sector_skew();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, headerCursor, {true, false});
write_sector_header(
bits, cursor, sectorData->logicalTrack, sectorData->logicalSector);
fillBitmapTo(bits, cursor, dataCursor, {true, false});
write_sector_data(bits, cursor, sectorData->data);
for (int sectorCount=0; sectorCount<BROTHER_SECTORS_PER_TRACK; sectorCount++)
{
int sectorId = charToInt(skew.at(sectorCount));
double headerMs = _config.post_index_gap_ms() + sectorCount*_config.sector_spacing_ms();
unsigned headerCursor = headerMs*1e3 / _config.clock_rate_us();
double dataMs = headerMs + _config.post_header_spacing_ms();
unsigned dataCursor = dataMs*1e3 / _config.clock_rate_us();
sectorCount++;
}
const auto& sectorData = image.get(physicalTrack, 0, sectorId);
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), {true, false});
fillBitmapTo(bits, cursor, headerCursor, { true, false });
write_sector_header(bits, cursor, sectorData->logicalTrack, sectorId);
fillBitmapTo(bits, cursor, dataCursor, { true, false });
write_sector_data(bits, cursor, sectorData->data);
}
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
// The pre-index gap is not normally reported.
// std::cerr << "pre-index gap " << 200.0 - (double)cursor*clockRateUs/1e3 << std::endl;
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, _config.clock_rate_us()*1e3);
return fluxmap;
}
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, _config.clock_rate_us() * 1e3);
return fluxmap;
}
private:
const BrotherEncoderProto& _config;
const BrotherEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createBrotherEncoder(const EncoderProto& config)
std::unique_ptr<AbstractEncoder> createBrotherEncoder(
const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new BrotherEncoder(config));
return std::unique_ptr<AbstractEncoder>(new BrotherEncoder(config));
}

View File

@@ -211,17 +211,16 @@ public:
{}
public:
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
std::vector<std::shared_ptr<const Sector>> collectSectors(const Location& location, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
if (physicalSide == 0)
if (location.head == 0)
{
int logicalTrack = physicalTrack / 2;
unsigned numSectors = sectorsForTrack(logicalTrack);
unsigned numSectors = sectorsForTrack(location.logicalCylinder);
for (int sectorId=0; sectorId<numSectors; sectorId++)
{
const auto& sector = image.get(logicalTrack, 0, sectorId);
const auto& sector = image.get(location.logicalCylinder, 0, sectorId);
if (sector)
sectors.push_back(sector);
}
@@ -230,7 +229,7 @@ public:
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
std::unique_ptr<Fluxmap> encode(const Location& location,
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
{
/* The format ID Character # 1 and # 2 are in the .d64 image only present
@@ -240,9 +239,6 @@ public:
* contains the BAM.
*/
if (physicalSide != 0)
return std::unique_ptr<Fluxmap>();
const auto& sectorData = image.get(C64_BAM_TRACK*2, 0, 0); //Read de BAM to get the DISK ID bytes
if (sectorData)
{
@@ -254,8 +250,7 @@ public:
else
_formatByte1 = _formatByte2 = 0;
int logicalTrack = physicalTrack / 2;
double clockRateUs = clockRateUsForTrack(logicalTrack) * _config.clock_compensation_factor();
double clockRateUs = clockRateUsForTrack(location.logicalCylinder) * _config.clock_compensation_factor();
int bitsPerRevolution = 200000.0 / clockRateUs;

View File

@@ -203,10 +203,10 @@ public:
_sector->status = (wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
}
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
std::set<unsigned> requiredSectors(const Location& location) const override
{
IbmDecoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, cylinder, head);
getTrackFormat(trackdata, location.logicalCylinder, location.head);
std::set<unsigned> s;
if (trackdata.has_sectors())

View File

@@ -39,9 +39,9 @@
* ^^^^^
* When shifted out of phase, the special 0xa1 byte becomes an illegal
* encoding (you can't do 10 00). So this can't be spoofed by user data.
*
*
* shifted: 10 00 10 01 00 01 00 1
*
*
* It's repeated three times.
*/
#define MFM_RECORD_SEPARATOR 0x4489
@@ -59,255 +59,256 @@
static uint8_t decodeUint16(uint16_t raw)
{
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
}
class IbmEncoder : public AbstractEncoder
{
public:
IbmEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.ibm())
{}
IbmEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.ibm())
{
}
private:
void writeRawBits(uint32_t data, int width)
{
_cursor += width;
_lastBit = data & 1;
for (int i=0; i<width; i++)
{
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
void writeRawBits(uint32_t data, int width)
{
_cursor += width;
_lastBit = data & 1;
for (int i = 0; i < width; i++)
{
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
void getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_cylinder() && (f.cylinder() != cylinder))
continue;
if (f.has_head() && (f.head() != head))
continue;
void getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata,
unsigned cylinder,
unsigned head)
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_cylinder() && (f.cylinder() != cylinder))
continue;
if (f.has_head() && (f.head() != head))
continue;
trackdata.MergeFrom(f);
}
}
trackdata.MergeFrom(f);
}
}
private:
static std::set<unsigned> getSectorIds(const IbmEncoderProto::TrackdataProto& trackdata)
{
std::set<unsigned> s;
if (trackdata.has_sectors())
{
for (int sectorId : trackdata.sectors().sector())
s.insert(sectorId);
}
else if (trackdata.has_sector_range())
{
int sectorId = trackdata.sector_range().min_sector();
while (sectorId <= trackdata.sector_range().max_sector())
{
s.insert(sectorId);
sectorId++;
}
}
return s;
}
static std::set<unsigned> getSectorIds(
const IbmEncoderProto::TrackdataProto& trackdata)
{
std::set<unsigned> s;
if (trackdata.has_sectors())
{
for (int sectorId : trackdata.sectors().sector())
s.insert(sectorId);
}
else if (trackdata.has_sector_range())
{
int sectorId = trackdata.sector_range().min_sector();
while (sectorId <= trackdata.sector_range().max_sector())
{
s.insert(sectorId);
sectorId++;
}
}
return s;
}
public:
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
IbmEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
std::vector<std::shared_ptr<const Sector>> collectSectors(
const Location& location, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
IbmEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, location.logicalCylinder, location.head);
int logicalSide = physicalSide ^ trackdata.swap_sides();
for (int sectorId : getSectorIds(trackdata))
int logicalSide = location.head ^ trackdata.swap_sides();
for (int sectorId : getSectorIds(trackdata))
{
const auto& sector = image.get(physicalTrack, logicalSide, sectorId);
if (sector)
sectors.push_back(sector);
const auto& sector = image.get(location.logicalCylinder, logicalSide, sectorId);
if (sector)
sectors.push_back(sector);
}
return sectors;
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
{
IbmEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
std::unique_ptr<Fluxmap> encode(
const Location& location,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
IbmEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, location.logicalCylinder, location.head);
auto writeBytes = [&](const Bytes& bytes)
{
if (trackdata.use_fm())
encodeFm(_bits, _cursor, bytes);
else
encodeMfm(_bits, _cursor, bytes, _lastBit);
};
auto writeBytes = [&](const Bytes& bytes)
{
if (trackdata.use_fm())
encodeFm(_bits, _cursor, bytes);
else
encodeMfm(_bits, _cursor, bytes, _lastBit);
};
auto writeFillerRawBytes = [&](int count, uint16_t byte)
{
for (int i=0; i<count; i++)
writeRawBits(byte, 16);
};
auto writeFillerRawBytes = [&](int count, uint16_t byte)
{
for (int i = 0; i < count; i++)
writeRawBits(byte, 16);
};
auto writeFillerBytes = [&](int count, uint8_t byte)
{
Bytes b { byte };
for (int i=0; i<count; i++)
writeBytes(b);
};
auto writeFillerBytes = [&](int count, uint8_t byte)
{
Bytes b{byte};
for (int i = 0; i < count; i++)
writeBytes(b);
};
double clockRateUs = 1e3 / trackdata.clock_rate_khz();
if (!trackdata.use_fm())
clockRateUs /= 2.0;
int bitsPerRevolution = (trackdata.track_length_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
double clockRateUs = 1e3 / trackdata.clock_rate_khz();
if (!trackdata.use_fm())
clockRateUs /= 2.0;
int bitsPerRevolution =
(trackdata.track_length_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
uint8_t idamUnencoded = decodeUint16(trackdata.idam_byte());
uint8_t damUnencoded = decodeUint16(trackdata.dam_byte());
uint8_t idamUnencoded = decodeUint16(trackdata.idam_byte());
uint8_t damUnencoded = decodeUint16(trackdata.dam_byte());
uint8_t sectorSize = 0;
{
int s = trackdata.sector_size() >> 7;
while (s > 1)
{
s >>= 1;
sectorSize += 1;
}
}
uint8_t sectorSize = 0;
{
int s = trackdata.sector_size() >> 7;
while (s > 1)
{
s >>= 1;
sectorSize += 1;
}
}
uint16_t gapFill = trackdata.gap_fill_byte();
uint16_t gapFill = trackdata.gap_fill_byte();
writeFillerRawBytes(trackdata.gap0(), gapFill);
if (trackdata.emit_iam())
{
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
writeRawBits(MFM_IAM_SEPARATOR, 16);
}
writeRawBits(trackdata.use_fm() ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
writeFillerRawBytes(trackdata.gap1(), gapFill);
}
writeFillerRawBytes(trackdata.gap0(), gapFill);
if (trackdata.emit_iam())
{
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i = 0; i < 3; i++)
writeRawBits(MFM_IAM_SEPARATOR, 16);
}
writeRawBits(
trackdata.use_fm() ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
writeFillerRawBytes(trackdata.gap1(), gapFill);
}
int logicalSide = physicalSide ^ trackdata.swap_sides();
bool first = true;
for (int sectorId : trackdata.sectors().sector())
{
if (!first)
writeFillerRawBytes(trackdata.gap3(), gapFill);
first = false;
bool first = true;
for (const auto& sectorData : sectors)
{
if (!first)
writeFillerRawBytes(trackdata.gap3(), gapFill);
first = false;
const auto& sectorData = image.get(physicalTrack, logicalSide, sectorId);
if (!sectorData)
continue;
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
{
Bytes header;
ByteWriter bw(header);
{
Bytes header;
ByteWriter bw(header);
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i = 0; i < 3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(idamUnencoded);
bw.write_8(sectorData->logicalTrack);
bw.write_8(sectorData->logicalSide);
bw.write_8(sectorData->logicalSector);
bw.write_8(sectorSize);
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(idamUnencoded);
bw.write_8(sectorData->logicalTrack);
bw.write_8(sectorData->logicalSide);
bw.write_8(sectorData->logicalSector);
bw.write_8(sectorSize);
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!trackdata.use_fm())
{
for (int i = 0; i < 3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(trackdata.idam_byte(), 16);
conventionalHeaderStart += 1;
int conventionalHeaderStart = 0;
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
writeBytes(header.slice(conventionalHeaderStart));
}
}
writeRawBits(trackdata.idam_byte(), 16);
conventionalHeaderStart += 1;
writeFillerRawBytes(trackdata.gap2(), gapFill);
writeBytes(header.slice(conventionalHeaderStart));
}
{
Bytes data;
ByteWriter bw(data);
writeFillerRawBytes(trackdata.gap2(), gapFill);
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i = 0; i < 3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(damUnencoded);
{
Bytes data;
ByteWriter bw(data);
Bytes truncatedData =
sectorData->data.slice(0, trackdata.sector_size());
bw += truncatedData;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(damUnencoded);
int conventionalHeaderStart = 0;
if (!trackdata.use_fm())
{
for (int i = 0; i < 3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(trackdata.dam_byte(), 16);
conventionalHeaderStart += 1;
Bytes truncatedData = sectorData->data.slice(0, trackdata.sector_size());
bw += truncatedData;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
writeBytes(data.slice(conventionalHeaderStart));
}
}
int conventionalHeaderStart = 0;
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeFillerRawBytes(1, gapFill);
}
writeRawBits(trackdata.dam_byte(), 16);
conventionalHeaderStart += 1;
writeBytes(data.slice(conventionalHeaderStart));
}
}
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeFillerRawBytes(1, gapFill);
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);
return fluxmap;
}
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs * 1e3);
return fluxmap;
}
private:
const IbmEncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
const IbmEncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
std::unique_ptr<AbstractEncoder> createIbmEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new IbmEncoder(config));
return std::unique_ptr<AbstractEncoder>(new IbmEncoder(config));
}

View File

@@ -183,8 +183,10 @@ public:
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
}
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
std::set<unsigned> requiredSectors(const Location& location) const override
{
unsigned cylinder = location.logicalCylinder;
int count;
if (cylinder < 16)
count = 12;

View File

@@ -14,44 +14,46 @@ static bool lastBit;
static double clockRateUsForTrack(unsigned track)
{
if (track < 16)
return 2.623;
if (track < 32)
return 2.861;
if (track < 48)
return 3.148;
if (track < 64)
return 3.497;
return 3.934;
if (track < 16)
return 2.623;
if (track < 32)
return 2.861;
if (track < 48)
return 3.148;
if (track < 64)
return 3.497;
return 3.934;
}
static unsigned sectorsForTrack(unsigned track)
{
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;
}
static int encode_data_gcr(uint8_t gcr)
{
switch (gcr)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
#define GCR_ENTRY(gcr, data) \
case data: \
return gcr;
#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 encode_crazy_data(const Bytes& input)
{
@@ -59,7 +61,7 @@ static Bytes encode_crazy_data(const Bytes& input)
ByteWriter bw(output);
ByteReader br(input);
uint8_t w1, w2, w3, w4;
uint8_t w1, w2, w3, w4;
static const int LOOKUP_LEN = MAC_SECTOR_LENGTH / 3;
@@ -67,92 +69,94 @@ static Bytes encode_crazy_data(const Bytes& input)
uint8_t b2[LOOKUP_LEN + 1];
uint8_t b3[LOOKUP_LEN + 1];
uint32_t c1 = 0;
uint32_t c2 = 0;
uint32_t c3 = 0;
for (int j=0;; j++)
{
c1 = (c1 & 0xff) << 1;
if (c1 & 0x0100)
c1++;
uint32_t c1 = 0;
uint32_t c2 = 0;
uint32_t c3 = 0;
for (int j = 0;; j++)
{
c1 = (c1 & 0xff) << 1;
if (c1 & 0x0100)
c1++;
uint8_t val = br.read_8();
c3 += val;
if (c1 & 0x0100)
{
c3++;
c1 &= 0xff;
}
b1[j] = (val ^ c1) & 0xff;
uint8_t val = br.read_8();
c3 += val;
if (c1 & 0x0100)
{
c3++;
c1 &= 0xff;
}
b1[j] = (val ^ c1) & 0xff;
val = br.read_8();
c2 += val;
if (c3 > 0xff)
{
c2++;
c3 &= 0xff;
}
b2[j] = (val ^ c3) & 0xff;
val = br.read_8();
c2 += val;
if (c3 > 0xff)
{
c2++;
c3 &= 0xff;
}
b2[j] = (val ^ c3) & 0xff;
if (br.pos == 524)
break;
if (br.pos == 524)
break;
val = br.read_8();
c1 += val;
if (c2 > 0xff)
{
c1++;
c2 &= 0xff;
}
b3[j] = (val ^ c2) & 0xff;
}
uint32_t c4 = ((c1 & 0xc0) >> 6) | ((c2 & 0xc0) >> 4) | ((c3 & 0xc0) >> 2);
b3[LOOKUP_LEN] = 0;
val = br.read_8();
c1 += val;
if (c2 > 0xff)
{
c1++;
c2 &= 0xff;
}
b3[j] = (val ^ c2) & 0xff;
}
uint32_t c4 = ((c1 & 0xc0) >> 6) | ((c2 & 0xc0) >> 4) | ((c3 & 0xc0) >> 2);
b3[LOOKUP_LEN] = 0;
for (int i = 0; i <= LOOKUP_LEN; i++)
{
w1 = b1[i] & 0x3f;
w2 = b2[i] & 0x3f;
w3 = b3[i] & 0x3f;
w4 = ((b1[i] & 0xc0) >> 2);
w4 |= ((b2[i] & 0xc0) >> 4);
w4 |= ((b3[i] & 0xc0) >> 6);
for (int i = 0; i <= LOOKUP_LEN; i++)
{
w1 = b1[i] & 0x3f;
w2 = b2[i] & 0x3f;
w3 = b3[i] & 0x3f;
w4 = ((b1[i] & 0xc0) >> 2);
w4 |= ((b2[i] & 0xc0) >> 4);
w4 |= ((b3[i] & 0xc0) >> 6);
bw.write_8(w4);
bw.write_8(w1);
bw.write_8(w2);
bw.write_8(w4);
bw.write_8(w1);
bw.write_8(w2);
if (i != LOOKUP_LEN)
bw.write_8(w3);
}
if (i != LOOKUP_LEN)
bw.write_8(w3);
}
bw.write_8(c4 & 0x3f);
bw.write_8(c3 & 0x3f);
bw.write_8(c2 & 0x3f);
bw.write_8(c1 & 0x3f);
bw.write_8(c4 & 0x3f);
bw.write_8(c3 & 0x3f);
bw.write_8(c2 & 0x3f);
bw.write_8(c1 & 0x3f);
return output;
return output;
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
{
for (bool bit : src)
{
if (cursor < bits.size())
bits[cursor++] = bit;
}
for (bool bit : src)
{
if (cursor < bits.size())
bits[cursor++] = bit;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
{
cursor += width;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
cursor += width;
for (int i = 0; i < width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
static uint8_t encode_side(uint8_t track, uint8_t side)
@@ -161,103 +165,116 @@ static uint8_t encode_side(uint8_t track, uint8_t side)
* bit 5) and also whether we're above track 0x3f (in bit 0).
*/
return (side ? 0x20 : 0x00) | ((track>0x3f) ? 0x01 : 0x00);
return (side ? 0x20 : 0x00) | ((track > 0x3f) ? 0x01 : 0x00);
}
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<const Sector>& sector)
static void write_sector(std::vector<bool>& bits,
unsigned& cursor,
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";
if ((sector->data.size() != 512) && (sector->data.size() != 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++)
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6*8); /* sync */
write_bits(bits, cursor, MAC_SECTOR_RECORD, 3*8);
write_bits(bits, cursor, 0xff, 1 * 8); /* pad byte */
for (int i = 0; i < 7; i++)
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6 * 8); /* sync */
write_bits(bits, cursor, MAC_SECTOR_RECORD, 3 * 8);
uint8_t encodedTrack = sector->logicalTrack & 0x3f;
uint8_t encodedSector = sector->logicalSector;
uint8_t encodedSide = encode_side(sector->logicalTrack, sector->logicalSide);
uint8_t formatByte = MAC_FORMAT_BYTE;
uint8_t headerChecksum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
uint8_t encodedSector = sector->logicalSector;
uint8_t encodedSide =
encode_side(sector->logicalTrack, sector->logicalSide);
uint8_t formatByte = MAC_FORMAT_BYTE;
uint8_t headerChecksum =
(encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
write_bits(bits, cursor, encode_data_gcr(encodedTrack), 1*8);
write_bits(bits, cursor, encode_data_gcr(encodedSector), 1*8);
write_bits(bits, cursor, encode_data_gcr(encodedSide), 1*8);
write_bits(bits, cursor, encode_data_gcr(formatByte), 1*8);
write_bits(bits, cursor, encode_data_gcr(headerChecksum), 1*8);
write_bits(bits, cursor, encode_data_gcr(encodedTrack), 1 * 8);
write_bits(bits, cursor, encode_data_gcr(encodedSector), 1 * 8);
write_bits(bits, cursor, encode_data_gcr(encodedSide), 1 * 8);
write_bits(bits, cursor, encode_data_gcr(formatByte), 1 * 8);
write_bits(bits, cursor, encode_data_gcr(headerChecksum), 1 * 8);
write_bits(bits, cursor, 0xdeaaff, 3*8);
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6*8); /* sync */
write_bits(bits, cursor, MAC_DATA_RECORD, 3*8);
write_bits(bits, cursor, encode_data_gcr(sector->logicalSector), 1*8);
write_bits(bits, cursor, 0xdeaaff, 3 * 8);
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6 * 8); /* sync */
write_bits(bits, cursor, MAC_DATA_RECORD, 3 * 8);
write_bits(bits, cursor, encode_data_gcr(sector->logicalSector), 1 * 8);
Bytes wireData;
wireData.writer().append(sector->data.slice(512, 12)).append(sector->data.slice(0, 512));
for (uint8_t b : encode_crazy_data(wireData))
write_bits(bits, cursor, encode_data_gcr(b), 1*8);
Bytes wireData;
wireData.writer()
.append(sector->data.slice(512, 12))
.append(sector->data.slice(0, 512));
for (uint8_t b : encode_crazy_data(wireData))
write_bits(bits, cursor, encode_data_gcr(b), 1 * 8);
write_bits(bits, cursor, 0xdeaaff, 3*8);
write_bits(bits, cursor, 0xdeaaff, 3 * 8);
}
class MacintoshEncoder : public AbstractEncoder
{
public:
MacintoshEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.macintosh())
{}
MacintoshEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.macintosh())
{
}
public:
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
std::vector<std::shared_ptr<const Sector>> collectSectors(
const Location& location, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
if ((physicalTrack >= 0) && (physicalTrack < MAC_TRACKS_PER_DISK))
if ((location.logicalCylinder >= 0) &&
(location.logicalCylinder < MAC_TRACKS_PER_DISK))
{
unsigned numSectors = sectorsForTrack(physicalTrack);
for (int sectorId=0; sectorId<numSectors; sectorId++)
unsigned numSectors = sectorsForTrack(location.logicalCylinder);
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
const auto& sector = image.get(
location.logicalCylinder, location.head, sectorId);
if (sector)
sectors.push_back(sector);
}
}
return sectors;
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
{
if ((physicalTrack < 0) || (physicalTrack >= MAC_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
std::unique_ptr<Fluxmap> encode(const Location& location,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
double clockRateUs = clockRateUsForTrack(location.logicalCylinder) *
_config.clock_compensation_factor();
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
double clockRateUs = clockRateUsForTrack(physicalTrack) * _config.clock_compensation_factor();
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits,
cursor,
_config.post_index_gap_us() / clockRateUs,
{true, false});
lastBit = false;
fillBitmapTo(bits, cursor, _config.post_index_gap_us() / clockRateUs, { true, false });
lastBit = false;
for (const auto& sector : sectors)
write_sector(bits, cursor, sector);
for (const auto& sector : sectors)
write_sector(bits, cursor, sector);
if (cursor >= bits.size())
Error() << fmt::format(
"track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), {true, false});
if (cursor >= bits.size())
Error() << fmt::format("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, clockRateUs*1e3);
return fluxmap;
}
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs * 1e3);
return fluxmap;
}
private:
const MacintoshEncoderProto& _config;
const MacintoshEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createMacintoshEncoder(const EncoderProto& config)
std::unique_ptr<AbstractEncoder> createMacintoshEncoder(
const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new MacintoshEncoder(config));
return std::unique_ptr<AbstractEncoder>(new MacintoshEncoder(config));
}

View File

@@ -173,7 +173,7 @@ public:
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
std::set<unsigned> requiredSectors(const Location& location) const override
{
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
return sectors;

View File

@@ -6,116 +6,123 @@
#include "image.h"
#include "lib/encoders/encoders.pb.h"
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<const Sector>& sector)
static void write_sector(std::vector<bool>& bits,
unsigned& cursor,
const std::shared_ptr<const Sector>& sector)
{
if ((sector->data.size() != 256) && (sector->data.size() != MICROPOLIS_ENCODED_SECTOR_SIZE))
Error() << "unsupported sector size --- you must pick 256 or 275";
if ((sector->data.size() != 256) &&
(sector->data.size() != MICROPOLIS_ENCODED_SECTOR_SIZE))
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>>();
fullSector->reserve(fullSectorSize);
/* sector preamble */
for (int i=0; i<40; i++)
fullSector->push_back(0);
Bytes sectorData;
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";
uint8_t wantChecksum = sector->data[1+2+266];
uint8_t gotChecksum = micropolisChecksum(sector->data.slice(1, 2+266));
if (wantChecksum != gotChecksum)
std::cerr << "Warning: checksum incorrect. Sector: " << sector->logicalSector << std::endl;
sectorData = sector->data;
}
else
{
ByteWriter writer(sectorData);
writer.write_8(0xff); /* Sync */
writer.write_8(sector->logicalTrack);
writer.write_8(sector->logicalSector);
for (int i=0; i<10; i++)
writer.write_8(0); /* Padding */
writer += sector->data;
writer.write_8(micropolisChecksum(sectorData.slice(1)));
for (int i=0; i<5; i++)
writer.write_8(0); /* 4 byte ECC and ECC not present flag */
}
for (uint8_t b : sectorData)
fullSector->push_back(b);
/* sector postamble */
for (int i=0; i<40; i++)
fullSector->push_back(0);
/* filler */
for (int i=0; i<35; i++)
fullSector->push_back(0);
int fullSectorSize = 40 + MICROPOLIS_ENCODED_SECTOR_SIZE + 40 + 35;
auto fullSector = std::make_shared<std::vector<uint8_t>>();
fullSector->reserve(fullSectorSize);
/* sector preamble */
for (int i = 0; i < 40; i++)
fullSector->push_back(0);
Bytes sectorData;
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";
uint8_t wantChecksum = sector->data[1 + 2 + 266];
uint8_t gotChecksum =
micropolisChecksum(sector->data.slice(1, 2 + 266));
if (wantChecksum != gotChecksum)
std::cerr << "Warning: checksum incorrect. Sector: "
<< sector->logicalSector << std::endl;
sectorData = sector->data;
}
else
{
ByteWriter writer(sectorData);
writer.write_8(0xff); /* Sync */
writer.write_8(sector->logicalTrack);
writer.write_8(sector->logicalSector);
for (int i = 0; i < 10; i++)
writer.write_8(0); /* Padding */
writer += sector->data;
writer.write_8(micropolisChecksum(sectorData.slice(1)));
for (int i = 0; i < 5; i++)
writer.write_8(0); /* 4 byte ECC and ECC not present flag */
}
for (uint8_t b : sectorData)
fullSector->push_back(b);
/* sector postamble */
for (int i = 0; i < 40; i++)
fullSector->push_back(0);
/* filler */
for (int i = 0; i < 35; i++)
fullSector->push_back(0);
if (fullSector->size() != fullSectorSize)
Error() << "sector mismatched length";
bool lastBit = false;
encodeMfm(bits, cursor, fullSector, lastBit);
/* filler */
for (int i=0; i<5; i++)
{
bits[cursor++] = 1;
bits[cursor++] = 0;
}
if (fullSector->size() != fullSectorSize)
Error() << "sector mismatched length";
bool lastBit = false;
encodeMfm(bits, cursor, fullSector, lastBit);
/* filler */
for (int i = 0; i < 5; i++)
{
bits[cursor++] = 1;
bits[cursor++] = 0;
}
}
class MicropolisEncoder : public AbstractEncoder
{
public:
MicropolisEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.micropolis())
{}
MicropolisEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.micropolis())
{
}
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
std::vector<std::shared_ptr<const Sector>> collectSectors(
const Location& location, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
if ((physicalTrack >= 0) && (physicalTrack < 77))
{
for (int sectorId = 0; sectorId < 16; sectorId++)
{
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
if (sector)
sectors.push_back(sector);
}
}
if ((location.logicalCylinder >= 0) && (location.logicalCylinder < 77))
{
for (int sectorId = 0; sectorId < 16; sectorId++)
{
const auto& sector = image.get(
location.logicalCylinder, location.head, sectorId);
if (sector)
sectors.push_back(sector);
}
}
return sectors;
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
{
int bitsPerRevolution = 100000;
double clockRateUs = 2.00;
std::unique_ptr<Fluxmap> encode(const Location& location,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
int bitsPerRevolution = 100000;
double clockRateUs = 2.00;
if ((physicalTrack < 0) || (physicalTrack >= 77) || sectors.empty())
return std::unique_ptr<Fluxmap>();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (const auto& sectorData : sectors)
write_sector(bits, cursor, sectorData);
for (const auto& sectorData : sectors)
write_sector(bits, cursor, sectorData);
if (cursor != bits.size())
Error() << "track data mismatched length";
if (cursor != bits.size())
Error() << "track data mismatched length";
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs * 1e3);
return fluxmap;
}
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs * 1e3);
return fluxmap;
}
private:
const MicropolisEncoderProto& _config;
const MicropolisEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createMicropolisEncoder(const EncoderProto& config)
std::unique_ptr<AbstractEncoder> createMicropolisEncoder(
const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new MicropolisEncoder(config));
return std::unique_ptr<AbstractEncoder>(new MicropolisEncoder(config));
}

View File

@@ -164,7 +164,7 @@ public:
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
std::set<unsigned> requiredSectors(const Location&) const override
{
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
return sectors;

View File

@@ -12,155 +12,178 @@
#define GAP_FILL_SIZE_DD 62
#define PRE_HEADER_GAP_FILL_SIZE_DD 16
#define GAP1_FILL_BYTE (0x4F)
#define GAP2_FILL_BYTE (0x4F)
#define GAP1_FILL_BYTE (0x4F)
#define GAP2_FILL_BYTE (0x4F)
#define TOTAL_SECTOR_BYTES ()
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<const Sector>& sector)
static void write_sector(std::vector<bool>& bits,
unsigned& cursor,
const std::shared_ptr<const Sector>& sector)
{
int preambleSize = 0;
int encodedSectorSize = 0;
int gapFillSize = 0;
int preHeaderGapFillSize = 0;
int preambleSize = 0;
int encodedSectorSize = 0;
int gapFillSize = 0;
int preHeaderGapFillSize = 0;
bool doubleDensity;
bool doubleDensity;
switch (sector->data.size()) {
case NORTHSTAR_PAYLOAD_SIZE_SD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD + NORTHSTAR_ENCODED_SECTOR_SIZE_SD + GAP_FILL_SIZE_SD;
gapFillSize = GAP_FILL_SIZE_SD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD;
doubleDensity = false;
break;
case NORTHSTAR_PAYLOAD_SIZE_DD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD + NORTHSTAR_ENCODED_SECTOR_SIZE_DD + GAP_FILL_SIZE_DD;
gapFillSize = GAP_FILL_SIZE_DD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD;
doubleDensity = true;
break;
default:
Error() << "unsupported sector size --- you must pick 256 or 512";
break;
}
switch (sector->data.size())
{
case NORTHSTAR_PAYLOAD_SIZE_SD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD +
NORTHSTAR_ENCODED_SECTOR_SIZE_SD +
GAP_FILL_SIZE_SD;
gapFillSize = GAP_FILL_SIZE_SD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD;
doubleDensity = false;
break;
case NORTHSTAR_PAYLOAD_SIZE_DD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD +
NORTHSTAR_ENCODED_SECTOR_SIZE_DD +
GAP_FILL_SIZE_DD;
gapFillSize = GAP_FILL_SIZE_DD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD;
doubleDensity = true;
break;
default:
Error() << "unsupported sector size --- you must pick 256 or 512";
break;
}
int fullSectorSize = preambleSize + encodedSectorSize;
auto fullSector = std::make_shared<std::vector<uint8_t>>();
fullSector->reserve(fullSectorSize);
int fullSectorSize = preambleSize + encodedSectorSize;
auto fullSector = std::make_shared<std::vector<uint8_t>>();
fullSector->reserve(fullSectorSize);
/* sector gap after index pulse */
for (int i = 0; i < preHeaderGapFillSize; i++)
fullSector->push_back(GAP1_FILL_BYTE);
/* sector gap after index pulse */
for (int i = 0; i < preHeaderGapFillSize; i++)
fullSector->push_back(GAP1_FILL_BYTE);
/* sector preamble */
for (int i = 0; i < preambleSize; i++)
fullSector->push_back(0);
/* sector preamble */
for (int i = 0; i < preambleSize; i++)
fullSector->push_back(0);
Bytes sectorData;
if (sector->data.size() == encodedSectorSize)
sectorData = sector->data;
else {
ByteWriter writer(sectorData);
writer.write_8(0xFB); /* sync character */
if (doubleDensity == true) {
writer.write_8(0xFB); /* Double-density has two sync characters */
}
writer += sector->data;
if (doubleDensity == true) {
writer.write_8(northstarChecksum(sectorData.slice(2)));
} else {
writer.write_8(northstarChecksum(sectorData.slice(1)));
}
}
for (uint8_t b : sectorData)
fullSector->push_back(b);
Bytes sectorData;
if (sector->data.size() == encodedSectorSize)
sectorData = sector->data;
else
{
ByteWriter writer(sectorData);
writer.write_8(0xFB); /* sync character */
if (doubleDensity == true)
{
writer.write_8(0xFB); /* Double-density has two sync characters */
}
writer += sector->data;
if (doubleDensity == true)
{
writer.write_8(northstarChecksum(sectorData.slice(2)));
}
else
{
writer.write_8(northstarChecksum(sectorData.slice(1)));
}
}
for (uint8_t b : sectorData)
fullSector->push_back(b);
if (sector->logicalSector != 9) {
/* sector postamble */
for (int i = 0; i < gapFillSize; i++)
fullSector->push_back(GAP2_FILL_BYTE);
if (sector->logicalSector != 9)
{
/* sector postamble */
for (int i = 0; i < gapFillSize; i++)
fullSector->push_back(GAP2_FILL_BYTE);
if (fullSector->size() != fullSectorSize)
Error() << "sector mismatched length (" << sector->data.size() << ") expected: " << fullSector->size() << " got " << fullSectorSize;
} else {
/* sector postamble */
for (int i = 0; i < gapFillSize; i++)
fullSector->push_back(GAP2_FILL_BYTE);
}
if (fullSector->size() != fullSectorSize)
Error() << "sector mismatched length (" << sector->data.size()
<< ") expected: " << fullSector->size() << " got "
<< fullSectorSize;
}
else
{
/* sector postamble */
for (int i = 0; i < gapFillSize; i++)
fullSector->push_back(GAP2_FILL_BYTE);
}
bool lastBit = false;
bool lastBit = false;
if (doubleDensity == true) {
encodeMfm(bits, cursor, fullSector, lastBit);
}
else {
encodeFm(bits, cursor, fullSector);
}
if (doubleDensity == true)
{
encodeMfm(bits, cursor, fullSector, lastBit);
}
else
{
encodeFm(bits, cursor, fullSector);
}
}
class NorthstarEncoder : public AbstractEncoder
{
public:
NorthstarEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.northstar())
{}
NorthstarEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.northstar())
{
}
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
std::vector<std::shared_ptr<const Sector>> collectSectors(
const Location& location, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
if ((physicalTrack >= 0) && (physicalTrack < 35))
{
for (int sectorId = 0; sectorId < 10; sectorId++)
{
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
if (sector)
sectors.push_back(sector);
}
}
if ((location.logicalCylinder >= 0) && (location.logicalCylinder < 35))
{
for (int sectorId = 0; sectorId < 10; sectorId++)
{
const auto& sector = image.get(
location.logicalCylinder, location.head, sectorId);
if (sector)
sectors.push_back(sector);
}
}
return sectors;
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
{
int bitsPerRevolution = 100000;
double clockRateUs = 4.00;
std::unique_ptr<Fluxmap> encode(const Location& location,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
int bitsPerRevolution = 100000;
double clockRateUs = 4.00;
if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty())
return std::unique_ptr<Fluxmap>();
const auto& sector = *sectors.begin();
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD)
{
bitsPerRevolution /= 2; // FM
}
else
{
clockRateUs /= 2.00;
}
const auto& sector = *sectors.begin();
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) {
bitsPerRevolution /= 2; // FM
} else {
clockRateUs /= 2.00;
}
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (const auto& sectorData : sectors)
write_sector(bits, cursor, sectorData);
for (const auto& sectorData : sectors)
write_sector(bits, cursor, sectorData);
if (cursor > bits.size())
Error() << "track data overrun";
if (cursor > bits.size())
Error() << "track data overrun";
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs * 1e3);
return fluxmap;
}
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs * 1e3);
return fluxmap;
}
private:
const NorthstarEncoderProto& _config;
const NorthstarEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createNorthstarEncoder(const EncoderProto& config)
std::unique_ptr<AbstractEncoder> createNorthstarEncoder(
const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config));
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config));
}

View File

@@ -11,158 +11,157 @@
static int charToInt(char c)
{
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
}
static uint8_t decodeUint16(uint16_t raw)
{
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
}
class Tids990Encoder : public AbstractEncoder
{
public:
Tids990Encoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.tids990())
{}
Tids990Encoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.tids990())
{
}
private:
void writeRawBits(uint32_t data, int width)
{
_cursor += width;
_lastBit = data & 1;
for (int i=0; i<width; i++)
{
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
void writeRawBits(uint32_t data, int width)
{
_cursor += width;
_lastBit = data & 1;
for (int i = 0; i < width; i++)
{
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
void writeBytes(const Bytes& bytes)
{
encodeMfm(_bits, _cursor, bytes, _lastBit);
}
void writeBytes(const Bytes& bytes)
{
encodeMfm(_bits, _cursor, bytes, _lastBit);
}
void writeBytes(int count, uint8_t byte)
{
Bytes bytes = { byte };
for (int i=0; i<count; i++)
writeBytes(bytes);
}
void writeBytes(int count, uint8_t byte)
{
Bytes bytes = {byte};
for (int i = 0; i < count; i++)
writeBytes(bytes);
}
public:
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
std::vector<std::shared_ptr<const Sector>> collectSectors(
const Location& location, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
for (char sectorChar : _config.sector_skew())
for (char sectorChar : _config.sector_skew())
{
int sectorId = charToInt(sectorChar);
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
if (sector)
sectors.push_back(sector);
int sectorId = charToInt(sectorChar);
const auto& sector =
image.get(location.logicalCylinder, location.head, sectorId);
if (sector)
sectors.push_back(sector);
}
return sectors;
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
{
double clockRateUs = 1e3 / _config.clock_rate_khz() / 2.0;
int bitsPerRevolution = (_config.track_length_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
std::unique_ptr<Fluxmap> encode(const Location& location,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
double clockRateUs = 1e3 / _config.clock_rate_khz() / 2.0;
int bitsPerRevolution =
(_config.track_length_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
uint8_t am1Unencoded = decodeUint16(_config.am1_byte());
uint8_t am2Unencoded = decodeUint16(_config.am2_byte());
uint8_t am1Unencoded = decodeUint16(_config.am1_byte());
uint8_t am2Unencoded = decodeUint16(_config.am2_byte());
writeBytes(_config.gap1_bytes(), 0x55);
writeBytes(_config.gap1_bytes(), 0x55);
bool first = true;
for (char sectorChar : _config.sector_skew())
{
int sectorId = charToInt(sectorChar);
if (!first)
writeBytes(_config.gap3_bytes(), 0x55);
first = false;
bool first = true;
for (const auto& sectorData : sectors)
{
if (!first)
writeBytes(_config.gap3_bytes(), 0x55);
first = false;
const auto& sectorData = image.get(physicalTrack, physicalSide, sectorId);
if (!sectorData)
Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId);
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
{
Bytes header;
ByteWriter bw(header);
{
Bytes header;
ByteWriter bw(header);
writeBytes(12, 0x55);
bw.write_8(am1Unencoded);
bw.write_8(sectorData->logicalSide << 3);
bw.write_8(sectorData->logicalTrack);
bw.write_8(_config.sector_count());
bw.write_8(sectorData->logicalSector);
bw.write_be16(sectorData->data.size());
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
writeBytes(12, 0x55);
bw.write_8(am1Unencoded);
bw.write_8(sectorData->logicalSide << 3);
bw.write_8(sectorData->logicalTrack);
bw.write_8(_config.sector_count());
bw.write_8(sectorData->logicalSector);
bw.write_be16(sectorData->data.size());
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
writeRawBits(_config.am1_byte(), 16);
writeBytes(header.slice(1));
}
writeRawBits(_config.am1_byte(), 16);
writeBytes(header.slice(1));
}
writeBytes(_config.gap2_bytes(), 0x55);
writeBytes(_config.gap2_bytes(), 0x55);
{
Bytes data;
ByteWriter bw(data);
{
Bytes data;
ByteWriter bw(data);
writeBytes(12, 0x55);
bw.write_8(am2Unencoded);
writeBytes(12, 0x55);
bw.write_8(am2Unencoded);
bw += sectorData->data;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
bw += sectorData->data;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
writeRawBits(_config.am2_byte(), 16);
writeBytes(data.slice(1));
}
}
writeRawBits(_config.am2_byte(), 16);
writeBytes(data.slice(1));
}
}
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeBytes(1, 0x55);
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeBytes(1, 0x55);
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);
return fluxmap;
}
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs * 1e3);
return fluxmap;
}
private:
const Tids990EncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
const Tids990EncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
std::unique_ptr<AbstractEncoder> createTids990Encoder(const EncoderProto& config)
std::unique_ptr<AbstractEncoder> createTids990Encoder(
const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new Tids990Encoder(config));
return std::unique_ptr<AbstractEncoder>(new Tids990Encoder(config));
}

View File

@@ -14,77 +14,84 @@
static bool lastBit;
static void write_zero_bits(std::vector<bool>& bits, unsigned& cursor, unsigned count)
static void write_zero_bits(
std::vector<bool>& bits, unsigned& cursor, unsigned count)
{
while (count--)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = 0;
}
{
if (cursor < bits.size())
lastBit = bits[cursor++] = 0;
}
}
static void write_one_bits(std::vector<bool>& bits, unsigned& cursor, unsigned count)
static void write_one_bits(
std::vector<bool>& bits, unsigned& cursor, unsigned count)
{
while (count--)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = 1;
}
{
if (cursor < bits.size())
lastBit = bits[cursor++] = 1;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
{
for (bool bit : src)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = bit;
}
for (bool bit : src)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = bit;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
{
cursor += width;
lastBit = data & 1;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
cursor += width;
lastBit = data & 1;
for (int i = 0; i < width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
static void write_bits(
std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
{
ByteReader br(bytes);
BitReader bitr(br);
ByteReader br(bytes);
BitReader bitr(br);
while (!bitr.eof())
{
if (cursor < bits.size())
bits[cursor++] = bitr.get();
}
while (!bitr.eof())
{
if (cursor < bits.size())
bits[cursor++] = bitr.get();
}
}
static int encode_data_gcr(uint8_t data)
{
switch (data & 0x0f)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
#define GCR_ENTRY(gcr, data) \
case data: \
return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
static void write_byte(std::vector<bool>& bits, unsigned& cursor, uint8_t b)
{
write_bits(bits, cursor, encode_data_gcr(b>>4), 5);
write_bits(bits, cursor, encode_data_gcr(b), 5);
write_bits(bits, cursor, encode_data_gcr(b >> 4), 5);
write_bits(bits, cursor, encode_data_gcr(b), 5);
}
static void write_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
static void write_bytes(
std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
{
for (uint8_t b : bytes)
write_byte(bits, cursor, b);
@@ -92,24 +99,27 @@ static void write_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes&
static void write_gap(std::vector<bool>& bits, unsigned& cursor, int length)
{
for (int i = 0; i < length/10; i++)
for (int i = 0; i < length / 10; i++)
write_byte(bits, cursor, '0');
}
static void write_sector(std::vector<bool>& bits, unsigned& cursor,
const Victor9kEncoderProto::TrackdataProto& trackdata,
const Sector& sector)
static void write_sector(std::vector<bool>& bits,
unsigned& cursor,
const Victor9kEncoderProto::TrackdataProto& trackdata,
const Sector& sector)
{
write_one_bits(bits, cursor, trackdata.pre_header_sync_bits());
write_bits(bits, cursor, VICTOR9K_SECTOR_RECORD, 10);
uint8_t encodedTrack = sector.logicalTrack | (sector.logicalSide<<7);
uint8_t encodedTrack = sector.logicalTrack | (sector.logicalSide << 7);
uint8_t encodedSector = sector.logicalSector;
write_bytes(bits, cursor, Bytes {
encodedTrack,
encodedSector,
(uint8_t)(encodedTrack + encodedSector),
});
write_bytes(bits,
cursor,
Bytes{
encodedTrack,
encodedSector,
(uint8_t)(encodedTrack + encodedSector),
});
write_gap(bits, cursor, trackdata.post_header_gap_bits());
@@ -127,82 +137,92 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor,
class Victor9kEncoder : public AbstractEncoder
{
public:
Victor9kEncoder(const EncoderProto& config):
Victor9kEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.victor9k())
{}
_config(config.victor9k())
{
}
private:
void getTrackFormat(Victor9kEncoderProto::TrackdataProto& trackdata,
unsigned cylinder,
unsigned head)
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_min_cylinder() && (cylinder < f.min_cylinder()))
continue;
if (f.has_max_cylinder() && (cylinder > f.max_cylinder()))
continue;
if (f.has_head() && (head != f.head()))
continue;
void getTrackFormat(Victor9kEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_min_cylinder() && (cylinder < f.min_cylinder()))
continue;
if (f.has_max_cylinder() && (cylinder > f.max_cylinder()))
continue;
if (f.has_head() && (head != f.head()))
continue;
trackdata.MergeFrom(f);
}
}
trackdata.MergeFrom(f);
}
}
public:
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
std::vector<std::shared_ptr<const Sector>> collectSectors(
const Location& location, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
Victor9kEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
Victor9kEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, location.logicalCylinder, location.head);
for (int i = 0; i < trackdata.sector_range().sector_count(); i++)
{
int sectorId = trackdata.sector_range().start_sector() + i;
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
if (sector)
sectors.push_back(sector);
const auto& sector =
image.get(location.logicalCylinder, location.head, sectorId);
if (sector)
sectors.push_back(sector);
}
return sectors;
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
std::unique_ptr<Fluxmap> encode(const Location& location,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
Victor9kEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
Victor9kEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, location.logicalCylinder, location.head);
unsigned bitsPerRevolution = trackdata.original_data_rate_khz() * trackdata.original_period_ms();
unsigned bitsPerRevolution =
trackdata.original_data_rate_khz() * trackdata.original_period_ms();
std::vector<bool> bits(bitsPerRevolution);
double clockRateUs = 166666.0 / bitsPerRevolution;
unsigned cursor = 0;
fillBitmapTo(bits, cursor, trackdata.post_index_gap_us() / clockRateUs, { true, false });
fillBitmapTo(bits,
cursor,
trackdata.post_index_gap_us() / clockRateUs,
{true, false});
lastBit = false;
for (const auto& sector : sectors)
write_sector(bits, cursor, trackdata, *sector);
if (cursor >= bits.size())
Error() << fmt::format("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), { true, false });
Error() << fmt::format(
"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, clockRateUs*1e3);
fluxmap->appendBits(bits, clockRateUs * 1e3);
return fluxmap;
}
private:
const Victor9kEncoderProto& _config;
const Victor9kEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createVictor9kEncoder(const EncoderProto& config)
std::unique_ptr<AbstractEncoder> createVictor9kEncoder(
const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new Victor9kEncoder(config));
return std::unique_ptr<AbstractEncoder>(new Victor9kEncoder(config));
}
// vim: sw=4 ts=4 et

View File

@@ -11,7 +11,7 @@ import "lib/drive.proto";
import "lib/mapper.proto";
import "lib/common.proto";
// NEXT_TAG: 16
// NEXT_TAG: 17
message ConfigProto {
optional string comment = 8;
optional bool is_extension = 13;
@@ -29,6 +29,7 @@ message ConfigProto {
optional RangeProto cylinders = 6;
optional RangeProto heads = 7;
optional int32 tpi = 16 [ (help) = "TPI of image; if 0, use TPI of drive" ];
optional SectorMappingProto sector_mapping = 14;
}

View File

@@ -60,12 +60,11 @@ std::unique_ptr<AbstractDecoder> AbstractDecoder::create(const DecoderProto& con
}
std::shared_ptr<const TrackDataFlux> AbstractDecoder::decodeToSectors(
std::shared_ptr<const Fluxmap> fluxmap, unsigned physicalCylinder, unsigned physicalHead)
std::shared_ptr<const Fluxmap> fluxmap, const Location& location)
{
_trackdata = std::make_shared<TrackDataFlux>();
_trackdata->fluxmap = fluxmap;
_trackdata->physicalCylinder = physicalCylinder;
_trackdata->physicalHead = physicalHead;
_trackdata->location = location;
FluxmapReader fmr(*fluxmap);
_fmr = &fmr;
@@ -73,8 +72,8 @@ std::shared_ptr<const TrackDataFlux> AbstractDecoder::decodeToSectors(
auto newSector = [&] {
_sector = std::make_shared<Sector>();
_sector->status = Sector::MISSING;
_sector->physicalCylinder = physicalCylinder;
_sector->physicalHead = physicalHead;
_sector->physicalCylinder = location.physicalCylinder;
_sector->physicalHead = location.head;
};
newSector();
@@ -218,7 +217,7 @@ uint64_t AbstractDecoder::readRaw64()
}
std::set<unsigned> AbstractDecoder::requiredSectors(unsigned cylinder, unsigned head) const
std::set<unsigned> AbstractDecoder::requiredSectors(const Location& location) const
{
static std::set<unsigned> set;
return set;

View File

@@ -18,23 +18,27 @@ extern void setDecoderManualClockRate(double clockrate_us);
extern Bytes decodeFmMfm(std::vector<bool>::const_iterator start,
std::vector<bool>::const_iterator end);
extern void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input, bool& lastBit);
extern void encodeFm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input);
extern void encodeMfm(std::vector<bool>& bits,
unsigned& cursor,
const Bytes& input,
bool& lastBit);
extern void encodeFm(
std::vector<bool>& bits, unsigned& cursor, const Bytes& input);
extern Bytes encodeMfm(const Bytes& input, bool& lastBit);
static inline Bytes decodeFmMfm(const std::vector<bool> bits)
{ return decodeFmMfm(bits.begin(), bits.end()); }
{
return decodeFmMfm(bits.begin(), bits.end());
}
class AbstractDecoder
{
public:
AbstractDecoder(const DecoderProto& config):
_config(config)
{}
AbstractDecoder(const DecoderProto& config): _config(config) {}
virtual ~AbstractDecoder() {}
static std::unique_ptr<AbstractDecoder> create(const DecoderProto& config);
static std::unique_ptr<AbstractDecoder> create(const DecoderProto& config);
public:
enum RecordType
@@ -45,47 +49,59 @@ public:
};
public:
std::shared_ptr<const TrackDataFlux> decodeToSectors(std::shared_ptr<const Fluxmap> fluxmap, unsigned cylinder, unsigned head);
void pushRecord(const Fluxmap::Position& start, const Fluxmap::Position& end);
std::shared_ptr<const TrackDataFlux> decodeToSectors(
std::shared_ptr<const Fluxmap> fluxmap,
const Location& location);
void resetFluxDecoder();
void pushRecord(
const Fluxmap::Position& start, const Fluxmap::Position& end);
void resetFluxDecoder();
std::vector<bool> readRawBits(unsigned count);
uint8_t readRaw8();
uint16_t readRaw16();
uint32_t readRaw20();
uint32_t readRaw24();
uint32_t readRaw32();
uint64_t readRaw48();
uint64_t readRaw64();
uint8_t readRaw8();
uint16_t readRaw16();
uint32_t readRaw20();
uint32_t readRaw24();
uint32_t readRaw32();
uint64_t readRaw48();
uint64_t readRaw64();
Fluxmap::Position tell()
{ return _fmr->tell(); }
{
return _fmr->tell();
}
void seek(const Fluxmap::Position& pos)
{ return _fmr->seek(pos); }
{
return _fmr->seek(pos);
}
nanoseconds_t seekToPattern(const FluxMatcher& pattern);
void seekToIndexMark();
nanoseconds_t seekToPattern(const FluxMatcher& pattern);
void seekToIndexMark();
bool eof() const
{ return _fmr->eof(); }
{
return _fmr->eof();
}
nanoseconds_t getFluxmapDuration() const
{ return _fmr->getDuration(); }
nanoseconds_t getFluxmapDuration() const
{
return _fmr->getDuration();
}
virtual std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const;
virtual std::set<unsigned> requiredSectors(const Location& location) const;
protected:
virtual void beginTrack() {};
virtual void beginTrack(){};
virtual nanoseconds_t advanceToNextRecord() = 0;
virtual void decodeSectorRecord() = 0;
virtual void decodeDataRecord() {};
virtual void decodeDataRecord(){};
const DecoderProto& _config;
std::shared_ptr<TrackDataFlux> _trackdata;
const DecoderProto& _config;
std::shared_ptr<TrackDataFlux> _trackdata;
std::shared_ptr<Sector> _sector;
std::unique_ptr<FluxDecoder> _decoder;
std::vector<bool> _recordBits;
std::unique_ptr<FluxDecoder> _decoder;
std::vector<bool> _recordBits;
private:
FluxmapReader* _fmr = nullptr;

View File

@@ -2,13 +2,33 @@ syntax = "proto2";
import "lib/common.proto";
message DriveProto {
optional int32 drive = 1 [default = 0, (help) = "which drive to write to (0 or 1)"];
optional IndexMode index_mode = 2 [default = INDEXMODE_DRIVE, (help) = "index pulse source"];
optional int32 hard_sector_count = 3 [default = 0, (help) = "number of hard sectors on disk"];
optional bool high_density = 4 [default = true, (help) = "set if this is a high density disk"];
optional bool sync_with_index = 5 [default = false, (help) = "start reading at index mark"];
optional double revolutions = 6 [default = 1.2, (help) = "number of revolutions to read"];
// Next: 13
message DriveProto
{
optional int32 drive = 1
[ default = 0, (help) = "which drive to write to (0 or 1)" ];
optional IndexMode index_mode = 2
[ default = INDEXMODE_DRIVE, (help) = "index pulse source" ];
optional int32 hard_sector_count = 3
[ default = 0, (help) = "number of hard sectors on disk" ];
optional bool high_density = 4
[ default = true, (help) = "set if this is a high density disk" ];
optional bool sync_with_index = 5
[ default = false, (help) = "start reading at index mark" ];
optional double revolutions = 6
[ default = 1.2, (help) = "number of revolutions to read" ];
optional int32 cylinders = 7
[ default = 81, (help) = "Number of cylinders supported by drive" ];
optional int32 heads = 8
[ default = 2, (help) = "Number of heads supported by drive" ];
optional int32 head_bias = 10 [
default = 0,
(help) = "Bias to apply to the head position (in cylinders)"
];
optional int32 head_width = 11
[ default = 1, (help) = "Width of the head (in cylinders)" ];
optional int32 tpi = 9 [ default = 96, (help) = "TPI of drive" ];
}
// vim: ts=4 sw=4 et

View File

@@ -15,45 +15,44 @@
#include "lib/encoders/encoders.pb.h"
#include "protocol.h"
std::unique_ptr<AbstractEncoder> AbstractEncoder::create(const EncoderProto& config)
std::unique_ptr<AbstractEncoder> AbstractEncoder::create(
const EncoderProto& config)
{
static const std::map<int,
std::function<std::unique_ptr<AbstractEncoder>(const EncoderProto&)>> encoders =
{
{ EncoderProto::kAmiga, createAmigaEncoder },
{ EncoderProto::kApple2, createApple2Encoder },
{ EncoderProto::kBrother, createBrotherEncoder },
{ EncoderProto::kC64, createCommodore64Encoder },
{ EncoderProto::kIbm, createIbmEncoder },
{ EncoderProto::kMacintosh, createMacintoshEncoder },
{ EncoderProto::kMicropolis,createMicropolisEncoder },
{ EncoderProto::kNorthstar, createNorthstarEncoder },
{ EncoderProto::kTids990, createTids990Encoder },
{ EncoderProto::kVictor9K, createVictor9kEncoder },
};
static const std::map<int,
std::function<std::unique_ptr<AbstractEncoder>(const EncoderProto&)>>
encoders = {
{EncoderProto::kAmiga, createAmigaEncoder },
{EncoderProto::kApple2, createApple2Encoder },
{EncoderProto::kBrother, createBrotherEncoder },
{EncoderProto::kC64, createCommodore64Encoder},
{EncoderProto::kIbm, createIbmEncoder },
{EncoderProto::kMacintosh, createMacintoshEncoder },
{EncoderProto::kMicropolis, createMicropolisEncoder },
{EncoderProto::kNorthstar, createNorthstarEncoder },
{EncoderProto::kTids990, createTids990Encoder },
{EncoderProto::kVictor9K, createVictor9kEncoder },
};
auto encoder = encoders.find(config.format_case());
if (encoder == encoders.end())
Error() << "no encoder specified";
auto encoder = encoders.find(config.format_case());
if (encoder == encoders.end())
Error() << "no encoder specified";
return (encoder->second)(config);
return (encoder->second)(config);
}
Fluxmap& Fluxmap::appendBits(const std::vector<bool>& bits, nanoseconds_t clock)
{
nanoseconds_t now = duration();
for (unsigned i=0; i<bits.size(); i++)
{
now += clock;
if (bits[i])
{
unsigned delta = (now - duration()) / NS_PER_TICK;
nanoseconds_t now = duration();
for (unsigned i = 0; i < bits.size(); i++)
{
now += clock;
if (bits[i])
{
unsigned delta = (now - duration()) / NS_PER_TICK;
appendInterval(delta);
appendPulse();
}
}
appendPulse();
}
}
return *this;
return *this;
}

View File

@@ -1,9 +1,10 @@
#ifndef ENCODERS_H
#define ENCODERS_H
class Fluxmap;
class EncoderProto;
class Fluxmap;
class Image;
class Location;
class Sector;
class AbstractEncoder
@@ -16,10 +17,9 @@ public:
public:
virtual std::vector<std::shared_ptr<const Sector>> collectSectors(
int physicalCylinder, int physicalHead, const Image& image) = 0;
const Location& location, const Image& image) = 0;
virtual std::unique_ptr<Fluxmap> encode(int physicalCylinder,
int physicalHead,
virtual std::unique_ptr<Fluxmap> encode(const Location& location,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) = 0;
};

View File

@@ -12,10 +12,25 @@ struct Record
Bytes rawData;
};
struct Location
{
unsigned physicalCylinder;
unsigned logicalCylinder;
unsigned head;
unsigned groupSize;
std::strong_ordering operator<=>(const Location& other) const
{
auto i = physicalCylinder <=> other.physicalCylinder;
if (i == std::strong_ordering::equal)
i = head <=> other.head;
return i;
}
};
struct TrackDataFlux
{
unsigned physicalCylinder;
unsigned physicalHead;
Location location;
std::shared_ptr<const Fluxmap> fluxmap;
std::vector<std::shared_ptr<const Record>> records;
std::vector<std::shared_ptr<const Sector>> sectors;
@@ -23,8 +38,7 @@ struct TrackDataFlux
struct TrackFlux
{
unsigned physicalCylinder;
unsigned physicalHead;
Location location;
std::vector<std::shared_ptr<const TrackDataFlux>> trackDatas;
std::set<std::shared_ptr<const Sector>> sectors;
};

View File

@@ -32,9 +32,10 @@ public:
appendBytes((const uint8_t*) s.c_str(), s.size());
}
Fluxmap(const Bytes bytes):
_bytes(bytes)
{}
Fluxmap(const Bytes bytes)
{
appendBytes(bytes);
}
nanoseconds_t duration() const { return _duration; }
unsigned ticks() const { return _ticks; }

View File

@@ -14,6 +14,7 @@
#include <cassert>
#include <climits>
#include <variant>
#include <optional>
#if defined(_WIN32) || defined(__WIN32__)
#include <direct.h>

View File

@@ -34,8 +34,6 @@ message ImgInputOutputProto {
repeated TrackdataProto trackdata = 4 [(help) = "per-track format information (repeatable)"];
optional int32 tracks = 5 [default=0, (help) = "number of tracks in image"];
optional int32 sides = 6 [default=0, (help) = "number of sides in image"];
optional int32 physical_offset = 7 [default=0, (help) = "logical:physical track offset"];
optional int32 physical_step = 8 [default=1, (help) = "logical:physical track step"];
optional Order order = 9 [default=CHS, (help) = "the order in which to emit tracks in the image"];
}

View File

@@ -35,8 +35,6 @@ public:
if (inputFile.eof())
break;
int physicalCylinder = track * _config.img().physical_step() +
_config.img().physical_offset();
ImgInputOutputProto::TrackdataProto trackdata;
getTrackFormat(_config.img(), trackdata, track, side);
@@ -46,11 +44,10 @@ public:
Bytes data(trackdata.sector_size());
inputFile.read((char*)data.begin(), data.size());
const auto& sector =
image->put(physicalCylinder, side, sectorId);
const auto& sector = image->put(track, side, sectorId);
sector->status = Sector::OK;
sector->logicalTrack = track;
sector->physicalCylinder = physicalCylinder;
sector->physicalCylinder = track;
sector->logicalSide = sector->physicalHead = side;
sector->logicalSector = sectorId;
sector->data = data;

View File

@@ -3,89 +3,143 @@
#include "image.h"
#include "fmt/format.h"
#include "logger.h"
#include "proto.h"
#include "mapper.h"
#include "flux.h"
#include "lib/mapper.pb.h"
typedef std::function<void(std::map<int, int>&, const SectorMappingProto::MappingProto&)> insertercb_t;
typedef std::function<void(
std::map<int, int>&, const SectorMappingProto::MappingProto&)>
insertercb_t;
static void getTrackFormat(const SectorMappingProto& proto,
SectorMappingProto::TrackdataProto& trackdata, unsigned track, unsigned side)
SectorMappingProto::TrackdataProto& trackdata,
unsigned track,
unsigned side)
{
trackdata.Clear();
for (const SectorMappingProto::TrackdataProto& f : proto.trackdata())
{
if (f.has_track() && f.has_up_to_track() && ((track < f.track()) || (track > f.up_to_track())))
continue;
if (f.has_track() && !f.has_up_to_track() && (track != f.track()))
continue;
if (f.has_side() && (f.side() != side))
continue;
trackdata.Clear();
for (const SectorMappingProto::TrackdataProto& f : proto.trackdata())
{
if (f.has_track() && f.has_up_to_track() &&
((track < f.track()) || (track > f.up_to_track())))
continue;
if (f.has_track() && !f.has_up_to_track() && (track != f.track()))
continue;
if (f.has_side() && (f.side() != side))
continue;
trackdata.MergeFrom(f);
}
trackdata.MergeFrom(f);
}
}
static std::unique_ptr<Image> remapImpl(const Image& source, const SectorMappingProto& mapping,
insertercb_t inserter_cb)
static std::unique_ptr<Image> remapImpl(const Image& source,
const SectorMappingProto& mapping,
insertercb_t inserter_cb)
{
typedef std::pair<int, int> tracksidekey_t;
std::map<tracksidekey_t, std::map<int, int>> cache;
typedef std::pair<int, int> tracksidekey_t;
std::map<tracksidekey_t, std::map<int, int>> cache;
auto getTrackdata =
[&](const tracksidekey_t& key) -> const std::map<int, int>& {
auto it = cache.find(key);
if (it != cache.end())
return it->second;
auto getTrackdata =
[&](const tracksidekey_t& key) -> const std::map<int, int>&
{
auto it = cache.find(key);
if (it != cache.end())
return it->second;
SectorMappingProto::TrackdataProto trackdata;
getTrackFormat(mapping, trackdata, key.first, key.second);
SectorMappingProto::TrackdataProto trackdata;
getTrackFormat(mapping, trackdata, key.first, key.second);
auto& map = cache[key];
for (const auto mappingsit : trackdata.mapping())
inserter_cb(map, mappingsit);
auto& map = cache[key];
for (const auto mappingsit : trackdata.mapping())
inserter_cb(map, mappingsit);
return map;
};
return map;
};
std::set<std::shared_ptr<const Sector>> destSectors;
for (const auto& sector : source)
{
tracksidekey_t key = { sector->logicalTrack, sector->logicalSide };
const auto& trackdata = getTrackdata(key);
if (trackdata.empty())
destSectors.insert(sector);
else
{
auto it = trackdata.find(sector->logicalSector);
if (it == trackdata.end())
Error() << fmt::format("mapping requested but mapping table has no entry for sector {}", sector->logicalSector);
std::set<std::shared_ptr<const Sector>> destSectors;
for (const auto& sector : source)
{
tracksidekey_t key = {sector->logicalTrack, sector->logicalSide};
const auto& trackdata = getTrackdata(key);
if (trackdata.empty())
destSectors.insert(sector);
else
{
auto it = trackdata.find(sector->logicalSector);
if (it == trackdata.end())
Error() << fmt::format(
"mapping requested but mapping table has no entry for "
"sector {}",
sector->logicalSector);
auto newSector = std::make_shared<Sector>(*sector);
newSector->logicalSector = it->second;
destSectors.insert(newSector);
}
}
auto newSector = std::make_shared<Sector>(*sector);
newSector->logicalSector = it->second;
destSectors.insert(newSector);
}
}
return std::make_unique<Image>(destSectors);
return std::make_unique<Image>(destSectors);
}
std::unique_ptr<Image> Mapper::remapPhysicalToLogical(const Image& source, const SectorMappingProto& mapping)
std::unique_ptr<const Image> Mapper::remapSectorsPhysicalToLogical(
const Image& source, const SectorMappingProto& mapping)
{
Logger() << "remapping sectors from physical IDs to logical IDs";
return remapImpl(source, mapping,
[](auto& map, const auto& pair)
{
map.insert({ pair.physical(), pair.logical() });
});
Logger() << "remapping sectors from physical IDs to logical IDs";
return remapImpl(source,
mapping,
[](auto& map, const auto& pair)
{
map.insert({pair.physical(), pair.logical()});
});
}
std::unique_ptr<Image> Mapper::remapLogicalToPhysical(const Image& source, const SectorMappingProto& mapping)
std::unique_ptr<const Image> Mapper::remapSectorsLogicalToPhysical(
const Image& source, const SectorMappingProto& mapping)
{
Logger() << "remapping sectors from logical IDs to physical IDs";
return remapImpl(source, mapping,
[](auto& map, const auto& pair)
{
map.insert({ pair.logical(), pair.physical() });
});
Logger() << "remapping sectors from logical IDs to physical IDs";
return remapImpl(source,
mapping,
[](auto& map, const auto& pair)
{
map.insert({pair.logical(), pair.physical()});
});
}
unsigned Mapper::remapCylinderPhysicalToLogical(unsigned pcylinder)
{
return (pcylinder - config.drive().head_bias()) /
config.drive().head_width();
}
unsigned Mapper::remapCylinderLogicalToPhysical(unsigned lcylinder)
{
Error() << "not working yet";
return config.drive().head_bias() + lcylinder * config.drive().head_width();
}
std::set<Location> Mapper::computeLocations()
{
std::set<Location> locations;
unsigned track_step =
(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";
for (unsigned logicalCylinder : iterate(config.cylinders()))
{
for (unsigned head : iterate(config.heads()))
{
unsigned physicalCylinder = config.drive().head_bias() + logicalCylinder * track_step;
locations.insert({.physicalCylinder = physicalCylinder,
.logicalCylinder = logicalCylinder,
.head = head,
.groupSize = track_step});
}
}
return locations;
}

View File

@@ -2,13 +2,21 @@
#define MAPPER_H
class SectorMappingProto;
class DriveProto;
class Location;
class Mapper
{
public:
static std::unique_ptr<Image> remapPhysicalToLogical(const Image& source, const SectorMappingProto& mapping);
static std::unique_ptr<Image> remapLogicalToPhysical(const Image& source, const SectorMappingProto& mapping);
static std::unique_ptr<const Image> remapSectorsPhysicalToLogical(
const Image& source, const SectorMappingProto& mapping);
static std::unique_ptr<const Image> remapSectorsLogicalToPhysical(
const Image& source, const SectorMappingProto& mapping);
static unsigned remapCylinderPhysicalToLogical(unsigned cylinder);
static unsigned remapCylinderLogicalToPhysical(unsigned cylinder);
static std::set<Location> computeLocations();
};
#endif

View File

@@ -23,13 +23,17 @@
static std::unique_ptr<FluxSink> outputFluxSink;
static std::shared_ptr<const Fluxmap> readFluxmap(FluxSourceIterator& fluxsourceIterator, unsigned cylinder, unsigned head)
static std::shared_ptr<const Fluxmap> readFluxmap(
FluxSourceIterator& fluxsourceIterator, unsigned cylinder, unsigned head)
{
Logger() << BeginReadOperationLogMessage { cylinder, head };
auto fluxmap = fluxsourceIterator.next()->rescale(1.0/config.flux_source().rescale());
Logger() << EndReadOperationLogMessage()
<< fmt::format("{0:.0} ms in {1} bytes", fluxmap->duration()/1e6, fluxmap->bytes());
return fluxmap;
Logger() << BeginReadOperationLogMessage{cylinder, head};
auto fluxmap = fluxsourceIterator.next()->rescale(
1.0 / config.flux_source().rescale());
Logger() << EndReadOperationLogMessage()
<< fmt::format("{0:.0} ms in {1} bytes",
fluxmap->duration() / 1e6,
fluxmap->bytes());
return fluxmap;
}
static bool conflictable(Sector::Status status)
@@ -38,7 +42,8 @@ static bool conflictable(Sector::Status status)
}
static std::set<std::shared_ptr<const Sector>> collect_sectors(
std::set<std::shared_ptr<const Sector>>& track_sectors, bool collapse_conflicts = true)
std::set<std::shared_ptr<const Sector>>& track_sectors,
bool collapse_conflicts = true)
{
typedef std::tuple<unsigned, unsigned, unsigned> key_t;
std::multimap<key_t, std::shared_ptr<const Sector>> sectors;
@@ -65,12 +70,12 @@ static std::set<std::shared_ptr<const Sector>> collect_sectors(
(right->status == Sector::OK) &&
(left->data != right->data))
{
if (!collapse_conflicts)
{
auto s = std::make_shared<Sector>(*right);
s->status = Sector::CONFLICT;
sector_set.insert(s);
}
if (!collapse_conflicts)
{
auto s = std::make_shared<Sector>(*right);
s->status = Sector::CONFLICT;
sector_set.insert(s);
}
auto s = std::make_shared<Sector>(*left);
s->status = Sector::CONFLICT;
return s;
@@ -91,164 +96,172 @@ static std::set<std::shared_ptr<const Sector>> collect_sectors(
return sector_set;
}
std::shared_ptr<const DiskFlux> readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder)
std::shared_ptr<const DiskFlux> readDiskCommand(
FluxSource& fluxsource, AbstractDecoder& decoder)
{
if (config.decoder().has_copy_flux_to())
outputFluxSink = FluxSink::create(config.decoder().copy_flux_to());
auto diskflux = std::make_shared<DiskFlux>();
bool failures = false;
for (int cylinder : iterate(config.cylinders()))
for (const auto& location : Mapper::computeLocations())
{
for (int head : iterate(config.heads()))
testForEmergencyStop();
auto track = std::make_shared<TrackFlux>();
track->location = location;
diskflux->tracks.push_back(track);
std::set<std::shared_ptr<const Sector>> track_sectors;
std::set<std::shared_ptr<const Record>> track_records;
Fluxmap totalFlux;
auto fluxsourceIterator =
fluxsource.readFlux(location.physicalCylinder, location.head);
int retriesRemaining = config.decoder().retries();
while (fluxsourceIterator->hasNext())
{
testForEmergencyStop();
auto fluxmap = readFluxmap(
*fluxsourceIterator, location.physicalCylinder, location.head);
totalFlux.appendDesync().appendBytes(fluxmap->rawBytes());
auto track = std::make_shared<TrackFlux>();
track->physicalCylinder = cylinder;
track->physicalHead = head;
diskflux->tracks.push_back(track);
auto trackdataflux = decoder.decodeToSectors(fluxmap, location);
track->trackDatas.push_back(trackdataflux);
std::set<std::shared_ptr<const Sector>> track_sectors;
std::set<std::shared_ptr<const Record>> track_records;
Fluxmap totalFlux;
track_sectors.insert(
trackdataflux->sectors.begin(), trackdataflux->sectors.end());
track_records.insert(
trackdataflux->records.begin(), trackdataflux->records.end());
auto fluxsourceIterator = fluxsource.readFlux(cylinder, head);
int retriesRemaining = config.decoder().retries();
while (fluxsourceIterator->hasNext())
bool hasBadSectors = false;
std::set<unsigned> required_sectors =
decoder.requiredSectors(location);
std::set<std::shared_ptr<const Sector>> result_sectors;
for (const auto& sector : collect_sectors(track_sectors))
{
auto fluxmap = readFluxmap(*fluxsourceIterator, cylinder, head);
totalFlux.appendDesync().appendBytes(fluxmap->rawBytes());
auto trackdataflux =
decoder.decodeToSectors(fluxmap, cylinder, head);
track->trackDatas.push_back(trackdataflux);
track_sectors.insert(trackdataflux->sectors.begin(),
trackdataflux->sectors.end());
track_records.insert(trackdataflux->records.begin(),
trackdataflux->records.end());
bool hasBadSectors = false;
std::set<unsigned> required_sectors =
decoder.requiredSectors(cylinder, head);
std::set<std::shared_ptr<const Sector>> result_sectors;
for (const auto& sector : collect_sectors(track_sectors))
{
result_sectors.insert(sector);
required_sectors.erase(sector->logicalSector);
if (sector->status != Sector::OK)
hasBadSectors = true;
}
for (unsigned logical_sector : required_sectors)
{
auto sector = std::make_shared<Sector>();
sector->logicalSector = logical_sector;
sector->status = Sector::MISSING;
result_sectors.insert(sector);
result_sectors.insert(sector);
required_sectors.erase(sector->logicalSector);
if (sector->status != Sector::OK)
hasBadSectors = true;
}
track->sectors = collect_sectors(result_sectors);
/* track can't be modified below this point. */
Logger() << TrackReadLogMessage { track };
if (hasBadSectors)
failures = false;
if (!hasBadSectors)
break;
if (!fluxsourceIterator->hasNext())
break;
if (fluxsource.isHardware())
{
retriesRemaining--;
if (retriesRemaining == 0)
{
Logger() << fmt::format("giving up");
break;
}
else
Logger()
<< fmt::format("retrying; {} retries remaining", retriesRemaining);
}
}
if (outputFluxSink)
outputFluxSink->writeFlux(cylinder, head, totalFlux);
if (config.decoder().dump_records())
for (unsigned logical_sector : required_sectors)
{
std::vector<std::shared_ptr<const Record>> sorted_records(track_records.begin(), track_records.end());
std::sort(sorted_records.begin(), sorted_records.end(),
[](const auto& o1, const auto& o2) {
return o1->startTime < o2->startTime;
});
auto sector = std::make_shared<Sector>();
sector->logicalSector = logical_sector;
sector->status = Sector::MISSING;
result_sectors.insert(sector);
std::cout << "\nRaw (undecoded) records follow:\n\n";
for (const auto& record : sorted_records)
{
std::cout << fmt::format("I+{:.2f}us with {:.2f}us clock\n",
record->startTime / 1000.0,
record->clock / 1000.0);
hexdump(std::cout, record->rawData);
std::cout << std::endl;
}
hasBadSectors = true;
}
if (config.decoder().dump_sectors())
track->sectors = collect_sectors(result_sectors);
/* track can't be modified below this point. */
Logger() << TrackReadLogMessage{track};
if (hasBadSectors)
failures = false;
if (!hasBadSectors)
break;
if (!fluxsourceIterator->hasNext())
break;
if (fluxsource.isHardware())
{
auto collected_sectors = collect_sectors(track_sectors, false);
std::vector<std::shared_ptr<const Sector>> sorted_sectors(collected_sectors.begin(), collected_sectors.end());
std::sort(sorted_sectors.begin(), sorted_sectors.end(),
[](const auto& o1, const auto& o2) {
return *o1 < *o2;
});
std::cout << "\nDecoded sectors follow:\n\n";
for (const auto& sector : sorted_sectors)
retriesRemaining--;
if (retriesRemaining == 0)
{
std::cout << fmt::format(
"{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock: "
"status {}\n",
sector->logicalTrack,
sector->logicalSide,
sector->logicalSector,
sector->headerStartTime / 1000.0,
sector->clock / 1000.0,
Sector::statusToString(sector->status));
hexdump(std::cout, sector->data);
std::cout << std::endl;
Logger() << fmt::format("giving up");
break;
}
else
Logger() << fmt::format(
"retrying; {} retries remaining", retriesRemaining);
}
}
}
if (outputFluxSink)
outputFluxSink->writeFlux(
location.physicalCylinder, location.head, totalFlux);
if (config.decoder().dump_records())
{
std::vector<std::shared_ptr<const Record>> sorted_records(
track_records.begin(), track_records.end());
std::sort(sorted_records.begin(),
sorted_records.end(),
[](const auto& o1, const auto& o2)
{
return o1->startTime < o2->startTime;
});
std::cout << "\nRaw (undecoded) records follow:\n\n";
for (const auto& record : sorted_records)
{
std::cout << fmt::format("I+{:.2f}us with {:.2f}us clock\n",
record->startTime / 1000.0,
record->clock / 1000.0);
hexdump(std::cout, record->rawData);
std::cout << std::endl;
}
}
if (config.decoder().dump_sectors())
{
auto collected_sectors = collect_sectors(track_sectors, false);
std::vector<std::shared_ptr<const Sector>> sorted_sectors(
collected_sectors.begin(), collected_sectors.end());
std::sort(sorted_sectors.begin(),
sorted_sectors.end(),
[](const auto& o1, const auto& o2)
{
return *o1 < *o2;
});
std::cout << "\nDecoded sectors follow:\n\n";
for (const auto& sector : sorted_sectors)
{
std::cout << fmt::format(
"{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock: "
"status {}\n",
sector->logicalTrack,
sector->logicalSide,
sector->logicalSector,
sector->headerStartTime / 1000.0,
sector->clock / 1000.0,
Sector::statusToString(sector->status));
hexdump(std::cout, sector->data);
std::cout << std::endl;
}
}
}
if (failures)
Logger() << "Warning: some sectors could not be decoded.";
if (failures)
Logger() << "Warning: some sectors could not be decoded.";
std::set<std::shared_ptr<const Sector>> all_sectors;
for (auto& track : diskflux->tracks)
for (auto& sector : track->sectors)
all_sectors.insert(sector);
all_sectors = collect_sectors(all_sectors);
diskflux->image = std::make_shared<Image>(all_sectors);
std::set<std::shared_ptr<const Sector>> all_sectors;
for (auto& track : diskflux->tracks)
for (auto& sector : track->sectors)
all_sectors.insert(sector);
all_sectors = collect_sectors(all_sectors);
diskflux->image = std::make_shared<Image>(all_sectors);
if (config.has_sector_mapping())
diskflux->image = std::move(Mapper::remapPhysicalToLogical(*diskflux->image, config.sector_mapping()));
if (config.has_sector_mapping())
diskflux->image = std::move(Mapper::remapSectorsPhysicalToLogical(
*diskflux->image, config.sector_mapping()));
/* diskflux can't be modified below this point. */
Logger() << DiskReadLogMessage { diskflux };
return diskflux;
/* diskflux can't be modified below this point. */
Logger() << DiskReadLogMessage{diskflux};
return diskflux;
}
void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWriter& writer)
void readDiskCommand(
FluxSource& fluxsource, AbstractDecoder& decoder, ImageWriter& writer)
{
auto diskflux = readDiskCommand(fluxsource, decoder);
auto diskflux = readDiskCommand(fluxsource, decoder);
writer.printMap(*diskflux->image);
if (config.decoder().has_write_csv_to())
@@ -258,14 +271,14 @@ void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWrit
void rawReadDiskCommand(FluxSource& fluxsource, FluxSink& fluxsink)
{
for (int cylinder : iterate(config.cylinders()))
{
for (int head : iterate(config.heads()))
{
testForEmergencyStop();
auto fluxsourceIterator = fluxsource.readFlux(cylinder, head);
auto fluxmap = readFluxmap(*fluxsourceIterator, cylinder, head);
fluxsink.writeFlux(cylinder, head, *fluxmap);
}
for (int cylinder : iterate(config.cylinders()))
{
for (int head : iterate(config.heads()))
{
testForEmergencyStop();
auto fluxsourceIterator = fluxsource.readFlux(cylinder, head);
auto fluxmap = readFluxmap(*fluxsourceIterator, cylinder, head);
fluxsink.writeFlux(cylinder, head, *fluxmap);
}
}
}

View File

@@ -55,14 +55,14 @@ Sector::Status Sector::stringToStatus(const std::string& value)
return Status::INTERNAL_ERROR;
}
bool sectorPointerSortPredicate(
std::shared_ptr<const Sector>& lhs, std::shared_ptr<const Sector>& rhs)
bool sectorPointerSortPredicate(const std::shared_ptr<const Sector>& lhs,
const std::shared_ptr<const Sector>& rhs)
{
return *lhs < *rhs;
}
bool sectorPointerEqualsPredicate(
std::shared_ptr<const Sector>& lhs, std::shared_ptr<const Sector>& rhs)
bool sectorPointerEqualsPredicate(const std::shared_ptr<const Sector>& lhs,
const std::shared_ptr<const Sector>& rhs)
{
if (!lhs && !rhs)
return true;

View File

@@ -66,8 +66,10 @@ public:
};
extern bool sectorPointerSortPredicate(
std::shared_ptr<const Sector>& lhs, std::shared_ptr<const Sector>& rhs);
const std::shared_ptr<const Sector>& lhs,
const std::shared_ptr<const Sector>& rhs);
extern bool sectorPointerEqualsPredicate(
std::shared_ptr<const Sector>& lhs, std::shared_ptr<const Sector>& rhs);
const std::shared_ptr<const Sector>& lhs,
const std::shared_ptr<const Sector>& rhs);
#endif

View File

@@ -7,6 +7,7 @@ message GreaseWeazleProto {
BUSTYPE_INVALID = 0;
IBMPC = 1;
SHUGART = 2;
APPLE2 = 3;
};
optional string port = 1

View File

@@ -9,6 +9,7 @@
#include "fluxsource/fluxsource.h"
#include "fluxsink/fluxsink.h"
#include "imagereader/imagereader.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
#include "sector.h"
#include "image.h"
@@ -17,129 +18,418 @@
#include "utils.h"
#include "lib/config.pb.h"
#include "proto.h"
#include <optional>
static std::set<std::shared_ptr<const Sector>> collectSectors(
std::set<std::shared_ptr<const Sector>>& track_sectors,
bool collapse_conflicts = true)
{
typedef std::tuple<unsigned, unsigned, unsigned> key_t;
std::multimap<key_t, std::shared_ptr<const Sector>> sectors;
for (const auto& sector : track_sectors)
{
key_t sectorid = {
sector->logicalTrack, sector->logicalSide, sector->logicalSector};
sectors.insert({sectorid, sector});
}
std::set<std::shared_ptr<const Sector>> sector_set;
auto it = sectors.begin();
while (it != sectors.end())
{
auto ub = sectors.upper_bound(it->first);
auto new_sector = std::accumulate(it,
ub,
it->second,
[&](auto left, auto& rightit) -> std::shared_ptr<const Sector>
{
auto& right = rightit.second;
if ((left->status == Sector::OK) &&
(right->status == Sector::OK) &&
(left->data != right->data))
{
if (!collapse_conflicts)
{
auto s = std::make_shared<Sector>(*right);
s->status = Sector::CONFLICT;
sector_set.insert(s);
}
auto s = std::make_shared<Sector>(*left);
s->status = Sector::CONFLICT;
return s;
}
if (left->status == Sector::CONFLICT)
return left;
if (right->status == Sector::CONFLICT)
return right;
if (left->status == Sector::OK)
return left;
if (right->status == Sector::OK)
return right;
return left;
});
sector_set.insert(new_sector);
it = ub;
}
return sector_set;
}
/* Returns true if the result contains bad sectors. */
bool combineRecordAndSectors(TrackFlux& trackFlux, AbstractDecoder& decoder)
{
std::set<std::shared_ptr<const Sector>> track_sectors;
for (auto& trackdataflux : trackFlux.trackDatas)
track_sectors.insert(
trackdataflux->sectors.begin(), trackdataflux->sectors.end());
for (unsigned logical_sector : decoder.requiredSectors(trackFlux.location))
{
auto sector = std::make_shared<Sector>();
sector->logicalSector = logical_sector;
sector->status = Sector::MISSING;
track_sectors.insert(sector);
}
trackFlux.sectors = collectSectors(track_sectors);
if (trackFlux.sectors.empty())
return true;
for (const auto& sector : trackFlux.sectors)
if (sector->status != Sector::OK)
return true;
return false;
}
/* Returns true if the result contains bad sectors. */
bool readGroup(FluxSource& fluxSource,
const Location& location,
TrackFlux& trackFlux,
AbstractDecoder& decoder)
{
for (unsigned offset = 0; offset < location.groupSize;
offset += config.drive().head_width())
{
auto fluxSourceIterator = fluxSource.readFlux(
location.physicalCylinder + offset, location.head);
Logger() << BeginReadOperationLogMessage{
location.physicalCylinder + offset, location.head};
std::shared_ptr<const Fluxmap> fluxmap =
fluxSourceIterator->next()->rescale(
1.0 / config.flux_source().rescale());
Logger() << EndReadOperationLogMessage()
<< fmt::format("{0:.0} ms in {1} bytes",
fluxmap->duration() / 1e6,
fluxmap->bytes());
auto trackdataflux = decoder.decodeToSectors(fluxmap, location);
trackFlux.trackDatas.push_back(trackdataflux);
if (!combineRecordAndSectors(trackFlux, decoder))
return false;
}
return true;
}
void writeTracks(FluxSink& fluxSink,
const std::function<std::unique_ptr<const Fluxmap>(int track, int side)> producer)
std::function<std::unique_ptr<const Fluxmap>(const Location& location)>
producer,
std::function<bool(const Location& location)> verifier)
{
for (unsigned cylinder : iterate(config.cylinders()))
Logger() << fmt::format("Writing to: {}", (std::string)fluxSink);
for (const auto& location : Mapper::computeLocations())
{
for (unsigned head : iterate(config.heads()))
testForEmergencyStop();
int retriesRemaining = config.decoder().retries();
for (;;)
{
testForEmergencyStop();
Logger() << BeginWriteOperationLogMessage{cylinder, head};
auto fluxmap = producer(cylinder, head);
if (!fluxmap)
for (unsigned offset = 0; offset < location.groupSize;
offset += config.drive().head_width())
{
/* Erase this track rather than writing. */
unsigned physicalCylinder = location.physicalCylinder + offset;
fluxmap.reset(new Fluxmap());
fluxSink.writeFlux(cylinder, head, *fluxmap);
Logger() << "erased";
Logger() << BeginWriteOperationLogMessage{physicalCylinder, location.head};
if (offset == 0)
{
auto fluxmap = producer(location);
if (!fluxmap)
goto erase;
auto scaled =
fluxmap->rescale(config.flux_sink().rescale());
/* Precompensation actually seems to make things worse, so
* let's leave it disabled for now. */
// fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS,
// 2);
fluxSink.writeFlux(physicalCylinder, location.head, *scaled);
Logger() << fmt::format("writing {0} ms in {1} bytes",
int(fluxmap->duration() / 1e6),
fluxmap->bytes());
}
else
{
erase:
/* Erase this track rather than writing. */
Fluxmap blank;
fluxSink.writeFlux(physicalCylinder, location.head, blank);
Logger() << "erased";
}
Logger() << EndWriteOperationLogMessage();
}
else
{
auto scaled = fluxmap->rescale(config.flux_sink().rescale());
/* Precompensation actually seems to make things worse, so let's
* leave it disabled for now. */
// fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS, 2);
fluxSink.writeFlux(cylinder, head, *scaled);
Logger() << fmt::format("{0} ms in {1} bytes",
int(fluxmap->duration() / 1e6),
fluxmap->bytes());
}
Logger() << EndWriteOperationLogMessage();
if (verifier(location))
break;
if (retriesRemaining == 0)
Error() << "fatal error on write";
Logger() << fmt::format(
"retrying; {} retries remaining", retriesRemaining);
retriesRemaining--;
}
}
}
static bool dontVerify(const Location&)
{
return true;
}
void writeTracks(
FluxSink& fluxSink, AbstractEncoder& encoder, const Image& image)
{
writeTracks(
fluxSink,
[&](const Location& location)
{
auto sectors = encoder.collectSectors(location, image);
return encoder.encode(location, sectors, image);
},
dontVerify);
}
void writeTracksAndVerify(FluxSink& fluxSink,
AbstractEncoder& encoder,
FluxSource& fluxSource,
AbstractDecoder& decoder,
const Image& image)
{
Logger() << fmt::format("Writing to: {}", (std::string)fluxSink);
writeTracks(
fluxSink,
[&](const Location& location)
{
auto sectors = encoder.collectSectors(location, image);
return encoder.encode(location, sectors, image);
},
[&](const Location& location)
{
auto trackFlux = std::make_shared<TrackFlux>();
readGroup(fluxSource, location, *trackFlux, decoder);
Logger() << TrackReadLogMessage{ trackFlux };
auto wantedSectors = encoder.collectSectors(location, image);
std::sort(wantedSectors.begin(), wantedSectors.end(),
sectorPointerSortPredicate);
std::vector<std::shared_ptr<const Sector>> gotSectors(
trackFlux->sectors.begin(), trackFlux->sectors.end());
std::sort(gotSectors.begin(), gotSectors.end(),
sectorPointerSortPredicate);
return std::equal(gotSectors.begin(),
gotSectors.end(),
wantedSectors.begin(),
wantedSectors.end(),
sectorPointerEqualsPredicate);
});
}
void writeDiskCommand(std::shared_ptr<const Image> image,
AbstractEncoder& encoder,
FluxSink& fluxSink,
AbstractDecoder* decoder,
FluxSource* fluxSource)
{
if (config.has_sector_mapping())
image = std::move(Mapper::remapSectorsLogicalToPhysical(
*image, config.sector_mapping()));
if (fluxSource && decoder)
writeTracksAndVerify(fluxSink, encoder, *fluxSource, *decoder, *image);
else
writeTracks(fluxSink, encoder, *image);
}
void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink)
{
writeTracks(
fluxSink,
[&](const Location& location)
{
return fluxSource
.readFlux(location.physicalCylinder, location.head)
->next();
},
dontVerify);
}
std::shared_ptr<const DiskFlux> readDiskCommand(
FluxSource& fluxSource, AbstractDecoder& decoder)
{
std::unique_ptr<FluxSink> outputFluxSink;
if (config.decoder().has_copy_flux_to())
outputFluxSink = FluxSink::create(config.decoder().copy_flux_to());
auto diskflux = std::make_shared<DiskFlux>();
bool failures = false;
for (const auto& location : Mapper::computeLocations())
{
testForEmergencyStop();
auto track = std::make_shared<TrackFlux>();
diskflux->tracks.push_back(track);
int retriesRemaining = config.decoder().retries();
for (;;)
{
if (!readGroup(fluxSource, location, *track, decoder))
break;
if (retriesRemaining == 0)
{
failures = true;
Logger() << fmt::format("giving up");
break;
}
Logger() << fmt::format(
"retrying; {} retries remaining", retriesRemaining);
retriesRemaining--;
}
if (outputFluxSink)
{
for (const auto& data : track->trackDatas)
outputFluxSink->writeFlux(
location.physicalCylinder, location.head, *data->fluxmap);
}
if (config.decoder().dump_records())
{
std::vector<std::shared_ptr<const Record>> sorted_records;
for (const auto& data : track->trackDatas)
sorted_records.insert(sorted_records.end(),
data->records.begin(),
data->records.end());
std::sort(sorted_records.begin(),
sorted_records.end(),
[](const auto& o1, const auto& o2)
{
return o1->startTime < o2->startTime;
});
std::cout << "\nRaw (undecoded) records follow:\n\n";
for (const auto& record : sorted_records)
{
std::cout << fmt::format("I+{:.2f}us with {:.2f}us clock\n",
record->startTime / 1000.0,
record->clock / 1000.0);
hexdump(std::cout, record->rawData);
std::cout << std::endl;
}
}
if (config.decoder().dump_sectors())
{
auto collected_sectors = collectSectors(track->sectors, false);
std::vector<std::shared_ptr<const Sector>> sorted_sectors(
collected_sectors.begin(), collected_sectors.end());
std::sort(sorted_sectors.begin(),
sorted_sectors.end(),
[](const auto& o1, const auto& o2)
{
return *o1 < *o2;
});
std::cout << "\nDecoded sectors follow:\n\n";
for (const auto& sector : sorted_sectors)
{
std::cout << fmt::format(
"{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock: "
"status {}\n",
sector->logicalTrack,
sector->logicalSide,
sector->logicalSector,
sector->headerStartTime / 1000.0,
sector->clock / 1000.0,
Sector::statusToString(sector->status));
hexdump(std::cout, sector->data);
std::cout << std::endl;
}
}
/* track can't be modified below this point. */
Logger() << TrackReadLogMessage{track};
}
if (failures)
Logger() << "Warning: some sectors could not be decoded.";
std::set<std::shared_ptr<const Sector>> all_sectors;
for (auto& track : diskflux->tracks)
for (auto& sector : track->sectors)
all_sectors.insert(sector);
all_sectors = collectSectors(all_sectors);
diskflux->image = std::make_shared<Image>(all_sectors);
if (config.has_sector_mapping())
diskflux->image = std::move(Mapper::remapSectorsPhysicalToLogical(
*diskflux->image, config.sector_mapping()));
/* diskflux can't be modified below this point. */
Logger() << DiskReadLogMessage{diskflux};
return diskflux;
}
void readDiskCommand(
FluxSource& fluxsource, AbstractDecoder& decoder, ImageWriter& writer)
{
auto diskflux = readDiskCommand(fluxsource, decoder);
writer.printMap(*diskflux->image);
if (config.decoder().has_write_csv_to())
writer.writeCsv(*diskflux->image, config.decoder().write_csv_to());
writer.writeImage(*diskflux->image);
}
void rawReadDiskCommand(FluxSource& fluxsource, FluxSink& fluxsink)
{
for (unsigned cylinder : iterate(config.cylinders()))
{
for (unsigned head : iterate(config.heads()))
{
testForEmergencyStop();
auto fluxSourceIterator = fluxsource.readFlux(cylinder, head);
auto sectors = encoder.collectSectors(cylinder, head, image);
std::unique_ptr<Fluxmap> fluxmap =
encoder.encode(cylinder, head, sectors, image);
if (!fluxmap)
{
/* Erase this track rather than writing. */
Logger() << BeginReadOperationLogMessage{cylinder, head};
auto fluxmap = fluxSourceIterator->next()->rescale(
1.0 / config.flux_source().rescale());
Logger() << EndReadOperationLogMessage()
<< fmt::format("{0:.0} ms in {1} bytes",
fluxmap->duration() / 1e6,
fluxmap->bytes());
Logger() << BeginWriteOperationLogMessage{cylinder, head};
fluxmap.reset(new Fluxmap());
fluxSink.writeFlux(cylinder, head, *fluxmap);
Logger() << EndWriteOperationLogMessage()
<< fmt::format("erased");
}
else
{
fluxmap->rescale(config.flux_sink().rescale());
std::sort(
sectors.begin(), sectors.end(), sectorPointerSortPredicate);
for (int retry = 0;; retry++)
{
/* Precompensation actually seems to make things worse, so
* let's leave it disabled for now. */
// fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS,
// 2);
Logger() << BeginWriteOperationLogMessage{cylinder, head};
fluxSink.writeFlux(cylinder, head, *fluxmap);
Logger() << EndWriteOperationLogMessage()
<< fmt::format("writing {0} ms in {1} bytes",
int(fluxmap->duration() / 1e6),
fluxmap->bytes());
Logger() << BeginReadOperationLogMessage{cylinder, head};
std::shared_ptr<const Fluxmap> writtenFluxmap = fluxSource.readFlux(cylinder, head)->next();
Logger() << EndReadOperationLogMessage()
<< fmt::format("verifying {0} ms in {1} bytes",
int(writtenFluxmap->duration() / 1e6),
writtenFluxmap->bytes());
const auto trackdata =
decoder.decodeToSectors(writtenFluxmap, cylinder, head);
std::vector<std::shared_ptr<const Sector>> gotSectors(
trackdata->sectors.begin(), trackdata->sectors.end());
gotSectors.erase(std::remove_if(gotSectors.begin(),
gotSectors.end(),
[](const auto& s)
{
return s->status != Sector::OK;
}),
gotSectors.end());
std::sort(gotSectors.begin(),
gotSectors.end(),
sectorPointerSortPredicate);
gotSectors.erase(std::unique(gotSectors.begin(),
gotSectors.end(),
sectorPointerEqualsPredicate),
gotSectors.end());
if (std::equal(gotSectors.begin(),
gotSectors.end(),
sectors.begin(),
sectors.end(),
sectorPointerEqualsPredicate))
break;
if (retry == config.decoder().retries())
Error() << "Write failed; uncorrectable error during "
"write.";
Logger() << "retrying";
}
}
fluxsink.writeFlux(cylinder, head, *fluxmap);
}
}
}
@@ -158,37 +448,3 @@ void fillBitmapTo(std::vector<bool>& bitmap,
}
}
}
void writeDiskCommand(
std::shared_ptr<const Image> image,
AbstractEncoder& encoder,
FluxSink& fluxSink,
AbstractDecoder* decoder,
FluxSource* fluxSource)
{
if (config.has_sector_mapping())
image = std::move(Mapper::remapLogicalToPhysical(*image, config.sector_mapping()));
if (fluxSource && decoder)
writeTracksAndVerify(fluxSink, encoder, *fluxSource, *decoder, *image);
else
writeTracks(fluxSink,
[&](int physicalTrack, int physicalSide) -> std::unique_ptr<Fluxmap>
{
const auto& sectors =
encoder.collectSectors(physicalTrack, physicalSide, *image);
if (sectors.empty())
return std::make_unique<Fluxmap>();
return encoder.encode(
physicalTrack, physicalSide, sectors, *image);
});
}
void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink)
{
writeTracks(fluxSink,
[&](int track, int side) -> std::unique_ptr<const Fluxmap>
{
return fluxSource.readFlux(track, side)->next();
});
}

View File

@@ -1,16 +1,17 @@
#ifndef WRITER_H
#define WRITER_H
class Fluxmap;
class AbstractDecoder;
class AbstractEncoder;
class ImageReader;
class FluxSource;
class FluxSink;
class FluxSource;
class Fluxmap;
class Image;
class ImageReader;
class Location;
extern void writeTracks(FluxSink& fluxSink,
const std::function<std::unique_ptr<const Fluxmap>(int track, int side)>
const std::function<std::unique_ptr<const Fluxmap>(const Location& location)>
producer);
extern void fillBitmapTo(std::vector<bool>& bitmap,

View File

@@ -477,7 +477,6 @@ buildlibrary libbackend.a \
lib/logger.cc \
lib/mapper.cc \
lib/proto.cc \
lib/reader.cc \
lib/sector.cc \
lib/usb/fluxengineusb.cc \
lib/usb/greaseweazle.cc \
@@ -489,13 +488,15 @@ buildlibrary libbackend.a \
lib/writer.cc \
FORMATS="\
40trackdrive \
acornadfs \
acorndfs \
agat840 \
aeslanier \
agat840 \
amiga \
ampro \
apple2 \
apple2drive \
appledos \
atarist360 \
atarist370 \
@@ -535,10 +536,11 @@ FORMATS="\
northstar87 \
prodos \
rx50 \
shugartdrive \
tids990 \
vgi \
victor9k_ss \
victor9k_ds \
victor9k_ss \
zilogmcz \
"

View File

@@ -0,0 +1,10 @@
comment: 'Adjust configuration for a 40-track drive'
is_extension: true
drive {
cylinders: 40
head_width: 1
tpi: 48
}

View File

@@ -30,8 +30,7 @@ encoder {
cylinders {
start: 0
end: 79
step: 2
end: 39
}
heads {
@@ -39,3 +38,5 @@ heads {
end: 0
}
tpi: 48

View File

@@ -0,0 +1,17 @@
comment: 'Adjust configuration for a 40-track Apple II drive'
is_extension: true
usb {
greaseweazle {
bus_type: APPLE2
}
}
drive {
cylinders: 160
heads: 1
head_width: 4
tpi: 48
}

View File

@@ -5,7 +5,6 @@ image_reader {
img {
tracks: 39
sides: 1
physical_step: 2
trackdata {
sector_size: 256
sector_range {
@@ -21,7 +20,6 @@ image_writer {
img {
tracks: 39
sides: 1
physical_step: 2
trackdata {
sector_size: 256
sector_range {
@@ -39,13 +37,16 @@ encoder {
}
decoder {
retries: 1
brother {}
}
drive {
head_bias: 1
}
cylinders {
start: 0
end: 77
end: 38
}
heads {
@@ -53,3 +54,4 @@ heads {
end: 0
}
tpi: 48

View File

@@ -38,6 +38,10 @@ decoder {
brother {}
}
drive {
head_bias: 1
}
cylinders {
start: 0
end: 77

View File

@@ -20,7 +20,7 @@ decoder {
cylinders {
start: 0
end: 79
end: 39
}
heads {
@@ -28,3 +28,5 @@ heads {
end: 0
}
tpi: 48

View File

@@ -5,7 +5,6 @@ image_reader {
img {
tracks: 40
sides: 1
physical_step: 2
trackdata {
sector_size: 512
sector_range {
@@ -54,7 +53,7 @@ decoder {
cylinders {
start: 0
end: 79
end: 39
}
heads {
@@ -62,3 +61,4 @@ heads {
end: 0
}
tpi: 48

View File

@@ -5,7 +5,6 @@ image_reader {
img {
tracks: 40
sides: 2
physical_step: 2
trackdata {
sector_size: 512
sector_range {
@@ -54,7 +53,7 @@ decoder {
cylinders {
start: 0
end: 79
end: 39
}
heads {
@@ -62,3 +61,5 @@ heads {
end: 1
}
tpi: 48

View File

@@ -0,0 +1,9 @@
comment: 'Adjust configuration for a Shugart drive'
is_extension: true
usb {
greaseweazle {
bus_type: SHUGART
}
}

View File

@@ -12,6 +12,7 @@
#include "decoders/decoders.h"
#include "lib/usb/usbfinder.h"
#include "fmt/format.h"
#include "mapper.h"
#include "utils.h"
#include "mainwindow.h"
#include <google/protobuf/text_format.h>
@@ -86,6 +87,7 @@ void MainWindow::OnReadFluxButton(wxCommandEvent&)
auto fluxSource = FluxSource::create(config.flux_source());
auto decoder = AbstractDecoder::create(config.decoder());
auto diskflux = readDiskCommand(*fluxSource, *decoder);
runOnUiThread(
[&]()
{

View File

@@ -158,7 +158,7 @@ void VisualisationControl::Clear()
void VisualisationControl::SetTrackData(std::shared_ptr<const TrackFlux> track)
{
key_t key = {track->physicalCylinder, track->physicalHead};
key_t key = {track->location.physicalCylinder, track->location.head};
_sectors.erase(key);
for (auto& sector : track->sectors)
_sectors.insert({key, sector});