Merge pull request #506 from davidgiven/logical

Rework to use logical track numbers throughout.
This commit is contained in:
David Given
2022-03-26 21:24:43 +00:00
committed by GitHub
133 changed files with 2596 additions and 2155 deletions

View File

@@ -37,11 +37,6 @@ FluxEngine features are available with the GreaseWeazle and it works out-of-the
box. See the [dedicated GreaseWeazle documentation page](doc/greaseweazle.md)
for more information.
**Important note.** On 2020-04-02 I changed the bytecode format (and firmware).
Flux files will need to be upgraded with `fluxengine upgradefluxfile`. The new
format should be more reliable and use way, way less bandwidth. Sorry for the
inconvenience.
Where?
------
@@ -66,14 +61,17 @@ following friendly articles:
flux files and image files ∾ knowing what you're doing
- [Using GreaseWeazle hardware with the FluxEngine client
software](doc/greaseweazle.md) ∾ what works ∾ what doesn't work ∾ where to
go for help
software](doc/greaseweazle.md) ∾ what works ∾ what doesn't work ∾ where to
go for help
- [Configuring for your drive](doc/drives.md) ∾ but I don't have a 80 track
drive! ∾ reading and writing 40 track disks ∾ Shugart and Apple II
- [Troubleshooting dubious disks](doc/problems.md) ∾ it's not an exact
science ∾ the sector map ∾ clock detection and the histogram
science ∾ the sector map ∾ clock detection and the histogram
- [Checking your drive](doc/driveresponse.md) ∾ you can't do that with that ∾
measuring your drive's ability to work with exotic formats
measuring your drive's ability to work with exotic formats
Which?
------

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

@@ -3,7 +3,7 @@
#include "encoders/encoders.h"
#include "amiga.h"
#include "crc.h"
#include "writer.h"
#include "readerwriter.h"
#include "image.h"
#include "arch/amiga/amiga.pb.h"
#include "lib/encoders/encoders.pb.h"
@@ -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.logicalTrack >= 0) &&
(location.logicalTrack < AMIGA_TRACKS_PER_DISK))
{
for (int sectorId = 0; sectorId < AMIGA_SECTORS_PER_TRACK;
sectorId++)
{
const auto& sector = image.get(
location.logicalTrack, 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

@@ -3,20 +3,22 @@
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "sector.h"
#include "writer.h"
#include "readerwriter.h"
#include "image.h"
#include "fmt/format.h"
#include "lib/encoders/encoders.pb.h"
#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.logicalTrack, 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

@@ -3,91 +3,95 @@
#include "encoders/encoders.h"
#include "brother.h"
#include "crc.h"
#include "writer.h"
#include "readerwriter.h"
#include "image.h"
#include "arch/brother/brother.pb.h"
#include "lib/encoders/encoders.pb.h"
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.logicalTrack >= BROTHER_TRACKS_PER_120KB_DISK)
return sectors;
break;
case BROTHER240:
if (location.logicalTrack >= 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.logicalTrack, 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

@@ -4,7 +4,7 @@
#include "c64.h"
#include "crc.h"
#include "sector.h"
#include "writer.h"
#include "readerwriter.h"
#include "image.h"
#include "fmt/format.h"
#include "arch/c64/c64.pb.h"
@@ -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.logicalTrack);
for (int sectorId=0; sectorId<numSectors; sectorId++)
{
const auto& sector = image.get(logicalTrack, 0, sectorId);
const auto& sector = image.get(location.logicalTrack, 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.logicalTrack) * _config.clock_compensation_factor();
int bitsPerRevolution = 200000.0 / clockRateUs;

View File

@@ -139,7 +139,7 @@ public:
bw += decodeFmMfm(bits).slice(0, IBM_IDAM_LEN);
IbmDecoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, _sector->physicalCylinder, _sector->physicalHead);
getTrackFormat(trackdata, _sector->physicalTrack, _sector->physicalHead);
_sector->logicalTrack = br.read_8();
_sector->logicalSide = br.read_8();
@@ -155,7 +155,7 @@ public:
if (trackdata.ignore_side_byte())
_sector->logicalSide = _sector->physicalHead;
if (trackdata.ignore_track_byte())
_sector->logicalTrack = _sector->physicalCylinder;
_sector->logicalTrack = _sector->physicalTrack;
for (int sector : trackdata.ignore_sector())
if (_sector->logicalSector == sector)
@@ -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.logicalTrack, location.head);
std::set<unsigned> s;
if (trackdata.has_sectors())
@@ -227,12 +227,12 @@ public:
}
private:
void getTrackFormat(IbmDecoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head) const
void getTrackFormat(IbmDecoderProto::TrackdataProto& trackdata, unsigned track, unsigned head) const
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_cylinder() && (f.cylinder() != cylinder))
if (f.has_track() && (f.track() != track))
continue;
if (f.has_head() && (f.head() != head))
continue;

View File

@@ -3,7 +3,7 @@
#include "encoders/encoders.h"
#include "ibm.h"
#include "crc.h"
#include "writer.h"
#include "readerwriter.h"
#include "image.h"
#include "arch/ibm/ibm.pb.h"
#include "lib/encoders/encoders.pb.h"
@@ -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 track,
unsigned head)
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_track() && (f.track() != track))
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.logicalTrack, 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.logicalTrack, 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.logicalTrack, 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

