mirror of
https://github.com/TomHarte/CLK.git
synced 2024-08-16 16:28:59 +00:00
Merge pull request #424 from TomHarte/TrackDivision
Makes disk head position explicitly something with sub-integral precision.
This commit is contained in:
commit
d1b889aa61
@ -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;
|
||||||
|
@ -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)));
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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) {
|
||||||
|
@ -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:
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
Loading…
Reference in New Issue
Block a user