mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-27 01:31:42 +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_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 §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_;
|
||||
}
|
||||
|
||||
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 §or_info : sector_infos) {
|
||||
for(auto §or : 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);
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user