mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 08:49:37 +00:00
Merge pull request #187 from TomHarte/CRCErrors
Extends the built-in [M]FM encoder to be able to produce incorrect CRC values, and adjusts the CPC .DSK handler to request them when appropriate
This commit is contained in:
commit
0f75525640
@ -114,22 +114,12 @@ class FMEncoder: public Encoder {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint8_t logarithmic_size_for_size(size_t size) {
|
|
||||||
switch(size) {
|
|
||||||
default: return 0;
|
|
||||||
case 256: return 1;
|
|
||||||
case 512: return 2;
|
|
||||||
case 1024: return 3;
|
|
||||||
case 2048: return 4;
|
|
||||||
case 4196: return 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T> std::shared_ptr<Storage::Disk::Track>
|
template<class T> std::shared_ptr<Storage::Disk::Track>
|
||||||
GetTrackWithSectors(
|
GetTrackWithSectors(
|
||||||
const std::vector<Sector> §ors,
|
const std::vector<Sector> §ors,
|
||||||
size_t post_index_address_mark_bytes, uint8_t post_index_address_mark_value,
|
size_t post_index_address_mark_bytes, uint8_t post_index_address_mark_value,
|
||||||
size_t pre_address_mark_bytes, size_t post_address_mark_bytes,
|
size_t pre_address_mark_bytes,
|
||||||
|
size_t post_address_mark_bytes, uint8_t post_address_mark_value,
|
||||||
size_t pre_data_mark_bytes, size_t post_data_bytes,
|
size_t pre_data_mark_bytes, size_t post_data_bytes,
|
||||||
size_t inter_sector_gap,
|
size_t inter_sector_gap,
|
||||||
size_t expected_track_bytes) {
|
size_t expected_track_bytes) {
|
||||||
@ -153,21 +143,30 @@ template<class T> std::shared_ptr<Storage::Disk::Track>
|
|||||||
shifter.add_byte(sector.track);
|
shifter.add_byte(sector.track);
|
||||||
shifter.add_byte(sector.side);
|
shifter.add_byte(sector.side);
|
||||||
shifter.add_byte(sector.sector);
|
shifter.add_byte(sector.sector);
|
||||||
uint8_t size = logarithmic_size_for_size(sector.data.size());
|
shifter.add_byte(sector.size);
|
||||||
shifter.add_byte(size);
|
shifter.add_crc(sector.has_header_crc_error);
|
||||||
shifter.add_crc();
|
|
||||||
|
|
||||||
// gap
|
// gap
|
||||||
for(size_t c = 0; c < post_address_mark_bytes; c++) shifter.add_byte(0x4e);
|
for(size_t c = 0; c < post_address_mark_bytes; c++) shifter.add_byte(post_address_mark_value);
|
||||||
for(size_t c = 0; c < pre_data_mark_bytes; c++) shifter.add_byte(0x00);
|
for(size_t c = 0; c < pre_data_mark_bytes; c++) shifter.add_byte(0x00);
|
||||||
|
|
||||||
// data
|
// data, if attached
|
||||||
shifter.add_data_address_mark();
|
if(!sector.data.empty()) {
|
||||||
for(size_t c = 0; c < sector.data.size(); c++)
|
if(sector.is_deleted)
|
||||||
{
|
shifter.add_deleted_data_address_mark();
|
||||||
shifter.add_byte(sector.data[c]);
|
else
|
||||||
|
shifter.add_data_address_mark();
|
||||||
|
|
||||||
|
size_t c = 0;
|
||||||
|
size_t declared_length = (size_t)(128 << sector.size);
|
||||||
|
for(c = 0; c < sector.data.size() && c < declared_length; c++) {
|
||||||
|
shifter.add_byte(sector.data[c]);
|
||||||
|
}
|
||||||
|
for(; c < declared_length; c++) {
|
||||||
|
shifter.add_byte(0x00);
|
||||||
|
}
|
||||||
|
shifter.add_crc(sector.has_data_crc_error);
|
||||||
}
|
}
|
||||||
shifter.add_crc();
|
|
||||||
|
|
||||||
// gap
|
// gap
|
||||||
for(size_t c = 0; c < post_data_bytes; c++) shifter.add_byte(0x00);
|
for(size_t c = 0; c < post_data_bytes; c++) shifter.add_byte(0x00);
|
||||||
@ -189,28 +188,32 @@ void Encoder::output_short(uint16_t value) {
|
|||||||
target_.push_back(value & 0xff);
|
target_.push_back(value & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Encoder::add_crc() {
|
void Encoder::add_crc(bool incorrectly) {
|
||||||
uint16_t crc_value = crc_generator_.get_value();
|
uint16_t crc_value = crc_generator_.get_value();
|
||||||
add_byte(crc_value >> 8);
|
add_byte(crc_value >> 8);
|
||||||
add_byte(crc_value & 0xff);
|
add_byte((crc_value & 0xff) ^ (incorrectly ? 1 : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetFMTrackWithSectors(const std::vector<Sector> §ors) {
|
const size_t Storage::Encodings::MFM::DefaultSectorGapLength = (size_t)~0;
|
||||||
|
|
||||||
|
std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetFMTrackWithSectors(const std::vector<Sector> §ors, size_t sector_gap_length, uint8_t sector_gap_filler_byte) {
|
||||||
return GetTrackWithSectors<FMEncoder>(
|
return GetTrackWithSectors<FMEncoder>(
|
||||||
sectors,
|
sectors,
|
||||||
16, 0x00,
|
16, 0x00,
|
||||||
6, 0,
|
6,
|
||||||
17, 14,
|
(sector_gap_length != DefaultSectorGapLength) ? sector_gap_length : 0, sector_gap_filler_byte,
|
||||||
|
(sector_gap_length != DefaultSectorGapLength) ? 0 : 17, 14,
|
||||||
0,
|
0,
|
||||||
6250); // i.e. 250kbps (including clocks) * 60 = 15000kpm, at 300 rpm => 50 kbits/rotation => 6250 bytes/rotation
|
6250); // i.e. 250kbps (including clocks) * 60 = 15000kpm, at 300 rpm => 50 kbits/rotation => 6250 bytes/rotation
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetMFMTrackWithSectors(const std::vector<Sector> §ors) {
|
std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetMFMTrackWithSectors(const std::vector<Sector> §ors, size_t sector_gap_length, uint8_t sector_gap_filler_byte) {
|
||||||
return GetTrackWithSectors<MFMEncoder>(
|
return GetTrackWithSectors<MFMEncoder>(
|
||||||
sectors,
|
sectors,
|
||||||
50, 0x4e,
|
50, 0x4e,
|
||||||
12, 22,
|
12,
|
||||||
12, 18,
|
(sector_gap_length != DefaultSectorGapLength) ? sector_gap_length : 22, sector_gap_filler_byte,
|
||||||
|
(sector_gap_length != DefaultSectorGapLength) ? 0 : 12, 18,
|
||||||
32,
|
32,
|
||||||
12500); // unintelligently: double the single-density bytes/rotation (or: 500kps @ 300 rpm)
|
12500); // unintelligently: double the single-density bytes/rotation (or: 500kps @ 300 rpm)
|
||||||
}
|
}
|
||||||
|
@ -36,13 +36,39 @@ const uint16_t MFMPostSyncCRCValue = 0xcdb4; // the value the CRC generator sho
|
|||||||
const uint8_t MFMIndexSyncByteValue = 0xc2;
|
const uint8_t MFMIndexSyncByteValue = 0xc2;
|
||||||
const uint8_t MFMSyncByteValue = 0xa1;
|
const uint8_t MFMSyncByteValue = 0xa1;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Represents a single [M]FM sector, identified by its track, side and sector records, a blob of data
|
||||||
|
and a few extra flags of metadata.
|
||||||
|
*/
|
||||||
struct Sector {
|
struct Sector {
|
||||||
uint8_t track, side, sector;
|
uint8_t track, side, sector, size;
|
||||||
std::vector<uint8_t> data;
|
std::vector<uint8_t> data;
|
||||||
|
|
||||||
|
bool has_data_crc_error;
|
||||||
|
bool has_header_crc_error;
|
||||||
|
bool is_deleted;
|
||||||
|
|
||||||
|
Sector() : track(0), side(0), sector(0), size(0), has_data_crc_error(false), has_header_crc_error(false), is_deleted(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<Storage::Disk::Track> GetMFMTrackWithSectors(const std::vector<Sector> §ors);
|
extern const size_t DefaultSectorGapLength;
|
||||||
std::shared_ptr<Storage::Disk::Track> GetFMTrackWithSectors(const std::vector<Sector> §ors);
|
/*!
|
||||||
|
Converts a vector of sectors into a properly-encoded MFM track.
|
||||||
|
|
||||||
|
@param sectors The sectors to write.
|
||||||
|
@param sector_gap_length If specified, sets the distance in whole bytes between each ID and its data.
|
||||||
|
@param sector_gap_filler_byte If specified, sets the value (unencoded) that is used to populate the gap between each ID and its data.
|
||||||
|
*/
|
||||||
|
std::shared_ptr<Storage::Disk::Track> GetMFMTrackWithSectors(const std::vector<Sector> §ors, size_t sector_gap_length = DefaultSectorGapLength, uint8_t sector_gap_filler_byte = 0x4e);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Converts a vector of sectors into a properly-encoded FM track.
|
||||||
|
|
||||||
|
@param sectors The sectors to write.
|
||||||
|
@param sector_gap_length If specified, sets the distance in whole bytes between each ID and its data.
|
||||||
|
@param sector_gap_filler_byte If specified, sets the value (unencoded) that is used to populate the gap between each ID and its data.
|
||||||
|
*/
|
||||||
|
std::shared_ptr<Storage::Disk::Track> GetFMTrackWithSectors(const std::vector<Sector> §ors, size_t sector_gap_length = DefaultSectorGapLength, uint8_t sector_gap_filler_byte = 0x4e);
|
||||||
|
|
||||||
class Encoder {
|
class Encoder {
|
||||||
public:
|
public:
|
||||||
@ -53,7 +79,9 @@ class Encoder {
|
|||||||
virtual void add_data_address_mark() = 0;
|
virtual void add_data_address_mark() = 0;
|
||||||
virtual void add_deleted_data_address_mark() = 0;
|
virtual void add_deleted_data_address_mark() = 0;
|
||||||
virtual void output_short(uint16_t value);
|
virtual void output_short(uint16_t value);
|
||||||
void add_crc();
|
|
||||||
|
/// Outputs the CRC for all data since the last address mask; if @c incorrectly is @c true then outputs an incorrect CRC.
|
||||||
|
void add_crc(bool incorrectly);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
NumberTheory::CRC16 crc_generator_;
|
NumberTheory::CRC16 crc_generator_;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
namespace {
|
namespace {
|
||||||
static const unsigned int sectors_per_track = 16;
|
static const unsigned int sectors_per_track = 16;
|
||||||
static const unsigned int bytes_per_sector = 256;
|
static const unsigned int bytes_per_sector = 256;
|
||||||
|
static const unsigned int sector_size = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace Storage::Disk;
|
using namespace Storage::Disk;
|
||||||
@ -69,6 +70,7 @@ std::shared_ptr<Track> AcornADF::get_uncached_track_at_position(unsigned int hea
|
|||||||
new_sector.track = (uint8_t)position;
|
new_sector.track = (uint8_t)position;
|
||||||
new_sector.side = (uint8_t)head;
|
new_sector.side = (uint8_t)head;
|
||||||
new_sector.sector = (uint8_t)sector;
|
new_sector.sector = (uint8_t)sector;
|
||||||
|
new_sector.size = sector_size;
|
||||||
|
|
||||||
new_sector.data.resize(bytes_per_sector);
|
new_sector.data.resize(bytes_per_sector);
|
||||||
fread(&new_sector.data[0], 1, bytes_per_sector, file_);
|
fread(&new_sector.data[0], 1, bytes_per_sector, file_);
|
||||||
|
@ -75,8 +75,8 @@ std::shared_ptr<Track> CPCDSK::get_uncached_track_at_position(unsigned int head,
|
|||||||
// Grab the track information.
|
// Grab the track information.
|
||||||
fseek(file_, 5, SEEK_CUR); // skip track number, side number, sector size — each is given per sector
|
fseek(file_, 5, SEEK_CUR); // skip track number, side number, sector size — each is given per sector
|
||||||
int number_of_sectors = fgetc(file_);
|
int number_of_sectors = fgetc(file_);
|
||||||
__unused uint8_t gap3_length = (uint8_t)fgetc(file_);
|
uint8_t gap3_length = (uint8_t)fgetc(file_);
|
||||||
__unused uint8_t filler_byte = (uint8_t)fgetc(file_);
|
uint8_t filler_byte = (uint8_t)fgetc(file_);
|
||||||
|
|
||||||
// Grab the sector information
|
// Grab the sector information
|
||||||
struct SectorInfo {
|
struct SectorInfo {
|
||||||
@ -111,6 +111,7 @@ std::shared_ptr<Track> CPCDSK::get_uncached_track_at_position(unsigned int head,
|
|||||||
new_sector.track = sector_info.track;
|
new_sector.track = sector_info.track;
|
||||||
new_sector.side = sector_info.side;
|
new_sector.side = sector_info.side;
|
||||||
new_sector.sector = sector_info.sector;
|
new_sector.sector = sector_info.sector;
|
||||||
|
new_sector.size = sector_info.length;
|
||||||
|
|
||||||
size_t data_size;
|
size_t data_size;
|
||||||
if(is_extended_) {
|
if(is_extended_) {
|
||||||
@ -122,32 +123,34 @@ std::shared_ptr<Track> CPCDSK::get_uncached_track_at_position(unsigned int head,
|
|||||||
new_sector.data.resize(data_size);
|
new_sector.data.resize(data_size);
|
||||||
fread(new_sector.data.data(), sizeof(uint8_t), data_size, file_);
|
fread(new_sector.data.data(), sizeof(uint8_t), data_size, file_);
|
||||||
|
|
||||||
// TODO: obey the status bytes, somehow (?)
|
if(sector_info.status1 & 0x08) {
|
||||||
if(sector_info.status1 || sector_info.status2) {
|
// The CRC failed in the ID field.
|
||||||
if(sector_info.status1 & 0x08) {
|
new_sector.has_header_crc_error = true;
|
||||||
// The CRC failed in the ID field.
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(sector_info.status2 & 0x20) {
|
if(sector_info.status2 & 0x20) {
|
||||||
// The CRC failed in the data field.
|
// The CRC failed in the data field.
|
||||||
}
|
new_sector.has_data_crc_error = true;
|
||||||
|
}
|
||||||
|
|
||||||
if(sector_info.status2 & 0x40) {
|
if(sector_info.status2 & 0x40) {
|
||||||
// This sector is marked as deleted.
|
// This sector is marked as deleted.
|
||||||
}
|
new_sector.is_deleted = true;
|
||||||
|
}
|
||||||
|
|
||||||
if(sector_info.status2 & 0x01) {
|
if(sector_info.status2 & 0x01) {
|
||||||
// Data field wasn't found.
|
// Data field wasn't found.
|
||||||
}
|
new_sector.data.clear();
|
||||||
|
|
||||||
printf("Unhandled: status errors\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sectors.push_back(std::move(new_sector));
|
sectors.push_back(std::move(new_sector));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: supply gay 3 length and filler byte
|
// TODO: extensions to the extended format; John Elliot's addition of single-density support,
|
||||||
if(sectors.size()) return Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors);
|
// and Simon Owen's weak/random sectors, subject to adding some logic to pick a potential
|
||||||
|
// FM/MFM encoding that can produce specified weak values.
|
||||||
|
|
||||||
|
if(sectors.size()) return Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors, gap3_length, filler_byte);
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ std::shared_ptr<Track> SSD::get_uncached_track_at_position(unsigned int head, un
|
|||||||
new_sector.track = (uint8_t)position;
|
new_sector.track = (uint8_t)position;
|
||||||
new_sector.side = 0;
|
new_sector.side = 0;
|
||||||
new_sector.sector = (uint8_t)sector;
|
new_sector.sector = (uint8_t)sector;
|
||||||
|
new_sector.size = 1;
|
||||||
|
|
||||||
new_sector.data.resize(256);
|
new_sector.data.resize(256);
|
||||||
fread(new_sector.data.data(), 1, 256, file_);
|
fread(new_sector.data.data(), 1, 256, file_);
|
||||||
|
Loading…
Reference in New Issue
Block a user