@@ -19,7 +19,7 @@
struct IbmIdam
{
uint8_t id;
uint8_t cylinder;
uint8_t track;
uint8_t side;
uint8_t sector;
uint8_t sectorSize;

View File

@@ -13,7 +13,7 @@ message IbmDecoderProto {
optional int32 max_sector = 2 [(help) = "require these sectors to exist for a good read"];
}
optional int32 cylinder = 7 [(help) = "if set, the format applies only to this track"];
optional int32 track = 7 [(help) = "if set, the format applies only to this track"];
optional int32 head = 8 [(help) = "if set, the format applies only to this head"];
optional bool ignore_side_byte = 2 [default = false, (help) = "ignore side byte in sector header"];
@@ -42,7 +42,7 @@ message IbmEncoderProto {
optional int32 max_sector = 2 [(help) = "write these sectors (in order) on each track"];
}
optional int32 cylinder = 15 [(help) = "if set, the format applies only to this track"];
optional int32 track = 15 [(help) = "if set, the format applies only to this track"];
optional int32 head = 16 [(help) = "if set, the format applies only to this head"];
optional double track_length_ms = 1 [(help) = "length of track"];

View File

@@ -144,7 +144,7 @@ public:
auto header = toBytes(readRawBits(7*8)).slice(0, 7);
uint8_t encodedTrack = decode_data_gcr(header[0]);
if (encodedTrack != (_sector->physicalCylinder & 0x3f))
if (encodedTrack != (_sector->physicalTrack & 0x3f))
return;
uint8_t encodedSector = decode_data_gcr(header[1]);
@@ -155,7 +155,7 @@ public:
if (encodedSector > 11)
return;
_sector->logicalTrack = _sector->physicalCylinder;
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalSide = decode_side(encodedSide);
_sector->logicalSector = encodedSector;
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
@@ -183,16 +183,18 @@ 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 track = location.logicalTrack;
int count;
if (cylinder < 16)
if (track < 16)
count = 12;
else if (cylinder < 32)
else if (track < 32)
count = 11;
else if (cylinder < 48)
else if (track < 48)
count = 10;
else if (cylinder < 64)
else if (track < 64)
count = 9;
else
count = 8;

View File

@@ -3,7 +3,7 @@
#include "encoders/encoders.h"
#include "macintosh.h"
#include "crc.h"
#include "writer.h"
#include "readerwriter.h"
#include "image.h"
#include "fmt/format.h"
#include "lib/encoders/encoders.pb.h"
@@ -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.logicalTrack >= 0) &&
(location.logicalTrack < MAC_TRACKS_PER_DISK))
{
unsigned numSectors = sectorsForTrack(physicalTrack);
for (int sectorId=0; sectorId<numSectors; sectorId++)
unsigned numSectors = sectorsForTrack(location.logicalTrack);
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
const auto& sector = image.get(
location.logicalTrack, 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.logicalTrack) *
_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

@@ -133,7 +133,7 @@ public:
return;
if (_sector->logicalTrack > 76)
return;
if (_sector->logicalTrack != _sector->physicalCylinder)
if (_sector->logicalTrack != _sector->physicalTrack)
return;
br.read(10); /* OS data or padding */
@@ -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.logicalTrack >= 0) && (location.logicalTrack < 77))
{
for (int sectorId = 0; sectorId < 16; sectorId++)
{
const auto& sector = image.get(
location.logicalTrack, 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

@@ -65,7 +65,7 @@ public:
gotChecksum += br.read_be16();
uint16_t wantChecksum = br.read_be16();
_sector->logicalTrack = _sector->physicalCylinder;
_sector->logicalTrack = _sector->physicalTrack;
_sector->logicalSide = _sector->physicalHead;
_sector->logicalSector = _currentSector;
_sector->data = bytes.slice(0, SECTOR_SIZE).swab();

View File

@@ -152,7 +152,7 @@ public:
_sector->logicalSide = _sector->physicalHead;
_sector->logicalSector = _hardSectorId;
_sector->logicalTrack = _sector->physicalCylinder;
_sector->logicalTrack = _sector->physicalTrack;
if (headerSize == NORTHSTAR_HEADER_SIZE_DD) {
br.read_8(); /* MFM second Sync char, usually 0xFB */
@@ -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.logicalTrack >= 0) && (location.logicalTrack < 35))
{
for (int sectorId = 0; sectorId < 10; sectorId++)
{
const auto& sector = image.get(
location.logicalTrack, 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

@@ -3,7 +3,7 @@
#include "encoders/encoders.h"
#include "tids990.h"
#include "crc.h"
#include "writer.h"
#include "readerwriter.h"
#include "image.h"
#include "arch/tids990/tids990.pb.h"
#include "lib/encoders/encoders.pb.h"
@@ -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.logicalTrack, 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

@@ -4,7 +4,7 @@
#include "victor9k.h"
#include "crc.h"
#include "sector.h"
#include "writer.h"
#include "readerwriter.h"
#include "image.h"
#include "fmt/format.h"
#include "arch/victor9k/victor9k.pb.h"
@@ -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 track,
unsigned head)
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_min_track() && (track < f.min_track()))
continue;
if (f.has_max_track() && (track > f.max_track()))
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.logicalTrack, 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.logicalTrack, 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.logicalTrack, 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

@@ -12,12 +12,12 @@ message Victor9kEncoderProto {
optional int32 sector_count = 2 [(help) = "number of sectors on track"];
}
optional int32 min_cylinder = 1 [(help) = "minimum cylinder this format applies to"];
optional int32 max_cylinder = 2 [(help) = "maximum cylinder this format applies to"];
optional int32 min_track = 1 [(help) = "minimum track this format applies to"];
optional int32 max_track = 2 [(help) = "maximum track this format applies to"];
optional int32 head = 3 [(help) = "which head this format applies to"];
optional double original_period_ms = 4 [(help) = "original rotational period of this cylinder"];
optional double original_data_rate_khz = 5 [(help) = "original data rate of this cylinder"];
optional double original_period_ms = 4 [(help) = "original rotational period of this track"];
optional double original_data_rate_khz = 5 [(help) = "original data rate of this track"];
optional double post_index_gap_us = 6 [(help) = "size of post-index gap"];
optional int32 pre_header_sync_bits = 10 [(help) = "number of sync bits before the sector header"];
optional int32 pre_data_sync_bits = 8 [(help) = "number of sync bits before the sector data"];

90
doc/drives.md Normal file
View File

@@ -0,0 +1,90 @@
Configuring for your drive
==========================
By default, the FluxEngine client assumes you have a PC 80 track double sided
high density drive, either 3.5" or 5.25", as these are the most common. This
may not be the case, and you need to tell the FluxEngine client what kind of
drive you have.
Forty track formats on an eighty track drive
--------------------------------------------
Forty-track drives have the same geometry as eighty-track drives, but have a
head that is twice as big, so halving the number of tracks on the disk.
Examples of forty track drives include the Commodore 1541, IBM 360kB, or the
Brother 120kB format (which uses a rare 3.5" single-sided forty-track drive).
When a forty-track disk is inserted into an eighty-track drive, then each head
position will only see _half_ of each track. For reading this isn't a problem
--- FluxEngine will actually read both halves and combine the results --- but
writing is more problematic. Traditionally, if you wanted to write a
forty-track disk in an eighty-track drive, you had to use a brand new disk; the
drive would write to one half of the track, leaving the other half blank. If
both halves contained data, then the wider head on a forty track drive would
pick both up, producing a garbled result. This led to a very confusing
situation where forty-track disks written on an eighty-track drive would read
and write fine on an eight-track drive but wouldn't work at all on a
forty-track drive.
FluxEngine is capable of both reading and writing forty-track formats on an
eighty-track drive. It avoids the situation described above by writing one half
of the track and then magnetically erasing the other half. This does produce a
weaker signal on the disk, but in my testing the disks work just fine in
forty-track drives.
Forty track formats on a forty track drive
------------------------------------------
If you actually have a forty track drive, you need to tell FluxEngine. This is
done by adding the special profile `40track_drive`:
```
fluxengine write ibm360_525 40track_drive -i image.img -d drive:0
```
It should then Just Work. This is supported by both FluxEngine and GreaseWeazle
hardware.
Obviously you can't write an eighty-track format using a forty-track drive!
Apple II drives
---------------
The Apple II had special drives which supported microstepping: when commanded
to move the head, then instead of moving in single-track steps as is done in
most other drives, the Apple II drive would move in quarter-track steps. This
allowed much less precise head alignment, as small errors could be corrected in
software. (The Brother word processor drives were similar.) The bus interface
is different from normal PC drives.
The FluxEngine client supports these with the `apple2_drive` profile:
```
fluxengine write apple2 apple2_drive -i image.img -d drive:0
```
This is supported only by GreaseWeazle hardware.
Shugart drives
--------------
PC drives have a standard interface which doesn't really have a name but is
commonly referred to as 'the PC 34-pin interface'. There are a few other
interfaces, most notably the Shugart standard. This is also 34 pin and is very
similar to the PC interface but isn't quite electrically compatible. It
supports up to four drives on a bus, unlike the PC interface's two drives, but
the drives must be jumpered to configure them. This was mostly used by older
3.5" drives, such as those on the Atari ST. [the How It Works
page](technical.md) for the pinout.
The FluxEngine client supports these with the `shugart_drive` profile:
```
fluxengine write atarist720 shugart_drive -i image.img -d drive:0
```
(If you have a 40-track Shugart drive, use _both_ `shugart_drive` and
`40track_drive`.)
This is supported only by GreaseWeazle hardware.

View File

@@ -88,6 +88,54 @@ GreaseWeazle hardware interchangeably with FluxEngine hardware. See the
[dedicated page](greaseweazle.md) for more information.
## Drive interface pinouts
For reference, here are the FDC pinouts:
```ditaa
:-E -s 0.75
+--+--+ +--+--+
DISKCHG ---+34+33+ DISKCHG ---+34+33+
+--+--+ +--+--+
SIDE1 ---+32+31+ SIDE1 ---+32+31+
+--+--+ +--+--+
RDATA ---+30+29+ RDATA ---+30+29+
+--+--+ +--+--+
WPT ---+28+27+ WPT ---+28+27+
+--+--+ +--+--+
TRK00 ---+26+25+ TRK00 ---+26+25+
+--+--+ +--+--+
WGATE ---+24+23+ WGATE ---+24+23+
+--+--+ +--+--+
WDATA ---+22+21+ WDATA ---+22+21+
+--+--+ +--+--+
STEP ---+20+19+ STEP ---+20+19+
+--+--+ +--+--+
DIR/SIDE1 ---+18+17+ DIR/SIDE1 ---+18+17+
+--+--+ +--+--+
MOTEB ---+16+15+ MOTEB ---+16+15+
+--+--+ +--+--+
DRVSA ---+14+13+ DS3 ---+14+13+
+--+--+ +--+--+
DRVSB ---+12+11+ DS2 ---+12+11+
+--+--+ +--+--+
MOTEA ---+10+9 + DS1 ---+10+9 +
+--+--+ +--+--+
INDEX ---+8 +7 + INDEX ---+8 +7 +
+--+--+ +--+--+
n/c ---+6 +5 + DS4 ---+6 +5 +
+--+--+ +--+--+
n/c ---+4 +3 + INU ---+4 +3 +
+--+--+ +--+--+
REDWC ---+2 +1 + REDWC ---+2 +1 +
+--+--+ +--+--+
PC interface Shugart interface
Odd numbered pins are always grounded
```
### Some useful links
- [The floppy disk user's

View File

@@ -66,12 +66,12 @@ Configurations can be specified either on the command line or in text files.
Here are some sample invocations:
```
# Read an PC disk, producing a disk image with the default name (ibm.img),
# autodetecting all parameters
$ fluxengine read ibm
# Read an PC 1440kB disk, producing a disk image with the default name
# (ibm.img)
$ fluxengine read ibm1440
# Write a PC 1440kB disk to drive 1
$ fluxengine write ibm -i image.img -d drive:1
$ fluxengine write ibm1440 -i image.img -d drive:1
# Read a Eco1 CP/M disk, making a copy of the flux into a file
$ fluxengine read eco1 --copy-flux-to copy.flux -o eco1.ldbs
@@ -108,8 +108,9 @@ protobuf syntax](https://developers.google.com/protocol-buffers), which is
hierarchical, type-safe, and easy to read.
The `ibm1440` string above is actually a reference to an internal configuration
file containing all the settings for writing PC 1440kB disks. You can see all
these settings by doing:
file containing all the settings for writing PC 1440kB disks. You may specify
as many profile names or textpb files as you wish; they are all merged left to
right. You can see all these settings by doing:
```
$ fluxengine write ibm1440 --config
@@ -404,7 +405,7 @@ containing valuable historical data, and you want to read them.
Typically I do this:
```
$ fluxengine read brother -s drive:0 -o brother.img --copy-flux-to=brother.flux --decoder.write_csv_to=brother.csv
$ fluxengine read brother240 -s drive:0 -o brother.img --copy-flux-to=brother.flux --decoder.write_csv_to=brother.csv
```
This will read the disk in drive 0 and write out an information CSV file. It'll

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;
@@ -27,8 +27,9 @@ message ConfigProto {
optional DecoderProto decoder = 4;
optional UsbProto usb = 5;
optional RangeProto cylinders = 6;
optional RangeProto tracks = 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->physicalTrack = location.physicalTrack;
_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 tracks = 7
[ default = 81, (help) = "Number of tracks 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 tracks)"
];
optional int32 head_width = 11
[ default = 1, (help) = "Width of the head (in tracks)" ];
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

@@ -10,7 +10,7 @@ enum FluxFileVersion {
}
message TrackFluxProto {
optional int32 cylinder = 1;
optional int32 track = 1;
optional int32 head = 2;
repeated bytes flux = 3;
}

View File

@@ -1,6 +1,9 @@
#ifndef FLUX_H
#define FLUX_H
#include "bytes.h"
class Fluxmap;
class Sector;
class Image;
@@ -12,10 +15,33 @@ struct Record
Bytes rawData;
};
struct Location
{
unsigned physicalTrack;
unsigned logicalTrack;
unsigned head;
unsigned groupSize;
bool operator==(const Location& other) const
{
if (physicalTrack == other.physicalTrack)
return true;
return head == other.head;
}
bool operator<(const Location& other) const
{
if (physicalTrack < other.physicalTrack)
return true;
if (physicalTrack == other.physicalTrack)
return head < other.head;
return false;
}
};
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 +49,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

@@ -26,14 +26,14 @@ public:
}
public:
void writeFlux(int cylinder, int head, const Fluxmap& fluxmap) override
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
{
unsigned totalTicks = fluxmap.ticks() + 2;
unsigned channels = _config.index_markers() ? 2 : 1;
mkdir(_config.directory().c_str(), 0744);
std::ofstream of(
fmt::format("{}/c{:02d}.h{:01d}.au", _config.directory(), cylinder, head),
fmt::format("{}/c{:02d}.h{:01d}.au", _config.directory(), track, head),
std::ios::out | std::ios::binary);
if (!of.is_open())
Error() << "cannot open output file";

View File

@@ -37,7 +37,7 @@ public:
for (const auto& e : _data)
{
auto track = proto.add_track();
track->set_cylinder(e.first.first);
track->set_track(e.first.first);
track->set_head(e.first.second);
for (const auto& fluxBytes : e.second)
track->add_flux(fluxBytes);
@@ -51,9 +51,9 @@ public:
}
public:
void writeFlux(int cylinder, int head, const Fluxmap& fluxmap) override
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
{
auto& vector = _data[std::make_pair(cylinder, head)];
auto& vector = _data[std::make_pair(track, head)];
vector.push_back(fluxmap.rawBytes());
}

View File

@@ -47,8 +47,8 @@ public:
_fileheader.file_id[2] = 'P';
_fileheader.version = 0x18; /* Version 1.8 of the spec */
_fileheader.type = _config.type_byte();
_fileheader.start_track = strackno(config.cylinders().start(), config.heads().start());
_fileheader.end_track = strackno(config.cylinders().end(), config.heads().end());
_fileheader.start_track = strackno(config.tracks().start(), config.heads().start());
_fileheader.end_track = strackno(config.tracks().end(), config.heads().end());
_fileheader.flags = SCP_FLAG_INDEXED
| SCP_FLAG_96TPI;
_fileheader.cell_width = 0;
@@ -80,16 +80,16 @@ public:
}
public:
void writeFlux(int cylinder, int head, const Fluxmap& fluxmap) override
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
{
ByteWriter trackdataWriter(_trackdata);
trackdataWriter.seekToEnd();
int strack = strackno(cylinder, head);
int strack = strackno(track, head);
if (strack >= std::size(_fileheader.track)) {
std::cout << fmt::format("SCP: cannot write track {} head {}, "
"there are not not enough Track Data Headers.\n",
cylinder, head);
track, head);
return;
}
ScpTrack trackheader = {0};

View File

@@ -20,11 +20,11 @@ public:
{}
public:
void writeFlux(int cylinder, int head, const Fluxmap& fluxmap) override
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
{
mkdir(_config.directory().c_str(), 0744);
std::ofstream of(
fmt::format("{}/c{:02d}.h{:01d}.vcd", _config.directory(), cylinder, head),
fmt::format("{}/c{:02d}.h{:01d}.vcd", _config.directory(), track, head),
std::ios::out | std::ios::binary);
if (!of.is_open())
Error() << "cannot open output file";

View File

@@ -14,10 +14,10 @@ struct CwfHeader
uint8_t version; // version of this file
uint8_t clock_rate; // clock rate used: 0, 1, 2 (2 = 28MHz)
uint8_t drive_type; // type of drive
uint8_t cylinders; // number of cylinders
uint8_t tracks; // number of tracks
uint8_t sides; // number of sides
uint8_t index_mark; // nonzero if index marks are included
uint8_t step; // cylinder stepping interval
uint8_t step; // track stepping interval
uint8_t filler[15]; // reserved for expansion
uint8_t comment[100]; // brief comment
};
@@ -58,11 +58,11 @@ public:
Error() << "unsupported clock rate";
}
std::cout << fmt::format("CWF {}x{} = {} cylinders, {} sides\n",
_header.cylinders, _header.step, _header.cylinders*_header.step, _header.sides);
std::cout << fmt::format("CWF {}x{} = {} tracks, {} sides\n",
_header.tracks, _header.step, _header.tracks*_header.step, _header.sides);
std::cout << fmt::format("CWF sample clock rate: {} MHz\n", _clockRate / 1e6);
int tracks = _header.cylinders*_header.sides;
int tracks = _header.tracks*_header.sides;
for (int i=0; i<tracks; i++)
{
CwfTrack trackheader;

View File

@@ -62,12 +62,12 @@ public:
}
public:
std::unique_ptr<FluxSourceIterator> readFlux(int cylinder, int head) override
std::unique_ptr<FluxSourceIterator> readFlux(int track, int head) override
{
for (const auto& track : _proto.track())
for (const auto& trackFlux : _proto.track())
{
if ((track.cylinder() == cylinder) && (track.head() == head))
return std::make_unique<Fl2FluxSourceIterator>(track);
if ((trackFlux.track() == track) && (trackFlux.head() == head))
return std::make_unique<Fl2FluxSourceIterator>(trackFlux);
}
return std::make_unique<EmptyFluxSourceIterator>();

View File

@@ -76,9 +76,9 @@ void FluxSource::updateConfigForFilename(FluxSourceProto* proto, const std::stri
class TrivialFluxSourceIterator : public FluxSourceIterator
{
public:
TrivialFluxSourceIterator(TrivialFluxSource* fluxSource, int cylinder, int head):
TrivialFluxSourceIterator(TrivialFluxSource* fluxSource, int track, int head):
_fluxSource(fluxSource),
_cylinder(cylinder),
_track(track),
_head(head)
{}
@@ -89,20 +89,20 @@ public:
std::unique_ptr<const Fluxmap> next() override
{
auto fluxmap = _fluxSource->readSingleFlux(_cylinder, _head);
auto fluxmap = _fluxSource->readSingleFlux(_track, _head);
_fluxSource = nullptr;
return fluxmap;
}
private:
TrivialFluxSource* _fluxSource;
int _cylinder;
int _track;
int _head;
};
std::unique_ptr<FluxSourceIterator> TrivialFluxSource::readFlux(int cylinder, int head)
std::unique_ptr<FluxSourceIterator> TrivialFluxSource::readFlux(int track, int head)
{
return std::make_unique<TrivialFluxSourceIterator>(this, cylinder, head);
return std::make_unique<TrivialFluxSourceIterator>(this, track, head);
}

View File

@@ -15,9 +15,9 @@ private:
{
public:
HardwareFluxSourceIterator(
const HardwareFluxSource& fluxsource, int cylinder, int head):
const HardwareFluxSource& fluxsource, int track, int head):
_fluxsource(fluxsource),
_cylinder(cylinder),
_track(track),
_head(head)
{
}
@@ -30,7 +30,7 @@ private:
std::unique_ptr<const Fluxmap> next()
{
usbSetDrive(config.drive().drive(), config.drive().high_density(), config.drive().index_mode());
usbSeek(_cylinder);
usbSeek(_track);
Bytes data = usbRead(_head,
config.drive().sync_with_index(),
@@ -43,7 +43,7 @@ private:
private:
const HardwareFluxSource& _fluxsource;
int _cylinder;
int _track;
int _head;
};
@@ -76,10 +76,10 @@ public:
~HardwareFluxSource() {}
public:
std::unique_ptr<FluxSourceIterator> readFlux(int cylinder, int head) override
std::unique_ptr<FluxSourceIterator> readFlux(int track, int head) override
{
return std::make_unique<HardwareFluxSourceIterator>(
*this, cylinder, head);
*this, track, head);
}
void recalibrate() override

View File

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

View File

@@ -5,6 +5,7 @@
#include "fmt/format.h"
#include "image.h"
#include "logger.h"
#include "mapper.h"
#include "proto.h"
#include <algorithm>
#include <iostream>
@@ -32,11 +33,11 @@ public:
data.writer() += inputFile;
ByteReader br(data);
unsigned numCylinders = 39;
unsigned numTracks = 39;
unsigned numHeads = 1;
unsigned numSectors = 0;
Logger() << fmt::format("D64: reading image with {} cylinders, {} heads", numCylinders, numHeads);
Logger() << fmt::format("D64: reading image with {} tracks, {} heads", numTracks, numHeads);
uint32_t offset = 0;
@@ -55,7 +56,7 @@ public:
for (int track = 0; track < 40; track++)
{
int numSectors = sectorsPerTrack(track);
int physicalCylinder = track * 2;
int physicalTrack = Mapper::remapTrackLogicalToPhysical(track);
for (int head = 0; head < numHeads; head++)
{
for (int sectorId = 0; sectorId < numSectors; sectorId++)
@@ -69,7 +70,7 @@ public:
sector->status = Sector::OK;
sector->logicalTrack = track;
sector->physicalCylinder = physicalCylinder;
sector->physicalTrack = physicalTrack;
sector->logicalSide = sector->physicalHead = head;
sector->logicalSector = sectorId;
sector->data.writer().append(payload);
@@ -79,7 +80,7 @@ public:
// DATA_MISSING
sector->status = Sector::DATA_MISSING;
sector->logicalTrack = track;
sector->physicalCylinder = physicalCylinder;
sector->physicalTrack = physicalTrack;
sector->logicalSide = sector->physicalHead = head;
sector->logicalSector = sectorId;
}

View File

@@ -5,6 +5,7 @@
#include "image.h"
#include "proto.h"
#include "logger.h"
#include "mapper.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
#include <algorithm>
@@ -62,21 +63,23 @@ public:
Logger() << "D88: overriding configured format";
auto ibm = config.mutable_encoder()->mutable_ibm();
int physicalStep = 1;
int clockRate = 500;
if (mediaFlag == 0x20)
{
Logger() << "D88: high density mode";
if (!config.drive().has_drive())
config.mutable_drive()->set_high_density(true);
if (!config.has_tpi())
config.set_tpi(96);
}
else
{
Logger() << "D88: single/double density mode";
physicalStep = 2;
clockRate = 300;
if (!config.drive().has_drive())
config.mutable_drive()->set_high_density(false);
if (!config.has_tpi())
config.set_tpi(48);
}
std::unique_ptr<Image> image(new Image);
@@ -87,7 +90,7 @@ public:
continue;
int currentTrackOffset = trackOffset;
int currentTrackCylinder = -1;
int currentTrackTrack = -1;
int currentSectorsInTrack =
0xffff; // don't know # of sectors until we read the first one
int trackSectorSize = -1;
@@ -105,7 +108,7 @@ public:
inputFile.read(
(char*)sectorHeader.begin(), sectorHeader.size());
ByteReader sectorHeaderReader(sectorHeader);
int cylinder = sectorHeaderReader.seek(0).read_8();
int track = sectorHeaderReader.seek(0).read_8();
int head = sectorHeaderReader.seek(1).read_8();
int sectorId = sectorHeaderReader.seek(2).read_8();
int sectorSize = 128 << sectorHeaderReader.seek(3).read_8();
@@ -132,21 +135,21 @@ public:
{
Error() << "D88: mismatched number of sectors in track";
}
if (currentTrackCylinder < 0)
if (currentTrackTrack < 0)
{
currentTrackCylinder = cylinder;
currentTrackTrack = track;
}
else if (currentTrackCylinder != cylinder)
else if (currentTrackTrack != track)
{
Error() << "D88: all sectors in a track must belong to the "
"same cylinder";
"same track";
}
if (trackSectorSize < 0)
{
trackSectorSize = sectorSize;
// this is the first sector we've read, use it settings for
// per-track data
trackdata->set_cylinder(cylinder * physicalStep);
trackdata->set_track(track);
trackdata->set_head(head);
trackdata->set_sector_size(sectorSize);
trackdata->set_use_fm(fm);
@@ -176,11 +179,10 @@ public:
}
Bytes data(sectorSize);
inputFile.read((char*)data.begin(), data.size());
const auto& sector =
image->put(cylinder * physicalStep, head, sectorId);
const auto& sector = image->put(track, head, sectorId);
sector->status = Sector::OK;
sector->logicalTrack = cylinder;
sector->physicalCylinder = cylinder * physicalStep;
sector->logicalTrack = track;
sector->physicalTrack = Mapper::remapTrackLogicalToPhysical(track);
sector->logicalSide = sector->physicalHead = head;
sector->logicalSector = sectorId;
sector->data = data;
@@ -188,7 +190,7 @@ public:
sectors->add_sector(sectorId);
}
if (physicalStep == 2)
if (mediaFlag != 0x20)
{
auto trackdata = ibm->add_trackdata();
trackdata->set_clock_rate_khz(clockRate);
@@ -209,11 +211,11 @@ public:
heads->set_end(geometry.numSides - 1);
}
if (!config.has_cylinders())
if (!config.has_tracks())
{
auto* cylinders = config.mutable_cylinders();
cylinders->set_start(0);
cylinders->set_end(geometry.numTracks - 1);
auto* tracks = config.mutable_tracks();
tracks->set_start(0);
tracks->set_end(geometry.numTracks - 1);
}
return image;

View File

@@ -4,6 +4,7 @@
#include "imagereader/imagereader.h"
#include "image.h"
#include "logger.h"
#include "mapper.h"
#include "proto.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
@@ -71,7 +72,6 @@ public:
{
if (inputFile.eof())
break;
int physicalCylinder = track;
for (int side = 0; side < 2; side++)
{
@@ -84,11 +84,10 @@ public:
Bytes data(sectorSize);
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->physicalTrack = Mapper::remapTrackLogicalToPhysical(track);
sector->logicalSide = sector->physicalHead = side;
sector->logicalSector = sectorId;
sector->data = data;
@@ -110,7 +109,7 @@ public:
case 0x00:
Logger() << "DIM: automatically setting format to 1.2MB "
"(1024 byte sectors)";
config.mutable_cylinders()->set_end(76);
config.mutable_tracks()->set_end(76);
trackdata->set_track_length_ms(167);
trackdata->set_sector_size(1024);
for (int i = 0; i < 9; i++)
@@ -156,11 +155,11 @@ public:
heads->set_end(geometry.numSides - 1);
}
if (!config.has_cylinders())
if (!config.has_tracks())
{
auto* cylinders = config.mutable_cylinders();
cylinders->set_start(0);
cylinders->set_end(geometry.numTracks - 1);
auto* tracks = config.mutable_tracks();
tracks->set_start(0);
tracks->set_end(geometry.numTracks - 1);
}
return image;

View File

@@ -4,6 +4,7 @@
#include "imagereader/imagereader.h"
#include "image.h"
#include "logger.h"
#include "mapper.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
#include <algorithm>
@@ -36,7 +37,7 @@ public:
uint8_t encoding = br.read_8();
uint8_t formatByte = br.read_8();
unsigned numCylinders = 80;
unsigned numTracks = 80;
unsigned numHeads = 2;
unsigned numSectors = 0;
bool mfm = false;
@@ -66,8 +67,8 @@ public:
}
Logger() << fmt::format(
"DC42: reading image with {} cylinders, {} heads; {}; {}",
numCylinders,
"DC42: reading image with {} tracks, {} heads; {}; {}",
numTracks,
numHeads,
mfm ? "MFM" : "GCR",
label);
@@ -92,7 +93,7 @@ public:
uint32_t tagPtr = dataPtr + dataSize;
std::unique_ptr<Image> image(new Image);
for (int track = 0; track < numCylinders; track++)
for (int track = 0; track < numTracks; track++)
{
int numSectors = sectorsPerTrack(track);
for (int head = 0; head < numHeads; head++)
@@ -109,7 +110,8 @@ public:
const auto& sector = image->put(track, head, sectorId);
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalCylinder = track;
sector->logicalTrack = track;
sector->physicalTrack = Mapper::remapTrackLogicalToPhysical(track);
sector->logicalSide = sector->physicalHead = head;
sector->logicalSector = sectorId;
sector->data.writer().append(payload).append(tag);
@@ -117,7 +119,7 @@ public:
}
}
image->setGeometry({.numTracks = numCylinders,
image->setGeometry({.numTracks = numTracks,
.numSides = numHeads,
.numSectors = 12,
.sectorSize = 512 + 12,

View File

@@ -5,6 +5,7 @@
#include "image.h"
#include "proto.h"
#include "logger.h"
#include "mapper.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
#include <algorithm>
@@ -50,7 +51,6 @@ public:
{
if (inputFile.eof())
break;
int physicalCylinder = track;
for (int side = 0; side < sides; side++)
{
@@ -64,10 +64,10 @@ public:
inputFile.read((char*)data.begin(), data.size());
const auto& sector =
image->put(physicalCylinder, side, sectorId);
image->put(track, side, sectorId);
sector->status = Sector::OK;
sector->logicalTrack = track;
sector->physicalCylinder = physicalCylinder;
sector->physicalTrack = Mapper::remapTrackLogicalToPhysical(track);
sector->logicalSide = sector->physicalHead = side;
sector->logicalSector = sectorId;
sector->data = data;
@@ -89,7 +89,7 @@ public:
case 0x90:
Logger() << "FDI: automatically setting format to 1.2MB "
"(1024 byte sectors)";
config.mutable_cylinders()->set_end(76);
config.mutable_tracks()->set_end(76);
trackdata->set_track_length_ms(167);
trackdata->set_sector_size(1024);
for (int i = 0; i < 9; i++)
@@ -125,11 +125,11 @@ public:
heads->set_end(geometry.numSides - 1);
}
if (!config.has_cylinders())
if (!config.has_tracks())
{
auto* cylinders = config.mutable_cylinders();
cylinders->set_start(0);
cylinders->set_end(geometry.numTracks - 1);
auto* tracks = config.mutable_tracks();
tracks->set_start(0);
tracks->set_end(geometry.numTracks - 1);
}
return image;

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

@@ -5,6 +5,7 @@
#include "image.h"
#include "proto.h"
#include "logger.h"
#include "mapper.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
#include <algorithm>
@@ -100,12 +101,12 @@ public:
1A byte - ASCII EOF character
- For each track on the disk:
1 byte Mode value see getModulationspeed for definition
1 byte Cylinder
1 byte Track
1 byte Head
1 byte number of sectors in track
1 byte sector size see getsectorsize for definition
sector numbering map
sector cylinder map (optional) definied in high byte of head (since head is 0 or 1)
sector track map (optional) definied in high byte of head (since head is 0 or 1)
sector head map (optional) definied in high byte of head (since head is 0 or 1)
sector data records
<End of file>
@@ -163,7 +164,7 @@ public:
headerPtr++;
sectorSize = getSectorSize(header.SectorSize);
//Read optional cylinder map To Do
//Read optional track map To Do
//Read optional sector head map To Do
@@ -218,7 +219,8 @@ public:
Error() << fmt::format("don't understand IMD disks with sector status {}", Status_Sector);
}
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalCylinder = header.track;
sector->logicalTrack = header.track;
sector->physicalTrack = Mapper::remapTrackLogicalToPhysical(header.track);
sector->logicalSide = sector->physicalHead = header.Head;
sector->logicalSector = (sector_map[s]);
}

View File

@@ -4,6 +4,7 @@
#include "imagereader/imagereader.h"
#include "image.h"
#include "logger.h"
#include "mapper.h"
#include "lib/config.pb.h"
#include "imginputoutpututils.h"
#include "fmt/format.h"
@@ -35,8 +36,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 +45,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->physicalTrack = Mapper::remapTrackLogicalToPhysical(track);
sector->logicalSide = sector->physicalHead = side;
sector->logicalSector = sectorId;
sector->data = data;

View File

@@ -3,6 +3,7 @@
#include "sector.h"
#include "imagereader/imagereader.h"
#include "image.h"
#include "mapper.h"
#include "logger.h"
#include "fmt/format.h"
#include "lib/config.pb.h"
@@ -123,8 +124,8 @@ public:
const auto& sector =
image->put(header.track, head, header.sector);
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalCylinder =
header.track;
sector->logicalTrack = header.track;
sector->physicalTrack = Mapper::remapTrackLogicalToPhysical(header.track);
sector->logicalSide = sector->physicalHead = head;
sector->logicalSector = header.sector;
sector->data = data;

View File

@@ -5,6 +5,7 @@
#include "image.h"
#include "proto.h"
#include "logger.h"
#include "mapper.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
#include <algorithm>
@@ -55,7 +56,7 @@ public:
Logger() << "NFD: overriding configured format";
auto ibm = config.mutable_encoder()->mutable_ibm();
config.mutable_cylinders()->set_end(0);
config.mutable_tracks()->set_end(0);
Logger() << "NFD: HD 1.2MB mode";
if (!config.drive().has_drive())
config.mutable_drive()->set_high_density(true);
@@ -67,14 +68,14 @@ public:
trackdata->set_clock_rate_khz(500);
trackdata->set_track_length_ms(167);
auto sectors = trackdata->mutable_sectors();
int currentTrackCylinder = -1;
int currentTrackTrack = -1;
int currentTrackHead = -1;
int trackSectorSize = -1;
for (int sectorInTrack = 0; sectorInTrack < 26; sectorInTrack++)
{
headerReader.seek(0x120 + track * 26 * 16 + sectorInTrack * 16);
int cylinder = headerReader.read_8();
int track = headerReader.read_8();
int head = headerReader.read_8();
int sectorId = headerReader.read_8();
int sectorSize = 128 << headerReader.read_8();
@@ -82,22 +83,22 @@ public:
int ddam = headerReader.read_8();
int status = headerReader.read_8();
headerReader.skip(9); // skip ST0, ST1, ST2, PDA, reserved(5)
if (cylinder == 0xFF)
if (track == 0xFF)
continue;
if (ddam != 0)
Error() << "NFD: nonzero ddam currently unsupported";
if (status != 0)
Error() << "NFD: nonzero fdd status codes are currently "
"unsupported";
if (currentTrackCylinder < 0)
if (currentTrackTrack < 0)
{
currentTrackCylinder = cylinder;
currentTrackTrack = track;
currentTrackHead = head;
}
else if (currentTrackCylinder != cylinder)
else if (currentTrackTrack != track)
{
Error() << "NFD: all sectors in a track must belong to the "
"same cylinder";
"same track";
}
else if (currentTrackHead != head)
{
@@ -109,7 +110,7 @@ public:
trackSectorSize = sectorSize;
// this is the first sector we've read, use it settings for
// per-track data
trackdata->set_cylinder(cylinder);
trackdata->set_track(track);
trackdata->set_head(head);
trackdata->set_sector_size(sectorSize);
trackdata->set_use_fm(!mfm);
@@ -139,17 +140,17 @@ public:
}
Bytes data(sectorSize);
inputFile.read((char*)data.begin(), data.size());
const auto& sector = image->put(cylinder, head, sectorId);
const auto& sector = image->put(track, head, sectorId);
sector->status = Sector::OK;
sector->logicalTrack = cylinder;
sector->physicalCylinder = cylinder;
sector->logicalTrack = track;
sector->physicalTrack = Mapper::remapTrackLogicalToPhysical(track);
sector->logicalSide = sector->physicalHead = head;
sector->logicalSector = sectorId;
sector->data = data;
sectors->add_sector(sectorId);
if (config.cylinders().end() < cylinder)
config.mutable_cylinders()->set_end(cylinder);
if (config.tracks().end() < track)
config.mutable_tracks()->set_end(track);
}
}

View File

@@ -7,6 +7,7 @@
#include "image.h"
#include "fmt/format.h"
#include "logger.h"
#include "mapper.h"
#include "lib/imagereader/imagereader.pb.h"
#include <algorithm>
#include <iostream>
@@ -32,7 +33,7 @@ public:
Logger() << fmt::format(
"NSI: Autodetecting geometry based on file size: {}", fsize);
unsigned numCylinders = 35;
unsigned numTracks = 35;
unsigned numSectors = 10;
unsigned numHeads = 2;
unsigned sectorSize = 512;
@@ -63,18 +64,18 @@ public:
Logger() << fmt::format(
"reading {} tracks, {} heads, {} sectors, {} bytes per sector, {} "
"kB total",
numCylinders,
numTracks,
numHeads,
numSectors,
sectorSize,
numCylinders * numHeads * trackSize / 1024);
numTracks * numHeads * trackSize / 1024);
std::unique_ptr<Image> image(new Image);
unsigned sectorFileOffset;
for (unsigned head = 0; head < numHeads; head++)
{
for (unsigned track = 0; track < numCylinders; track++)
for (unsigned track = 0; track < numTracks; track++)
{
for (unsigned sectorId = 0; sectorId < numSectors; sectorId++)
{
@@ -86,8 +87,8 @@ public:
else
{ /* Head 1 is from track 70-35 */
sectorFileOffset =
(trackSize * numCylinders) + /* Skip over side 0 */
((numCylinders - track - 1) * trackSize) +
(trackSize * numTracks) + /* Skip over side 0 */
((numTracks - track - 1) * trackSize) +
(sectorId * sectorSize); /* Sector offset from
beginning of track. */
}
@@ -99,7 +100,8 @@ public:
const auto& sector = image->put(track, head, sectorId);
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalCylinder = track;
sector->logicalTrack = track;
sector->physicalTrack = Mapper::remapTrackLogicalToPhysical(track);
sector->logicalSide = sector->physicalHead = head;
sector->logicalSector = sectorId;
sector->data = data;
@@ -107,7 +109,7 @@ public:
}
}
image->setGeometry({.numTracks = numCylinders,
image->setGeometry({.numTracks = numTracks,
.numSides = numHeads,
.numSectors = numSectors,
.sectorSize = sectorSize});

View File

@@ -5,6 +5,7 @@
#include "image.h"
#include "crc.h"
#include "logger.h"
#include "mapper.h"
#include "fmt/format.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
@@ -111,7 +112,7 @@ public:
if (sectorCount == 0xff)
break;
uint8_t physicalCylinder = br.read_8();
uint8_t physicalTrack = br.read_8();
uint8_t physicalHead = br.read_8() & 1;
br.skip(1); /* crc */
@@ -185,7 +186,7 @@ public:
const auto& sector =
image->put(logicalTrack, logicalSide, sectorId);
sector->status = Sector::OK;
sector->physicalCylinder = physicalCylinder;
sector->physicalTrack = physicalTrack;
sector->physicalHead = physicalHead;
sector->data = data.slice(0, sectorSize);
totalSize += sectorSize;

View File

@@ -97,7 +97,7 @@ void ImageWriter::writeCsv(const Image& image, const std::string& filename)
for (const auto& sector : image)
{
f << fmt::format("{},{},{},{},{},{},{},{},{},{},{},{},{}\n",
sector->physicalCylinder,
sector->physicalTrack,
sector->physicalHead,
sector->logicalTrack,
sector->logicalSide,

View File

@@ -31,7 +31,7 @@ public:
return;
}
Logger() << fmt::format("Writing {} cylinders, {} sides, {} sectors, {} ({} bytes/sector), {} kB total",
Logger() << fmt::format("Writing {} tracks, {} sides, {} sectors, {} ({} bytes/sector), {} kB total",
geometry.numTracks, geometry.numSides,
geometry.numSectors, geometry.sectorSize == 256 ? "SD" : "DD", geometry.sectorSize,
geometry.numTracks * geometry.numSides * geometry.numSectors * geometry.sectorSize / 1024);

View File

@@ -30,7 +30,7 @@ public:
return;
}
Logger() << fmt::format("RAW: writing {} cylinders, {} sides",
Logger() << fmt::format("RAW: writing {} tracks, {} sides",
geometry.numTracks, geometry.numSides);
std::ofstream outputFile(_config.filename(), std::ios::out | std::ios::binary);

View File

@@ -62,14 +62,14 @@ std::string Logger::toString(const AnyLogMessage& message)
/* Indicates that we're starting a write operation. */
[&](const BeginWriteOperationLogMessage& m)
{
stream << fmt::format("{:2}.{}: ", m.cylinder, m.head);
stream << fmt::format("{:2}.{}: ", m.track, m.head);
indented = true;
},
/* Indicates that we're starting a read operation. */
[&](const BeginReadOperationLogMessage& m)
{
stream << fmt::format("{:2}.{}: ", m.cylinder, m.head);
stream << fmt::format("{:2}.{}: ", m.track, m.head);
indented = true;
},

View File

@@ -34,7 +34,7 @@ struct DiskReadLogMessage
struct BeginReadOperationLogMessage
{
unsigned cylinder;
unsigned track;
unsigned head;
};
@@ -46,7 +46,7 @@ struct EndReadOperationLogMessage
struct BeginWriteOperationLogMessage
{
unsigned cylinder;
unsigned track;
unsigned head;
};

View File

@@ -3,89 +3,147 @@
#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::remapTrackPhysicalToLogical(unsigned ptrack)
{
return (ptrack - config.drive().head_bias()) /
config.drive().head_width();
}
static unsigned getTrackStep()
{
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";
return track_step;
}
unsigned Mapper::remapTrackLogicalToPhysical(unsigned ltrack)
{
return config.drive().head_bias() + ltrack*getTrackStep();
}
std::set<Location> Mapper::computeLocations()
{
std::set<Location> locations;
unsigned track_step = getTrackStep();
for (unsigned logicalTrack : iterate(config.tracks()))
{
for (unsigned head : iterate(config.heads()))
{
unsigned physicalTrack = config.drive().head_bias() + logicalTrack * track_step;
locations.insert({.physicalTrack = physicalTrack,
.logicalTrack = logicalTrack,
.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 remapTrackPhysicalToLogical(unsigned track);
static unsigned remapTrackLogicalToPhysical(unsigned track);
static std::set<Location> computeLocations();
};
#endif

View File

@@ -1,271 +0,0 @@
#include "globals.h"
#include "flags.h"
#include "usb/usb.h"
#include "fluxsource/fluxsource.h"
#include "fluxsink/fluxsink.h"
#include "reader.h"
#include "fluxmap.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "bytes.h"
#include "decoders/rawbits.h"
#include "flux.h"
#include "image.h"
#include "imagewriter/imagewriter.h"
#include "logger.h"
#include "fmt/format.h"
#include "proto.h"
#include "utils.h"
#include "mapper.h"
#include "lib/decoders/decoders.pb.h"
#include <iostream>
#include <fstream>
static std::unique_ptr<FluxSink> outputFluxSink;
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;
}
static bool conflictable(Sector::Status status)
{
return (status == Sector::OK) || (status == Sector::CONFLICT);
}
static std::set<std::shared_ptr<const Sector>> collect_sectors(
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;
}
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 (int head : iterate(config.heads()))
{
testForEmergencyStop();
auto track = std::make_shared<TrackFlux>();
track->physicalCylinder = cylinder;
track->physicalHead = head;
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(cylinder, head);
int retriesRemaining = config.decoder().retries();
while (fluxsourceIterator->hasNext())
{
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);
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())
{
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.";
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()));
/* 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 (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

@@ -1,19 +0,0 @@
#ifndef READER_H
#define READER_H
class AbstractDecoder;
class DiskFlux;
class FluxSink;
class FluxSource;
class Fluxmap;
class ImageWriter;
class TrackDataFlux;
extern std::unique_ptr<TrackDataFlux> readAndDecodeTrack(
FluxSource& source, AbstractDecoder& decoder, unsigned cylinder, unsigned head);
extern std::shared_ptr<const DiskFlux> readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder);
extern void readDiskCommand(FluxSource& source, AbstractDecoder& decoder, ImageWriter& writer);
extern void rawReadDiskCommand(FluxSource& source, FluxSink& sink);
#endif

503
lib/readerwriter.cc Normal file
View File

@@ -0,0 +1,503 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "readerwriter.h"
#include "protocol.h"
#include "usb/usb.h"
#include "encoders/encoders.h"
#include "decoders/decoders.h"
#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"
#include "logger.h"
#include "mapper.h"
#include "utils.h"
#include "lib/config.pb.h"
#include "proto.h"
#include <optional>
enum ReadResult
{
GOOD_READ,
BAD_AND_CAN_RETRY,
BAD_AND_CAN_NOT_RETRY
};
/* In order to allow rereads in file-based flux sources, we need to persist the
* FluxSourceIterator (as that's where the state for which read to return is
* held). This class handles that. */
class FluxSourceIteratorHolder
{
public:
FluxSourceIteratorHolder(FluxSource& fluxSource): _fluxSource(fluxSource) {}
FluxSourceIterator& getIterator(unsigned physicalCylinder, unsigned head)
{
auto& it = _cache[std::make_pair(physicalCylinder, head)];
if (!it)
it = _fluxSource.readFlux(physicalCylinder, head);
return *it;
}
private:
FluxSource& _fluxSource;
std::map<std::pair<unsigned, unsigned>, std::unique_ptr<FluxSourceIterator>>
_cache;
};
/* Given a set of sectors, deduplicates them sensibly (e.g. if there is a good
* and bad version of the same sector, the bad version is dropped). */
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, const Location& location)
{
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>(location);
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;
}
ReadResult readGroup(FluxSourceIteratorHolder& fluxSourceIteratorHolder,
const Location& location,
TrackFlux& trackFlux,
AbstractDecoder& decoder)
{
ReadResult result = BAD_AND_CAN_NOT_RETRY;
for (unsigned offset = 0; offset < location.groupSize;
offset += config.drive().head_width())
{
auto& fluxSourceIterator = fluxSourceIteratorHolder.getIterator(
location.physicalTrack + offset, location.head);
if (!fluxSourceIterator.hasNext())
continue;
Logger() << BeginReadOperationLogMessage{
location.physicalTrack + 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, location))
return GOOD_READ;
if (fluxSourceIterator.hasNext())
result = BAD_AND_CAN_RETRY;
}
return result;
}
void writeTracks(FluxSink& fluxSink,
std::function<std::unique_ptr<const Fluxmap>(const Location& location)>
producer,
std::function<bool(const Location& location)> verifier)
{
Logger() << fmt::format("Writing to: {}", (std::string)fluxSink);
for (const auto& location : Mapper::computeLocations())
{
testForEmergencyStop();
int retriesRemaining = config.decoder().retries();
for (;;)
{
for (unsigned offset = 0; offset < location.groupSize;
offset += config.drive().head_width())
{
unsigned physicalTrack = location.physicalTrack + offset;
Logger() << BeginWriteOperationLogMessage{
physicalTrack, 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(physicalTrack, 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(physicalTrack, location.head, blank);
Logger() << "erased";
}
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)
{
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>();
trackFlux->location = location;
FluxSourceIteratorHolder fluxSourceIteratorHolder(fluxSource);
readGroup(fluxSourceIteratorHolder, 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.physicalTrack, 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;
FluxSourceIteratorHolder fluxSourceIteratorHolder(fluxSource);
for (const auto& location : Mapper::computeLocations())
{
testForEmergencyStop();
auto trackFlux = std::make_shared<TrackFlux>();
trackFlux->location = location;
diskflux->tracks.push_back(trackFlux);
int retriesRemaining = config.decoder().retries();
for (;;)
{
auto result = readGroup(
fluxSourceIteratorHolder, location, *trackFlux, decoder);
if (result == GOOD_READ)
break;
if (result == BAD_AND_CAN_NOT_RETRY)
{
failures = true;
Logger() << fmt::format("no more data; giving up");
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 : trackFlux->trackDatas)
outputFluxSink->writeFlux(
location.physicalTrack, location.head, *data->fluxmap);
}
if (config.decoder().dump_records())
{
std::vector<std::shared_ptr<const Record>> sorted_records;
for (const auto& data : trackFlux->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(trackFlux->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{trackFlux};
}
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 track : iterate(config.tracks()))
{
for (unsigned head : iterate(config.heads()))
{
testForEmergencyStop();
auto fluxSourceIterator = fluxsource.readFlux(track, head);
Logger() << BeginReadOperationLogMessage{track, 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());
fluxsink.writeFlux(track, head, *fluxmap);
}
}
}
void fillBitmapTo(std::vector<bool>& bitmap,
unsigned& cursor,
unsigned terminateAt,
const std::vector<bool>& pattern)
{
while (cursor < terminateAt)
{
for (bool b : pattern)
{
if (cursor < bitmap.size())
bitmap[cursor++] = b;
}
}
}

View File

@@ -1,16 +1,20 @@
#ifndef WRITER_H
#define WRITER_H
class Fluxmap;
class AbstractDecoder;
class AbstractEncoder;
class ImageReader;
class FluxSource;
class DiskFlux;
class FluxSink;
class FluxSource;
class Fluxmap;
class Image;
class ImageReader;
class ImageWriter;
class Location;
class TrackDataFlux;
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,
@@ -26,4 +30,11 @@ extern void writeDiskCommand(std::shared_ptr<const Image> image,
extern void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink);
extern std::unique_ptr<TrackDataFlux> readAndDecodeTrack(
FluxSource& source, AbstractDecoder& decoder, unsigned track, unsigned head);
extern std::shared_ptr<const DiskFlux> readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder);
extern void readDiskCommand(FluxSource& source, AbstractDecoder& decoder, ImageWriter& writer);
extern void rawReadDiskCommand(FluxSource& source, FluxSink& sink);
#endif

View File

@@ -1,7 +1,15 @@
#include "globals.h"
#include "flux.h"
#include "sector.h"
#include "fmt/format.h"
Sector::Sector(const Location& location):
physicalTrack(location.physicalTrack),
physicalHead(location.head),
logicalTrack(location.logicalTrack),
logicalSide(location.head)
{}
std::string Sector::statusToString(Status status)
{
switch (status)
@@ -55,14 +63,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

@@ -5,6 +5,7 @@
#include "fluxmap.h"
class Record;
class Location;
/*
* Note that sectors here used zero-based numbering throughout (to make the
@@ -35,7 +36,7 @@ public:
nanoseconds_t headerEndTime = 0;
nanoseconds_t dataStartTime = 0;
nanoseconds_t dataEndTime = 0;
unsigned physicalCylinder = 0;
unsigned physicalTrack = 0;
unsigned physicalHead = 0;
unsigned logicalTrack = 0;
unsigned logicalSide = 0;
@@ -43,6 +44,10 @@ public:
Bytes data;
std::vector<std::shared_ptr<Record>> records;
Sector() {}
Sector(const Location& location);
std::tuple<int, int, int, Status> key() const
{
return std::make_tuple(
@@ -66,8 +71,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

@@ -23,7 +23,7 @@ static const char* gw_error(int e)
case ACK_NO_BUS: return "No bus";
case ACK_BAD_UNIT: return "Invalid unit";
case ACK_BAD_PIN: return "Invalid pin";
case ACK_BAD_CYLINDER: return "Invalid cylinder";
case ACK_BAD_CYLINDER: return "Invalid track";
default: return "Unknown error";
}
}

View File

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

View File

@@ -1,194 +0,0 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "writer.h"
#include "protocol.h"
#include "usb/usb.h"
#include "encoders/encoders.h"
#include "decoders/decoders.h"
#include "fluxsource/fluxsource.h"
#include "fluxsink/fluxsink.h"
#include "imagereader/imagereader.h"
#include "fmt/format.h"
#include "sector.h"
#include "image.h"
#include "logger.h"
#include "mapper.h"
#include "utils.h"
#include "lib/config.pb.h"
#include "proto.h"
void writeTracks(FluxSink& fluxSink,
const std::function<std::unique_ptr<const Fluxmap>(int track, int side)> producer)
{
for (unsigned cylinder : iterate(config.cylinders()))
{
for (unsigned head : iterate(config.heads()))
{
testForEmergencyStop();
Logger() << BeginWriteOperationLogMessage{cylinder, head};
auto fluxmap = producer(cylinder, head);
if (!fluxmap)
{
/* Erase this track rather than writing. */
fluxmap.reset(new Fluxmap());
fluxSink.writeFlux(cylinder, head, *fluxmap);
Logger() << "erased";
}
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();
}
}
}
void writeTracksAndVerify(FluxSink& fluxSink,
AbstractEncoder& encoder,
FluxSource& fluxSource,
AbstractDecoder& decoder,
const Image& image)
{
Logger() << fmt::format("Writing to: {}", (std::string)fluxSink);
for (unsigned cylinder : iterate(config.cylinders()))
{
for (unsigned head : iterate(config.heads()))
{
testForEmergencyStop();
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() << 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";
}
}
}
}
}
void fillBitmapTo(std::vector<bool>& bitmap,
unsigned& cursor,
unsigned terminateAt,
const std::vector<bool>& pattern)
{
while (cursor < terminateAt)
{
for (bool b : pattern)
{
if (cursor < bitmap.size())
bitmap[cursor++] = b;
}
}
}
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

@@ -477,7 +477,7 @@ buildlibrary libbackend.a \
lib/logger.cc \
lib/mapper.cc \
lib/proto.cc \
lib/reader.cc \
lib/readerwriter.cc \
lib/sector.cc \
lib/usb/fluxengineusb.cc \
lib/usb/greaseweazle.cc \
@@ -486,16 +486,17 @@ buildlibrary libbackend.a \
lib/usb/usb.cc \
lib/usb/usbfinder.cc \
lib/utils.cc \
lib/writer.cc \
FORMATS="\
40track_drive \
acornadfs \
acorndfs \
agat840 \
aeslanier \
agat840 \
amiga \
ampro \
apple2 \
apple2_drive \
appledos \
atarist360 \
atarist370 \
@@ -535,10 +536,11 @@ FORMATS="\
northstar87 \
prodos \
rx50 \
shugart_drive \
tids990 \
vgi \
victor9k_ss \
victor9k_ds \
victor9k_ss \
zilogmcz \
"

View File

@@ -4,7 +4,7 @@
#include "bitmap.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "writer.h"
#include "readerwriter.h"
#include "protocol.h"
#include "proto.h"
#include "fluxsink/fluxsink.h"
@@ -24,9 +24,9 @@ static StringFlag destFlux(
FluxSink::updateConfigForFilename(config.mutable_flux_sink(), value);
});
static IntFlag destCylinder(
static IntFlag destTrack(
{ "--cylinder", "-c" },
"cylinder to write to",
"track to write to",
0);
static IntFlag destHead(
@@ -213,7 +213,7 @@ int mainAnalyseDriveResponse(int argc, const char* argv[])
usbSetDrive(config.drive().drive(),
config.drive().high_density(),
config.drive().index_mode());
usbSeek(destCylinder);
usbSeek(destTrack);
std::cout << "Measuring rotational speed...\n";
nanoseconds_t period = usbGetRotationalPeriod(0);

View File

@@ -82,9 +82,9 @@ void visualiseSectorsToFile(const Image& image, const std::string& filename)
? (panel_centre + side*panel_size)
: panel_centre);
for (int physicalCylinder = 0; physicalCylinder < TRACKS; physicalCylinder++)
for (int physicalTrack = 0; physicalTrack < TRACKS; physicalTrack++)
{
double visibleDistance = (TRACKS * 0.5) + (TRACKS - physicalCylinder);
double visibleDistance = (TRACKS * 0.5) + (TRACKS - physicalTrack);
double radius = (disk_radius*visibleDistance)/(TRACKS * 1.5);
painter.noFill();
painter.lineColor(0x88, 0x88, 0x88);
@@ -96,7 +96,7 @@ void visualiseSectorsToFile(const Image& image, const std::string& filename)
{
for (const auto& sector : image)
{
if ((sector->physicalHead == side) && (sector->physicalCylinder == physicalCylinder)
if ((sector->physicalHead == side) && (sector->physicalTrack == physicalTrack)
&& (sector->logicalSector == alignWithSector))
{
offset = sector->headerStartTime;
@@ -124,7 +124,7 @@ void visualiseSectorsToFile(const Image& image, const std::string& filename)
/* Sadly, Images aren't indexable by physical track. */
for (const auto& sector : image)
{
if ((sector->physicalHead == side) && (sector->physicalCylinder == physicalCylinder))
if ((sector->physicalHead == side) && (sector->physicalTrack == physicalTrack))
{
painter.lineColor(0xff, 0x00, 0x00);
if (sector->status == Sector::OK)
@@ -192,7 +192,7 @@ static void readRow(const std::vector<std::string>& row, Image& image)
int logicalSector = std::stoi(row[4]);
const auto& sector = image.put(logicalTrack, logicalSide, logicalSector);
sector->physicalCylinder = std::stoi(row[0]);
sector->physicalTrack = std::stoi(row[0]);
sector->physicalHead = std::stoi(row[1]);
sector->logicalTrack = logicalTrack;
sector->logicalSide = logicalSide;

View File

@@ -1,6 +1,6 @@
#include "globals.h"
#include "flags.h"
#include "reader.h"
#include "readerwriter.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "decoders/fluxdecoder.h"
@@ -23,9 +23,9 @@ static StringFlag sourceFlux(
FluxSource::updateConfigForFilename(config.mutable_flux_source(), value);
});
static IntFlag cylinderFlag(
static IntFlag trackFlag(
{ "--cylinder", "-c" },
"Cylinder to read.",
"Track to read.",
0);
static IntFlag headFlag(
@@ -205,7 +205,7 @@ int mainInspect(int argc, const char* argv[])
flags.parseFlagsWithConfigFiles(argc, argv, {});
std::unique_ptr<FluxSource> fluxSource(FluxSource::create(config.flux_source()));
const auto fluxmap = fluxSource->readFlux(cylinderFlag, headFlag)->next();
const auto fluxmap = fluxSource->readFlux(trackFlag, headFlag)->next();
std::cout << fmt::format("0x{:x} bytes of data in {:.3f}ms\n",
fluxmap->bytes(),

View File

@@ -1,6 +1,6 @@
#include "globals.h"
#include "flags.h"
#include "reader.h"
#include "readerwriter.h"
#include "fluxmap.h"
#include "decoders/decoders.h"
#include "macintosh/macintosh.h"
@@ -36,13 +36,13 @@ static StringFlag destFlux(
FluxSink::updateConfigForFilename(config.mutable_flux_sink(), value);
});
static StringFlag srcCylinders(
static StringFlag srcTracks(
{ "--cylinders", "-c" },
"cylinders to read from",
"tracks to read from",
"",
[](const auto& value)
{
setRange(config.mutable_cylinders(), value);
setRange(config.mutable_tracks(), value);
});
static StringFlag srcHeads(
@@ -56,7 +56,7 @@ static StringFlag srcHeads(
int mainRawRead(int argc, const char* argv[])
{
setRange(config.mutable_cylinders(), "0-79");
setRange(config.mutable_tracks(), "0-79");
setRange(config.mutable_heads(), "0-1");
if (argc == 1)

View File

@@ -1,8 +1,7 @@
#include "globals.h"
#include "flags.h"
#include "reader.h"
#include "readerwriter.h"
#include "fluxmap.h"
#include "writer.h"
#include "sector.h"
#include "proto.h"
#include "lib/fluxsource/fluxsource.h"
@@ -32,13 +31,13 @@ static StringFlag destFlux(
FluxSink::updateConfigForFilename(config.mutable_flux_sink(), value);
});
static StringFlag destCylinders(
static StringFlag destTracks(
{ "--cylinders", "-c" },
"cylinders to write to",
"tracks to write to",
"",
[](const auto& value)
{
setRange(config.mutable_cylinders(), value);
setRange(config.mutable_tracks(), value);
});
static StringFlag destHeads(
@@ -60,7 +59,7 @@ static ActionFlag eraseFlag(
int mainRawWrite(int argc, const char* argv[])
{
setRange(config.mutable_cylinders(), "0-79");
setRange(config.mutable_tracks(), "0-79");
setRange(config.mutable_heads(), "0-1");
if (argc == 1)

View File

@@ -1,6 +1,6 @@
#include "globals.h"
#include "flags.h"
#include "reader.h"
#include "readerwriter.h"
#include "fluxmap.h"
#include "decoders/decoders.h"
#include "macintosh/macintosh.h"
@@ -45,13 +45,13 @@ static StringFlag copyFluxTo(
FluxSink::updateConfigForFilename(config.mutable_decoder()->mutable_copy_flux_to(), value);
});
static StringFlag srcCylinders(
static StringFlag srcTracks(
{ "--cylinders", "-c" },
"cylinders to read from",
"tracks to read from",
"",
[](const auto& value)
{
setRange(config.mutable_cylinders(), value);
setRange(config.mutable_tracks(), value);
});
static StringFlag srcHeads(

View File

@@ -16,9 +16,9 @@ static StringFlag sourceFlux(
FluxSource::updateConfigForFilename(config.mutable_flux_source(), value);
});
static IntFlag cylinder(
static IntFlag track(
{ "--cylinder", "-c" },
"cylinder to seek to",
"track to seek to",
0);
extern const std::map<std::string, std::string> readables;
@@ -31,6 +31,6 @@ int mainSeek(int argc, const char* argv[])
Error() << "this only makes sense with a real disk drive";
usbSetDrive(config.drive().drive(), false, config.drive().index_mode());
usbSeek(cylinder);
usbSeek(track);
return 0;
}

View File

@@ -1,6 +1,6 @@
#include "globals.h"
#include "flags.h"
#include "writer.h"
#include "readerwriter.h"
#include "fluxmap.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
@@ -38,13 +38,13 @@ static StringFlag destFlux(
FluxSource::updateConfigForFilename(config.mutable_flux_source(), value);
});
static StringFlag destCylinders(
static StringFlag destTracks(
{ "--cylinders", "-c" },
"cylinders to write to",
"tracks to write to",
"",
[](const auto& value)
{
setRange(config.mutable_cylinders(), value);
setRange(config.mutable_tracks(), value);
});
static StringFlag destHeads(

View File

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

View File

@@ -19,7 +19,7 @@ decoder {
}
}
cylinders {
tracks {
start: 0
end: 79
}

View File

@@ -19,7 +19,7 @@ decoder {
}
}
cylinders {
tracks {
start: 0
end: 79
}

View File

@@ -9,7 +9,7 @@ decoder {
aeslanier {}
}
cylinders {
tracks {
start: 0
end: 76
}

View File

@@ -15,7 +15,7 @@ decoder {
agat {}
}
cylinders {
tracks {
start: 0
end: 79
}

View File

@@ -38,7 +38,7 @@ decoder {
amiga {}
}
cylinders {
tracks {
start: 0
end: 79
}

View File

@@ -15,7 +15,7 @@ decoder {
ibm {}
}
cylinders {
tracks {
start: 0
end: 79
}

View File

@@ -28,10 +28,9 @@ encoder {
apple2 {}
}
cylinders {
tracks {
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 {
tracks: 160
heads: 1
head_width: 4
tpi: 192
}

View File

@@ -67,7 +67,7 @@ decoder {
}
}
cylinders {
tracks {
start: 0
end: 79
}

View File

@@ -66,7 +66,7 @@ decoder {
}
}
cylinders {
tracks {
start: 0
end: 81
}

View File

@@ -67,7 +67,7 @@ decoder {
}
}
cylinders {
tracks {
start: 0
end: 79
}

View File

@@ -67,7 +67,7 @@ decoder {
}
}
cylinders {
tracks {
start: 0
end: 81
}

View File

@@ -66,7 +66,7 @@ decoder {
}
}
cylinders {
tracks {
start: 0
end: 79
}

View File

@@ -66,7 +66,7 @@ decoder {
}
}
cylinders {
tracks {
start: 0
end: 81
}

View File

@@ -67,7 +67,7 @@ decoder {
}
}
cylinders {
tracks {
start: 0
end: 79
}

View File

@@ -67,7 +67,7 @@ decoder {
}
}
cylinders {
tracks {
start: 0
end: 81
}

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 {}
}
cylinders {
drive {
head_bias: 1
}
tracks {
start: 0
end: 77
end: 38
}
heads {
@@ -53,3 +54,4 @@ heads {
end: 0
}
tpi: 48

View File

@@ -38,7 +38,11 @@ decoder {
brother {}
}
cylinders {
drive {
head_bias: 1
}
tracks {
start: 0
end: 77
}

View File

@@ -18,9 +18,9 @@ decoder {
c64 {}
}
cylinders {
tracks {
start: 0
end: 79
end: 39
}
heads {
@@ -28,3 +28,5 @@ heads {
end: 0
}
tpi: 48

View File

@@ -68,7 +68,7 @@ decoder {
}
}
cylinders {
tracks {
start: 0
end: 79
}

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