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:
parent
05a93ba237
commit
b5406b90cd
@ -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 */,
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
16
Storage/Disk/DiskImage.cpp
Normal file
16
Storage/Disk/DiskImage.cpp
Normal 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
147
Storage/Disk/DiskImage.hpp
Normal 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 */
|
@ -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) {
|
||||
|
@ -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_);
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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>();
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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_);
|
||||
}
|
||||
|
@ -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_;
|
||||
|
@ -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_);
|
||||
}
|
||||
|
@ -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_;
|
||||
|
@ -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_;
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user