mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-18 01:30:56 +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:
parent
0248c6a282
commit
833f8c02a4
@ -26,14 +26,133 @@ CPCDSK::CPCDSK(const char *file_name) :
|
|||||||
head_position_count_ = fgetc(file_);
|
head_position_count_ = fgetc(file_);
|
||||||
head_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_) {
|
if(is_extended_) {
|
||||||
// Skip two unused bytes and grab the track size table.
|
// Skip two unused bytes and grab the track size table.
|
||||||
fseek(file_, 2, SEEK_CUR);
|
fseek(file_, 2, SEEK_CUR);
|
||||||
for(int c = 0; c < head_position_count_ * head_count_; c++) {
|
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 {
|
} 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 §or = 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 §or: 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_;
|
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.
|
// 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);
|
size_t chronological_track = static_cast<size_t>((address.position * head_count_) + address.head);
|
||||||
|
|
||||||
// All DSK images reserve 0x100 bytes for their headers.
|
// Return a nullptr if out of range or not provided.
|
||||||
long file_offset = 0x100;
|
if(chronological_track >= tracks_.size()) return nullptr;
|
||||||
if(is_extended_) {
|
|
||||||
// Tracks are a variable size in the original DSK file format.
|
Track *track = tracks_[chronological_track].get();
|
||||||
|
if(!track) return nullptr;
|
||||||
|
|
||||||
// Check that there is anything stored for this track.
|
// Transcribe sectors and return.
|
||||||
if(!track_sizes_[chronological_track]) {
|
// TODO: is this transcription really necessary?
|
||||||
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);
|
|
||||||
std::vector<Storage::Encodings::MFM::Sector> sectors;
|
std::vector<Storage::Encodings::MFM::Sector> sectors;
|
||||||
for(auto §or_info : sector_infos) {
|
for(auto §or : track->sectors) {
|
||||||
Storage::Encodings::MFM::Sector new_sector;
|
Storage::Encodings::MFM::Sector new_sector;
|
||||||
new_sector.address.track = sector_info.track;
|
new_sector.address.track = sector.track;
|
||||||
new_sector.address.side = sector_info.side;
|
new_sector.address.side = sector.side;
|
||||||
new_sector.address.sector = sector_info.sector;
|
new_sector.address.sector = sector.sector;
|
||||||
new_sector.size = sector_info.length;
|
new_sector.size = sector.size;
|
||||||
|
|
||||||
size_t data_size;
|
// TODO: deal with weak/fuzzy sectors.
|
||||||
if(is_extended_) {
|
new_sector.data.insert(new_sector.data.begin(), sector.data[0].begin(), sector.data[0].end());
|
||||||
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_);
|
|
||||||
|
|
||||||
if(sector_info.status2 & 0x20) {
|
if(sector.fdc_status2 & 0x20) {
|
||||||
// The CRC failed in the data field.
|
// The CRC failed in the data field.
|
||||||
new_sector.has_data_crc_error = true;
|
new_sector.has_data_crc_error = true;
|
||||||
} else {
|
} else {
|
||||||
if(sector_info.status1 & 0x20) {
|
if(sector.fdc_status1 & 0x20) {
|
||||||
// The CRC failed in the ID field.
|
// The CRC failed in the ID field.
|
||||||
new_sector.has_header_crc_error = true;
|
new_sector.has_header_crc_error = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sector_info.status2 & 0x40) {
|
if(sector.fdc_status2 & 0x40) {
|
||||||
// This sector is marked as deleted.
|
// This sector is marked as deleted.
|
||||||
new_sector.is_deleted = true;
|
new_sector.is_deleted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sector_info.status2 & 0x01) {
|
if(sector.fdc_status2 & 0x01) {
|
||||||
// Data field wasn't found.
|
// Data field wasn't found.
|
||||||
new_sector.data.clear();
|
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));
|
sectors.push_back(std::move(new_sector));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: extensions to the extended format; John Elliot's addition of single-density support,
|
// TODO: FM encoding, data rate?
|
||||||
// and Simon Owen's weak/random sectors, subject to adding some logic to pick a potential
|
return Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors, track->gap3_length, track->filler_byte);
|
||||||
// FM/MFM encoding that can produce specified weak values.
|
|
||||||
|
|
||||||
if(sectors.size()) return Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors, gap3_length, filler_byte);
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
@ -38,18 +38,42 @@ class CPCDSK: public DiskImage, public Storage::FileHolder {
|
|||||||
int get_head_position_count() override;
|
int get_head_position_count() override;
|
||||||
int get_head_count() override;
|
int get_head_count() override;
|
||||||
using DiskImage::get_is_read_only;
|
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:
|
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_count_;
|
||||||
int head_position_count_;
|
int head_position_count_;
|
||||||
bool is_extended_;
|
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_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user