From f65c65569a386091447289501851dd41ba6e43bd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 6 May 2018 23:17:36 -0400 Subject: [PATCH] Makes disk head position explicitly something with sub-integral precision. Also as a drive-by fix, corrects accidental assumption of 10 sectors for all MFMSectorDump descendants. --- Analyser/Static/Commodore/Disk.cpp | 6 +- Analyser/Static/DiskII/StaticAnalyser.cpp | 2 +- Components/1770/1770.cpp | 2 +- Components/8272/i8272.cpp | 2 +- Components/DiskII/DiskII.cpp | 2 +- .../Commodore/1540/Implementation/C1540.cpp | 2 +- Storage/Disk/Disk.hpp | 2 +- Storage/Disk/DiskImage/DiskImage.hpp | 6 +- .../DiskImage/DiskImageImplementation.hpp | 6 +- Storage/Disk/DiskImage/Formats/AcornADF.cpp | 6 +- Storage/Disk/DiskImage/Formats/AcornADF.hpp | 2 +- Storage/Disk/DiskImage/Formats/AppleDSK.cpp | 8 +-- Storage/Disk/DiskImage/Formats/AppleDSK.hpp | 2 +- Storage/Disk/DiskImage/Formats/CPCDSK.cpp | 10 ++-- Storage/Disk/DiskImage/Formats/CPCDSK.hpp | 2 +- Storage/Disk/DiskImage/Formats/D64.cpp | 12 ++-- Storage/Disk/DiskImage/Formats/D64.hpp | 2 +- Storage/Disk/DiskImage/Formats/DMK.cpp | 6 +- Storage/Disk/DiskImage/Formats/DMK.hpp | 2 +- Storage/Disk/DiskImage/Formats/G64.cpp | 13 ++--- Storage/Disk/DiskImage/Formats/G64.hpp | 2 +- Storage/Disk/DiskImage/Formats/HFE.cpp | 6 +- Storage/Disk/DiskImage/Formats/HFE.hpp | 2 +- .../Disk/DiskImage/Formats/MFMSectorDump.cpp | 2 +- Storage/Disk/DiskImage/Formats/MSXDSK.cpp | 6 +- Storage/Disk/DiskImage/Formats/MSXDSK.hpp | 2 +- Storage/Disk/DiskImage/Formats/NIB.cpp | 6 +- Storage/Disk/DiskImage/Formats/NIB.hpp | 2 +- Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp | 8 +-- Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp | 2 +- Storage/Disk/DiskImage/Formats/SSD.cpp | 6 +- Storage/Disk/DiskImage/Formats/SSD.hpp | 2 +- .../Formats/Utility/ImplicitSectors.cpp | 4 +- .../Formats/Utility/ImplicitSectors.hpp | 2 +- Storage/Disk/DiskImage/Formats/WOZ.cpp | 11 +--- Storage/Disk/DiskImage/Formats/WOZ.hpp | 2 +- Storage/Disk/Drive.cpp | 10 ++-- Storage/Disk/Drive.hpp | 4 +- Storage/Disk/Encodings/MFM/Parser.cpp | 2 +- Storage/Disk/Track/Track.hpp | 56 ++++++++++++++++++- 40 files changed, 135 insertions(+), 97 deletions(-) diff --git a/Analyser/Static/Commodore/Disk.cpp b/Analyser/Static/Commodore/Disk.cpp index e917d940d..2ce9199d8 100644 --- a/Analyser/Static/Commodore/Disk.cpp +++ b/Analyser/Static/Commodore/Disk.cpp @@ -45,9 +45,11 @@ class CommodoreGCRParser: public Storage::Disk::Controller { if(difference) { 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; if(track >= 18) zone = 2; diff --git a/Analyser/Static/DiskII/StaticAnalyser.cpp b/Analyser/Static/DiskII/StaticAnalyser.cpp index bdaae8f91..af0aa0781 100644 --- a/Analyser/Static/DiskII/StaticAnalyser.cpp +++ b/Analyser/Static/DiskII/StaticAnalyser.cpp @@ -49,7 +49,7 @@ Analyser::Static::TargetList Analyser::Static::DiskII::GetTargets(const Media &m if(media.disks.empty()) return {}; // 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( Storage::Disk::track_serialisation(*track_zero, Storage::Time(1, 50000))); diff --git a/Components/1770/1770.cpp b/Components/1770/1770.cpp index 7071d1eb8..ec817271a 100644 --- a/Components/1770/1770.cpp +++ b/Components/1770/1770.cpp @@ -282,7 +282,7 @@ void WD1770::posit_event(int new_event_type) { track_ = 0; goto verify; } - get_drive().step(step_direction_ ? 1 : -1); + get_drive().step(Storage::Disk::HeadPosition(step_direction_ ? 1 : -1)); unsigned int time_to_wait; switch(command_ & 3) { default: diff --git a/Components/8272/i8272.cpp b/Components/8272/i8272.cpp index 288d68064..64edc2c83 100644 --- a/Components/8272/i8272.cpp +++ b/Components/8272/i8272.cpp @@ -115,7 +115,7 @@ void i8272::run_for(Cycles cycles) { 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); 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; // Check for completion. diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp index 102669e35..3564e7a72 100644 --- a/Components/DiskII/DiskII.cpp +++ b/Components/DiskII/DiskII.cpp @@ -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, // 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; } } diff --git a/Machines/Commodore/1540/Implementation/C1540.cpp b/Machines/Commodore/1540/Implementation/C1540.cpp index b3d702bc2..f08deb661 100644 --- a/Machines/Commodore/1540/Implementation/C1540.cpp +++ b/Machines/Commodore/1540/Implementation/C1540.cpp @@ -142,7 +142,7 @@ void MachineBase::process_index_hole() {} // MARK: - Drive VIA delegate 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) { diff --git a/Storage/Disk/Disk.hpp b/Storage/Disk/Disk.hpp index c929bff3a..ffdd8d621 100644 --- a/Storage/Disk/Disk.hpp +++ b/Storage/Disk/Disk.hpp @@ -31,7 +31,7 @@ class Disk { 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. */ - 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. diff --git a/Storage/Disk/DiskImage/DiskImage.hpp b/Storage/Disk/DiskImage/DiskImage.hpp index 86dbcb657..017165c33 100644 --- a/Storage/Disk/DiskImage/DiskImage.hpp +++ b/Storage/Disk/DiskImage/DiskImage.hpp @@ -35,12 +35,12 @@ class 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 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. @@ -87,7 +87,7 @@ template class DiskImageHolder: public DiskImageHolderBase { disk_image_(args...) {} ~DiskImageHolder(); - int get_head_position_count(); + HeadPosition get_maximum_head_position(); int get_head_count(); std::shared_ptr get_track_at_position(Track::Address address); void set_track_at_position(Track::Address address, const std::shared_ptr &track); diff --git a/Storage/Disk/DiskImage/DiskImageImplementation.hpp b/Storage/Disk/DiskImage/DiskImageImplementation.hpp index 0f051cfa4..3ece59688 100644 --- a/Storage/Disk/DiskImage/DiskImageImplementation.hpp +++ b/Storage/Disk/DiskImage/DiskImageImplementation.hpp @@ -6,8 +6,8 @@ // Copyright © 2017 Thomas Harte. All rights reserved. // -template int DiskImageHolder::get_head_position_count() { - return disk_image_.get_head_position_count(); +template HeadPosition DiskImageHolder::get_maximum_head_position() { + return disk_image_.get_maximum_head_position(); } template int DiskImageHolder::get_head_count() { @@ -44,7 +44,7 @@ template void DiskImageHolder::set_track_at_position(Track::Addr template std::shared_ptr DiskImageHolder::get_track_at_position(Track::Address address) { 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); if(cached_track != cached_tracks_.end()) return cached_track->second; diff --git a/Storage/Disk/DiskImage/Formats/AcornADF.cpp b/Storage/Disk/DiskImage/Formats/AcornADF.cpp index df5864453..f3aba25af 100644 --- a/Storage/Disk/DiskImage/Formats/AcornADF.cpp +++ b/Storage/Disk/DiskImage/Formats/AcornADF.cpp @@ -36,8 +36,8 @@ AcornADF::AcornADF(const std::string &file_name) : MFMSectorDump(file_name) { set_geometry(sectors_per_track, sector_size, 0, true); } -int AcornADF::get_head_position_count() { - return 80; +HeadPosition AcornADF::get_maximum_head_position() { + return HeadPosition(80); } int AcornADF::get_head_count() { @@ -45,5 +45,5 @@ int AcornADF::get_head_count() { } 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; } diff --git a/Storage/Disk/DiskImage/Formats/AcornADF.hpp b/Storage/Disk/DiskImage/Formats/AcornADF.hpp index 11e24a95c..b2ce0e3ab 100644 --- a/Storage/Disk/DiskImage/Formats/AcornADF.hpp +++ b/Storage/Disk/DiskImage/Formats/AcornADF.hpp @@ -29,7 +29,7 @@ class AcornADF: public MFMSectorDump { */ AcornADF(const std::string &file_name); - int get_head_position_count() override; + HeadPosition get_maximum_head_position() override; int get_head_count() override; private: diff --git a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp index 8fa740d18..4c6334245 100644 --- a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp @@ -38,17 +38,17 @@ AppleDSK::AppleDSK(const std::string &file_name) : } } -int AppleDSK::get_head_position_count() { - return number_of_tracks * 4; +HeadPosition AppleDSK::get_maximum_head_position() { + return HeadPosition(number_of_tracks); } std::shared_ptr 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); const std::vector track_data = file_.read(static_cast(bytes_per_sector * sectors_per_track_)); Storage::Disk::PCMSegment segment; - const uint8_t track = static_cast(address.position >> 2); + const uint8_t track = static_cast(address.position.as_int()); // In either case below, the code aims for exactly 50,000 bits per track. if(sectors_per_track_ == 16) { diff --git a/Storage/Disk/DiskImage/Formats/AppleDSK.hpp b/Storage/Disk/DiskImage/Formats/AppleDSK.hpp index 92fd152d0..cc783e1a9 100644 --- a/Storage/Disk/DiskImage/Formats/AppleDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/AppleDSK.hpp @@ -32,7 +32,7 @@ class AppleDSK: public DiskImage { AppleDSK(const std::string &file_name); // implemented to satisfy @c Disk - int get_head_position_count() override; + HeadPosition get_maximum_head_position() override; std::shared_ptr get_track_at_position(Track::Address address) override; private: diff --git a/Storage/Disk/DiskImage/Formats/CPCDSK.cpp b/Storage/Disk/DiskImage/Formats/CPCDSK.cpp index db5090b97..ee64a785f 100644 --- a/Storage/Disk/DiskImage/Formats/CPCDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/CPCDSK.cpp @@ -186,8 +186,8 @@ CPCDSK::CPCDSK(const std::string &file_name) : } } -int CPCDSK::get_head_position_count() { - return head_position_count_; +HeadPosition CPCDSK::get_maximum_head_position() { + return HeadPosition(head_position_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) { - return static_cast((address.position * head_count_) + address.head); + return static_cast((address.position.as_int() * head_count_) + address.head); } std::shared_ptr 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); if(chronological_track >= tracks_.size()) { 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. Track *track = tracks_[chronological_track].get(); if(!track) { track = new Track; - track->track = static_cast(pair.first.position); + track->track = static_cast(pair.first.position.as_int()); track->side = static_cast(pair.first.head); track->data_rate = Track::DataRate::SingleOrDoubleDensity; track->data_encoding = Track::DataEncoding::MFM; diff --git a/Storage/Disk/DiskImage/Formats/CPCDSK.hpp b/Storage/Disk/DiskImage/Formats/CPCDSK.hpp index d946ea3f8..58dfa9d10 100644 --- a/Storage/Disk/DiskImage/Formats/CPCDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/CPCDSK.hpp @@ -33,7 +33,7 @@ class CPCDSK: public DiskImage { CPCDSK(const std::string &file_name); // implemented to satisfy @c Disk - int get_head_position_count() override; + HeadPosition get_maximum_head_position() override; int get_head_count() override; bool get_is_read_only() override; diff --git a/Storage/Disk/DiskImage/Formats/D64.cpp b/Storage/Disk/DiskImage/Formats/D64.cpp index d82826b1c..c0a098653 100644 --- a/Storage/Disk/DiskImage/Formats/D64.cpp +++ b/Storage/Disk/DiskImage/Formats/D64.cpp @@ -35,18 +35,14 @@ D64::D64(const std::string &file_name) : } } -int D64::get_head_position_count() { - return number_of_tracks_*2; +HeadPosition D64::get_maximum_head_position() { + return HeadPosition(number_of_tracks_); } std::shared_ptr 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(); - // figure out where this track starts on the disk 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 sectors_by_zone[] = {21, 19, 18, 17}; @@ -96,7 +92,7 @@ std::shared_ptr D64::get_track_at_position(Track::Address address) { sector_data[0] = sector_data[1] = sector_data[2] = 0xff; uint8_t sector_number = static_cast(sector); // sectors count from 0 - uint8_t track_number = static_cast((address.position >> 1) + 1); // tracks count from 1 + uint8_t track_number = static_cast(address.position.as_int() + 1); // tracks count from 1 uint8_t checksum = static_cast(sector_number ^ track_number ^ disk_id_ ^ (disk_id_ >> 8)); uint8_t header_start[4] = { 0x08, checksum, sector_number, track_number diff --git a/Storage/Disk/DiskImage/Formats/D64.hpp b/Storage/Disk/DiskImage/Formats/D64.hpp index a412607ac..e7f465e30 100644 --- a/Storage/Disk/DiskImage/Formats/D64.hpp +++ b/Storage/Disk/DiskImage/Formats/D64.hpp @@ -29,7 +29,7 @@ class D64: public DiskImage { D64(const std::string &file_name); // implemented to satisfy @c Disk - int get_head_position_count() override; + HeadPosition get_maximum_head_position() override; using DiskImage::get_is_read_only; std::shared_ptr get_track_at_position(Track::Address address) override; diff --git a/Storage/Disk/DiskImage/Formats/DMK.cpp b/Storage/Disk/DiskImage/Formats/DMK.cpp index 92fe728a7..a3099bf7a 100644 --- a/Storage/Disk/DiskImage/Formats/DMK.cpp +++ b/Storage/Disk/DiskImage/Formats/DMK.cpp @@ -61,8 +61,8 @@ DMK::DMK(const std::string &file_name) : if(format) throw Error::InvalidFormat; } -int DMK::get_head_position_count() { - return head_position_count_; +HeadPosition DMK::get_maximum_head_position() { + return HeadPosition(head_position_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) { - 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) { diff --git a/Storage/Disk/DiskImage/Formats/DMK.hpp b/Storage/Disk/DiskImage/Formats/DMK.hpp index af772530e..087f5bb49 100644 --- a/Storage/Disk/DiskImage/Formats/DMK.hpp +++ b/Storage/Disk/DiskImage/Formats/DMK.hpp @@ -31,7 +31,7 @@ class DMK: public DiskImage { DMK(const std::string &file_name); // implemented to satisfy @c Disk - int get_head_position_count() override; + HeadPosition get_maximum_head_position() override; int get_head_count() override; bool get_is_read_only() override; diff --git a/Storage/Disk/DiskImage/Formats/G64.cpp b/Storage/Disk/DiskImage/Formats/G64.cpp index 87ee86763..379c0716e 100644 --- a/Storage/Disk/DiskImage/Formats/G64.cpp +++ b/Storage/Disk/DiskImage/Formats/G64.cpp @@ -30,22 +30,17 @@ G64::G64(const std::string &file_name) : 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, // 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 G64::get_track_at_position(Track::Address address) { std::shared_ptr 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 - file_.seek(static_cast((address.position * 4) + 0xc), SEEK_SET); + file_.seek(static_cast((address.position.as_half() * 4) + 0xc), SEEK_SET); // read the track offset uint32_t track_offset; @@ -66,7 +61,7 @@ std::shared_ptr G64::get_track_at_position(Track::Address address) { file_.read(&track_contents[0], track_length); // seek to this track's entry in the speed zone table - file_.seek(static_cast((address.position * 4) + 0x15c), SEEK_SET); + file_.seek(static_cast((address.position.as_half() * 4) + 0x15c), SEEK_SET); // read the speed zone offsrt uint32_t speed_zone_offset; diff --git a/Storage/Disk/DiskImage/Formats/G64.hpp b/Storage/Disk/DiskImage/Formats/G64.hpp index 2b27fca73..311690bda 100644 --- a/Storage/Disk/DiskImage/Formats/G64.hpp +++ b/Storage/Disk/DiskImage/Formats/G64.hpp @@ -32,7 +32,7 @@ class G64: public DiskImage { G64(const std::string &file_name); // implemented to satisfy @c Disk - int get_head_position_count() override; + HeadPosition get_maximum_head_position() override; std::shared_ptr get_track_at_position(Track::Address address) override; using DiskImage::get_is_read_only; diff --git a/Storage/Disk/DiskImage/Formats/HFE.cpp b/Storage/Disk/DiskImage/Formats/HFE.cpp index 59c6f3636..69dd6fe33 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.cpp +++ b/Storage/Disk/DiskImage/Formats/HFE.cpp @@ -29,8 +29,8 @@ HFE::HFE(const std::string &file_name) : HFE::~HFE() { } -int HFE::get_head_position_count() { - return track_count_; +HeadPosition HFE::get_maximum_head_position() { + return HeadPosition(track_count_); } int HFE::get_head_count() { @@ -47,7 +47,7 @@ int HFE::get_head_count() { uint16_t HFE::seek_track(Track::Address address) { // Get track position and length from the lookup table; data is then always interleaved // 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(file_.get16le()) << 9; uint16_t track_length = file_.get16le(); diff --git a/Storage/Disk/DiskImage/Formats/HFE.hpp b/Storage/Disk/DiskImage/Formats/HFE.hpp index bf66af727..f3c506ae0 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.hpp +++ b/Storage/Disk/DiskImage/Formats/HFE.hpp @@ -33,7 +33,7 @@ class HFE: public DiskImage { ~HFE(); // implemented to satisfy @c Disk - int get_head_position_count() override; + HeadPosition get_maximum_head_position() override; int get_head_count() override; bool get_is_read_only() override; void set_tracks(const std::map> &tracks) override; diff --git a/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp b/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp index 7e510cd68..ed4672c16 100644 --- a/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp +++ b/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp @@ -33,7 +33,7 @@ std::shared_ptr MFMSectorDump::get_track_at_position(Track::Address addre file_.read(sectors, sizeof(sectors)); } - return track_for_sectors(sectors, static_cast(address.position), static_cast(address.head), first_sector_, sector_size_, is_double_density_); + return track_for_sectors(sectors, sectors_per_track_, static_cast(address.position.as_int()), static_cast(address.head), first_sector_, sector_size_, is_double_density_); } void MFMSectorDump::set_tracks(const std::map> &tracks) { diff --git a/Storage/Disk/DiskImage/Formats/MSXDSK.cpp b/Storage/Disk/DiskImage/Formats/MSXDSK.cpp index 9b03de6d6..210a29dde 100644 --- a/Storage/Disk/DiskImage/Formats/MSXDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/MSXDSK.cpp @@ -46,8 +46,8 @@ MSXDSK::MSXDSK(const std::string &file_name) : set_geometry(sectors_per_track, sector_size, 1, true); } -int MSXDSK::get_head_position_count() { - return track_count_; +HeadPosition MSXDSK::get_maximum_head_position() { + return HeadPosition(track_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) { - return (address.position*head_count_ + address.head) * 512 * 9; + return (address.position.as_int()*head_count_ + address.head) * 512 * 9; } diff --git a/Storage/Disk/DiskImage/Formats/MSXDSK.hpp b/Storage/Disk/DiskImage/Formats/MSXDSK.hpp index 74d7f9247..9acd77336 100644 --- a/Storage/Disk/DiskImage/Formats/MSXDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/MSXDSK.hpp @@ -23,7 +23,7 @@ namespace Disk { class MSXDSK: public MFMSectorDump { public: MSXDSK(const std::string &file_name); - int get_head_position_count() override; + HeadPosition get_maximum_head_position() override; int get_head_count() override; private: diff --git a/Storage/Disk/DiskImage/Formats/NIB.cpp b/Storage/Disk/DiskImage/Formats/NIB.cpp index a1396622e..29e780ef9 100644 --- a/Storage/Disk/DiskImage/Formats/NIB.cpp +++ b/Storage/Disk/DiskImage/Formats/NIB.cpp @@ -32,15 +32,15 @@ NIB::NIB(const std::string &file_name) : // TODO: all other validation. I.e. does this look like a GCR disk? } -int NIB::get_head_position_count() { - return number_of_tracks * 4; +HeadPosition NIB::get_maximum_head_position() { + return HeadPosition(number_of_tracks); } 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. if(address.head) return nullptr; - const long file_track = static_cast(address.position >> 2); + const long file_track = static_cast(address.position.as_int()); file_.seek(file_track * track_length, SEEK_SET); std::vector track_data = file_.read(track_length); diff --git a/Storage/Disk/DiskImage/Formats/NIB.hpp b/Storage/Disk/DiskImage/Formats/NIB.hpp index bde1f4322..cba3ffa56 100644 --- a/Storage/Disk/DiskImage/Formats/NIB.hpp +++ b/Storage/Disk/DiskImage/Formats/NIB.hpp @@ -24,7 +24,7 @@ class NIB: public DiskImage { public: 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; diff --git a/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp b/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp index 38ec3ac75..95509011a 100644 --- a/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp @@ -29,8 +29,8 @@ OricMFMDSK::OricMFMDSK(const std::string &file_name) : throw Error::InvalidFormat; } -int OricMFMDSK::get_head_position_count() { - return static_cast(track_count_); +HeadPosition OricMFMDSK::get_maximum_head_position() { + return HeadPosition(static_cast(track_count_)); } int OricMFMDSK::get_head_count() { @@ -41,10 +41,10 @@ long OricMFMDSK::get_file_offset_for_position(Track::Address address) { int seek_offset = 0; switch(geometry_type_) { case 1: - seek_offset = address.head * static_cast(track_count_) + address.position; + seek_offset = address.head * static_cast(track_count_) + address.position.as_int(); break; case 2: - seek_offset = address.position * static_cast(track_count_ * head_count_) + address.head; + seek_offset = address.position.as_int() * static_cast(track_count_ * head_count_) + address.head; break; } return static_cast(seek_offset) * 6400 + 256; diff --git a/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp b/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp index 14fa16ecd..1fa4ed4e9 100644 --- a/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp @@ -30,7 +30,7 @@ class OricMFMDSK: public DiskImage { OricMFMDSK(const std::string &file_name); // implemented to satisfy @c DiskImage - int get_head_position_count() override; + HeadPosition get_maximum_head_position() override; int get_head_count() override; bool get_is_read_only() override; diff --git a/Storage/Disk/DiskImage/Formats/SSD.cpp b/Storage/Disk/DiskImage/Formats/SSD.cpp index 7d8647023..de2e02bad 100644 --- a/Storage/Disk/DiskImage/Formats/SSD.cpp +++ b/Storage/Disk/DiskImage/Formats/SSD.cpp @@ -36,8 +36,8 @@ SSD::SSD(const std::string &file_name) : MFMSectorDump(file_name) { set_geometry(sectors_per_track, sector_size, 0, false); } -int SSD::get_head_position_count() { - return track_count_; +HeadPosition SSD::get_maximum_head_position() { + return HeadPosition(track_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) { - return (address.position * head_count_ + address.head) * 256 * 10; + return (address.position.as_int() * head_count_ + address.head) * 256 * 10; } diff --git a/Storage/Disk/DiskImage/Formats/SSD.hpp b/Storage/Disk/DiskImage/Formats/SSD.hpp index 7eeba9173..0b204a0df 100644 --- a/Storage/Disk/DiskImage/Formats/SSD.hpp +++ b/Storage/Disk/DiskImage/Formats/SSD.hpp @@ -27,7 +27,7 @@ class SSD: public MFMSectorDump { */ SSD(const std::string &file_name); - int get_head_position_count() override; + HeadPosition get_maximum_head_position() override; int get_head_count() override; private: diff --git a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp index ca0015a25..a55364367 100644 --- a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp +++ b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp @@ -18,12 +18,12 @@ using namespace Storage::Disk; -std::shared_ptr 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 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 sectors; off_t byte_size = static_cast(128 << size); off_t source_pointer = 0; - for(int sector = 0; sector < 10; sector++) { + for(int sector = 0; sector < number_of_sectors; sector++) { sectors.emplace_back(); Storage::Encodings::MFM::Sector &new_sector = sectors.back(); diff --git a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.hpp b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.hpp index e1be6f6e8..9d07f3716 100644 --- a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.hpp +++ b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.hpp @@ -16,7 +16,7 @@ namespace Storage { namespace Disk { -std::shared_ptr 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_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); } diff --git a/Storage/Disk/DiskImage/Formats/WOZ.cpp b/Storage/Disk/DiskImage/Formats/WOZ.cpp index da6395a4e..db696aded 100644 --- a/Storage/Disk/DiskImage/Formats/WOZ.cpp +++ b/Storage/Disk/DiskImage/Formats/WOZ.cpp @@ -69,9 +69,8 @@ WOZ::WOZ(const std::string &file_name) : if(tracks_offset_ == -1 || !has_tmap) throw Error::InvalidFormat; } -int WOZ::get_head_position_count() { - // TODO: deal with the elephant in the room of non-integral track coordinates. - return is_3_5_disk_ ? 80 : 160; +HeadPosition WOZ::get_maximum_head_position() { + return is_3_5_disk_ ? HeadPosition(80) : HeadPosition(160, 4); } int WOZ::get_head_count() { @@ -79,12 +78,8 @@ int WOZ::get_head_count() { } std::shared_ptr 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. - 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; // Seek to the real track. diff --git a/Storage/Disk/DiskImage/Formats/WOZ.hpp b/Storage/Disk/DiskImage/Formats/WOZ.hpp index afae68e5e..f49dd7a95 100644 --- a/Storage/Disk/DiskImage/Formats/WOZ.hpp +++ b/Storage/Disk/DiskImage/Formats/WOZ.hpp @@ -24,7 +24,7 @@ class WOZ: public DiskImage { public: WOZ(const std::string &file_name); - int get_head_position_count() override; + HeadPosition get_maximum_head_position() override; int get_head_count() override; std::shared_ptr get_track_at_position(Track::Address address) override; diff --git a/Storage/Disk/Drive.cpp b/Storage/Disk/Drive.cpp index f1de0609c..d62654171 100644 --- a/Storage/Disk/Drive.cpp +++ b/Storage/Disk/Drive.cpp @@ -44,13 +44,13 @@ bool Drive::is_sleeping() { } bool Drive::get_is_track_zero() { - return head_position_ == 0; + return head_position_ == HeadPosition(0); } -void Drive::step(int direction) { - int old_head_position = head_position_; - head_position_ = std::max(head_position_ + direction, 0); -// printf("Step %d -> %d\n", direction, head_position_); +void Drive::step(HeadPosition offset) { + HeadPosition old_head_position = head_position_; + head_position_ += offset; + if(head_position_ < HeadPosition(0)) head_position_ = HeadPosition(0); // If the head moved, flush the old track. if(head_position_ != old_head_position) { diff --git a/Storage/Disk/Drive.hpp b/Storage/Disk/Drive.hpp index b57f68cbd..be32071e7 100644 --- a/Storage/Disk/Drive.hpp +++ b/Storage/Disk/Drive.hpp @@ -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), negative numbers step outwards (i.e. towards track 0). */ - void step(int direction); + void step(HeadPosition offset); /*! Sets the current read head. @@ -139,7 +139,7 @@ class Drive: public Sleeper, public TimedEventLoop { int cycles_since_index_hole_ = 0; // A record of head position and active head. - int head_position_ = 0; + HeadPosition head_position_; int head_ = 0; int available_heads_ = 0; diff --git a/Storage/Disk/Encodings/MFM/Parser.cpp b/Storage/Disk/Encodings/MFM/Parser.cpp index 154beab9c..7244171aa 100644 --- a/Storage/Disk/Encodings/MFM/Parser.cpp +++ b/Storage/Disk/Encodings/MFM/Parser.cpp @@ -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) { - Disk::Track::Address address(head, track); + Disk::Track::Address address(head, Storage::Disk::HeadPosition(track)); install_sectors_from_track(address); auto sectors = sectors_by_address_by_track_.find(address); diff --git a/Storage/Disk/Track/Track.hpp b/Storage/Disk/Track/Track.hpp index 967330737..4437c9300 100644 --- a/Storage/Disk/Track/Track.hpp +++ b/Storage/Disk/Track/Track.hpp @@ -15,6 +15,53 @@ namespace Storage { 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 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. */ struct Address { - int head, position; + int head; + HeadPosition position; 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) {} }; /*!