1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-30 19:30:17 +00:00

Merge pull request #424 from TomHarte/TrackDivision

Makes disk head position explicitly something with sub-integral precision.
This commit is contained in:
Thomas Harte 2018-05-06 23:19:56 -04:00 committed by GitHub
commit d1b889aa61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 135 additions and 97 deletions

View File

@ -45,9 +45,11 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
if(difference) { if(difference) {
int direction = difference < 0 ? -1 : 1; int direction = difference < 0 ? -1 : 1;
difference *= 2 * direction; difference *= direction;
for(int c = 0; c < difference; c++) get_drive().step(direction); for(int c = 0; c < difference; c++) {
get_drive().step(Storage::Disk::HeadPosition(direction));
}
unsigned int zone = 3; unsigned int zone = 3;
if(track >= 18) zone = 2; if(track >= 18) zone = 2;

View File

@ -49,7 +49,7 @@ Analyser::Static::TargetList Analyser::Static::DiskII::GetTargets(const Media &m
if(media.disks.empty()) return {}; if(media.disks.empty()) return {};
// Grab track 0, sector 0: the boot sector. // Grab track 0, sector 0: the boot sector.
auto track_zero = media.disks.front()->get_track_at_position(Storage::Disk::Track::Address(0, 0)); auto track_zero = media.disks.front()->get_track_at_position(Storage::Disk::Track::Address(0, Storage::Disk::HeadPosition(0)));
auto sector_map = Storage::Encodings::AppleGCR::sectors_from_segment( auto sector_map = Storage::Encodings::AppleGCR::sectors_from_segment(
Storage::Disk::track_serialisation(*track_zero, Storage::Time(1, 50000))); Storage::Disk::track_serialisation(*track_zero, Storage::Time(1, 50000)));

View File

@ -282,7 +282,7 @@ void WD1770::posit_event(int new_event_type) {
track_ = 0; track_ = 0;
goto verify; goto verify;
} }
get_drive().step(step_direction_ ? 1 : -1); get_drive().step(Storage::Disk::HeadPosition(step_direction_ ? 1 : -1));
unsigned int time_to_wait; unsigned int time_to_wait;
switch(command_ & 3) { switch(command_ & 3) {
default: default:

View File

@ -115,7 +115,7 @@ void i8272::run_for(Cycles cycles) {
int direction = (drives_[c].target_head_position < drives_[c].head_position) ? -1 : 1; int direction = (drives_[c].target_head_position < drives_[c].head_position) ? -1 : 1;
printf("Target %d versus believed %d\n", drives_[c].target_head_position, drives_[c].head_position); printf("Target %d versus believed %d\n", drives_[c].target_head_position, drives_[c].head_position);
select_drive(c); select_drive(c);
get_drive().step(direction); get_drive().step(Storage::Disk::HeadPosition(direction));
if(drives_[c].target_head_position >= 0) drives_[c].head_position += direction; if(drives_[c].target_head_position >= 0) drives_[c].head_position += direction;
// Check for completion. // Check for completion.

View File

@ -57,7 +57,7 @@ void DiskII::set_control(Control control, bool on) {
// Compare to the stepper position to decide whether that pulls in the current cog notch, // Compare to the stepper position to decide whether that pulls in the current cog notch,
// or grabs a later one. // or grabs a later one.
drives_[active_drive_].step(-direction); drives_[active_drive_].step(Storage::Disk::HeadPosition(-direction, 4));
stepper_position_ = (stepper_position_ - direction + 8) & 7; stepper_position_ = (stepper_position_ - direction + 8) & 7;
} }
} }

View File

@ -142,7 +142,7 @@ void MachineBase::process_index_hole() {}
// MARK: - Drive VIA delegate // MARK: - Drive VIA delegate
void MachineBase::drive_via_did_step_head(void *driveVIA, int direction) { void MachineBase::drive_via_did_step_head(void *driveVIA, int direction) {
drive_->step(direction); drive_->step(Storage::Disk::HeadPosition(direction, 2));
} }
void MachineBase::drive_via_did_set_data_density(void *driveVIA, int density) { void MachineBase::drive_via_did_set_data_density(void *driveVIA, int density) {

View File

@ -31,7 +31,7 @@ class Disk {
This is not necessarily a track count. There is no implicit guarantee that every position will This is not necessarily a track count. There is no implicit guarantee that every position will
return a distinct track, or e.g. if the media is holeless will return any track at all. return a distinct track, or e.g. if the media is holeless will return any track at all.
*/ */
virtual int get_head_position_count() = 0; virtual HeadPosition get_maximum_head_position() = 0;
/*! /*!
@returns the number of heads (and, therefore, impliedly surfaces) available on this disk. @returns the number of heads (and, therefore, impliedly surfaces) available on this disk.

View File

@ -35,12 +35,12 @@ class DiskImage {
virtual ~DiskImage() {} virtual ~DiskImage() {}
/*! /*!
@returns the number of discrete positions that this disk uses to model its complete surface area. @returns the distance at which there stops being any further content.
This is not necessarily a track count. There is no implicit guarantee that every position will This is not necessarily a track count. There is no implicit guarantee that every position will
return a distinct track, or e.g. if the media is holeless will return any track at all. return a distinct track, or e.g. if the media is holeless will return any track at all.
*/ */
virtual int get_head_position_count() = 0; virtual HeadPosition get_maximum_head_position() = 0;
/*! /*!
@returns the number of heads (and, therefore, impliedly surfaces) available on this disk. @returns the number of heads (and, therefore, impliedly surfaces) available on this disk.
@ -87,7 +87,7 @@ template <typename T> class DiskImageHolder: public DiskImageHolderBase {
disk_image_(args...) {} disk_image_(args...) {}
~DiskImageHolder(); ~DiskImageHolder();
int get_head_position_count(); HeadPosition get_maximum_head_position();
int get_head_count(); int get_head_count();
std::shared_ptr<Track> get_track_at_position(Track::Address address); std::shared_ptr<Track> get_track_at_position(Track::Address address);
void set_track_at_position(Track::Address address, const std::shared_ptr<Track> &track); void set_track_at_position(Track::Address address, const std::shared_ptr<Track> &track);

View File

@ -6,8 +6,8 @@
// Copyright © 2017 Thomas Harte. All rights reserved. // Copyright © 2017 Thomas Harte. All rights reserved.
// //
template <typename T> int DiskImageHolder<T>::get_head_position_count() { template <typename T> HeadPosition DiskImageHolder<T>::get_maximum_head_position() {
return disk_image_.get_head_position_count(); return disk_image_.get_maximum_head_position();
} }
template <typename T> int DiskImageHolder<T>::get_head_count() { template <typename T> int DiskImageHolder<T>::get_head_count() {
@ -44,7 +44,7 @@ template <typename T> void DiskImageHolder<T>::set_track_at_position(Track::Addr
template <typename T> std::shared_ptr<Track> DiskImageHolder<T>::get_track_at_position(Track::Address address) { template <typename T> std::shared_ptr<Track> DiskImageHolder<T>::get_track_at_position(Track::Address address) {
if(address.head >= get_head_count()) return nullptr; if(address.head >= get_head_count()) return nullptr;
if(address.position >= get_head_position_count()) return nullptr; if(address.position >= get_maximum_head_position()) return nullptr;
auto cached_track = cached_tracks_.find(address); auto cached_track = cached_tracks_.find(address);
if(cached_track != cached_tracks_.end()) return cached_track->second; if(cached_track != cached_tracks_.end()) return cached_track->second;

View File

@ -36,8 +36,8 @@ AcornADF::AcornADF(const std::string &file_name) : MFMSectorDump(file_name) {
set_geometry(sectors_per_track, sector_size, 0, true); set_geometry(sectors_per_track, sector_size, 0, true);
} }
int AcornADF::get_head_position_count() { HeadPosition AcornADF::get_maximum_head_position() {
return 80; return HeadPosition(80);
} }
int AcornADF::get_head_count() { int AcornADF::get_head_count() {
@ -45,5 +45,5 @@ int AcornADF::get_head_count() {
} }
long AcornADF::get_file_offset_for_position(Track::Address address) { long AcornADF::get_file_offset_for_position(Track::Address address) {
return (address.position * 1 + address.head) * (128 << sector_size) * sectors_per_track; return address.position.as_int() * (128 << sector_size) * sectors_per_track;
} }

View File

@ -29,7 +29,7 @@ class AcornADF: public MFMSectorDump {
*/ */
AcornADF(const std::string &file_name); AcornADF(const std::string &file_name);
int get_head_position_count() override; HeadPosition get_maximum_head_position() override;
int get_head_count() override; int get_head_count() override;
private: private:

View File

@ -38,17 +38,17 @@ AppleDSK::AppleDSK(const std::string &file_name) :
} }
} }
int AppleDSK::get_head_position_count() { HeadPosition AppleDSK::get_maximum_head_position() {
return number_of_tracks * 4; return HeadPosition(number_of_tracks);
} }
std::shared_ptr<Track> AppleDSK::get_track_at_position(Track::Address address) { std::shared_ptr<Track> AppleDSK::get_track_at_position(Track::Address address) {
const long file_offset = (address.position >> 2) * bytes_per_sector * sectors_per_track_; const long file_offset = address.position.as_int() * bytes_per_sector * sectors_per_track_;
file_.seek(file_offset, SEEK_SET); file_.seek(file_offset, SEEK_SET);
const std::vector<uint8_t> track_data = file_.read(static_cast<size_t>(bytes_per_sector * sectors_per_track_)); const std::vector<uint8_t> track_data = file_.read(static_cast<size_t>(bytes_per_sector * sectors_per_track_));
Storage::Disk::PCMSegment segment; Storage::Disk::PCMSegment segment;
const uint8_t track = static_cast<uint8_t>(address.position >> 2); const uint8_t track = static_cast<uint8_t>(address.position.as_int());
// In either case below, the code aims for exactly 50,000 bits per track. // In either case below, the code aims for exactly 50,000 bits per track.
if(sectors_per_track_ == 16) { if(sectors_per_track_ == 16) {

View File

@ -32,7 +32,7 @@ class AppleDSK: public DiskImage {
AppleDSK(const std::string &file_name); AppleDSK(const std::string &file_name);
// implemented to satisfy @c Disk // implemented to satisfy @c Disk
int get_head_position_count() override; HeadPosition get_maximum_head_position() override;
std::shared_ptr<Track> get_track_at_position(Track::Address address) override; std::shared_ptr<Track> get_track_at_position(Track::Address address) override;
private: private:

View File

@ -186,8 +186,8 @@ CPCDSK::CPCDSK(const std::string &file_name) :
} }
} }
int CPCDSK::get_head_position_count() { HeadPosition CPCDSK::get_maximum_head_position() {
return head_position_count_; return HeadPosition(head_position_count_);
} }
int CPCDSK::get_head_count() { int CPCDSK::get_head_count() {
@ -195,7 +195,7 @@ int CPCDSK::get_head_count() {
} }
std::size_t CPCDSK::index_for_track(::Storage::Disk::Track::Address address) { std::size_t CPCDSK::index_for_track(::Storage::Disk::Track::Address address) {
return static_cast<std::size_t>((address.position * head_count_) + address.head); return static_cast<std::size_t>((address.position.as_int() * head_count_) + address.head);
} }
std::shared_ptr<Track> CPCDSK::get_track_at_position(::Storage::Disk::Track::Address address) { std::shared_ptr<Track> CPCDSK::get_track_at_position(::Storage::Disk::Track::Address address) {
@ -231,14 +231,14 @@ void CPCDSK::set_tracks(const std::map<::Storage::Disk::Track::Address, std::sha
std::size_t chronological_track = index_for_track(pair.first); std::size_t chronological_track = index_for_track(pair.first);
if(chronological_track >= tracks_.size()) { if(chronological_track >= tracks_.size()) {
tracks_.resize(chronological_track+1); tracks_.resize(chronological_track+1);
head_position_count_ = pair.first.position; head_position_count_ = pair.first.position.as_int();
} }
// Get the track, or create it if necessary. // Get the track, or create it if necessary.
Track *track = tracks_[chronological_track].get(); Track *track = tracks_[chronological_track].get();
if(!track) { if(!track) {
track = new Track; track = new Track;
track->track = static_cast<uint8_t>(pair.first.position); track->track = static_cast<uint8_t>(pair.first.position.as_int());
track->side = static_cast<uint8_t>(pair.first.head); track->side = static_cast<uint8_t>(pair.first.head);
track->data_rate = Track::DataRate::SingleOrDoubleDensity; track->data_rate = Track::DataRate::SingleOrDoubleDensity;
track->data_encoding = Track::DataEncoding::MFM; track->data_encoding = Track::DataEncoding::MFM;

View File

@ -33,7 +33,7 @@ class CPCDSK: public DiskImage {
CPCDSK(const std::string &file_name); CPCDSK(const std::string &file_name);
// implemented to satisfy @c Disk // implemented to satisfy @c Disk
int get_head_position_count() override; HeadPosition get_maximum_head_position() override;
int get_head_count() override; int get_head_count() override;
bool get_is_read_only() override; bool get_is_read_only() override;

View File

@ -35,18 +35,14 @@ D64::D64(const std::string &file_name) :
} }
} }
int D64::get_head_position_count() { HeadPosition D64::get_maximum_head_position() {
return number_of_tracks_*2; return HeadPosition(number_of_tracks_);
} }
std::shared_ptr<Track> D64::get_track_at_position(Track::Address address) { std::shared_ptr<Track> D64::get_track_at_position(Track::Address address) {
// every other track is missing, as is any head above 0
if(address.position&1 || address.head)
return std::shared_ptr<Track>();
// figure out where this track starts on the disk // figure out where this track starts on the disk
int offset_to_track = 0; int offset_to_track = 0;
int tracks_to_traverse = address.position >> 1; int tracks_to_traverse = address.position.as_int();
int zone_sizes[] = {17, 7, 6, 10}; int zone_sizes[] = {17, 7, 6, 10};
int sectors_by_zone[] = {21, 19, 18, 17}; int sectors_by_zone[] = {21, 19, 18, 17};
@ -96,7 +92,7 @@ std::shared_ptr<Track> D64::get_track_at_position(Track::Address address) {
sector_data[0] = sector_data[1] = sector_data[2] = 0xff; sector_data[0] = sector_data[1] = sector_data[2] = 0xff;
uint8_t sector_number = static_cast<uint8_t>(sector); // sectors count from 0 uint8_t sector_number = static_cast<uint8_t>(sector); // sectors count from 0
uint8_t track_number = static_cast<uint8_t>((address.position >> 1) + 1); // tracks count from 1 uint8_t track_number = static_cast<uint8_t>(address.position.as_int() + 1); // tracks count from 1
uint8_t checksum = static_cast<uint8_t>(sector_number ^ track_number ^ disk_id_ ^ (disk_id_ >> 8)); uint8_t checksum = static_cast<uint8_t>(sector_number ^ track_number ^ disk_id_ ^ (disk_id_ >> 8));
uint8_t header_start[4] = { uint8_t header_start[4] = {
0x08, checksum, sector_number, track_number 0x08, checksum, sector_number, track_number

View File

@ -29,7 +29,7 @@ class D64: public DiskImage {
D64(const std::string &file_name); D64(const std::string &file_name);
// implemented to satisfy @c Disk // implemented to satisfy @c Disk
int get_head_position_count() override; HeadPosition get_maximum_head_position() 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<Track> get_track_at_position(Track::Address address) override;

View File

@ -61,8 +61,8 @@ DMK::DMK(const std::string &file_name) :
if(format) throw Error::InvalidFormat; if(format) throw Error::InvalidFormat;
} }
int DMK::get_head_position_count() { HeadPosition DMK::get_maximum_head_position() {
return head_position_count_; return HeadPosition(head_position_count_);
} }
int DMK::get_head_count() { int DMK::get_head_count() {
@ -76,7 +76,7 @@ bool DMK::get_is_read_only() {
} }
long DMK::get_file_offset_for_position(Track::Address address) { long DMK::get_file_offset_for_position(Track::Address address) {
return (address.head*head_count_ + address.position) * track_length_ + 16; return (address.head*head_count_ + address.position.as_int()) * track_length_ + 16;
} }
std::shared_ptr<::Storage::Disk::Track> DMK::get_track_at_position(::Storage::Disk::Track::Address address) { std::shared_ptr<::Storage::Disk::Track> DMK::get_track_at_position(::Storage::Disk::Track::Address address) {

View File

@ -31,7 +31,7 @@ class DMK: public DiskImage {
DMK(const std::string &file_name); DMK(const std::string &file_name);
// implemented to satisfy @c Disk // implemented to satisfy @c Disk
int get_head_position_count() override; HeadPosition get_maximum_head_position() override;
int get_head_count() override; int get_head_count() override;
bool get_is_read_only() override; bool get_is_read_only() override;

View File

@ -30,22 +30,17 @@ G64::G64(const std::string &file_name) :
maximum_track_size_ = file_.get16le(); maximum_track_size_ = file_.get16le();
} }
int G64::get_head_position_count() { HeadPosition G64::get_maximum_head_position() {
// give at least 84 tracks, to yield the normal geometry but, // give at least 84 tracks, to yield the normal geometry but,
// if there are more, shove them in // if there are more, shove them in
return number_of_tracks_ > 84 ? number_of_tracks_ : 84; return HeadPosition(number_of_tracks_ > 84 ? number_of_tracks_ : 84, 2);
} }
std::shared_ptr<Track> G64::get_track_at_position(Track::Address address) { std::shared_ptr<Track> G64::get_track_at_position(Track::Address address) {
std::shared_ptr<Track> resulting_track; std::shared_ptr<Track> resulting_track;
// if there's definitely no track here, return the empty track
// (TODO: should be supplying one with an index hole?)
if(address.position >= number_of_tracks_) return resulting_track;
if(address.head >= 1) return resulting_track;
// seek to this track's entry in the track table // seek to this track's entry in the track table
file_.seek(static_cast<long>((address.position * 4) + 0xc), SEEK_SET); file_.seek(static_cast<long>((address.position.as_half() * 4) + 0xc), SEEK_SET);
// read the track offset // read the track offset
uint32_t track_offset; uint32_t track_offset;
@ -66,7 +61,7 @@ std::shared_ptr<Track> G64::get_track_at_position(Track::Address address) {
file_.read(&track_contents[0], track_length); file_.read(&track_contents[0], track_length);
// seek to this track's entry in the speed zone table // seek to this track's entry in the speed zone table
file_.seek(static_cast<long>((address.position * 4) + 0x15c), SEEK_SET); file_.seek(static_cast<long>((address.position.as_half() * 4) + 0x15c), SEEK_SET);
// read the speed zone offsrt // read the speed zone offsrt
uint32_t speed_zone_offset; uint32_t speed_zone_offset;

View File

@ -32,7 +32,7 @@ class G64: public DiskImage {
G64(const std::string &file_name); G64(const std::string &file_name);
// implemented to satisfy @c Disk // implemented to satisfy @c Disk
int get_head_position_count() override; HeadPosition get_maximum_head_position() override;
std::shared_ptr<Track> get_track_at_position(Track::Address address) override; std::shared_ptr<Track> get_track_at_position(Track::Address address) override;
using DiskImage::get_is_read_only; using DiskImage::get_is_read_only;

View File

@ -29,8 +29,8 @@ HFE::HFE(const std::string &file_name) :
HFE::~HFE() { HFE::~HFE() {
} }
int HFE::get_head_position_count() { HeadPosition HFE::get_maximum_head_position() {
return track_count_; return HeadPosition(track_count_);
} }
int HFE::get_head_count() { int HFE::get_head_count() {
@ -47,7 +47,7 @@ int HFE::get_head_count() {
uint16_t HFE::seek_track(Track::Address address) { uint16_t HFE::seek_track(Track::Address address) {
// Get track position and length from the lookup table; data is then always interleaved // Get track position and length from the lookup table; data is then always interleaved
// based on an assumption of two heads. // based on an assumption of two heads.
file_.seek(track_list_offset_ + address.position * 4, SEEK_SET); file_.seek(track_list_offset_ + address.position.as_int() * 4, SEEK_SET);
long track_offset = static_cast<long>(file_.get16le()) << 9; long track_offset = static_cast<long>(file_.get16le()) << 9;
uint16_t track_length = file_.get16le(); uint16_t track_length = file_.get16le();

View File

@ -33,7 +33,7 @@ class HFE: public DiskImage {
~HFE(); ~HFE();
// implemented to satisfy @c Disk // implemented to satisfy @c Disk
int get_head_position_count() override; HeadPosition get_maximum_head_position() override;
int get_head_count() override; int get_head_count() override;
bool get_is_read_only() override; bool get_is_read_only() override;
void set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tracks) override; void set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tracks) override;

View File

@ -33,7 +33,7 @@ std::shared_ptr<Track> MFMSectorDump::get_track_at_position(Track::Address addre
file_.read(sectors, sizeof(sectors)); file_.read(sectors, sizeof(sectors));
} }
return track_for_sectors(sectors, static_cast<uint8_t>(address.position), static_cast<uint8_t>(address.head), first_sector_, sector_size_, is_double_density_); return track_for_sectors(sectors, sectors_per_track_, static_cast<uint8_t>(address.position.as_int()), static_cast<uint8_t>(address.head), first_sector_, sector_size_, is_double_density_);
} }
void MFMSectorDump::set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tracks) { void MFMSectorDump::set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tracks) {

View File

@ -46,8 +46,8 @@ MSXDSK::MSXDSK(const std::string &file_name) :
set_geometry(sectors_per_track, sector_size, 1, true); set_geometry(sectors_per_track, sector_size, 1, true);
} }
int MSXDSK::get_head_position_count() { HeadPosition MSXDSK::get_maximum_head_position() {
return track_count_; return HeadPosition(track_count_);
} }
int MSXDSK::get_head_count() { int MSXDSK::get_head_count() {
@ -55,5 +55,5 @@ int MSXDSK::get_head_count() {
} }
long MSXDSK::get_file_offset_for_position(Track::Address address) { long MSXDSK::get_file_offset_for_position(Track::Address address) {
return (address.position*head_count_ + address.head) * 512 * 9; return (address.position.as_int()*head_count_ + address.head) * 512 * 9;
} }

View File

@ -23,7 +23,7 @@ namespace Disk {
class MSXDSK: public MFMSectorDump { class MSXDSK: public MFMSectorDump {
public: public:
MSXDSK(const std::string &file_name); MSXDSK(const std::string &file_name);
int get_head_position_count() override; HeadPosition get_maximum_head_position() override;
int get_head_count() override; int get_head_count() override;
private: private:

View File

@ -32,15 +32,15 @@ NIB::NIB(const std::string &file_name) :
// TODO: all other validation. I.e. does this look like a GCR disk? // TODO: all other validation. I.e. does this look like a GCR disk?
} }
int NIB::get_head_position_count() { HeadPosition NIB::get_maximum_head_position() {
return number_of_tracks * 4; return HeadPosition(number_of_tracks);
} }
std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Disk::Track::Address address) { std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Disk::Track::Address address) {
// NIBs contain data for even-numbered tracks underneath a single head only. // NIBs contain data for even-numbered tracks underneath a single head only.
if(address.head) return nullptr; if(address.head) return nullptr;
const long file_track = static_cast<long>(address.position >> 2); const long file_track = static_cast<long>(address.position.as_int());
file_.seek(file_track * track_length, SEEK_SET); file_.seek(file_track * track_length, SEEK_SET);
std::vector<uint8_t> track_data = file_.read(track_length); std::vector<uint8_t> track_data = file_.read(track_length);

View File

@ -24,7 +24,7 @@ class NIB: public DiskImage {
public: public:
NIB(const std::string &file_name); NIB(const std::string &file_name);
int get_head_position_count() override; HeadPosition get_maximum_head_position() override;
std::shared_ptr<::Storage::Disk::Track> get_track_at_position(::Storage::Disk::Track::Address address) override; std::shared_ptr<::Storage::Disk::Track> get_track_at_position(::Storage::Disk::Track::Address address) override;

View File

@ -29,8 +29,8 @@ OricMFMDSK::OricMFMDSK(const std::string &file_name) :
throw Error::InvalidFormat; throw Error::InvalidFormat;
} }
int OricMFMDSK::get_head_position_count() { HeadPosition OricMFMDSK::get_maximum_head_position() {
return static_cast<int>(track_count_); return HeadPosition(static_cast<int>(track_count_));
} }
int OricMFMDSK::get_head_count() { int OricMFMDSK::get_head_count() {
@ -41,10 +41,10 @@ long OricMFMDSK::get_file_offset_for_position(Track::Address address) {
int seek_offset = 0; int seek_offset = 0;
switch(geometry_type_) { switch(geometry_type_) {
case 1: case 1:
seek_offset = address.head * static_cast<int>(track_count_) + address.position; seek_offset = address.head * static_cast<int>(track_count_) + address.position.as_int();
break; break;
case 2: case 2:
seek_offset = address.position * static_cast<int>(track_count_ * head_count_) + address.head; seek_offset = address.position.as_int() * static_cast<int>(track_count_ * head_count_) + address.head;
break; break;
} }
return static_cast<long>(seek_offset) * 6400 + 256; return static_cast<long>(seek_offset) * 6400 + 256;

View File

@ -30,7 +30,7 @@ class OricMFMDSK: public DiskImage {
OricMFMDSK(const std::string &file_name); OricMFMDSK(const std::string &file_name);
// implemented to satisfy @c DiskImage // implemented to satisfy @c DiskImage
int get_head_position_count() override; HeadPosition get_maximum_head_position() override;
int get_head_count() override; int get_head_count() override;
bool get_is_read_only() override; bool get_is_read_only() override;

View File

@ -36,8 +36,8 @@ SSD::SSD(const std::string &file_name) : MFMSectorDump(file_name) {
set_geometry(sectors_per_track, sector_size, 0, false); set_geometry(sectors_per_track, sector_size, 0, false);
} }
int SSD::get_head_position_count() { HeadPosition SSD::get_maximum_head_position() {
return track_count_; return HeadPosition(track_count_);
} }
int SSD::get_head_count() { int SSD::get_head_count() {
@ -45,5 +45,5 @@ int SSD::get_head_count() {
} }
long SSD::get_file_offset_for_position(Track::Address address) { long SSD::get_file_offset_for_position(Track::Address address) {
return (address.position * head_count_ + address.head) * 256 * 10; return (address.position.as_int() * head_count_ + address.head) * 256 * 10;
} }

View File

@ -27,7 +27,7 @@ class SSD: public MFMSectorDump {
*/ */
SSD(const std::string &file_name); SSD(const std::string &file_name);
int get_head_position_count() override; HeadPosition get_maximum_head_position() override;
int get_head_count() override; int get_head_count() override;
private: private:

View File

@ -18,12 +18,12 @@
using namespace Storage::Disk; using namespace Storage::Disk;
std::shared_ptr<Track> Storage::Disk::track_for_sectors(uint8_t *const source, uint8_t track, uint8_t side, uint8_t first_sector, uint8_t size, bool is_double_density) { std::shared_ptr<Track> Storage::Disk::track_for_sectors(uint8_t *const source, int number_of_sectors, uint8_t track, uint8_t side, uint8_t first_sector, uint8_t size, bool is_double_density) {
std::vector<Storage::Encodings::MFM::Sector> sectors; std::vector<Storage::Encodings::MFM::Sector> sectors;
off_t byte_size = static_cast<off_t>(128 << size); off_t byte_size = static_cast<off_t>(128 << size);
off_t source_pointer = 0; off_t source_pointer = 0;
for(int sector = 0; sector < 10; sector++) { for(int sector = 0; sector < number_of_sectors; sector++) {
sectors.emplace_back(); sectors.emplace_back();
Storage::Encodings::MFM::Sector &new_sector = sectors.back(); Storage::Encodings::MFM::Sector &new_sector = sectors.back();

View File

@ -16,7 +16,7 @@
namespace Storage { namespace Storage {
namespace Disk { namespace Disk {
std::shared_ptr<Track> track_for_sectors(uint8_t *const source, uint8_t track, uint8_t side, uint8_t first_sector, uint8_t size, bool is_double_density); std::shared_ptr<Track> track_for_sectors(uint8_t *const source, int number_of_sectors, uint8_t track, uint8_t side, uint8_t first_sector, uint8_t size, bool is_double_density);
void decode_sectors(Track &track, uint8_t *const destination, uint8_t first_sector, uint8_t last_sector, uint8_t sector_size, bool is_double_density); void decode_sectors(Track &track, uint8_t *const destination, uint8_t first_sector, uint8_t last_sector, uint8_t sector_size, bool is_double_density);
} }

View File

@ -69,9 +69,8 @@ WOZ::WOZ(const std::string &file_name) :
if(tracks_offset_ == -1 || !has_tmap) throw Error::InvalidFormat; if(tracks_offset_ == -1 || !has_tmap) throw Error::InvalidFormat;
} }
int WOZ::get_head_position_count() { HeadPosition WOZ::get_maximum_head_position() {
// TODO: deal with the elephant in the room of non-integral track coordinates. return is_3_5_disk_ ? HeadPosition(80) : HeadPosition(160, 4);
return is_3_5_disk_ ? 80 : 160;
} }
int WOZ::get_head_count() { int WOZ::get_head_count() {
@ -79,12 +78,8 @@ int WOZ::get_head_count() {
} }
std::shared_ptr<Track> WOZ::get_track_at_position(Track::Address address) { std::shared_ptr<Track> WOZ::get_track_at_position(Track::Address address) {
// Out-of-bounds => no track.
if(address.head >= get_head_count()) return nullptr;
if(address.position >= get_head_position_count()) return nullptr;
// Calculate table position; if this track is defined to be unformatted, return no track. // Calculate table position; if this track is defined to be unformatted, return no track.
const int table_position = address.head * get_head_position_count() + address.position; const int table_position = address.head * (is_3_5_disk_ ? 80 : 160) + (is_3_5_disk_ ? address.position.as_int() : address.position.as_quarter());
if(track_map_[table_position] == 0xff) return nullptr; if(track_map_[table_position] == 0xff) return nullptr;
// Seek to the real track. // Seek to the real track.

View File

@ -24,7 +24,7 @@ class WOZ: public DiskImage {
public: public:
WOZ(const std::string &file_name); WOZ(const std::string &file_name);
int get_head_position_count() override; HeadPosition get_maximum_head_position() override;
int get_head_count() override; int get_head_count() override;
std::shared_ptr<Track> get_track_at_position(Track::Address address) override; std::shared_ptr<Track> get_track_at_position(Track::Address address) override;

View File

@ -44,13 +44,13 @@ bool Drive::is_sleeping() {
} }
bool Drive::get_is_track_zero() { bool Drive::get_is_track_zero() {
return head_position_ == 0; return head_position_ == HeadPosition(0);
} }
void Drive::step(int direction) { void Drive::step(HeadPosition offset) {
int old_head_position = head_position_; HeadPosition old_head_position = head_position_;
head_position_ = std::max(head_position_ + direction, 0); head_position_ += offset;
// printf("Step %d -> %d\n", direction, head_position_); if(head_position_ < HeadPosition(0)) head_position_ = HeadPosition(0);
// If the head moved, flush the old track. // If the head moved, flush the old track.
if(head_position_ != old_head_position) { if(head_position_ != old_head_position) {

View File

@ -45,7 +45,7 @@ class Drive: public Sleeper, public TimedEventLoop {
Steps the disk head the specified number of tracks. Positive numbers step inwards (i.e. away from track 0), Steps the disk head the specified number of tracks. Positive numbers step inwards (i.e. away from track 0),
negative numbers step outwards (i.e. towards track 0). negative numbers step outwards (i.e. towards track 0).
*/ */
void step(int direction); void step(HeadPosition offset);
/*! /*!
Sets the current read head. Sets the current read head.
@ -139,7 +139,7 @@ class Drive: public Sleeper, public TimedEventLoop {
int cycles_since_index_hole_ = 0; int cycles_since_index_hole_ = 0;
// A record of head position and active head. // A record of head position and active head.
int head_position_ = 0; HeadPosition head_position_;
int head_ = 0; int head_ = 0;
int available_heads_ = 0; int available_heads_ = 0;

View File

@ -39,7 +39,7 @@ void Parser::install_sectors_from_track(const Storage::Disk::Track::Address &add
} }
Sector *Parser::get_sector(int head, int track, uint8_t sector) { Sector *Parser::get_sector(int head, int track, uint8_t sector) {
Disk::Track::Address address(head, track); Disk::Track::Address address(head, Storage::Disk::HeadPosition(track));
install_sectors_from_track(address); install_sectors_from_track(address);
auto sectors = sectors_by_address_by_track_.find(address); auto sectors = sectors_by_address_by_track_.find(address);

View File

@ -15,6 +15,53 @@
namespace Storage { namespace Storage {
namespace Disk { namespace Disk {
/*!
Contains a head position, with some degree of sub-integral precision.
*/
class HeadPosition {
public:
/// Creates an instance decribing position @c value at a resolution of @c scale ticks per track.
HeadPosition(int value, int scale) : position_(value * (4/scale)) {}
explicit HeadPosition(int value) : HeadPosition(value, 1) {}
HeadPosition() : HeadPosition(0) {}
/// @returns the whole number part of the position.
int as_int() const { return position_ >> 2; }
/// @returns n where n/2 is the head position.
int as_half() const { return position_ >> 1; }
/// @returns n where n/4 is the head position.
int as_quarter() const { return position_; }
/// @returns the head position at maximal but unspecified precision.
int as_largest() const { return as_quarter(); }
HeadPosition &operator +=(const HeadPosition &rhs) {
position_ += rhs.position_;
return *this;
}
bool operator ==(const HeadPosition &rhs) {
return position_ == rhs.position_;
}
bool operator !=(const HeadPosition &rhs) {
return position_ != rhs.position_;
}
bool operator <(const HeadPosition &rhs) {
return position_ < rhs.position_;
}
bool operator <=(const HeadPosition &rhs) {
return position_ <= rhs.position_;
}
bool operator >(const HeadPosition &rhs) {
return position_ > rhs.position_;
}
bool operator >=(const HeadPosition &rhs) {
return position_ >= rhs.position_;
}
private:
int position_ = 0;
};
/*! /*!
Models a single track on a disk as a series of events, each event being of arbitrary length Models a single track on a disk as a series of events, each event being of arbitrary length
and resulting in either a flux transition or the sensing of an index hole. and resulting in either a flux transition or the sensing of an index hole.
@ -27,12 +74,15 @@ class Track {
Describes the location of a track, implementing < to allow for use as a set key. Describes the location of a track, implementing < to allow for use as a set key.
*/ */
struct Address { struct Address {
int head, position; int head;
HeadPosition position;
bool operator < (const Address &rhs) const { bool operator < (const Address &rhs) const {
return std::tie(head, position) < std::tie(rhs.head, rhs.position); int largest_position = position.as_largest();
int rhs_largest_position = rhs.position.as_largest();
return std::tie(head, largest_position) < std::tie(rhs.head, rhs_largest_position);
} }
Address(int head, int position) : head(head), position(position) {} Address(int head, HeadPosition position) : head(head), position(position) {}
}; };
/*! /*!