1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Expands the [M]FM encoder to respect some new Sector flags: it will now wilfully make CRC errors, omit data, include data that is different than the ID's declared length, write deleted data, and can be commanded as to header/data gaps and what should be within them. All based around expanding towards the needs for reproduction of the CPC's .DSK file format.

This commit is contained in:
Thomas Harte 2017-08-11 14:24:50 -04:00
parent 9c0b75faba
commit fcf63a7547
4 changed files with 60 additions and 39 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,24 @@ 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;
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);
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 +64,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_) {
@ -126,28 +127,30 @@ std::shared_ptr<Track> CPCDSK::get_uncached_track_at_position(unsigned int head,
if(sector_info.status1 || sector_info.status2) {
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.
new_sector.has_data_crc_error = true;
}
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.
new_sector.data.clear();
}
printf("Unhandled: status errors\n");
}
sectors.push_back(std::move(new_sector));
}
// TODO: supply gay 3 length and filler byte
if(sectors.size()) return Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors);
if(sectors.size()) return Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors, gap3_length, filler_byte);
return nullptr;
}