diff --git a/Storage/Disk/Encodings/MFM.cpp b/Storage/Disk/Encodings/MFM.cpp index 1c2f19a4c..846ece7f6 100644 --- a/Storage/Disk/Encodings/MFM.cpp +++ b/Storage/Disk/Encodings/MFM.cpp @@ -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 std::shared_ptr GetTrackWithSectors( const std::vector §ors, 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 std::shared_ptr 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::Encodings::MFM::GetFMTrackWithSectors(const std::vector §ors) { +const size_t Storage::Encodings::MFM::DefaultSectorGapLength = (size_t)~0; + +std::shared_ptr Storage::Encodings::MFM::GetFMTrackWithSectors(const std::vector §ors, size_t sector_gap_length, uint8_t sector_gap_filler_byte) { return GetTrackWithSectors( 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::Encodings::MFM::GetMFMTrackWithSectors(const std::vector §ors) { +std::shared_ptr Storage::Encodings::MFM::GetMFMTrackWithSectors(const std::vector §ors, size_t sector_gap_length, uint8_t sector_gap_filler_byte) { return GetTrackWithSectors( 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) } diff --git a/Storage/Disk/Encodings/MFM.hpp b/Storage/Disk/Encodings/MFM.hpp index 37a8178f3..eed23e5b3 100644 --- a/Storage/Disk/Encodings/MFM.hpp +++ b/Storage/Disk/Encodings/MFM.hpp @@ -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 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 GetMFMTrackWithSectors(const std::vector §ors); -std::shared_ptr GetFMTrackWithSectors(const std::vector §ors); +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 GetMFMTrackWithSectors(const std::vector §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 GetFMTrackWithSectors(const std::vector §ors, 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_; diff --git a/Storage/Disk/Formats/AcornADF.cpp b/Storage/Disk/Formats/AcornADF.cpp index f4e0664d7..91f4846d2 100644 --- a/Storage/Disk/Formats/AcornADF.cpp +++ b/Storage/Disk/Formats/AcornADF.cpp @@ -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 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_); diff --git a/Storage/Disk/Formats/CPCDSK.cpp b/Storage/Disk/Formats/CPCDSK.cpp index 99f25132e..bf3082384 100644 --- a/Storage/Disk/Formats/CPCDSK.cpp +++ b/Storage/Disk/Formats/CPCDSK.cpp @@ -75,8 +75,8 @@ std::shared_ptr 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 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 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; } diff --git a/Storage/Disk/Formats/SSD.cpp b/Storage/Disk/Formats/SSD.cpp index 5045e536b..8bea6b936 100644 --- a/Storage/Disk/Formats/SSD.cpp +++ b/Storage/Disk/Formats/SSD.cpp @@ -60,6 +60,7 @@ std::shared_ptr 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_);