1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-06 01:28:57 +00:00

Switches the CPC DSK implementation to building an in-memory version of the structure up front.

Preparatory to making these things writeable.
This commit is contained in:
Thomas Harte 2017-10-31 19:41:16 -04:00
parent 0248c6a282
commit 833f8c02a4
2 changed files with 173 additions and 91 deletions

View File

@ -26,14 +26,133 @@ CPCDSK::CPCDSK(const char *file_name) :
head_position_count_ = fgetc(file_);
head_count_ = fgetc(file_);
// Used only for non-extended disks.
long size_of_a_track;
// Used only for extended disks.
std::vector<size_t> track_sizes;
if(is_extended_) {
// Skip two unused bytes and grab the track size table.
fseek(file_, 2, SEEK_CUR);
for(int c = 0; c < head_position_count_ * head_count_; c++) {
track_sizes_.push_back(static_cast<size_t>(fgetc(file_) << 8));
track_sizes.push_back(static_cast<size_t>(fgetc(file_) << 8));
}
} else {
size_of_a_track_ = fgetc16le();
size_of_a_track = fgetc16le();
}
long file_offset = 0x100;
for(size_t c = 0; c < static_cast<size_t>(head_position_count_ * head_count_); c++) {
if(!is_extended_ || (track_sizes[c] > 0)) {
// Skip the introductory text, 'Track-Info\r\n' and its unused bytes.
fseek(file_, file_offset + 16, SEEK_SET);
tracks_.emplace_back(new Track);
Track *track = tracks_.back().get();
// Track and side are stored, being a byte each.
track->track = static_cast<uint8_t>(fgetc(file_));
track->side = static_cast<uint8_t>(fgetc(file_));
// If this is an extended disk image then John Elliott's extension provides some greater
// data rate and encoding context. Otherwise the next two bytes have no defined meaning.
if(is_extended_) {
switch(fgetc(file_)) {
default: track->data_rate = Track::DataRate::Unknown; break;
case 1: track->data_rate = Track::DataRate::SingleOrDoubleDensity; break;
case 2: track->data_rate = Track::DataRate::HighDensity; break;
case 3: track->data_rate = Track::DataRate::ExtendedDensity; break;
}
switch(fgetc(file_)) {
default: track->data_encoding = Track::DataEncoding::Unknown; break;
case 1: track->data_encoding = Track::DataEncoding::FM; break;
case 2: track->data_encoding = Track::Track::DataEncoding::MFM; break;
}
} else {
track->data_rate = Track::DataRate::Unknown;
track->data_encoding = Track::DataEncoding::Unknown;
fseek(file_, 2, SEEK_CUR);
}
// Sector size, number of sectors, gap 3 length and the filler byte are then common
// between both variants of DSK.
track->sector_length = static_cast<uint8_t>(fgetc(file_));
size_t number_of_sectors = static_cast<size_t>(fgetc(file_));
track->gap3_length = static_cast<uint8_t>(fgetc(file_));
track->filler_byte = static_cast<uint8_t>(fgetc(file_));
// Sector information begins immediately after the track information table.
while(number_of_sectors--) {
track->sectors.emplace_back();
Track::Sector &sector = track->sectors.back();
// 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.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_));
// 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;
size_t number_of_samplings = 1;
if(is_extended_) {
// In an extended DSK, oblige two Simon Owen extensions:
//
// Allow a declared data size less than the sector's declared size to act as an abbreviation.
// Extended DSK varies the 8kb -> 0x1800 bytes special case by this means.
//
// Use a declared data size greater than the sector's declared size as a record that this
// sector was weak or fuzzy and that multiple samplings are provided. If the greater size
// is not an exact multiple then my reading of the documentation is that this is an invalid
// disk image.
size_t declared_data_size = fgetc16le();
if(declared_data_size != stored_data_size) {
if(declared_data_size > data_size) {
number_of_samplings = declared_data_size / data_size;
if(declared_data_size % data_size)
throw ErrorNotCPCDSK;
} else {
stored_data_size = declared_data_size;
}
}
} else {
// In a regular DSK, these two bytes are unused, and a special case is applied that ostensibly 8kb
// sectors are abbreviated to only 0x1800 bytes.
if(data_size == 0x2000) stored_data_size = 0x1800;
fseek(file_, 2, SEEK_CUR);
}
// 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);
while(number_of_samplings--) {
sector.data[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) {
fread(data.data(), 1, data.size(), file_);
}
}
} else {
// An extended disk image, which declares that there is no data stored for this track.
tracks_.emplace_back();
}
// Advance to the beginning of the next track.
if(is_extended_)
file_offset += static_cast<long>(track_sizes[c]);
else
file_offset += size_of_a_track;
}
}
@ -45,101 +164,45 @@ int CPCDSK::get_head_count() {
return head_count_;
}
std::shared_ptr<Track> CPCDSK::get_track_at_position(Track::Address address) {
std::shared_ptr<Track> CPCDSK::get_track_at_position(::Storage::Disk::Track::Address address) {
// Given that thesea are interleaved images, determine which track, chronologically, is being requested.
size_t chronological_track = static_cast<size_t>((address.position * head_count_) + address.head);
// All DSK images reserve 0x100 bytes for their headers.
long file_offset = 0x100;
if(is_extended_) {
// Tracks are a variable size in the original DSK file format.
// Return a nullptr if out of range or not provided.
if(chronological_track >= tracks_.size()) return nullptr;
Track *track = tracks_[chronological_track].get();
if(!track) return nullptr;
// Check that there is anything stored for this track.
if(!track_sizes_[chronological_track]) {
return nullptr;
}
// Sum the lengths of all tracks prior to the interesting one to get a file offset.
size_t t = 0;
while(t < chronological_track && t < track_sizes_.size()) {
file_offset += track_sizes_[t];
t++;
}
} else {
// Tracks are a fixed size in the original DSK file format.
file_offset += size_of_a_track_ * static_cast<long>(chronological_track);
}
// Find the track, and skip the unused part of track information.
fseek(file_, file_offset + 16, SEEK_SET);
// 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_);
uint8_t gap3_length = static_cast<uint8_t>(fgetc(file_));
uint8_t filler_byte = static_cast<uint8_t>(fgetc(file_));
// Grab the sector information
struct SectorInfo {
uint8_t track;
uint8_t side;
uint8_t sector;
uint8_t length;
uint8_t status1;
uint8_t status2;
size_t actual_length;
};
std::vector<SectorInfo> sector_infos;
while(number_of_sectors--) {
SectorInfo sector_info;
sector_info.track = static_cast<uint8_t>(fgetc(file_));
sector_info.side = static_cast<uint8_t>(fgetc(file_));
sector_info.sector = static_cast<uint8_t>(fgetc(file_));
sector_info.length = static_cast<uint8_t>(fgetc(file_));
sector_info.status1 = static_cast<uint8_t>(fgetc(file_));
sector_info.status2 = static_cast<uint8_t>(fgetc(file_));
sector_info.actual_length = fgetc16le();
sector_infos.push_back(sector_info);
}
// Get the sectors.
fseek(file_, file_offset + 0x100, SEEK_SET);
// Transcribe sectors and return.
// TODO: is this transcription really necessary?
std::vector<Storage::Encodings::MFM::Sector> sectors;
for(auto &sector_info : sector_infos) {
for(auto &sector : track->sectors) {
Storage::Encodings::MFM::Sector new_sector;
new_sector.address.track = sector_info.track;
new_sector.address.side = sector_info.side;
new_sector.address.sector = sector_info.sector;
new_sector.size = sector_info.length;
new_sector.address.track = sector.track;
new_sector.address.side = sector.side;
new_sector.address.sector = sector.sector;
new_sector.size = sector.size;
size_t data_size;
if(is_extended_) {
data_size = sector_info.actual_length;
} else {
data_size = static_cast<size_t>(128 << sector_info.length);
if(data_size == 0x2000) data_size = 0x1800;
}
new_sector.data.resize(data_size);
fread(new_sector.data.data(), sizeof(uint8_t), data_size, file_);
// TODO: deal with weak/fuzzy sectors.
new_sector.data.insert(new_sector.data.begin(), sector.data[0].begin(), sector.data[0].end());
if(sector_info.status2 & 0x20) {
if(sector.fdc_status2 & 0x20) {
// The CRC failed in the data field.
new_sector.has_data_crc_error = true;
} else {
if(sector_info.status1 & 0x20) {
if(sector.fdc_status1 & 0x20) {
// The CRC failed in the ID field.
new_sector.has_header_crc_error = true;
}
}
if(sector_info.status2 & 0x40) {
if(sector.fdc_status2 & 0x40) {
// This sector is marked as deleted.
new_sector.is_deleted = true;
}
if(sector_info.status2 & 0x01) {
if(sector.fdc_status2 & 0x01) {
// Data field wasn't found.
new_sector.data.clear();
}
@ -147,11 +210,6 @@ std::shared_ptr<Track> CPCDSK::get_track_at_position(Track::Address address) {
sectors.push_back(std::move(new_sector));
}
// 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;
// TODO: FM encoding, data rate?
return Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors, track->gap3_length, track->filler_byte);
}

View File

@ -38,18 +38,42 @@ class CPCDSK: public DiskImage, public Storage::FileHolder {
int get_head_position_count() override;
int get_head_count() override;
using DiskImage::get_is_read_only;
std::shared_ptr<Track> get_track_at_position(Track::Address address) override;
std::shared_ptr<::Storage::Disk::Track> get_track_at_position(::Storage::Disk::Track::Address address) override;
private:
struct Track {
uint8_t track;
uint8_t side;
enum class DataRate {
Unknown, SingleOrDoubleDensity, HighDensity, ExtendedDensity
} data_rate;
enum class DataEncoding {
Unknown, FM, MFM
} data_encoding;
uint8_t sector_length;
uint8_t gap3_length;
uint8_t filler_byte;
struct Sector {
uint8_t track;
uint8_t side;
uint8_t sector;
uint8_t size;
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;
};
std::vector<std::unique_ptr<Track>> tracks_;
int head_count_;
int head_position_count_;
bool is_extended_;
// Used only for non-extended disks.
long size_of_a_track_;
// Used only for extended disks.
std::vector<size_t> track_sizes_;
};
}