1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-04-21 18:37:11 +00:00

Improve constness, remove unnecessary virtuals.

This commit is contained in:
Thomas Harte 2025-02-24 18:18:12 -05:00
parent 43353ce892
commit 8dcccf11bf
49 changed files with 276 additions and 226 deletions

View File

@ -40,6 +40,10 @@ public:
get_drive().set_disk(disk);
}
const Storage::Disk::Disk *disk() const {
return get_drive().disk();
}
void set_activity_observer(Activity::Observer *observer) {
get_drive().set_activity_observer(observer, "Drive 1", true);
}

View File

@ -696,6 +696,12 @@ template<Model model> class ConcreteMachine:
return !media.tapes.empty() || (!media.disks.empty() && model == Model::Plus3);
}
ChangeEffect effect_for_file_did_change(const std::string &file_name) override {
fdc_->disk();
return ChangeEffect::None;
}
// MARK: - ClockingHint::Observer.
void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) override {

View File

@ -82,11 +82,6 @@ class MachineDocument:
case .none: fallthrough
@unknown default: break
}
Swift.print("Content changed")
// TODO: tell machine content has changed. Machine will need to indicate
// whether it's the one that changed it. If not, restart the machine.
})
} else {
throw NSError(domain: "MachineDocument", code: -1, userInfo: nil)

View File

@ -32,18 +32,18 @@ public:
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 HeadPosition get_maximum_head_position() = 0;
virtual HeadPosition get_maximum_head_position() const = 0;
/*!
@returns the number of heads (and, therefore, impliedly surfaces) available on this disk.
*/
virtual int get_head_count() = 0;
virtual int get_head_count() const = 0;
/*!
@returns the @c Track at @c position underneath @c head if there are any detectable events there;
returns @c nullptr otherwise.
*/
virtual Track *track_at_position(Track::Address) = 0;
virtual Track *track_at_position(Track::Address) const = 0;
/*!
Replaces the Track at position @c position underneath @c head with @c track. Ignored if this disk is read-only.
@ -58,13 +58,23 @@ public:
/*!
@returns whether the disk image is read only. Defaults to @c true if not overridden.
*/
virtual bool get_is_read_only() = 0;
virtual bool get_is_read_only() const = 0;
/*!
@returns @c true if the tracks at the two addresses are different. @c false if they are the same track.
This can avoid some degree of work when disk images offer sub-head-position precision.
*/
virtual bool tracks_differ(Track::Address, Track::Address) = 0;
virtual bool tracks_differ(Track::Address, Track::Address) const = 0;
/*!
@returns @c true if the file named by the string is what underlies this disk image; @c false otherwise.
*/
virtual bool represents(const std::string &) const = 0;
/*!
@returns @c true if this disk has been written to at any point; @c false otherwise.
*/
virtual bool has_written() const = 0;
};
}

View File

