1
0
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:
Thomas Harte 2017-08-11 14:34:31 -04:00 committed by GitHub
commit 0f75525640
5 changed files with 91 additions and 54 deletions

View File

@ -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>
GetTrackWithSectors(
const std::vector<Sector> &sectors,
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 inter_sector_gap,
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.side);
shifter.add_byte(sector.sector);
uint8_t size = logarithmic_size_for_size(sector.data.size());
shifter.add_byte(size);
shifter.add_crc();
shifter.add_byte(sector.size);
shifter.add_crc(sector.has_header_crc_error);
// 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);
// data
shifter.add_data_address_mark();
for(size_t c = 0; c < sector.data.size(); c++)
{
shifter.add_byte(sector.data[c]);
// data, if attached
if(!sector.data.empty()) {
if(sector.is_deleted)
shifter.add_deleted_data_address_mark();
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
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);
}
void Encoder::add_crc() {
void Encoder::add_crc(bool incorrectly) {
uint16_t crc_value = crc_generator_.get_value();
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> &sectors) {
const size_t Storage::Encodings::MFM::DefaultSectorGapLength = (size_t)~0;
std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetFMTrackWithSectors(const std::vector<Sector> &sectors, size_t sector_gap_length, uint8_t sector_gap_filler_byte) {
return GetTrackWithSectors<FMEncoder>(
sectors,
16, 0x00,
6, 0,
17, 14,
6,
(sector_gap_length != DefaultSectorGapLength) ? sector_gap_length : 0, sector_gap_filler_byte,
(sector_gap_length != DefaultSectorGapLength) ? 0 : 17, 14,
0,
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> &sectors) {
std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetMFMTrackWithSectors(const std::vector<Sector> &sectors, size_t sector_gap_length, uint8_t sector_gap_filler_byte) {
return GetTrackWithSectors<MFMEncoder>(
sectors,
50, 0x4e,
12, 22,
12, 18,
12,
(sector_gap_length != DefaultSectorGapLength) ? sector_gap_length : 22, sector_gap_filler_byte,
(sector_gap_length != DefaultSectorGapLength) ? 0 : 12, 18,
32,
12500); // unintelligently: double the single-density bytes/rotation (or: 500kps @ 300 rpm)
}

View File

@ -36,13 +36,39 @@ const uint16_t MFMPostSyncCRCValue = 0xcdb4; // the value the CRC generator sho
const uint8_t MFMIndexSyncByteValue = 0xc2;
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 {
uint8_t track, side, sector;
uint8_t track, side, sector, size;
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> &sectors);
std::shared_ptr<Storage::Disk::Track> GetFMTrackWithSectors(const std::vector<Sector> &sectors);
extern const size_t DefaultSectorGapLength;
/*!
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> &sectors, 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> &sectors, size_t sector_gap_length = DefaultSectorGapLength, uint8_t sector_gap_filler_byte = 0x4e);
class Encoder {
public:
@ -53,7 +79,9 @@ class Encoder {
virtual void add_data_address_mark() = 0;
virtual void add_deleted_data_address_mark() = 0;
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:
NumberTheory::CRC16 crc_generator_;

View File

@ -14,6 +14,7 @@
namespace {
static const unsigned int sectors_per_track = 16;
static const unsigned int bytes_per_sector = 256;
static const unsigned int sector_size = 1;
}
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.side = (uint8_t)head;
new_sector.sector = (uint8_t)sector;
new_sector.size = sector_size;
new_sector.data.resize(bytes_per_sector);
fread(&new_sector.data[0], 1, bytes_per_sector, file_);

View File

@ -75,8 +75,8 @@ std::shared_ptr<Track> CPCDSK::get_uncached_track_at_position(unsigned int head,
// Grab the track information.
fseek(file_, 5, SEEK_CUR); // skip track number, side number, sector size — each is given per sector
int number_of_sectors = fgetc(file_);
__unused uint8_t gap3_length = (uint8_t)fgetc(file_);
__unused uint8_t filler_byte = (uint8_t)fgetc(file_);
uint8_t gap3_length = (uint8_t)fgetc(file_);
uint8_t filler_byte = (uint8_t)fgetc(file_);
// Grab the sector information
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.side = sector_info.side;
new_sector.sector = sector_info.sector;
new_sector.size = sector_info.length;
size_t data_size;
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);
fread(new_sector.data.data(), sizeof(uint8_t), data_size, file_);
// TODO: obey the status bytes, somehow (?)
if(sector_info.status1 || sector_info.status2) {
if(sector_info.status1 & 0x08) {
// The CRC failed in the ID field.
}
if(sector_info.status1 & 0x08) {
// The CRC failed in the ID field.
new_sector.has_header_crc_error = true;
}
if(sector_info.status2 & 0x20) {
// The CRC failed in the data field.
}
if(sector_info.status2 & 0x20) {
// The CRC failed in the data field.
new_sector.has_data_crc_error = true;
}
if(sector_info.status2 & 0x40) {
// This sector is marked as deleted.
}
if(sector_info.status2 & 0x40) {
// This sector is marked as deleted.
new_sector.is_deleted = true;
}
if(sector_info.status2 & 0x01) {
// Data field wasn't found.
}
printf("Unhandled: status errors\n");
if(sector_info.status2 & 0x01) {
// Data field wasn't found.
new_sector.data.clear();
}
sectors.push_back(std::move(new_sector));
}
// TODO: supply gay 3 length and filler byte
if(sectors.size()) return Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors);
// TODO: extensions to the extended format; John Elliot's addition of single-density support,
// 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;
}

View File

@ -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.side = 0;
new_sector.sector = (uint8_t)sector;
new_sector.size = 1;
new_sector.data.resize(256);
fread(new_sector.data.data(), 1, 256, file_);