diff --git a/StaticAnalyser/Acorn/Disk.cpp b/StaticAnalyser/Acorn/Disk.cpp index 6ab4ab585..40cfa820a 100644 --- a/StaticAnalyser/Acorn/Disk.cpp +++ b/StaticAnalyser/Acorn/Disk.cpp @@ -23,17 +23,18 @@ std::unique_ptr StaticAnalyser::Acorn::GetDFSCatalogue(const std::sha Storage::Encodings::MFM::Sector *details = parser.get_sector(0, 0, 1); if(!names || !details) return nullptr; - if(names->data.size() != 256 || details->data.size() != 256) return nullptr; + if(names->samples.empty() || details->samples.empty()) return nullptr; + if(names->samples[0].size() != 256 || details->samples[0].size() != 256) return nullptr; - uint8_t final_file_offset = details->data[5]; + uint8_t final_file_offset = details->samples[0][5]; if(final_file_offset&7) return nullptr; if(final_file_offset < 8) return nullptr; char disk_name[13]; - snprintf(disk_name, 13, "%.8s%.4s", &names->data[0], &details->data[0]); + snprintf(disk_name, 13, "%.8s%.4s", &names->samples[0][0], &details->samples[0][0]); catalogue->name = disk_name; - switch((details->data[6] >> 4)&3) { + switch((details->samples[0][6] >> 4)&3) { case 0: catalogue->bootOption = Catalogue::BootOption::None; break; case 1: catalogue->bootOption = Catalogue::BootOption::LoadBOOT; break; case 2: catalogue->bootOption = Catalogue::BootOption::RunBOOT; break; @@ -45,14 +46,14 @@ std::unique_ptr StaticAnalyser::Acorn::GetDFSCatalogue(const std::sha for(size_t file_offset = final_file_offset - 8; file_offset > 0; file_offset -= 8) { File new_file; char name[10]; - snprintf(name, 10, "%c.%.7s", names->data[file_offset + 7] & 0x7f, &names->data[file_offset]); + snprintf(name, 10, "%c.%.7s", names->samples[0][file_offset + 7] & 0x7f, &names->samples[0][file_offset]); new_file.name = name; - new_file.load_address = (uint32_t)(details->data[file_offset] | (details->data[file_offset+1] << 8) | ((details->data[file_offset+6]&0x0c) << 14)); - new_file.execution_address = (uint32_t)(details->data[file_offset+2] | (details->data[file_offset+3] << 8) | ((details->data[file_offset+6]&0xc0) << 10)); - new_file.is_protected = !!(names->data[file_offset + 7] & 0x80); + new_file.load_address = (uint32_t)(details->samples[0][file_offset] | (details->samples[0][file_offset+1] << 8) | ((details->samples[0][file_offset+6]&0x0c) << 14)); + new_file.execution_address = (uint32_t)(details->samples[0][file_offset+2] | (details->samples[0][file_offset+3] << 8) | ((details->samples[0][file_offset+6]&0xc0) << 10)); + new_file.is_protected = !!(names->samples[0][file_offset + 7] & 0x80); - long data_length = static_cast(details->data[file_offset+4] | (details->data[file_offset+5] << 8) | ((details->data[file_offset+6]&0x30) << 12)); - int start_sector = details->data[file_offset+7] | ((details->data[file_offset+6]&0x03) << 8); + long data_length = static_cast(details->samples[0][file_offset+4] | (details->samples[0][file_offset+5] << 8) | ((details->samples[0][file_offset+6]&0x30) << 12)); + int start_sector = details->samples[0][file_offset+7] | ((details->samples[0][file_offset+6]&0x03) << 8); new_file.data.reserve(static_cast(data_length)); if(start_sector < 2) continue; @@ -65,7 +66,7 @@ std::unique_ptr StaticAnalyser::Acorn::GetDFSCatalogue(const std::sha if(!next_sector) break; long length_from_sector = std::min(data_length, 256l); - new_file.data.insert(new_file.data.end(), next_sector->data.begin(), next_sector->data.begin() + length_from_sector); + new_file.data.insert(new_file.data.end(), next_sector->samples[0].begin(), next_sector->samples[0].begin() + length_from_sector); data_length -= length_from_sector; } if(!data_length) catalogue->files.push_front(new_file); @@ -85,7 +86,7 @@ std::unique_ptr StaticAnalyser::Acorn::GetADFSCatalogue(const std::sh for(uint8_t c = 2; c < 7; c++) { Storage::Encodings::MFM::Sector *sector = parser.get_sector(0, 0, c); if(!sector) return nullptr; - root_directory.insert(root_directory.end(), sector->data.begin(), sector->data.end()); + root_directory.insert(root_directory.end(), sector->samples[0].begin(), sector->samples[0].end()); } // Quick sanity checks. @@ -93,7 +94,7 @@ std::unique_ptr StaticAnalyser::Acorn::GetADFSCatalogue(const std::sh if(root_directory[1] != 'H' || root_directory[2] != 'u' || root_directory[3] != 'g' || root_directory[4] != 'o') return nullptr; if(root_directory[0x4FB] != 'H' || root_directory[0x4FC] != 'u' || root_directory[0x4FD] != 'g' || root_directory[0x4FE] != 'o') return nullptr; - switch(free_space_map_second_half->data[0xfd]) { + switch(free_space_map_second_half->samples[0][0xfd]) { default: catalogue->bootOption = Catalogue::BootOption::None; break; case 1: catalogue->bootOption = Catalogue::BootOption::LoadBOOT; break; case 2: catalogue->bootOption = Catalogue::BootOption::RunBOOT; break; diff --git a/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp b/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp index 70d066e9b..943f85255 100644 --- a/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp +++ b/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp @@ -153,12 +153,12 @@ static void InspectCatalogue( static bool CheckBootSector(const std::shared_ptr &disk, StaticAnalyser::Target &target) { Storage::Encodings::MFM::Parser parser(true, disk); Storage::Encodings::MFM::Sector *boot_sector = parser.get_sector(0, 0, 0x41); - if(boot_sector != nullptr) { + if(boot_sector != nullptr && !boot_sector->samples.empty()) { // Check that the first 64 bytes of the sector aren't identical; if they are then probably // this disk was formatted and the filler byte never replaced. bool matched = true; for(size_t c = 1; c < 64; c++) { - if(boot_sector->data[c] != boot_sector->data[0]) { + if(boot_sector->samples[0][c] != boot_sector->samples[0][0]) { matched = false; break; } diff --git a/Storage/Disk/DiskImage/Formats/CPCDSK.cpp b/Storage/Disk/DiskImage/Formats/CPCDSK.cpp index ccb2a4344..921aac070 100644 --- a/Storage/Disk/DiskImage/Formats/CPCDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/CPCDSK.cpp @@ -27,7 +27,7 @@ CPCDSK::CPCDSK(const char *file_name) : head_count_ = fgetc(file_); // Used only for non-extended disks. - long size_of_a_track; + long size_of_a_track = 0; // Used only for extended disks. std::vector track_sizes; @@ -89,13 +89,33 @@ CPCDSK::CPCDSK(const char *file_name) : // Track, side, sector, size and two FDC8272-esque status bytes are stored // per sector, in both regular and extended DSK files. - sector.track = static_cast(fgetc(file_)); - sector.side = static_cast(fgetc(file_)); - sector.sector = static_cast(fgetc(file_)); + sector.address.track = static_cast(fgetc(file_)); + sector.address.side = static_cast(fgetc(file_)); + sector.address.sector = static_cast(fgetc(file_)); sector.size = static_cast(fgetc(file_)); sector.fdc_status1 = static_cast(fgetc(file_)); sector.fdc_status2 = static_cast(fgetc(file_)); + if(sector.fdc_status2 & 0x20) { + // The CRC failed in the data field. + sector.has_data_crc_error = true; + } else { + if(sector.fdc_status1 & 0x20) { + // The CRC failed in the ID field. + sector.has_header_crc_error = true; + } + } + + if(sector.fdc_status2 & 0x40) { + // This sector is marked as deleted. + sector.is_deleted = true; + } + + if(sector.fdc_status2 & 0x01) { + // Data field wasn't found. + sector.samples.clear(); + } + // Figuring out the actual data size is a little more work... size_t data_size = static_cast(128 << sector.size); size_t stored_data_size = data_size; @@ -130,16 +150,17 @@ CPCDSK::CPCDSK(const char *file_name) : // As per the weak/fuzzy sector extension, multiple samplings may be stored here. // Plan to tead as many as there were. - sector.data.resize(number_of_samplings); + sector.samples.emplace_back(); + sector.samples.resize(number_of_samplings); while(number_of_samplings--) { - sector.data[number_of_samplings].resize(stored_data_size); + sector.samples[number_of_samplings].resize(stored_data_size); } } - + // Sector contents are at offset 0x100 into the track. fseek(file_, file_offset + 0x100, SEEK_SET); for(auto §or: track->sectors) { - for(auto &data : sector.data) { + for(auto &data : sector.samples) { fread(data.data(), 1, data.size(), file_); } } @@ -174,40 +195,9 @@ std::shared_ptr CPCDSK::get_track_at_position(::Storage::Disk::Track::Add Track *track = tracks_[chronological_track].get(); if(!track) return nullptr; - // Transcribe sectors and return. - // TODO: is this transcription really necessary? - std::vector sectors; + std::vector sectors; for(auto §or : track->sectors) { - Storage::Encodings::MFM::Sector new_sector; - new_sector.address.track = sector.track; - new_sector.address.side = sector.side; - new_sector.address.sector = sector.sector; - new_sector.size = sector.size; - - // TODO: deal with weak/fuzzy sectors. - new_sector.data.insert(new_sector.data.begin(), sector.data[0].begin(), sector.data[0].end()); - - if(sector.fdc_status2 & 0x20) { - // The CRC failed in the data field. - new_sector.has_data_crc_error = true; - } else { - if(sector.fdc_status1 & 0x20) { - // The CRC failed in the ID field. - new_sector.has_header_crc_error = true; - } - } - - if(sector.fdc_status2 & 0x40) { - // This sector is marked as deleted. - new_sector.is_deleted = true; - } - - if(sector.fdc_status2 & 0x01) { - // Data field wasn't found. - new_sector.data.clear(); - } - - sectors.push_back(std::move(new_sector)); + sectors.push_back(§or); } // TODO: FM encoding, data rate? diff --git a/Storage/Disk/DiskImage/Formats/CPCDSK.hpp b/Storage/Disk/DiskImage/Formats/CPCDSK.hpp index d5670d9b1..905d58d1f 100644 --- a/Storage/Disk/DiskImage/Formats/CPCDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/CPCDSK.hpp @@ -11,6 +11,7 @@ #include "../DiskImage.hpp" #include "../../../FileHolder.hpp" +#include "../../Encodings/MFM/Sector.hpp" #include @@ -54,17 +55,9 @@ class CPCDSK: public DiskImage, public Storage::FileHolder { uint8_t gap3_length; uint8_t filler_byte; - struct Sector { - uint8_t track; - uint8_t side; - uint8_t sector; - uint8_t size; + struct Sector: public ::Storage::Encodings::MFM::Sector { uint8_t fdc_status1; uint8_t fdc_status2; - - // If multiple copies are present, that implies a sector with weak bits, for which multiple - // samplings were obtained. - std::vector> data; }; std::vector sectors; diff --git a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp index 21ab01df6..5c1f62852 100644 --- a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp +++ b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp @@ -31,7 +31,8 @@ std::shared_ptr Storage::Disk::track_for_sectors(uint8_t *const source, u first_sector++; new_sector.size = size; - new_sector.data.insert(new_sector.data.begin(), source + source_pointer, source + source_pointer + byte_size); + new_sector.samples.emplace_back(); + new_sector.samples[0].insert(new_sector.samples[0].begin(), source + source_pointer, source + source_pointer + byte_size); source_pointer += byte_size; } @@ -53,6 +54,7 @@ void Storage::Disk::decode_sectors(Track &track, uint8_t *const destination, uin if(pair.second.address.sector > last_sector) continue; if(pair.second.address.sector < first_sector) continue; if(pair.second.size != sector_size) continue; - memcpy(&destination[pair.second.address.sector * byte_size], pair.second.data.data(), std::min(pair.second.data.size(), byte_size)); + if(pair.second.samples.empty()) continue; + memcpy(&destination[pair.second.address.sector * byte_size], pair.second.samples[0].data(), std::min(pair.second.samples[0].size(), byte_size)); } } diff --git a/Storage/Disk/Encodings/MFM/Encoder.cpp b/Storage/Disk/Encodings/MFM/Encoder.cpp index d01da9420..b961191f3 100644 --- a/Storage/Disk/Encodings/MFM/Encoder.cpp +++ b/Storage/Disk/Encodings/MFM/Encoder.cpp @@ -119,7 +119,7 @@ class FMEncoder: public Encoder { template std::shared_ptr GetTrackWithSectors( - const std::vector §ors, + 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, uint8_t post_address_mark_value, @@ -137,38 +137,39 @@ template std::shared_ptr for(size_t c = 0; c < post_index_address_mark_bytes; c++) shifter.add_byte(post_index_address_mark_value); // add sectors - for(const Sector §or : sectors) { + for(const Sector *sector : sectors) { // gap for(size_t c = 0; c < pre_address_mark_bytes; c++) shifter.add_byte(0x00); // sector header shifter.add_ID_address_mark(); - shifter.add_byte(sector.address.track); - shifter.add_byte(sector.address.side); - shifter.add_byte(sector.address.sector); - shifter.add_byte(sector.size); - shifter.add_crc(sector.has_header_crc_error); + shifter.add_byte(sector->address.track); + shifter.add_byte(sector->address.side); + shifter.add_byte(sector->address.sector); + 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(post_address_mark_value); for(size_t c = 0; c < pre_data_mark_bytes; c++) shifter.add_byte(0x00); // data, if attached - if(!sector.data.empty()) { - if(sector.is_deleted) + // TODO: allow for weak/fuzzy data. + if(!sector->samples.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 = static_cast(128 << sector.size); - for(c = 0; c < sector.data.size() && c < declared_length; c++) { - shifter.add_byte(sector.data[c]); + size_t declared_length = static_cast(128 << sector->size); + for(c = 0; c < sector->samples[0].size() && c < declared_length; c++) { + shifter.add_byte(sector->samples[0][c]); } for(; c < declared_length; c++) { shifter.add_byte(0x00); } - shifter.add_crc(sector.has_data_crc_error); + shifter.add_crc(sector->has_data_crc_error); } // gap @@ -202,7 +203,26 @@ void Encoder::add_crc(bool incorrectly) { const size_t Storage::Encodings::MFM::DefaultSectorGapLength = std::numeric_limits::max(); +static std::vector sector_pointers(const std::vector §ors) { + std::vector pointers; + for(const Sector §or: sectors) { + pointers.push_back(§or); + } + return pointers; +} + std::shared_ptr Storage::Encodings::MFM::GetFMTrackWithSectors(const std::vector §ors, size_t sector_gap_length, uint8_t sector_gap_filler_byte) { + return GetTrackWithSectors( + sector_pointers(sectors), + 26, 0xff, + 6, + 11, 0xff, + 6, + (sector_gap_length != DefaultSectorGapLength) ? sector_gap_length : 27, 0xff, + 6250); // i.e. 250kbps (including clocks) * 60 = 15000kpm, at 300 rpm => 50 kbits/rotation => 6250 bytes/rotation +} + +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, 26, 0xff, @@ -214,6 +234,17 @@ std::shared_ptr Storage::Encodings::MFM::GetFMTrackWithSec } std::shared_ptr Storage::Encodings::MFM::GetMFMTrackWithSectors(const std::vector §ors, size_t sector_gap_length, uint8_t sector_gap_filler_byte) { + return GetTrackWithSectors( + sector_pointers(sectors), + 50, 0x4e, + 12, + 22, 0x4e, + 12, + (sector_gap_length != DefaultSectorGapLength) ? sector_gap_length : 54, 0xff, + 12500); // unintelligently: double the single-density bytes/rotation (or: 500kbps @ 300 rpm) +} + +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, diff --git a/Storage/Disk/Encodings/MFM/Encoder.hpp b/Storage/Disk/Encodings/MFM/Encoder.hpp index 41cd6f007..791f10450 100644 --- a/Storage/Disk/Encodings/MFM/Encoder.hpp +++ b/Storage/Disk/Encodings/MFM/Encoder.hpp @@ -28,6 +28,7 @@ extern const size_t DefaultSectorGapLength; @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); +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. @@ -37,6 +38,7 @@ std::shared_ptr GetMFMTrackWithSectors(const std::vector GetFMTrackWithSectors(const std::vector §ors, size_t sector_gap_length = DefaultSectorGapLength, uint8_t sector_gap_filler_byte = 0x4e); +std::shared_ptr GetFMTrackWithSectors(const std::vector §ors, size_t sector_gap_length = DefaultSectorGapLength, uint8_t sector_gap_filler_byte = 0x4e); class Encoder { public: diff --git a/Storage/Disk/Encodings/MFM/Sector.hpp b/Storage/Disk/Encodings/MFM/Sector.hpp index 559f770f2..38db6683e 100644 --- a/Storage/Disk/Encodings/MFM/Sector.hpp +++ b/Storage/Disk/Encodings/MFM/Sector.hpp @@ -34,7 +34,9 @@ struct Sector { Address address; uint8_t size = 0; - std::vector data; + + // Multiple samplings of the underlying data are accepted, to allow weak and fuzzy data to be communicated. + std::vector> samples; bool has_data_crc_error = false; bool has_header_crc_error = false; @@ -45,7 +47,7 @@ struct Sector { Sector(const Sector &&rhs) noexcept : address(rhs.address), size(rhs.size), - data(std::move(rhs.data)), + samples(std::move(rhs.samples)), has_data_crc_error(rhs.has_data_crc_error), has_header_crc_error(rhs.has_header_crc_error), is_deleted(rhs.is_deleted ){} diff --git a/Storage/Disk/Encodings/MFM/SegmentParser.cpp b/Storage/Disk/Encodings/MFM/SegmentParser.cpp index 25fed0618..d40d6431b 100644 --- a/Storage/Disk/Encodings/MFM/SegmentParser.cpp +++ b/Storage/Disk/Encodings/MFM/SegmentParser.cpp @@ -62,7 +62,8 @@ std::map Storage::Encodings::MFM::secto shifter.set_should_obey_syncs(true); break; default: - new_sector->data.push_back(shifter.get_byte()); + if(new_sector->samples.empty()) new_sector->samples.emplace_back(); + new_sector->samples[0].push_back(shifter.get_byte()); ++position; if(position == size + 4) { result.insert(std::make_pair(start_location, std::move(*new_sector))); diff --git a/Storage/Disk/Parsers/CPM.cpp b/Storage/Disk/Parsers/CPM.cpp index 0a4889664..81e26b6ae 100644 --- a/Storage/Disk/Parsers/CPM.cpp +++ b/Storage/Disk/Parsers/CPM.cpp @@ -27,12 +27,12 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( size_t size_read = 0; do { Storage::Encodings::MFM::Sector *sector_contents = parser.get_sector(0, static_cast(track), static_cast(parameters.first_sector + sector)); - if(!sector_contents) { + if(!sector_contents || sector_contents->samples.empty()) { return nullptr; } - catalogue.insert(catalogue.end(), sector_contents->data.begin(), sector_contents->data.end()); - sector_size = sector_contents->data.size(); + catalogue.insert(catalogue.end(), sector_contents->samples[0].begin(), sector_contents->samples[0].end()); + sector_size = sector_contents->samples[0].size(); size_read += sector_size; sector++; @@ -136,7 +136,7 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( for(int s = 0; s < sectors_per_block && record < number_of_records; s++) { Storage::Encodings::MFM::Sector *sector_contents = parser.get_sector(0, static_cast(track), static_cast(parameters.first_sector + sector)); - if(!sector_contents) break; + if(!sector_contents || sector_contents->samples.empty()) break; sector++; if(sector == parameters.sectors_per_track) { sector = 0; @@ -144,7 +144,7 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( } int records_to_copy = std::min(entry->number_of_records - record, records_per_sector); - memcpy(&new_file.data[entry->extent * bytes_per_catalogue_entry + static_cast(record) * 128], sector_contents->data.data(), static_cast(records_to_copy) * 128); + memcpy(&new_file.data[entry->extent * bytes_per_catalogue_entry + static_cast(record) * 128], sector_contents->samples[0].data(), static_cast(records_to_copy) * 128); record += records_to_copy; } }