@ -39,51 +39,51 @@ public:
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 HeadPosition get_maximum_head_position() = 0;
// virtual HeadPosition get_maximum_head_position() = 0;
/*!
@returns the number of heads (and, therefore, impliedly surfaces) available on this disk.
*/
virtual int get_head_count() { return 1; }
int get_head_count() const { return 1; }
/*!
@returns the @c Track at @c position underneath @c head if there are any detectable events there;
returns @c nullptr otherwise.
*/
virtual std::unique_ptr<Track> track_at_position(Track::Address address) = 0;
// virtual std::unique_ptr<Track> track_at_position(Track::Address address) = 0;
/*!
Replaces the Tracks indicated by the map, that maps from physical address to track content.
*/
virtual void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &) {}
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &) {}
/*!
Communicates that it is likely to be a while before any more tracks are written.
*/
virtual void flush_tracks() {}
void flush_tracks() {}
/*!
@returns whether the disk image is read only. Defaults to @c true if not overridden.
*/
virtual bool get_is_read_only() { return true; }
bool get_is_read_only() const { return true; }
/*!
@returns @c true if the tracks at the two addresses are different. @c false if they are the same track.
This can avoid some degree of work when disk images offer sub-head-position precision.
*/
virtual bool tracks_differ(Track::Address lhs, Track::Address rhs) { return lhs != rhs; }
bool tracks_differ(Track::Address lhs, Track::Address rhs) const { return lhs != rhs; }
/*!
Maps from an address to its canonical form; this provides a means for images that duplicate the same
track at different addresses to declare as much.
*/
virtual Track::Address canonical_address(Track::Address address) { return address; }
Track::Address canonical_address(Track::Address address) const { return address; }
};
class DiskImageHolderBase: public Disk {
protected:
std::set<Track::Address> unwritten_tracks_;
std::map<Track::Address, std::shared_ptr<Track>> cached_tracks_;
mutable std::map<Track::Address, std::shared_ptr<Track>> cached_tracks_;
std::unique_ptr<Concurrency::AsyncTaskQueue<true>> update_queue_;
};
@ -95,22 +95,26 @@ class DiskImageHolderBase: public Disk {
Implements TargetPlatform::TypeDistinguisher to return either no information whatsoever, if
the underlying image doesn't implement TypeDistinguisher, or else to pass the call along.
*/
template <typename T> class DiskImageHolder: public DiskImageHolderBase, public TargetPlatform::Distinguisher {
template <typename T>
class DiskImageHolder: public DiskImageHolderBase, public TargetPlatform::Distinguisher {
public:
template <typename... Ts> DiskImageHolder(Ts&&... args) :
disk_image_(args...) {}
~DiskImageHolder();
HeadPosition get_maximum_head_position();
int get_head_count();
Track *track_at_position(Track::Address address);
void set_track_at_position(Track::Address address, const std::shared_ptr<Track> &track);
void flush_tracks();
bool get_is_read_only();
bool tracks_differ(Track::Address lhs, Track::Address rhs);
HeadPosition get_maximum_head_position() const override;
int get_head_count() const override;
Track *track_at_position(Track::Address address) const override;
void set_track_at_position(Track::Address address, const std::shared_ptr<Track> &track) override;
void flush_tracks() override;
bool tracks_differ(Track::Address lhs, Track::Address rhs) const override;
bool get_is_read_only() const override;
bool represents(const std::string &) const override;
bool has_written() const override;
private:
T disk_image_;
bool has_written_ = false;
TargetPlatform::Type target_platforms() final {
if constexpr (std::is_base_of<TargetPlatform::Distinguisher, T>::value) {

View File

@ -6,19 +6,34 @@
// Copyright 2017 Thomas Harte. All rights reserved.
//
template <typename T> HeadPosition DiskImageHolder<T>::get_maximum_head_position() {
template <typename T>
HeadPosition DiskImageHolder<T>::get_maximum_head_position() const {
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() const {
return disk_image_.get_head_count();
}
template <typename T> bool DiskImageHolder<T>::get_is_read_only() {
template <typename T>
bool DiskImageHolder<T>::get_is_read_only() const {
return disk_image_.get_is_read_only();
}
template <typename T> void DiskImageHolder<T>::flush_tracks() {
template <typename T>
bool DiskImageHolder<T>::represents(const std::string &file) const {
return false; // TODO.
// return disk_image_.represents(file);
}
template <typename T>
bool DiskImageHolder<T>::has_written() const {
return has_written_;
}
template <typename T>
void DiskImageHolder<T>::flush_tracks() {
if(!unwritten_tracks_.empty()) {
if(!update_queue_) update_queue_ = std::make_unique<Concurrency::AsyncTaskQueue<true>>();
@ -35,14 +50,17 @@ template <typename T> void DiskImageHolder<T>::flush_tracks() {
}
}
template <typename T> void DiskImageHolder<T>::set_track_at_position(Track::Address address, const std::shared_ptr<Track> &track) {
template <typename T>
void DiskImageHolder<T>::set_track_at_position(Track::Address address, const std::shared_ptr<Track> &track) {
if(disk_image_.get_is_read_only()) return;
has_written_ = true;
unwritten_tracks_.insert(address);
cached_tracks_[address] = track;
}
template <typename T> Track *DiskImageHolder<T>::track_at_position(Track::Address address) {
template <typename T>
Track *DiskImageHolder<T>::track_at_position(Track::Address address) const {
if(address.head >= get_head_count()) return nullptr;
if(address.position >= get_maximum_head_position()) return nullptr;
@ -56,10 +74,12 @@ template <typename T> Track *DiskImageHolder<T>::track_at_position(Track::Addres
return track.get();
}
template <typename T> DiskImageHolder<T>::~DiskImageHolder() {
template <typename T>
DiskImageHolder<T>::~DiskImageHolder() {
if(update_queue_) update_queue_->flush();
}
template <typename T> bool DiskImageHolder<T>::tracks_differ(Track::Address lhs, Track::Address rhs) {
template <typename T>
bool DiskImageHolder<T>::tracks_differ(Track::Address lhs, Track::Address rhs) const {
return disk_image_.tracks_differ(lhs, rhs);
}

View File

@ -78,7 +78,8 @@ Disk2MG::DiskOrMassStorageDevice Disk2MG::open(const std::string &file_name) {
// 'ProDOS order', which could still mean Macintosh-style (ie. not ProDOS, but whatever)
// or Apple II-style. Try them both.
try {
return new DiskImageHolder<Storage::Disk::MacintoshIMG>(file_name, MacintoshIMG::FixedType::GCR, data_start, data_size);
return new DiskImageHolder<Storage::Disk::MacintoshIMG>(
file_name, MacintoshIMG::FixedType::GCR, data_start, data_size);
} catch(...) {}
// TODO: Apple II-style.

View File

@ -111,14 +111,14 @@ AcornADF::AcornADF(const std::string &file_name) : MFMSectorDump(file_name) {
set_geometry(sectors_per_track_, sector_size_, 0, density);
}
HeadPosition AcornADF::get_maximum_head_position() {
HeadPosition AcornADF::get_maximum_head_position() const {
return HeadPosition(80);
}
int AcornADF::get_head_count() {
int AcornADF::get_head_count() const {
return head_count_;
}
long AcornADF::get_file_offset_for_position(Track::Address address) {
long AcornADF::get_file_offset_for_position(Track::Address address) const {
return (address.position.as_int() * head_count_ + address.head) * (128 << sector_size_) * sectors_per_track_;
}

View File

@ -27,11 +27,11 @@ public:
*/
AcornADF(const std::string &file_name);
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
HeadPosition get_maximum_head_position() const final;
int get_head_count() const final;
private:
long get_file_offset_for_position(Track::Address) final;
long get_file_offset_for_position(Track::Address) const final;
int head_count_ = 1;
uint8_t sector_size_ = 1;
int sectors_per_track_ = 16;

View File

@ -116,15 +116,15 @@ AmigaADF::AmigaADF(const std::string &file_name) :
if(file_.stats().st_size != 901120) throw Error::InvalidFormat;
}
HeadPosition AmigaADF::get_maximum_head_position() {
HeadPosition AmigaADF::get_maximum_head_position() const {
return HeadPosition(80);
}
int AmigaADF::get_head_count() {
int AmigaADF::get_head_count() const {
return 2;
}
std::unique_ptr<Track> AmigaADF::track_at_position(Track::Address address) {
std::unique_ptr<Track> AmigaADF::track_at_position(Track::Address address) const {
using namespace Storage::Encodings;
// Create an MFM encoder.
@ -182,6 +182,6 @@ std::unique_ptr<Track> AmigaADF::track_at_position(Track::Address address) {
return std::make_unique<Storage::Disk::PCMTrack>(std::move(encoded_segment));
}
long AmigaADF::get_file_offset_for_position(Track::Address address) {
long AmigaADF::get_file_offset_for_position(Track::Address address) const {
return (address.position.as_int() * 2 + address.head) * 512 * 11;
}

View File

@ -29,13 +29,13 @@ public:
AmigaADF(const std::string &file_name);
// implemented to satisfy @c Disk
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
std::unique_ptr<Track> track_at_position(Track::Address) final;
HeadPosition get_maximum_head_position() const;
int get_head_count() const;
std::unique_ptr<Track> track_at_position(Track::Address) const;
private:
Storage::FileHolder file_;
long get_file_offset_for_position(Track::Address);
mutable Storage::FileHolder file_;
long get_file_offset_for_position(Track::Address) const;
};

View File

@ -42,26 +42,26 @@ AppleDSK::AppleDSK(const std::string &file_name) :
}
}
HeadPosition AppleDSK::get_maximum_head_position() {
HeadPosition AppleDSK::get_maximum_head_position() const {
return HeadPosition(number_of_tracks);
}
bool AppleDSK::get_is_read_only() {
bool AppleDSK::get_is_read_only() const {
return file_.get_is_known_read_only();
}
long AppleDSK::file_offset(Track::Address address) {
long AppleDSK::file_offset(Track::Address address) const {
return address.position.as_int() * bytes_per_sector * sectors_per_track_;
}
size_t AppleDSK::logical_sector_for_physical_sector(size_t physical) {
size_t AppleDSK::logical_sector_for_physical_sector(size_t physical) const {
// DOS and Pro DOS interleave sectors on disk, and they're represented in a disk
// image in physical order rather than logical.
if(physical == 15) return 15;
return (physical * (is_prodos_ ? 8 : 7)) % 15;
}
std::unique_ptr<Track> AppleDSK::track_at_position(Track::Address address) {
std::unique_ptr<Track> AppleDSK::track_at_position(Track::Address address) const {
std::vector<uint8_t> track_data;
{
std::lock_guard lock_guard(file_.get_file_access_mutex());

View File

@ -30,18 +30,18 @@ public:
AppleDSK(const std::string &file_name);
// Implemented to satisfy @c DiskImage.
HeadPosition get_maximum_head_position() final;
std::unique_ptr<Track> track_at_position(Track::Address) final;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &) final;
bool get_is_read_only() final;
HeadPosition get_maximum_head_position() const;
std::unique_ptr<Track> track_at_position(Track::Address) const;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &);
bool get_is_read_only() const;
private:
Storage::FileHolder file_;
mutable Storage::FileHolder file_;
int sectors_per_track_ = 16;
bool is_prodos_ = false;
long file_offset(Track::Address);
size_t logical_sector_for_physical_sector(size_t physical);
long file_offset(Track::Address) const;
size_t logical_sector_for_physical_sector(size_t physical) const;
};
}

View File

@ -186,19 +186,19 @@ CPCDSK::CPCDSK(const std::string &file_name) :
}
}
HeadPosition CPCDSK::get_maximum_head_position() {
HeadPosition CPCDSK::get_maximum_head_position() const {
return HeadPosition(head_position_count_);
}
int CPCDSK::get_head_count() {
int CPCDSK::get_head_count() const {
return 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) const {
return size_t((address.position.as_int() * head_count_) + address.head);
}
std::unique_ptr<Track> CPCDSK::track_at_position(::Storage::Disk::Track::Address address) {
std::unique_ptr<Track> CPCDSK::track_at_position(::Storage::Disk::Track::Address address) const {
// Given that thesea are interleaved images, determine which track, chronologically, is being requested.
const std::size_t chronological_track = index_for_track(address);
@ -385,6 +385,6 @@ void CPCDSK::set_tracks(const std::map<::Storage::Disk::Track::Address, std::uni
}
}
bool CPCDSK::get_is_read_only() {
bool CPCDSK::get_is_read_only() const {
return is_read_only_;
}

View File

@ -31,11 +31,11 @@ public:
CPCDSK(const std::string &file_name);
// DiskImage interface.
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
bool get_is_read_only() final;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &) final;
std::unique_ptr<::Storage::Disk::Track> track_at_position(::Storage::Disk::Track::Address) final;
HeadPosition get_maximum_head_position() const;
int get_head_count() const;
bool get_is_read_only() const;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &);
std::unique_ptr<::Storage::Disk::Track> track_at_position(::Storage::Disk::Track::Address) const;
private:
struct Track {
@ -60,7 +60,7 @@ private:
};
std::string file_name_;
std::vector<std::unique_ptr<Track>> tracks_;
std::size_t index_for_track(::Storage::Disk::Track::Address address);
std::size_t index_for_track(::Storage::Disk::Track::Address address) const;
int head_count_;
int head_position_count_;

View File

@ -34,11 +34,11 @@ D64::D64(const std::string &file_name) :
}
}
HeadPosition D64::get_maximum_head_position() {
HeadPosition D64::get_maximum_head_position() const {
return HeadPosition(number_of_tracks_);
}
std::unique_ptr<Track> D64::track_at_position(const Track::Address address) {
std::unique_ptr<Track> D64::track_at_position(const Track::Address address) const {
// Figure out where this track starts on the disk.
int offset_to_track = 0;
int tracks_to_traverse = address.position.as_int();

View File

@ -26,11 +26,11 @@ public:
*/
D64(const std::string &file_name);
HeadPosition get_maximum_head_position() final;
std::unique_ptr<Track> track_at_position(Track::Address) final;
HeadPosition get_maximum_head_position() const;
std::unique_ptr<Track> track_at_position(Track::Address) const;
private:
Storage::FileHolder file_;
mutable Storage::FileHolder file_;
int number_of_tracks_;
uint16_t disk_id_;
};

View File

@ -61,25 +61,25 @@ DMK::DMK(const std::string &file_name) :
if(format) throw Error::InvalidFormat;
}
HeadPosition DMK::get_maximum_head_position() {
HeadPosition DMK::get_maximum_head_position() const {
return HeadPosition(head_position_count_);
}
int DMK::get_head_count() {
int DMK::get_head_count() const {
return head_count_;
}
bool DMK::get_is_read_only() {
bool DMK::get_is_read_only() const {
return true;
// Given that track serialisation is not yet implemented, treat all DMKs as read-only.
// return is_read_only_;
}
long DMK::get_file_offset_for_position(Track::Address address) {
long DMK::get_file_offset_for_position(const Track::Address address) const {
return (address.head*head_count_ + address.position.as_int()) * track_length_ + 16;
}
std::unique_ptr<::Storage::Disk::Track> DMK::track_at_position(::Storage::Disk::Track::Address address) {
std::unique_ptr<::Storage::Disk::Track> DMK::track_at_position(const ::Storage::Disk::Track::Address address) const {
file_.seek(get_file_offset_for_position(address), SEEK_SET);
// Read the IDAM table.

View File

@ -28,16 +28,15 @@ public:
*/
DMK(const std::string &file_name);
// implemented to satisfy @c Disk
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
bool get_is_read_only() final;
HeadPosition get_maximum_head_position() const;
int get_head_count() const;
bool get_is_read_only() const;
std::unique_ptr<Track> track_at_position(Track::Address) final;
std::unique_ptr<Track> track_at_position(Track::Address) const;
private:
FileHolder file_;
long get_file_offset_for_position(Track::Address address);
mutable FileHolder file_;
long get_file_offset_for_position(Track::Address address) const;
bool is_read_only_;
int head_position_count_;

View File

@ -49,14 +49,14 @@ FAT12::FAT12(const std::string &file_name) :
);
}
HeadPosition FAT12::get_maximum_head_position() {
HeadPosition FAT12::get_maximum_head_position() const {
return HeadPosition(track_count_);
}
int FAT12::get_head_count() {
int FAT12::get_head_count() const {
return head_count_;
}
long FAT12::get_file_offset_for_position(Track::Address address) {
long FAT12::get_file_offset_for_position(Track::Address address) const {
return (address.position.as_int() * head_count_ + address.head) * sector_size_ * sector_count_;
}

View File

@ -21,11 +21,11 @@ namespace Storage::Disk {
class FAT12: public MFMSectorDump {
public:
FAT12(const std::string &file_name);
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
HeadPosition get_maximum_head_position() const final;
int get_head_count() const final;
private:
long get_file_offset_for_position(Track::Address address) final;
long get_file_offset_for_position(Track::Address address) const;
int head_count_;
int track_count_;

View File

@ -30,13 +30,13 @@ G64::G64(const std::string &file_name) :
maximum_track_size_ = file_.get_le<uint16_t>();
}
HeadPosition G64::get_maximum_head_position() {
HeadPosition G64::get_maximum_head_position() const {
// give at least 84 tracks, to yield the normal geometry but,
// if there are more, shove them in
return HeadPosition(number_of_tracks_ > 84 ? number_of_tracks_ : 84, 2);
}
std::unique_ptr<Track> G64::track_at_position(Track::Address address) {
std::unique_ptr<Track> G64::track_at_position(const Track::Address address) const {
// seek to this track's entry in the track table
file_.seek(long((address.position.as_half() * 4) + 0xc), SEEK_SET);

View File

@ -30,12 +30,12 @@ public:
G64(const std::string &file_name);
// implemented to satisfy @c Disk
HeadPosition get_maximum_head_position() final;
std::unique_ptr<Track> track_at_position(Track::Address) final;
HeadPosition get_maximum_head_position() const;
std::unique_ptr<Track> track_at_position(Track::Address) const;
using DiskImage::get_is_read_only;
private:
Storage::FileHolder file_;
mutable Storage::FileHolder file_;
uint8_t number_of_tracks_;
uint16_t maximum_track_size_;
};

View File

@ -25,11 +25,11 @@ HFE::HFE(const std::string &file_name) :
track_list_offset_ = long(file_.get_le<uint16_t>()) << 9;
}
HeadPosition HFE::get_maximum_head_position() {
HeadPosition HFE::get_maximum_head_position() const {
return HeadPosition(track_count_);
}
int HFE::get_head_count() {
int HFE::get_head_count() const {
return head_count_;
}
@ -40,7 +40,7 @@ int HFE::get_head_count() {
To read the track, start from the current file position, read 256 bytes,
skip 256 bytes, read 256 bytes, skip 256 bytes, etc.
*/
uint16_t HFE::seek_track(Track::Address address) {
uint16_t HFE::seek_track(const Track::Address address) const {
// 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.as_int() * 4, SEEK_SET);
@ -54,7 +54,7 @@ uint16_t HFE::seek_track(Track::Address address) {
return track_length / 2; // Divide by two to give the track length for a single side.
}
std::unique_ptr<Track> HFE::track_at_position(Track::Address address) {
std::unique_ptr<Track> HFE::track_at_position(const Track::Address address) const {
PCMSegment segment;
{
std::lock_guard lock_guard(file_.get_file_access_mutex());
@ -125,6 +125,6 @@ void HFE::set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tra
}
}
bool HFE::get_is_read_only() {
bool HFE::get_is_read_only() const {
return file_.get_is_known_read_only();
}

View File

@ -30,15 +30,15 @@ public:
HFE(const std::string &file_name);
// implemented to satisfy @c Disk
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
bool get_is_read_only() final;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tracks) final;
std::unique_ptr<Track> track_at_position(Track::Address) final;
HeadPosition get_maximum_head_position() const;
int get_head_count() const;
bool get_is_read_only() const;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tracks);
std::unique_ptr<Track> track_at_position(Track::Address) const;
private:
Storage::FileHolder file_;
uint16_t seek_track(Track::Address address);
mutable Storage::FileHolder file_;
uint16_t seek_track(Track::Address address) const;
int head_count_;
int track_count_;

View File

@ -90,15 +90,15 @@ IMD::IMD(const std::string &file_name) : file_(file_name) {
++ heads_;
}
HeadPosition IMD::get_maximum_head_position() {
HeadPosition IMD::get_maximum_head_position() const {
return HeadPosition(cylinders_);
}
int IMD::get_head_count() {
int IMD::get_head_count() const {
return heads_ + 1;
}
std::unique_ptr<Track> IMD::track_at_position(Track::Address address) {
std::unique_ptr<Track> IMD::track_at_position(const Track::Address address) const {
auto location = track_locations_.find(address);
if(location == track_locations_.end()) {
return nullptr;

View File

@ -29,12 +29,12 @@ public:
IMD(const std::string &file_name);
// DiskImage interface.
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
std::unique_ptr<Track> track_at_position(Track::Address) final;
HeadPosition get_maximum_head_position() const;
int get_head_count() const;
std::unique_ptr<Track> track_at_position(Track::Address) const;
private:
FileHolder file_;
mutable FileHolder file_;
std::map<Storage::Disk::Track::Address, long> track_locations_;
uint8_t cylinders_ = 0, heads_ = 0;
};

View File

@ -109,7 +109,7 @@ IPF::IPF(const std::string &file_name) : file_(file_name) {
// If the file didn't declare anything, default to supporting everything.
if(!platform_type_) {
platform_type_ = ~0;
platform_type_ = ~TargetPlatform::IntType(0);
}
// Ignore: disk number, creator ID, reserved area.
@ -178,15 +178,15 @@ IPF::IPF(const std::string &file_name) : file_(file_name) {
}
}
HeadPosition IPF::get_maximum_head_position() {
HeadPosition IPF::get_maximum_head_position() const {
return HeadPosition(track_count_);
}
int IPF::get_head_count() {
int IPF::get_head_count() const {
return head_count_;
}
std::unique_ptr<Track> IPF::track_at_position([[maybe_unused]] Track::Address address) {
std::unique_ptr<Track> IPF::track_at_position(const Track::Address address) const {
// Get the track description, if it exists, and check either that the file has contents for the track.
auto pair = tracks_.find(address);
if(pair == tracks_.end()) {
@ -322,7 +322,7 @@ std::unique_ptr<Track> IPF::track_at_position([[maybe_unused]] Track::Address ad
/// densities (or, equivalently, lengths) in the file, densities are named according to their protection scheme and the decoder
/// is required to know all named protection schemes. Which makes IPF unable to handle arbitrary disks (or, indeed, disks
/// with multiple protection schemes on a single track).
Storage::Time IPF::bit_length(TrackDescription::Density density, int block) {
Storage::Time IPF::bit_length(TrackDescription::Density density, int block) const {
constexpr unsigned int us = 100'000'000;
static constexpr auto us170 = Storage::Time::simplified(170, us);
static constexpr auto us180 = Storage::Time::simplified(180, us);
@ -378,7 +378,7 @@ Storage::Time IPF::bit_length(TrackDescription::Density density, int block) {
return us200; // i.e. default to 2µs.
}
void IPF::add_gap(std::vector<Storage::Disk::PCMSegment> &track, Time bit_length, size_t num_bits, uint32_t value) {
void IPF::add_gap(std::vector<Storage::Disk::PCMSegment> &track, Time bit_length, size_t num_bits, uint32_t value) const {
auto &segment = track.emplace_back();
segment.length_of_a_bit = bit_length;
@ -396,7 +396,7 @@ void IPF::add_gap(std::vector<Storage::Disk::PCMSegment> &track, Time bit_length
segment.data.resize(num_bits);
}
void IPF::add_unencoded_data(std::vector<Storage::Disk::PCMSegment> &track, Time bit_length, size_t num_bits) {
void IPF::add_unencoded_data(std::vector<Storage::Disk::PCMSegment> &track, Time bit_length, size_t num_bits) const {
auto &segment = track.emplace_back();
segment.length_of_a_bit = bit_length;
@ -415,7 +415,7 @@ void IPF::add_unencoded_data(std::vector<Storage::Disk::PCMSegment> &track, Time
segment.data.resize(num_bits * 2);
}
void IPF::add_raw_data(std::vector<Storage::Disk::PCMSegment> &track, Time bit_length, size_t num_bits) {
void IPF::add_raw_data(std::vector<Storage::Disk::PCMSegment> &track, Time bit_length, size_t num_bits) const {
auto &segment = track.emplace_back();
segment.length_of_a_bit = bit_length;

View File

@ -36,12 +36,12 @@ public:
IPF(const std::string &file_name);
// implemented to satisfy @c Disk
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
std::unique_ptr<Track> track_at_position(Track::Address) final;
HeadPosition get_maximum_head_position() const;
int get_head_count() const;
std::unique_ptr<Track> track_at_position(Track::Address) const;
private:
Storage::FileHolder file_;
mutable Storage::FileHolder file_;
uint16_t seek_track(Track::Address address);
struct TrackDescription {
@ -77,10 +77,10 @@ private:
}
TargetPlatform::IntType platform_type_ = TargetPlatform::Amiga;
Time bit_length(TrackDescription::Density, int block);
void add_gap(std::vector<Storage::Disk::PCMSegment> &, Time bit_length, size_t num_bits, uint32_t value);
void add_unencoded_data(std::vector<Storage::Disk::PCMSegment> &, Time bit_length, size_t num_bits);
void add_raw_data(std::vector<Storage::Disk::PCMSegment> &, Time bit_length, size_t num_bits);
Time bit_length(TrackDescription::Density, int block) const;
void add_gap(std::vector<Storage::Disk::PCMSegment> &, Time bit_length, size_t num_bits, uint32_t value) const;
void add_unencoded_data(std::vector<Storage::Disk::PCMSegment> &, Time bit_length, size_t num_bits) const;
void add_raw_data(std::vector<Storage::Disk::PCMSegment> &, Time bit_length, size_t num_bits) const;
};
}

View File

@ -21,7 +21,7 @@ void MFMSectorDump::set_geometry(int sectors_per_track, uint8_t sector_size, uin
first_sector_ = first_sector;
}
std::unique_ptr<Track> MFMSectorDump::track_at_position(Track::Address address) {
std::unique_ptr<Track> MFMSectorDump::track_at_position(Track::Address address) const {
if(address.head >= get_head_count()) return nullptr;
if(address.position.as_largest() >= get_maximum_head_position().as_largest()) return nullptr;
@ -68,6 +68,6 @@ void MFMSectorDump::set_tracks(const std::map<Track::Address, std::unique_ptr<Tr
file_.flush();
}
bool MFMSectorDump::get_is_read_only() {
bool MFMSectorDump::get_is_read_only() const {
return file_.get_is_known_read_only();
}

View File

@ -23,16 +23,18 @@ class MFMSectorDump: public DiskImage {
public:
MFMSectorDump(const std::string &file_name);
bool get_is_read_only() final;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tracks) final;
std::unique_ptr<Track> track_at_position(Track::Address) final;
bool get_is_read_only() const;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tracks);
std::unique_ptr<Track> track_at_position(Track::Address) const;
protected:
Storage::FileHolder file_;
void set_geometry(int sectors_per_track, uint8_t sector_size, uint8_t first_sector, Encodings::MFM::Density density);
mutable Storage::FileHolder file_;
void set_geometry(int sectors_per_track, uint8_t sector_size, uint8_t first_sector, Encodings::MFM::Density);
private:
virtual long get_file_offset_for_position(Track::Address address) = 0;
virtual int get_head_count() const = 0;
virtual HeadPosition get_maximum_head_position() const = 0;
virtual long get_file_offset_for_position(Track::Address) const = 0;
int sectors_per_track_ = 0;
uint8_t sector_size_ = 0;

View File

@ -75,7 +75,7 @@ MSA::MSA(const std::string &file_name) :
throw Error::InvalidFormat;
}
std::unique_ptr<Track> MSA::track_at_position(Track::Address address) {
std::unique_ptr<Track> MSA::track_at_position(Track::Address address) const {
if(address.head >= sides_) return nullptr;
const auto position = address.position.as_int();
@ -87,10 +87,10 @@ std::unique_ptr<Track> MSA::track_at_position(Track::Address address) {
return track_for_sectors(track.data(), sectors_per_track_, uint8_t(position), uint8_t(address.head), 1, 2, Storage::Encodings::MFM::Density::Double);
}
HeadPosition MSA::get_maximum_head_position() {
HeadPosition MSA::get_maximum_head_position() const {
return HeadPosition(ending_track_ + 1);
}
int MSA::get_head_count() {
int MSA::get_head_count() const {
return sides_;
}

View File

@ -24,13 +24,13 @@ public:
MSA(const std::string &file_name);
// Implemented to satisfy @c DiskImage.
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
std::unique_ptr<Track> track_at_position(Track::Address) final;
bool get_is_read_only() final { return false; }
HeadPosition get_maximum_head_position() const;
int get_head_count() const;
std::unique_ptr<Track> track_at_position(Track::Address) const;
bool get_is_read_only() const { return false; }
private:
FileHolder file_;
mutable FileHolder file_;
uint16_t sectors_per_track_;
uint16_t sides_;
uint16_t starting_track_;

View File

@ -143,7 +143,7 @@ void MacintoshIMG::construct_raw_gcr(size_t offset, size_t size) {
}
}
uint32_t MacintoshIMG::checksum(const std::vector<uint8_t> &data, size_t bytes_to_skip) {
uint32_t MacintoshIMG::checksum(const std::vector<uint8_t> &data, size_t bytes_to_skip) const {
uint32_t result = 0;
// Checksum algorithm is: take each two bytes as a big-endian word; add that to a
@ -157,21 +157,21 @@ uint32_t MacintoshIMG::checksum(const std::vector<uint8_t> &data, size_t bytes_t
return result;
}
HeadPosition MacintoshIMG::get_maximum_head_position() {
HeadPosition MacintoshIMG::get_maximum_head_position() const {
return HeadPosition(80);
}
int MacintoshIMG::get_head_count() {
int MacintoshIMG::get_head_count() const {
// Bit 5 in the format field indicates whether this disk is double
// sided, regardless of whether it is GCR or MFM.
return 1 + ((format_ & 0x20) >> 5);
}
bool MacintoshIMG::get_is_read_only() {
bool MacintoshIMG::get_is_read_only() const {
return file_.get_is_known_read_only();
}
std::unique_ptr<Track> MacintoshIMG::track_at_position(Track::Address address) {
std::unique_ptr<Track> MacintoshIMG::track_at_position(Track::Address address) const {
/*
The format_ byte has the following meanings:
@ -197,8 +197,8 @@ std::unique_ptr<Track> MacintoshIMG::track_at_position(Track::Address address) {
if(start_sector*512 >= data_.size()) return nullptr;
uint8_t *const sector = &data_[512 * start_sector];
uint8_t *const tags = tags_.size() ? &tags_[12 * start_sector] : nullptr;
const uint8_t *const sector = &data_[512 * start_sector];
const uint8_t *const tags = tags_.size() ? &tags_[12 * start_sector] : nullptr;
Storage::Disk::PCMSegment segment;
segment += Encodings::AppleGCR::six_and_two_sync(24);

View File

@ -43,15 +43,15 @@ public:
MacintoshIMG(const std::string &file_name, FixedType type, size_t offset = 0, size_t length = 0);
// implemented to satisfy @c Disk
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
bool get_is_read_only() final;
HeadPosition get_maximum_head_position() const;
int get_head_count() const;
bool get_is_read_only() const;
std::unique_ptr<Track> track_at_position(Track::Address) final;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tracks) final;
std::unique_ptr<Track> track_at_position(Track::Address) const;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tracks);
private:
Storage::FileHolder file_;
mutable Storage::FileHolder file_;
enum class Encoding {
GCR400,
@ -64,9 +64,9 @@ private:
std::vector<uint8_t> data_;
std::vector<uint8_t> tags_;
bool is_diskCopy_file_ = false;
std::mutex buffer_mutex_;
mutable std::mutex buffer_mutex_;
uint32_t checksum(const std::vector<uint8_t> &, size_t bytes_to_skip = 0);
uint32_t checksum(const std::vector<uint8_t> &, size_t bytes_to_skip = 0) const;
void construct_raw_gcr(size_t offset, size_t length = 0);
long raw_offset_ = 0;
};

View File

@ -42,26 +42,26 @@ NIB::NIB(const std::string &file_name) :
}
}
HeadPosition NIB::get_maximum_head_position() {
HeadPosition NIB::get_maximum_head_position() const {
return HeadPosition(number_of_tracks);
}
bool NIB::get_is_read_only() {
bool NIB::get_is_read_only() const {
return file_.get_is_known_read_only();
}
long NIB::file_offset(Track::Address address) {
long NIB::file_offset(const Track::Address address) const {
return long(address.position.as_int()) * track_length;
}
Track::Address NIB::canonical_address(Track::Address address) {
Track::Address NIB::canonical_address(const Track::Address address) const {
return Track::Address(
address.head,
HeadPosition(address.position.as_int())
);
}
std::unique_ptr<Track> NIB::track_at_position(Track::Address address) {
std::unique_ptr<Track> NIB::track_at_position(const Track::Address address) const {
static constexpr size_t MinimumSyncByteCount = 4;
// NIBs contain data for a fixed quantity of integer-position tracks underneath a single head only.

View File

@ -26,16 +26,16 @@ public:
NIB(const std::string &file_name);
// Implemented to satisfy @c DiskImage.
HeadPosition get_maximum_head_position() final;
Track::Address canonical_address(Track::Address) final;
std::unique_ptr<Track> track_at_position(Track::Address) final;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tracks) final;
bool get_is_read_only() final;
HeadPosition get_maximum_head_position() const;
Track::Address canonical_address(Track::Address) const;
std::unique_ptr<Track> track_at_position(Track::Address) const;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tracks);
bool get_is_read_only() const;
private:
FileHolder file_;
long get_file_offset_for_position(Track::Address address);
long file_offset(Track::Address address);
mutable FileHolder file_;
long get_file_offset_for_position(Track::Address address) const;
long file_offset(Track::Address address) const;
};
}

View File

@ -29,15 +29,15 @@ OricMFMDSK::OricMFMDSK(const std::string &file_name) :
throw Error::InvalidFormat;
}
HeadPosition OricMFMDSK::get_maximum_head_position() {
HeadPosition OricMFMDSK::get_maximum_head_position() const {
return HeadPosition(int(track_count_));
}
int OricMFMDSK::get_head_count() {
int OricMFMDSK::get_head_count() const {
return int(head_count_);
}
long OricMFMDSK::get_file_offset_for_position(Track::Address address) {
long OricMFMDSK::get_file_offset_for_position(Track::Address address) const {
int seek_offset = 0;
switch(geometry_type_) {
case 1:
@ -50,7 +50,7 @@ long OricMFMDSK::get_file_offset_for_position(Track::Address address) {
return long(seek_offset) * 6400 + 256;
}
std::unique_ptr<Track> OricMFMDSK::track_at_position(Track::Address address) {
std::unique_ptr<Track> OricMFMDSK::track_at_position(const Track::Address address) const {
PCMSegment segment;
{
std::lock_guard lock_guard(file_.get_file_access_mutex());
@ -164,6 +164,6 @@ void OricMFMDSK::set_tracks(const std::map<Track::Address, std::unique_ptr<Track
}
}
bool OricMFMDSK::get_is_read_only() {
bool OricMFMDSK::get_is_read_only() const {
return file_.get_is_known_read_only();
}

View File

@ -28,16 +28,16 @@ public:
OricMFMDSK(const std::string &file_name);
// implemented to satisfy @c DiskImage
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
bool get_is_read_only() final;
HeadPosition get_maximum_head_position() const;
int get_head_count() const;
bool get_is_read_only() const;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tracks) final;
std::unique_ptr<Track> track_at_position(Track::Address) final;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tracks);
std::unique_ptr<Track> track_at_position(Track::Address) const;
private:
Storage::FileHolder file_;
long get_file_offset_for_position(Track::Address address);
mutable Storage::FileHolder file_;
long get_file_offset_for_position(Track::Address address) const;
uint32_t head_count_;
uint32_t track_count_;

View File

@ -55,14 +55,14 @@ PCBooter::PCBooter(const std::string &file_name) :
// as it can also be used to disambiguate FAT12 disks.
}
HeadPosition PCBooter::get_maximum_head_position() {
HeadPosition PCBooter::get_maximum_head_position() const {
return HeadPosition(track_count_);
}
int PCBooter::get_head_count() {
int PCBooter::get_head_count() const {
return head_count_;
}
long PCBooter::get_file_offset_for_position(Track::Address address) {
long PCBooter::get_file_offset_for_position(Track::Address address) const {
return (address.position.as_int() * head_count_ + address.head) * 512 * sector_count_;
}

View File

@ -21,11 +21,11 @@ namespace Storage::Disk {
class PCBooter: public MFMSectorDump {
public:
PCBooter(const std::string &file_name);
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
HeadPosition get_maximum_head_position() const final;
int get_head_count() const final;
private:
long get_file_offset_for_position(Track::Address address) final;
long get_file_offset_for_position(Track::Address address) const final;
int head_count_;
int track_count_;

View File

@ -32,14 +32,14 @@ SSD::SSD(const std::string &file_name) : MFMSectorDump(file_name) {
set_geometry(sectors_per_track, sector_size, 0, Encodings::MFM::Density::Single);
}
HeadPosition SSD::get_maximum_head_position() {
HeadPosition SSD::get_maximum_head_position() const {
return HeadPosition(track_count_);
}
int SSD::get_head_count() {
int SSD::get_head_count() const {
return head_count_;
}
long SSD::get_file_offset_for_position(Track::Address address) {
long SSD::get_file_offset_for_position(const Track::Address address) const {
return (address.position.as_int() * head_count_ + address.head) * 256 * 10;
}

View File

@ -25,11 +25,11 @@ public:
*/
SSD(const std::string &file_name);
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
HeadPosition get_maximum_head_position() const final;
int get_head_count() const final;
private:
long get_file_offset_for_position(Track::Address address) final;
long get_file_offset_for_position(Track::Address address) const final;
int head_count_;
int track_count_;

View File

@ -440,15 +440,15 @@ STX::STX(const std::string &file_name) : file_(file_name) {
}
}
HeadPosition STX::get_maximum_head_position() {
HeadPosition STX::get_maximum_head_position() const {
return HeadPosition(track_count_ + 1); // Same issue as MSA; must fix!
}
int STX::get_head_count() {
int STX::get_head_count() const {
return head_count_;
}
std::unique_ptr<Track> STX::track_at_position(Track::Address address) {
std::unique_ptr<Track> STX::track_at_position(const Track::Address address) const {
// These images have two sides, at most.
if(address.head > 1) return nullptr;

View File

@ -27,13 +27,13 @@ public:
*/
STX(const std::string &file_name);
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
HeadPosition get_maximum_head_position() const;
int get_head_count() const;
std::unique_ptr<Track> track_at_position(Track::Address) final;
std::unique_ptr<Track> track_at_position(Track::Address) const;
private:
FileHolder file_;
mutable FileHolder file_;
int track_count_;
int head_count_;

View File

@ -107,15 +107,15 @@ WOZ::WOZ(const std::string &file_name) :
if(tracks_offset_ == -1 || !has_tmap) throw Error::InvalidFormat;
}
HeadPosition WOZ::get_maximum_head_position() {
HeadPosition WOZ::get_maximum_head_position() const {
return is_3_5_disk_ ? HeadPosition(80) : HeadPosition(160, 4);
}
int WOZ::get_head_count() {
int WOZ::get_head_count() const {
return is_3_5_disk_ ? 2 : 1;
}
long WOZ::file_offset(Track::Address address) {
long WOZ::file_offset(Track::Address address) const {
// Calculate table position.
int table_position;
if(!is_3_5_disk_) {
@ -141,13 +141,13 @@ long WOZ::file_offset(Track::Address address) {
}
}
bool WOZ::tracks_differ(Track::Address lhs, Track::Address rhs) {
bool WOZ::tracks_differ(Track::Address lhs, Track::Address rhs) const {
const long offset1 = file_offset(lhs);
const long offset2 = file_offset(rhs);
return offset1 != offset2;
}
std::unique_ptr<Track> WOZ::track_at_position(Track::Address address) {
std::unique_ptr<Track> WOZ::track_at_position(Track::Address address) const {
const long offset = file_offset(address);
if(offset == NoSuchTrack) {
return nullptr;
@ -218,7 +218,7 @@ void WOZ::set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tra
file_.write(post_crc_contents_);
}
bool WOZ::get_is_read_only() {
bool WOZ::get_is_read_only() const {
/*
There is an unintended issue with the disk code that sits above here: it doesn't understand the idea
of multiple addresses mapping to the same track, yet it maintains a cache of track contents. Therefore

View File

@ -24,15 +24,15 @@ public:
WOZ(const std::string &file_name);
// Implemented to satisfy @c DiskImage.
HeadPosition get_maximum_head_position() final;
int get_head_count() final;
std::unique_ptr<Track> track_at_position(Track::Address) final;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tracks) final;
bool get_is_read_only() final;
bool tracks_differ(Track::Address, Track::Address) final;
HeadPosition get_maximum_head_position() const;
int get_head_count() const;
std::unique_ptr<Track> track_at_position(Track::Address) const;
void set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tracks);
bool get_is_read_only() const;
bool tracks_differ(Track::Address, Track::Address) const;
private:
Storage::FileHolder file_;
mutable Storage::FileHolder file_;
enum class Type {
WOZ1, WOZ2
} type_ = Type::WOZ1;
@ -49,7 +49,7 @@ private:
@returns The offset within the file of the track at @c address or @c NoSuchTrack if
the track does not exit.
*/
long file_offset(Track::Address address);
long file_offset(Track::Address address) const;
constexpr static long NoSuchTrack = 0; // This is an offset a track definitely can't lie at.
};

View File

@ -66,6 +66,10 @@ void Drive::set_disk(const std::shared_ptr<Disk> &disk) {
update_clocking_observer();
}
const Disk *Drive::disk() const {
return disk_.get();
}
bool Drive::has_disk() const {
return has_disk_;
}

View File

@ -50,6 +50,11 @@ public:
*/
void set_disk(const std::shared_ptr<Disk> &disk);
/*!
@returns The attached disk, if any.
*/
const Disk *disk() const;
/*!
@returns @c true if a disk is currently inserted; @c false otherwise.
*/