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) {
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;

View File

@ -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)));

View File

@ -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:

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;
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.

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,
// 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;
}
}

View File

@ -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) {

View File

@ -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.

View File

@ -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 <typename T> 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<Track> get_track_at_position(Track::Address address);
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.
//
template <typename T> int DiskImageHolder<T>::get_head_position_count() {
return disk_image_.get_head_position_count();
template <typename T> HeadPosition DiskImageHolder<T>::get_maximum_head_position() {
return disk_image_.get_maximum_head_position();
}
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) {
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;

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);
}
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;
}

View File

@ -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:

View File

@ -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<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);
const std::vector<uint8_t> track_data = file_.read(static_cast<size_t>(bytes_per_sector * sectors_per_track_));
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.
if(sectors_per_track_ == 16) {

View File

@ -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<Track> get_track_at_position(Track::Address address) override;
private:

View File

@ -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<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) {
@ -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<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->data_rate = Track::DataRate::SingleOrDoubleDensity;
track->data_encoding = Track::DataEncoding::MFM;

View File

@ -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;

View File

@ -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<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
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<Track> D64::get_track_at_position(Track::Address address) {
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 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 header_start[4] = {
0x08, checksum, sector_number, track_number

View File

@ -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<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;
}
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) {

View File

@ -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;

View File

@ -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<Track> G64::get_track_at_position(Track::Address address) {
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
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
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);
// 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
uint32_t speed_zone_offset;

View File

@ -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<Track> get_track_at_position(Track::Address address) override;
using DiskImage::get_is_read_only;

View File

@ -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<long>(file_.get16le()) << 9;
uint16_t track_length = file_.get16le();

View File

@ -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<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));
}
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) {

View File

@ -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;
}

View File

@ -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:

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?
}
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<long>(address.position >> 2);
const long file_track = static_cast<long>(address.position.as_int());
file_.seek(file_track * track_length, SEEK_SET);
std::vector<uint8_t> track_data = file_.read(track_length);

View File

@ -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;

View File

@ -29,8 +29,8 @@ OricMFMDSK::OricMFMDSK(const std::string &file_name) :
throw Error::InvalidFormat;
}
int OricMFMDSK::get_head_position_count() {
return static_cast<int>(track_count_);
HeadPosition OricMFMDSK::get_maximum_head_position() {
return HeadPosition(static_cast<int>(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<int>(track_count_) + address.position;
seek_offset = address.head * static_cast<int>(track_count_) + address.position.as_int();
break;
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;
}
return static_cast<long>(seek_offset) * 6400 + 256;

View File

@ -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;

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);
}
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;
}

View File

@ -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:

View File

@ -18,12 +18,12 @@
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;
off_t byte_size = static_cast<off_t>(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();

View File

@ -16,7 +16,7 @@
namespace Storage {
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);
}

View File

@ -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<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.
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.

View File

@ -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<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() {
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) {

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),
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;

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) {
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);

View File

@ -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) {}
};
/*!