1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-04-09 00:37:27 +00:00

Generalises the concept of multiple samplings of an FM/MFM sector, simplifying CPC DSK support and paving the way for generic weak/fuzzy bit support.

This commit is contained in:
Thomas Harte 2017-10-31 21:32:28 -04:00
parent 833f8c02a4
commit f807a6b608
10 changed files with 110 additions and 88 deletions

View File

@ -23,17 +23,18 @@ std::unique_ptr<Catalogue> 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<Catalogue> 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<long>(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<long>(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<size_t>(data_length));
if(start_sector < 2) continue;
@ -65,7 +66,7 @@ std::unique_ptr<Catalogue> 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<Catalogue> 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<Catalogue> 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;

View File

@ -153,12 +153,12 @@ static void InspectCatalogue(
static bool CheckBootSector(const std::shared_ptr<Storage::Disk::Disk> &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;
}

View File

@ -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<size_t> 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<uint8_t>(fgetc(file_));
sector.side = static_cast<uint8_t>(fgetc(file_));
sector.sector = static_cast<uint8_t>(fgetc(file_));
sector.address.track = static_cast<uint8_t>(fgetc(file_));
sector.address.side = static_cast<uint8_t>(fgetc(file_));
sector.address.sector = static_cast<uint8_t>(fgetc(file_));
sector.size = static_cast<uint8_t>(fgetc(file_));
sector.fdc_status1 = static_cast<uint8_t>(fgetc(file_));
sector.fdc_status2 = static_cast<uint8_t>(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<size_t>(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 &sector: 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<Track> 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<Storage::Encodings::MFM::Sector> sectors;
std::vector<const Storage::Encodings::MFM::Sector *> sectors;
for(auto &sector : 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(&sector);
}
// TODO: FM encoding, data rate?

View File

@ -11,6 +11,7 @@
#include "../DiskImage.hpp"
#include "../../../FileHolder.hpp"
#include "../../Encodings/MFM/Sector.hpp"
#include <vector>
@ -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<std::vector<uint8_t>> data;
};
std::vector<Sector> sectors;

View File

@ -31,7 +31,8 @@ std::shared_ptr<Track> 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));
}
}

View File

@ -119,7 +119,7 @@ class FMEncoder: public Encoder {
template<class T> std::shared_ptr<Storage::Disk::Track>
GetTrackWithSectors(
const std::vector<Sector> &sectors,
const std::vector<const 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, uint8_t post_address_mark_value,
@ -137,38 +137,39 @@ template<class T> std::shared_ptr<Storage::Disk::Track>
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 &sector : 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<size_t>(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<size_t>(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<size_t>::max();
static std::vector<const Sector *> sector_pointers(const std::vector<Sector> &sectors) {
std::vector<const Sector *> pointers;
for(const Sector &sector: sectors) {
pointers.push_back(&sector);
}
return pointers;
}
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>(
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::Disk::Track> Storage::Encodings::MFM::GetFMTrackWithSectors(const std::vector<const Sector *> &sectors, size_t sector_gap_length, uint8_t sector_gap_filler_byte) {
return GetTrackWithSectors<FMEncoder>(
sectors,
26, 0xff,
@ -214,6 +234,17 @@ std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetFMTrackWithSec
}
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>(
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::Disk::Track> Storage::Encodings::MFM::GetMFMTrackWithSectors(const std::vector<const Sector *> &sectors, size_t sector_gap_length, uint8_t sector_gap_filler_byte) {
return GetTrackWithSectors<MFMEncoder>(
sectors,
50, 0x4e,

View File

@ -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<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> GetMFMTrackWithSectors(const std::vector<const 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.
@ -37,6 +38,7 @@ std::shared_ptr<Storage::Disk::Track> GetMFMTrackWithSectors(const std::vector<S
@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);
std::shared_ptr<Storage::Disk::Track> GetFMTrackWithSectors(const std::vector<const Sector *> &sectors, size_t sector_gap_length = DefaultSectorGapLength, uint8_t sector_gap_filler_byte = 0x4e);
class Encoder {
public:

View File

@ -34,7 +34,9 @@ struct Sector {
Address address;
uint8_t size = 0;
std::vector<uint8_t> data;
// Multiple samplings of the underlying data are accepted, to allow weak and fuzzy data to be communicated.
std::vector<std::vector<uint8_t>> 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 ){}

View File

@ -62,7 +62,8 @@ std::map<size_t, Storage::Encodings::MFM::Sector> 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)));

View File

@ -27,12 +27,12 @@ std::unique_ptr<Storage::Disk::CPM::Catalogue> Storage::Disk::CPM::GetCatalogue(
size_t size_read = 0;
do {
Storage::Encodings::MFM::Sector *sector_contents = parser.get_sector(0, static_cast<uint8_t>(track), static_cast<uint8_t>(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::Catalogue> 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<uint8_t>(track), static_cast<uint8_t>(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::Catalogue> 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<size_t>(record) * 128], sector_contents->data.data(), static_cast<size_t>(records_to_copy) * 128);
memcpy(&new_file.data[entry->extent * bytes_per_catalogue_entry + static_cast<size_t>(record) * 128], sector_contents->samples[0].data(), static_cast<size_t>(records_to_copy) * 128);
record += records_to_copy;
}
}