1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-11 08:30:55 +00:00

Introduces a new class hierarchy for disk images.

Increasing independence of format-specific stuff and generic caching without mangling them into a common namespace, and allowing in some cases for a decrease in read/write blocking.
This commit is contained in:
Thomas Harte 2017-09-22 20:28:11 -04:00
parent 05a93ba237
commit b5406b90cd
24 changed files with 335 additions and 271 deletions

View File

@ -100,6 +100,7 @@
4B8378E21F336920005CA9E4 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8378E01F336920005CA9E4 /* CharacterMapper.cpp */; };
4B8378E51F3378C4005CA9E4 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8378E31F3378C4005CA9E4 /* CharacterMapper.cpp */; };
4B838F1F1F35FDCD0016B5E6 /* CPCDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B838F1D1F35FDCD0016B5E6 /* CPCDSK.cpp */; };
4B84E24A1F7498120054AB7D /* DiskImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B84E2481F7498120054AB7D /* DiskImage.cpp */; };
4B8805F01DCFC99C003085B1 /* Acorn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805EE1DCFC99C003085B1 /* Acorn.cpp */; };
4B8805F41DCFD22A003085B1 /* Commodore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805F21DCFD22A003085B1 /* Commodore.cpp */; };
4B8805F71DCFF6C9003085B1 /* Commodore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805F51DCFF6C9003085B1 /* Commodore.cpp */; };
@ -123,7 +124,6 @@
4BA22B071D8817CE0008C640 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA22B051D8817CE0008C640 /* Disk.cpp */; };
4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */; };
4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA799931D8B656E0045123D /* StaticAnalyser.cpp */; };
4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */; };
4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B31D327F7E00DF5BA0 /* G64.cpp */; };
4BAB62B81D3302CA00DF5BA0 /* PCMTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */; };
4BACC5B11F3DFF7C0037C015 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BACC5AF1F3DFF7C0037C015 /* CharacterMapper.cpp */; };
@ -649,6 +649,8 @@
4B8378E41F3378C4005CA9E4 /* CharacterMapper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CharacterMapper.hpp; sourceTree = "<group>"; };
4B838F1D1F35FDCD0016B5E6 /* CPCDSK.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CPCDSK.cpp; sourceTree = "<group>"; };
4B838F1E1F35FDCD0016B5E6 /* CPCDSK.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CPCDSK.hpp; sourceTree = "<group>"; };
4B84E2481F7498120054AB7D /* DiskImage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DiskImage.cpp; sourceTree = "<group>"; };
4B84E2491F7498120054AB7D /* DiskImage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiskImage.hpp; sourceTree = "<group>"; };
4B8805EE1DCFC99C003085B1 /* Acorn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Acorn.cpp; path = Parsers/Acorn.cpp; sourceTree = "<group>"; };
4B8805EF1DCFC99C003085B1 /* Acorn.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Acorn.hpp; path = Parsers/Acorn.hpp; sourceTree = "<group>"; };
4B8805F21DCFD22A003085B1 /* Commodore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Commodore.cpp; path = Parsers/Commodore.cpp; sourceTree = "<group>"; };
@ -686,7 +688,6 @@
4BA799931D8B656E0045123D /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StaticAnalyser.cpp; path = ../../StaticAnalyser/Atari/StaticAnalyser.cpp; sourceTree = "<group>"; };
4BA799941D8B656E0045123D /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = ../../StaticAnalyser/Atari/StaticAnalyser.hpp; sourceTree = "<group>"; };
4BA9C3CF1D8164A9002DDB61 /* ConfigurationTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ConfigurationTarget.hpp; sourceTree = "<group>"; };
4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Disk.cpp; sourceTree = "<group>"; };
4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Disk.hpp; sourceTree = "<group>"; };
4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Storage.hpp; sourceTree = "<group>"; };
4BAB62B31D327F7E00DF5BA0 /* G64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = G64.cpp; sourceTree = "<group>"; };
@ -1637,8 +1638,8 @@
isa = PBXGroup;
children = (
4B0BE4261D3481E700D5256B /* DigitalPhaseLockedLoop.cpp */,
4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */,
4B6C73BB1D387AE500AFCFCA /* DiskController.cpp */,
4B84E2481F7498120054AB7D /* DiskImage.cpp */,
4B30512B1D989E2200B4FED8 /* Drive.cpp */,
4BBC95201F36B16C008F4C34 /* MFMDiskController.cpp */,
4B3F1B441E0388D200DB26EE /* PCMPatchedTrack.cpp */,
@ -1649,6 +1650,7 @@
4B0BE4271D3481E700D5256B /* DigitalPhaseLockedLoop.hpp */,
4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */,
4B6C73BC1D387AE500AFCFCA /* DiskController.hpp */,
4B84E2491F7498120054AB7D /* DiskImage.hpp */,
4B30512C1D989E2200B4FED8 /* Drive.hpp */,
4BBC95211F36B16C008F4C34 /* MFMDiskController.hpp */,
4B3F1B451E0388D200DB26EE /* PCMPatchedTrack.hpp */,
@ -2800,7 +2802,6 @@
buildActionMask = 2147483647;
files = (
4B2BFC5F1D613E0200BA3AA9 /* TapePRG.cpp in Sources */,
4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */,
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */,
4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */,
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */,
@ -2820,6 +2821,7 @@
4B8334951F5E25B60097E338 /* C1540.cpp in Sources */,
4B1497921EE4B5A800CE2596 /* ZX8081.cpp in Sources */,
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */,
4B84E24A1F7498120054AB7D /* DiskImage.cpp in Sources */,
4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */,
4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */,
4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */,

View File

@ -77,22 +77,22 @@ static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType
}
if(lowercase_extension) {
Format("80", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 80
Format("81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 81
Format("a26", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // A26
Format("adf", result.disks, Disk::AcornADF, TargetPlatform::Acorn) // ADF
Format("bin", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN
Format("cdt", result.tapes, Tape::TZX, TargetPlatform::AmstradCPC) // CDT
Format("csw", result.tapes, Tape::CSW, TargetPlatform::AllTape) // CSW
Format("d64", result.disks, Disk::D64, TargetPlatform::Commodore) // D64
Format("dsd", result.disks, Disk::SSD, TargetPlatform::Acorn) // DSD
Format("dsk", result.disks, Disk::CPCDSK, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC)
Format("dsk", result.disks, Disk::OricMFMDSK, TargetPlatform::Oric) // DSK (Oric)
Format("g64", result.disks, Disk::G64, TargetPlatform::Commodore) // G64
Format("hfe", result.disks, Disk::HFE, TargetPlatform::AmstradCPC) // HFE (TODO: plus other target platforms)
Format("o", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // O
Format("p", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P
Format("p81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P81
Format("80", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 80
Format("81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 81
Format("a26", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // A26
Format("adf", result.disks, Disk::DiskImageHolder<Storage::Disk::AcornADF>, TargetPlatform::Acorn) // ADF
Format("bin", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN
Format("cdt", result.tapes, Tape::TZX, TargetPlatform::AmstradCPC) // CDT
Format("csw", result.tapes, Tape::CSW, TargetPlatform::AllTape) // CSW
Format("d64", result.disks, Disk::DiskImageHolder<Storage::Disk::D64>, TargetPlatform::Commodore) // D64
Format("dsd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // DSD
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::CPCDSK>, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC)
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::OricMFMDSK>, TargetPlatform::Oric) // DSK (Oric)
Format("g64", result.disks, Disk::DiskImageHolder<Storage::Disk::G64>, TargetPlatform::Commodore) // G64
Format("hfe", result.disks, Disk::DiskImageHolder<Storage::Disk::HFE>, TargetPlatform::AmstradCPC) // HFE (TODO: plus other target platforms)
Format("o", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // O
Format("p", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P
Format("p81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P81
// PRG
if(!strcmp(lowercase_extension, "prg")) {
@ -106,12 +106,12 @@ static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType
}
}
Format("rom", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Acorn) // ROM
Format("ssd", result.disks, Disk::SSD, TargetPlatform::Acorn) // SSD
Format("tap", result.tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP (Commodore)
Format("tap", result.tapes, Tape::OricTAP, TargetPlatform::Oric) // TAP (Oric)
Format("tzx", result.tapes, Tape::TZX, TargetPlatform::ZX8081) // TZX
Format("uef", result.tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape)
Format("rom", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Acorn) // ROM
Format("ssd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // SSD
Format("tap", result.tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP (Commodore)
Format("tap", result.tapes, Tape::OricTAP, TargetPlatform::Oric) // TAP (Oric)
Format("tzx", result.tapes, Tape::TZX, TargetPlatform::ZX8081) // TZX
Format("uef", result.tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape)
#undef Format
#undef Insert

View File

@ -1,49 +0,0 @@
//
// Disk.cpp
// Clock Signal
//
// Created by Thomas Harte on 10/07/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Disk.hpp"
using namespace Storage::Disk;
int Disk::get_id_for_track_at_position(unsigned int head, unsigned int position) {
return (int)(position * get_head_count() + head);
}
void Disk::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) {
if(get_is_read_only()) return;
int address = get_id_for_track_at_position(head, position);
cached_tracks_[address] = track;
if(!update_queue_) update_queue_.reset(new Concurrency::AsyncTaskQueue);
std::shared_ptr<Track> track_copy(track->clone());
update_queue_->enqueue([this, head, position, track_copy] {
store_updated_track_at_position(head, position, track_copy, file_access_mutex_);
});
}
std::shared_ptr<Track> Disk::get_track_at_position(unsigned int head, unsigned int position) {
if(head >= get_head_count()) return nullptr;
if(position >= get_head_position_count()) return nullptr;
int address = get_id_for_track_at_position(head, position);
std::map<int, std::shared_ptr<Track>>::iterator cached_track = cached_tracks_.find(address);
if(cached_track != cached_tracks_.end()) return cached_track->second;
std::lock_guard<std::mutex> lock_guard(file_access_mutex_);
std::shared_ptr<Track> track = get_uncached_track_at_position(head, position);
if(!track) return nullptr;
cached_tracks_[address] = track;
return track;
}
void Disk::store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex) {}
void Disk::flush_updates() {
if(update_queue_) update_queue_->flush();
}

View File

@ -60,18 +60,6 @@ class Track {
virtual Track *clone() = 0;
};
/*!
Models a disk as a collection of tracks, providing a range of possible track positions and allowing
a point sampling of the track beneath any of those positions (if any).
The intention is not that tracks necessarily be evenly spaced; a head_position_count of 3 wih track
A appearing in positions 0 and 1, and track B appearing in position 2 is an appropriate use of this API
if it matches the media.
The track returned is point sampled only; if a particular disk drive has a sufficiently large head to
pick up multiple tracks at once then the drive responsible for asking for multiple tracks and for
merging the results.
*/
class Disk {
public:
virtual ~Disk() {}
@ -87,60 +75,25 @@ class Disk {
/*!
@returns the number of heads (and, therefore, impliedly surfaces) available on this disk.
*/
virtual unsigned int get_head_count() { return 1; }
virtual unsigned int get_head_count() = 0;
/*!
@returns the @c Track at @c position underneath @c head if there are any detectable events there;
returns @c nullptr otherwise.
*/
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
virtual std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position) = 0;
/*!
Replaces the Track at position @c position underneath @c head with @c track. Ignored if this disk is read-only.
Subclasses that are not read-only should use the protected methods @c get_is_modified and, optionally,
@c get_modified_track_at_position to query for changes when closing.
*/
void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track);
virtual void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) = 0;
/*!
@returns whether the disk image is read only. Defaults to @c true if not overridden.
*/
virtual bool get_is_read_only() { return true; }
protected:
/*!
Subclasses should implement this to return the @c Track at @c position underneath @c head. Returned tracks
are cached internally so subclasses shouldn't attempt to build their own caches or worry about preparing
for track accesses at file load time. Appropriate behaviour is to create them lazily, on demand.
*/
virtual std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position) = 0;
/*!
Subclasses that support writing should implement @c store_updated_track_at_position to determine which bytes
have to be written from @c track, then obtain @c file_access_mutex and write new data to their file to represent
the track underneath @c head at @c position.
The base class will ensure that calls are made to @c get_uncached_track_at_position only while it holds @c file_access_mutex;
that mutex therefore provides serialisation of file access.
This method will be called asynchronously. Subclasses are responsible for any synchronisation other than that
provided automatically via @c file_access_mutex.
*/
virtual void store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex);
/*!
Subclasses that support writing should call @c flush_updates during their destructor if there is anything they
do in @c store_updated_track_at_position that would not be valid after their destructor has completed but prior
to Disk's constructor running.
*/
void flush_updates();
private:
int get_id_for_track_at_position(unsigned int head, unsigned int position);
std::map<int, std::shared_ptr<Track>> cached_tracks_;
std::mutex file_access_mutex_;
std::unique_ptr<Concurrency::AsyncTaskQueue> update_queue_;
virtual bool get_is_read_only() = 0;
};
}

View File

@ -0,0 +1,16 @@
//
// DiskImage.cpp
// Clock Signal
//
// Created by Thomas Harte on 21/09/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#include "DiskImage.hpp"
using namespace Storage::Disk;
int DiskImageHolderBase::get_id_for_track_at_position(unsigned int head, unsigned int position) {
return (int)(position * get_head_count() + head);
}

147
Storage/Disk/DiskImage.hpp Normal file
View File

@ -0,0 +1,147 @@
//
// DiskImage.hpp
// Clock Signal
//
// Created by Thomas Harte on 21/09/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef DiskImage_hpp
#define DiskImage_hpp
#include <cstdint>
#include <memory>
#include <vector>
#include "Disk.hpp"
namespace Storage {
namespace Disk {
/*!
Models a disk as a collection of tracks, providing a range of possible track positions and allowing
a point sampling of the track beneath any of those positions (if any).
The intention is not that tracks necessarily be evenly spaced; a head_position_count of 3 wih track
A appearing in positions 0 and 1, and track B appearing in position 2 is an appropriate use of this API
if it matches the media.
The track returned is point sampled only; if a particular disk drive has a sufficiently large head to
pick up multiple tracks at once then the drive responsible for asking for multiple tracks and for
merging the results.
*/
class DiskImage {
public:
virtual ~DiskImage() {}
/*!
@returns the number of discrete positions that this disk uses to model its complete surface area.
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 unsigned int get_head_position_count() = 0;
/*!
@returns the number of heads (and, therefore, impliedly surfaces) available on this disk.
*/
virtual unsigned int get_head_count() { 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::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position) = 0;
/*!
Replaces the Track at position @c position underneath @c head with @c track. Ignored if this disk is read-only.
Subclasses that are not read-only should use the protected methods @c get_is_modified and, optionally,
@c get_modified_track_at_position to query for changes when closing.
*/
virtual void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) {}
/*!
@returns whether the disk image is read only. Defaults to @c true if not overridden.
*/
virtual bool get_is_read_only() { return true; }
};
class PatchingDiskImage {
public:
struct TrackUpdate {
long file_offset;
std::vector<uint8_t> data;
};
};
class DiskImageHolderBase: public Disk {
protected:
int get_id_for_track_at_position(unsigned int head, unsigned int position);
std::map<int, std::shared_ptr<Track>> cached_tracks_;
std::unique_ptr<Concurrency::AsyncTaskQueue> update_queue_;
};
template <typename T> class DiskImageHolder: public DiskImageHolderBase {
public:
template <typename... Ts> DiskImageHolder(Ts&&... args) :
disk_image_(args...) {}
~DiskImageHolder();
unsigned int get_head_position_count();
unsigned int get_head_count();
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track);
bool get_is_read_only();
private:
T disk_image_;
};
template <typename T> unsigned int DiskImageHolder<T>::get_head_position_count() {
return disk_image_.get_head_position_count();
}
template <typename T> unsigned int DiskImageHolder<T>::get_head_count() {
return disk_image_.get_head_count();
}
template <typename T> bool DiskImageHolder<T>::get_is_read_only() {
return disk_image_.get_is_read_only();
}
template <typename T> void DiskImageHolder<T>::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) {
if(disk_image_.get_is_read_only()) return;
int address = get_id_for_track_at_position(head, position);
cached_tracks_[address] = track;
if(!update_queue_) update_queue_.reset(new Concurrency::AsyncTaskQueue);
std::shared_ptr<Track> track_copy(track->clone());
update_queue_->enqueue([this, head, position, track_copy] {
disk_image_.set_track_at_position(head, position, track_copy);
});
}
template <typename T> std::shared_ptr<Track> DiskImageHolder<T>::get_track_at_position(unsigned int head, unsigned int position) {
if(head >= get_head_count()) return nullptr;
if(position >= get_head_position_count()) return nullptr;
int address = get_id_for_track_at_position(head, position);
std::map<int, std::shared_ptr<Track>>::iterator cached_track = cached_tracks_.find(address);
if(cached_track != cached_tracks_.end()) return cached_track->second;
std::shared_ptr<Track> track = disk_image_.get_track_at_position(head, position);
if(!track) return nullptr;
cached_tracks_[address] = track;
return track;
}
template <typename T> DiskImageHolder<T>::~DiskImageHolder() {
if(update_queue_) update_queue_->flush();
}
}
}
#endif /* DiskImage_hpp */

View File

@ -256,7 +256,7 @@ Parser::Parser(bool is_mfm, const std::shared_ptr<Storage::Disk::Disk> &disk) :
Parser::Parser(bool is_mfm, const std::shared_ptr<Storage::Disk::Track> &track) :
Parser(is_mfm) {
drive_->set_disk(std::make_shared<Disk::SingleTrackDisk>(track));
drive_->set_disk(std::make_shared<Disk::DiskImageHolder<Disk::SingleTrackDiskImage>>(track));
}
void Parser::seek_to_track(uint8_t track) {

View File

@ -37,10 +37,6 @@ AcornADF::AcornADF(const char *file_name) :
if(bytes[0] != 'H' || bytes[1] != 'u' || bytes[2] != 'g' || bytes[3] != 'o') throw ErrorNotAcornADF;
}
AcornADF::~AcornADF() {
flush_updates();
}
unsigned int AcornADF::get_head_position_count() {
return 80;
}
@ -57,27 +53,31 @@ long AcornADF::get_file_offset_for_position(unsigned int head, unsigned int posi
return (position * 1 + head) * bytes_per_sector * sectors_per_track;
}
std::shared_ptr<Track> AcornADF::get_uncached_track_at_position(unsigned int head, unsigned int position) {
std::shared_ptr<Track> AcornADF::get_track_at_position(unsigned int head, unsigned int position) {
std::shared_ptr<Track> track;
if(head >= 2) return track;
long file_offset = get_file_offset_for_position(head, position);
fseek(file_, file_offset, SEEK_SET);
std::vector<Storage::Encodings::MFM::Sector> sectors;
for(unsigned int sector = 0; sector < sectors_per_track; sector++) {
Storage::Encodings::MFM::Sector new_sector;
new_sector.track = (uint8_t)position;
new_sector.side = (uint8_t)head;
new_sector.sector = (uint8_t)sector;
new_sector.size = sector_size;
{
std::lock_guard<std::mutex> lock_guard(file_access_mutex_);
fseek(file_, file_offset, SEEK_SET);
new_sector.data.resize(bytes_per_sector);
fread(&new_sector.data[0], 1, bytes_per_sector, file_);
if(feof(file_))
break;
for(unsigned int sector = 0; sector < sectors_per_track; sector++) {
Storage::Encodings::MFM::Sector new_sector;
new_sector.track = (uint8_t)position;
new_sector.side = (uint8_t)head;
new_sector.sector = (uint8_t)sector;
new_sector.size = sector_size;
sectors.push_back(std::move(new_sector));
new_sector.data.resize(bytes_per_sector);
fread(&new_sector.data[0], 1, bytes_per_sector, file_);
if(feof(file_))
break;
sectors.push_back(std::move(new_sector));
}
}
if(sectors.size()) return Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors);
@ -85,7 +85,7 @@ std::shared_ptr<Track> AcornADF::get_uncached_track_at_position(unsigned int hea
return track;
}
void AcornADF::store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex) {
void AcornADF::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) {
std::vector<uint8_t> parsed_track;
Storage::Encodings::MFM::Parser parser(true, track);
for(unsigned int c = 0; c < sectors_per_track; c++) {
@ -99,8 +99,9 @@ void AcornADF::store_updated_track_at_position(unsigned int head, unsigned int p
}
}
std::lock_guard<std::mutex> lock_guard(file_access_mutex);
long file_offset = get_file_offset_for_position(head, position);
std::lock_guard<std::mutex> lock_guard(file_access_mutex_);
ensure_file_is_at_least_length(file_offset);
fseek(file_, file_offset, SEEK_SET);
fwrite(parsed_track.data(), 1, parsed_track.size(), file_);

View File

@ -9,7 +9,7 @@
#ifndef AcornADF_hpp
#define AcornADF_hpp
#include "../Disk.hpp"
#include "../DiskImage.hpp"
#include "../../FileHolder.hpp"
namespace Storage {
@ -18,7 +18,7 @@ namespace Disk {
/*!
Provies a @c Disk containing an ADF disk image a decoded sector dump of an Acorn ADFS disk.
*/
class AcornADF: public Disk, public Storage::FileHolder {
class AcornADF: public DiskImage, public Storage::FileHolder {
public:
/*!
Construct an @c AcornADF containing content from the file with name @c file_name.
@ -27,20 +27,19 @@ class AcornADF: public Disk, public Storage::FileHolder {
@throws ErrorNotAcornADF if the file doesn't appear to contain an Acorn .ADF format image.
*/
AcornADF(const char *file_name);
~AcornADF();
enum {
ErrorNotAcornADF,
};
// implemented to satisfy @c Disk
unsigned int get_head_position_count();
unsigned int get_head_count();
bool get_is_read_only();
void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track);
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private:
void store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex);
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
std::mutex file_access_mutex_;
long get_file_offset_for_position(unsigned int head, unsigned int position);
};

View File

@ -50,7 +50,7 @@ bool CPCDSK::get_is_read_only() {
return false;
}
std::shared_ptr<Track> CPCDSK::get_uncached_track_at_position(unsigned int head, unsigned int position) {
std::shared_ptr<Track> CPCDSK::get_track_at_position(unsigned int head, unsigned int position) {
// Given that thesea are interleaved images, determine which track, chronologically, is being requested.
unsigned int chronological_track = (position * head_count_) + head;

View File

@ -9,7 +9,7 @@
#ifndef CPCDSK_hpp
#define CPCDSK_hpp
#include "../Disk.hpp"
#include "../DiskImage.hpp"
#include "../../FileHolder.hpp"
#include <vector>
@ -20,7 +20,7 @@ namespace Disk {
/*!
Provies a @c Disk containing an Amstrad CPC-stype disk image some arrangement of sectors with status bits.
*/
class CPCDSK: public Disk, public Storage::FileHolder {
class CPCDSK: public DiskImage, public Storage::FileHolder {
public:
/*!
Construct an @c AcornADF containing content from the file with name @c file_name.
@ -38,9 +38,9 @@ class CPCDSK: public Disk, public Storage::FileHolder {
unsigned int get_head_position_count();
unsigned int get_head_count();
bool get_is_read_only();
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private:
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
unsigned int head_count_;
unsigned int head_position_count_;

View File

@ -38,7 +38,7 @@ unsigned int D64::get_head_position_count() {
return number_of_tracks_*2;
}
std::shared_ptr<Track> D64::get_uncached_track_at_position(unsigned int head, unsigned int position) {
std::shared_ptr<Track> D64::get_track_at_position(unsigned int head, unsigned int position) {
// every other track is missing, as is any head above 0
if(position&1 || head)
return std::shared_ptr<Track>();

View File

@ -9,7 +9,7 @@
#ifndef D64_hpp
#define D64_hpp
#include "../Disk.hpp"
#include "../DiskImage.hpp"
#include "../../FileHolder.hpp"
namespace Storage {
@ -18,7 +18,7 @@ namespace Disk {
/*!
Provies a @c Disk containing a D64 disk image a decoded sector dump of a C1540-format disk.
*/
class D64: public Disk, public Storage::FileHolder {
class D64: public DiskImage, public Storage::FileHolder {
public:
/*!
Construct a @c D64 containing content from the file with name @c file_name.
@ -34,9 +34,9 @@ class D64: public Disk, public Storage::FileHolder {
// implemented to satisfy @c Disk
unsigned int get_head_position_count();
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private:
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
unsigned int number_of_tracks_;
uint16_t disk_id_;
};

View File

@ -34,7 +34,7 @@ unsigned int G64::get_head_position_count() {
return number_of_tracks_ > 84 ? number_of_tracks_ : 84;
}
std::shared_ptr<Track> G64::get_uncached_track_at_position(unsigned int head, unsigned int position) {
std::shared_ptr<Track> G64::get_track_at_position(unsigned int head, unsigned int position) {
std::shared_ptr<Track> resulting_track;
// if there's definitely no track here, return the empty track

View File

@ -9,7 +9,7 @@
#ifndef G64_hpp
#define G64_hpp
#include "../Disk.hpp"
#include "../DiskImage.hpp"
#include "../../FileHolder.hpp"
namespace Storage {
@ -18,7 +18,7 @@ namespace Disk {
/*!
Provies a @c Disk containing a G64 disk image a raw but perfectly-clocked GCR stream.
*/
class G64: public Disk, public Storage::FileHolder {
class G64: public DiskImage, public Storage::FileHolder {
public:
/*!
Construct a @c G64 containing content from the file with name @c file_name.
@ -37,9 +37,9 @@ class G64: public Disk, public Storage::FileHolder {
// implemented to satisfy @c Disk
unsigned int get_head_position_count();
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private:
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
uint8_t number_of_tracks_;
uint16_t maximum_track_size_;
};

View File

@ -35,11 +35,7 @@ unsigned int HFE::get_head_count() {
return head_count_;
}
bool HFE::get_is_read_only() {
return true;
}
std::shared_ptr<Track> HFE::get_uncached_track_at_position(unsigned int head, unsigned int position) {
std::shared_ptr<Track> HFE::get_track_at_position(unsigned int head, unsigned int position) {
// Get track position and length from the lookup table; data is then always interleaved
// based on an assumption of two heads.
fseek(file_, track_list_offset_ + position * 4, SEEK_SET);
@ -84,3 +80,4 @@ std::shared_ptr<Track> HFE::get_uncached_track_at_position(unsigned int head, un
std::shared_ptr<Track> track(new PCMTrack(segment));
return track;
}

View File

@ -9,7 +9,7 @@
#ifndef HFE_hpp
#define HFE_hpp
#include "../Disk.hpp"
#include "../DiskImage.hpp"
#include "../../FileHolder.hpp"
namespace Storage {
@ -18,7 +18,7 @@ namespace Disk {
/*!
Provies a @c Disk containing an HFE disk image a bit stream representation of a floppy.
*/
class HFE: public Disk, public Storage::FileHolder {
class HFE: public DiskImage, public Storage::FileHolder {
public:
/*!
Construct an @c SSD containing content from the file with name @c file_name.
@ -36,10 +36,9 @@ class HFE: public Disk, public Storage::FileHolder {
// implemented to satisfy @c Disk
unsigned int get_head_position_count();
unsigned int get_head_count();
bool get_is_read_only();
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private:
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
unsigned int head_count_;
unsigned int track_count_;

View File

@ -25,10 +25,6 @@ OricMFMDSK::OricMFMDSK(const char *file_name) :
throw ErrorNotOricMFMDSK;
}
OricMFMDSK::~OricMFMDSK() {
flush_updates();
}
unsigned int OricMFMDSK::get_head_position_count() {
return track_count_;
}
@ -54,59 +50,61 @@ long OricMFMDSK::get_file_offset_for_position(unsigned int head, unsigned int po
return (seek_offset * 6400) + 256;
}
std::shared_ptr<Track> OricMFMDSK::get_uncached_track_at_position(unsigned int head, unsigned int position) {
fseek(file_, get_file_offset_for_position(head, position), SEEK_SET);
std::shared_ptr<Track> OricMFMDSK::get_track_at_position(unsigned int head, unsigned int position) {
PCMSegment segment;
{
std::lock_guard<std::mutex> lock_guard(file_access_mutex_);
fseek(file_, get_file_offset_for_position(head, position), SEEK_SET);
// The file format omits clock bits. So it's not a genuine MFM capture.
// A consumer must contextually guess when an FB, FC, etc is meant to be a control mark.
size_t track_offset = 0;
uint8_t last_header[6] = {0, 0, 0, 0, 0, 0};
std::unique_ptr<Encodings::MFM::Encoder> encoder = Encodings::MFM::GetMFMEncoder(segment.data);
bool did_sync = false;
while(track_offset < 6250) {
uint8_t next_byte = (uint8_t)fgetc(file_);
track_offset++;
// The file format omits clock bits. So it's not a genuine MFM capture.
// A consumer must contextually guess when an FB, FC, etc is meant to be a control mark.
size_t track_offset = 0;
uint8_t last_header[6] = {0, 0, 0, 0, 0, 0};
std::unique_ptr<Encodings::MFM::Encoder> encoder = Encodings::MFM::GetMFMEncoder(segment.data);
bool did_sync = false;
while(track_offset < 6250) {
uint8_t next_byte = (uint8_t)fgetc(file_);
track_offset++;
switch(next_byte) {
default: {
encoder->add_byte(next_byte);
if(did_sync) {
switch(next_byte) {
default: break;
switch(next_byte) {
default: {
encoder->add_byte(next_byte);
if(did_sync) {
switch(next_byte) {
default: break;
case 0xfe:
for(int byte = 0; byte < 6; byte++) {
last_header[byte] = (uint8_t)fgetc(file_);
encoder->add_byte(last_header[byte]);
track_offset++;
if(track_offset == 6250) break;
}
break;
case 0xfe:
for(int byte = 0; byte < 6; byte++) {
last_header[byte] = (uint8_t)fgetc(file_);
encoder->add_byte(last_header[byte]);
track_offset++;
if(track_offset == 6250) break;
}
break;
case 0xfb:
for(int byte = 0; byte < (128 << last_header[3]) + 2; byte++) {
encoder->add_byte((uint8_t)fgetc(file_));
track_offset++;
if(track_offset == 6250) break;
}
break;
case 0xfb:
for(int byte = 0; byte < (128 << last_header[3]) + 2; byte++) {
encoder->add_byte((uint8_t)fgetc(file_));
track_offset++;
if(track_offset == 6250) break;
}
break;
}
}
did_sync = false;
}
break;
did_sync = false;
case 0xa1: // a synchronisation mark that implies a sector or header coming
encoder->output_short(Storage::Encodings::MFM::MFMSync);
did_sync = true;
break;
case 0xc2: // an 'ordinary' synchronisation mark
encoder->output_short(Storage::Encodings::MFM::MFMIndexSync);
break;
}
break;
case 0xa1: // a synchronisation mark that implies a sector or header coming
encoder->output_short(Storage::Encodings::MFM::MFMSync);
did_sync = true;
break;
case 0xc2: // an 'ordinary' synchronisation mark
encoder->output_short(Storage::Encodings::MFM::MFMIndexSync);
break;
}
}
@ -116,13 +114,13 @@ std::shared_ptr<Track> OricMFMDSK::get_uncached_track_at_position(unsigned int h
return track;
}
void OricMFMDSK::store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex) {
void OricMFMDSK::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) {
Storage::Encodings::MFM::Parser parser(true, track);
std::vector<uint8_t> parsed_track = parser.get_track(0);
long file_offset = get_file_offset_for_position(head, position);
std::lock_guard<std::mutex> lock_guard(file_access_mutex);
fseek(file_, file_offset, SEEK_SET);
std::lock_guard<std::mutex> lock_guard(file_access_mutex_);
fseek(file_, file_offset, SEEK_SET);
size_t track_size = std::min((size_t)6400, parsed_track.size());
fwrite(parsed_track.data(), 1, track_size, file_);
}

View File

@ -9,7 +9,7 @@
#ifndef OricMFMDSK_hpp
#define OricMFMDSK_hpp
#include "../Disk.hpp"
#include "../DiskImage.hpp"
#include "../../FileHolder.hpp"
namespace Storage {
@ -18,16 +18,14 @@ namespace Disk {
/*!
Provies a @c Disk containing an Oric MFM-stype disk image a stream of the MFM data bits with clocks omitted.
*/
class OricMFMDSK: public Disk, public Storage::FileHolder {
class OricMFMDSK: public DiskImage, public Storage::FileHolder {
public:
/*!
Construct an @c AcornADF containing content from the file with name @c file_name.
Construct an @c OricMFMDSK containing content from the file with name @c file_name.
@throws ErrorCantOpen if this file can't be opened.
@throws ErrorNotAcornADF if the file doesn't appear to contain an Acorn .ADF format image.
@throws ErrorNotOricMFMDSK if the file doesn't appear to contain an Oric MFM format image.
*/
OricMFMDSK(const char *file_name);
~OricMFMDSK();
enum {
ErrorNotOricMFMDSK,
@ -37,10 +35,11 @@ class OricMFMDSK: public Disk, public Storage::FileHolder {
unsigned int get_head_position_count();
unsigned int get_head_count();
bool get_is_read_only();
void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track);
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private:
void store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex);
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
std::mutex file_access_mutex_;
long get_file_offset_for_position(unsigned int head, unsigned int position);
uint32_t head_count_;

View File

@ -28,10 +28,6 @@ SSD::SSD(const char *file_name) :
else if(track_count_ < 80) track_count_ = 80;
}
SSD::~SSD() {
flush_updates();
}
unsigned int SSD::get_head_position_count() {
return track_count_;
}
@ -48,29 +44,33 @@ long SSD::get_file_offset_for_position(unsigned int head, unsigned int position)
return (position * head_count_ + head) * 256 * 10;
}
std::shared_ptr<Track> SSD::get_uncached_track_at_position(unsigned int head, unsigned int position) {
std::shared_ptr<Track> SSD::get_track_at_position(unsigned int head, unsigned int position) {
std::shared_ptr<Track> track;
if(head >= head_count_) return track;
fseek(file_, get_file_offset_for_position(head, position), SEEK_SET);
std::vector<Storage::Encodings::MFM::Sector> sectors;
for(int sector = 0; sector < 10; sector++) {
Storage::Encodings::MFM::Sector new_sector;
new_sector.track = (uint8_t)position;
new_sector.side = 0;
new_sector.sector = (uint8_t)sector;
new_sector.size = 1;
{
std::lock_guard<std::mutex> lock_guard(file_access_mutex_);
fseek(file_, get_file_offset_for_position(head, position), SEEK_SET);
new_sector.data.resize(256);
fread(new_sector.data.data(), 1, 256, file_);
for(int sector = 0; sector < 10; sector++) {
Storage::Encodings::MFM::Sector new_sector;
new_sector.track = (uint8_t)position;
new_sector.side = 0;
new_sector.sector = (uint8_t)sector;
new_sector.size = 1;
// zero out if this wasn't present in the disk image; it's still appropriate to put a sector
// on disk because one will have been placed during formatting, but there's no reason to leak
// information from outside the emulated machine's world
if(feof(file_)) memset(new_sector.data.data(), 0, 256);
new_sector.data.resize(256);
fread(new_sector.data.data(), 1, 256, file_);
sectors.push_back(std::move(new_sector));
// zero out if this wasn't present in the disk image; it's still appropriate to put a sector
// on disk because one will have been placed during formatting, but there's no reason to leak
// information from outside the emulated machine's world
if(feof(file_)) memset(new_sector.data.data(), 0, 256);
sectors.push_back(std::move(new_sector));
}
}
if(sectors.size()) return Storage::Encodings::MFM::GetFMTrackWithSectors(sectors);
@ -78,23 +78,24 @@ std::shared_ptr<Track> SSD::get_uncached_track_at_position(unsigned int head, un
return track;
}
void SSD::store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex) {
std::vector<uint8_t> parsed_track;
void SSD::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) {
std::vector<uint8_t> data;
Storage::Encodings::MFM::Parser parser(false, track);
for(unsigned int c = 0; c < 10; c++) {
std::shared_ptr<Storage::Encodings::MFM::Sector> sector = parser.get_sector(0, (uint8_t)position, (uint8_t)c);
if(sector) {
parsed_track.insert(parsed_track.end(), sector->data.begin(), sector->data.end());
data.insert(data.end(), sector->data.begin(), sector->data.end());
} else {
// TODO: what's correct here? Warn the user that whatever has been written to the disk,
// it can no longer be stored as an SSD? If so, warn them by what route?
parsed_track.resize(parsed_track.size() + 256);
data.resize(data.size() + 256);
}
}
std::lock_guard<std::mutex> lock_guard(file_access_mutex);
long file_offset = get_file_offset_for_position(head, position);
std::lock_guard<std::mutex> lock_guard(file_access_mutex_);
ensure_file_is_at_least_length(file_offset);
fseek(file_, file_offset, SEEK_SET);
fwrite(parsed_track.data(), 1, parsed_track.size(), file_);
fwrite(data.data(), 1, data.size(), file_);
}

View File

@ -9,7 +9,7 @@
#ifndef SSD_hpp
#define SSD_hpp
#include "../Disk.hpp"
#include "../DiskImage.hpp"
#include "../../FileHolder.hpp"
namespace Storage {
@ -18,7 +18,7 @@ namespace Disk {
/*!
Provies a @c Disk containing a DSD or SSD disk image a decoded sector dump of an Acorn DFS disk.
*/
class SSD: public Disk, public Storage::FileHolder {
class SSD: public DiskImage, public Storage::FileHolder {
public:
/*!
Construct an @c SSD containing content from the file with name @c file_name.
@ -27,7 +27,6 @@ class SSD: public Disk, public Storage::FileHolder {
@throws ErrorNotSSD if the file doesn't appear to contain a .SSD format image.
*/
SSD(const char *file_name);
~SSD();
enum {
ErrorNotSSD,
@ -37,10 +36,11 @@ class SSD: public Disk, public Storage::FileHolder {
unsigned int get_head_position_count();
unsigned int get_head_count();
bool get_is_read_only();
void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track);
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private:
void store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex);
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
std::mutex file_access_mutex_;
long get_file_offset_for_position(unsigned int head, unsigned int position);
unsigned int head_count_;

View File

@ -10,13 +10,13 @@
using namespace Storage::Disk;
SingleTrackDisk::SingleTrackDisk(const std::shared_ptr<Track> &track) :
SingleTrackDiskImage::SingleTrackDiskImage(const std::shared_ptr<Track> &track) :
track_(track) {}
unsigned int SingleTrackDisk::get_head_position_count() {
unsigned int SingleTrackDiskImage::get_head_position_count() {
return 1;
}
std::shared_ptr<Track> SingleTrackDisk::get_uncached_track_at_position(unsigned int head, unsigned int position) {
std::shared_ptr<Track> SingleTrackDiskImage::get_track_at_position(unsigned int head, unsigned int position) {
return track_;
}

View File

@ -9,7 +9,7 @@
#ifndef SingleTrackDisk_hpp
#define SingleTrackDisk_hpp
#include "Disk.hpp"
#include "DiskImage.hpp"
namespace Storage {
namespace Disk {
@ -17,16 +17,15 @@ namespace Disk {
/*!
Provides a disk that has houses a single track.
*/
class SingleTrackDisk: public Disk {
class SingleTrackDiskImage: public DiskImage {
public:
/// Constructs a single-track disk with the track @c track.
SingleTrackDisk(const std::shared_ptr<Track> &track);
SingleTrackDiskImage(const std::shared_ptr<Track> &track);
unsigned int get_head_position_count();
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private:
std::shared_ptr<Track> track_;
unsigned int get_head_position_count();
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
};
}

View File

@ -12,6 +12,7 @@
#include <sys/stat.h>
#include <cstdio>
#include <cstdint>
#include <mutex>
#include <string>
namespace Storage {
@ -124,6 +125,7 @@ class FileHolder {
FILE *file_;
struct stat file_stats_;
bool is_read_only_;
std::mutex file_access_mutex_;
const std::string name_;
};