diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 8876fcf48..3e198b866 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -84,6 +84,7 @@ 4B50730A1DDFCFDF00C48FBD /* ArrayBuilderTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B5073091DDFCFDF00C48FBD /* ArrayBuilderTests.mm */; }; 4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5C1C3B7D6F0093A61B /* CSOpenGLView.m */; }; 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */; }; + 4B58601E1F806AB200AEE2E3 /* MFMSectorDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B58601C1F806AB200AEE2E3 /* MFMSectorDump.cpp */; }; 4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B59199A1DAC6C46005BB85C /* OricTAP.cpp */; }; 4B5A12571DD55862007A2231 /* Disassembler6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5A12551DD55862007A2231 /* Disassembler6502.cpp */; }; 4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADB81DE3151600AEC565 /* FileHolder.cpp */; }; @@ -631,6 +632,8 @@ 4B55CE5B1C3B7D6F0093A61B /* CSOpenGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSOpenGLView.h; sourceTree = ""; }; 4B55CE5C1C3B7D6F0093A61B /* CSOpenGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSOpenGLView.m; sourceTree = ""; }; 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachineDocument.swift; sourceTree = ""; }; + 4B58601C1F806AB200AEE2E3 /* MFMSectorDump.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MFMSectorDump.cpp; sourceTree = ""; }; + 4B58601D1F806AB200AEE2E3 /* MFMSectorDump.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MFMSectorDump.hpp; sourceTree = ""; }; 4B59199A1DAC6C46005BB85C /* OricTAP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OricTAP.cpp; sourceTree = ""; }; 4B59199B1DAC6C46005BB85C /* OricTAP.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OricTAP.hpp; sourceTree = ""; }; 4B5A12551DD55862007A2231 /* Disassembler6502.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Disassembler6502.cpp; path = ../../StaticAnalyser/Disassembler/Disassembler6502.cpp; sourceTree = ""; }; @@ -1472,7 +1475,6 @@ 4B45188C1F75FD1B00926311 /* Formats */ = { isa = PBXGroup; children = ( - 4BFDD7891F7F2DB4008579B9 /* Utility */, 4B45188D1F75FD1B00926311 /* AcornADF.cpp */, 4B45188E1F75FD1B00926311 /* AcornADF.hpp */, 4B45188F1F75FD1B00926311 /* CPCDSK.cpp */, @@ -1483,10 +1485,13 @@ 4B4518941F75FD1B00926311 /* G64.hpp */, 4B4518951F75FD1B00926311 /* HFE.cpp */, 4B4518961F75FD1B00926311 /* HFE.hpp */, + 4B58601C1F806AB200AEE2E3 /* MFMSectorDump.cpp */, + 4B58601D1F806AB200AEE2E3 /* MFMSectorDump.hpp */, 4B4518971F75FD1B00926311 /* OricMFMDSK.cpp */, 4B4518981F75FD1B00926311 /* OricMFMDSK.hpp */, 4B4518991F75FD1B00926311 /* SSD.cpp */, 4B45189A1F75FD1B00926311 /* SSD.hpp */, + 4BFDD7891F7F2DB4008579B9 /* Utility */, ); path = Formats; sourceTree = ""; @@ -2463,8 +2468,8 @@ 4BFDD7891F7F2DB4008579B9 /* Utility */ = { isa = PBXGroup; children = ( - 4BFDD78A1F7F2DB4008579B9 /* ImplicitSectors.hpp */, 4BFDD78B1F7F2DB4008579B9 /* ImplicitSectors.cpp */, + 4BFDD78A1F7F2DB4008579B9 /* ImplicitSectors.hpp */, ); path = Utility; sourceTree = ""; @@ -2898,6 +2903,7 @@ 4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */, 4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */, 4B7136861F78724F008B8ED9 /* Encoder.cpp in Sources */, + 4B58601E1F806AB200AEE2E3 /* MFMSectorDump.cpp in Sources */, 4B448E841F1C4C480009ABD6 /* PulseQueuedTape.cpp in Sources */, 4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */, 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, diff --git a/Storage/Disk/DiskImage/Formats/AcornADF.cpp b/Storage/Disk/DiskImage/Formats/AcornADF.cpp index 2d1893b63..dee5bb169 100644 --- a/Storage/Disk/DiskImage/Formats/AcornADF.cpp +++ b/Storage/Disk/DiskImage/Formats/AcornADF.cpp @@ -12,18 +12,16 @@ namespace { static const unsigned int sectors_per_track = 16; - static const size_t bytes_per_sector = 256; static const unsigned int sector_size = 1; } using namespace Storage::Disk; -AcornADF::AcornADF(const char *file_name) : - Storage::FileHolder(file_name) { +AcornADF::AcornADF(const char *file_name) : MFMSectorDump(file_name) { // very loose validation: the file needs to be a multiple of 256 bytes // and not ungainly large - if(file_stats_.st_size % (off_t)bytes_per_sector) throw ErrorNotAcornADF; - if(file_stats_.st_size < 7 * (off_t)bytes_per_sector) throw ErrorNotAcornADF; + if(file_stats_.st_size % (off_t)(128 << sector_size)) throw ErrorNotAcornADF; + if(file_stats_.st_size < 7 * (off_t)(128 << sector_size)) throw ErrorNotAcornADF; // check that the initial directory's 'Hugo's are present fseek(file_, 513, SEEK_SET); @@ -34,6 +32,8 @@ AcornADF::AcornADF(const char *file_name) : fseek(file_, 0x6fb, SEEK_SET); fread(bytes, 1, 4, file_); if(bytes[0] != 'H' || bytes[1] != 'u' || bytes[2] != 'g' || bytes[3] != 'o') throw ErrorNotAcornADF; + + set_geometry(sectors_per_track, sector_size, true); } unsigned int AcornADF::get_head_position_count() { @@ -44,37 +44,6 @@ unsigned int AcornADF::get_head_count() { return 1; } -bool AcornADF::get_is_read_only() { - return is_read_only_; -} - long AcornADF::get_file_offset_for_position(unsigned int head, unsigned int position) { - return (position * 1 + head) * bytes_per_sector * sectors_per_track; -} - -std::shared_ptr AcornADF::get_track_at_position(unsigned int head, unsigned int position) { - uint8_t sectors[bytes_per_sector*sectors_per_track]; - - if(head > 1) return nullptr; - long file_offset = get_file_offset_for_position(head, position); - - { - std::lock_guard lock_guard(file_access_mutex_); - fseek(file_, file_offset, SEEK_SET); - fread(sectors, 1, sizeof(sectors), file_); - } - - return track_for_sectors(sectors, (uint8_t)position, (uint8_t)head, 0, sector_size, true); -} - -void AcornADF::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr &track) { - uint8_t parsed_track[bytes_per_sector*sectors_per_track]; - decode_sectors(*track, parsed_track, 0, sectors_per_track-1, sector_size, true); - - long file_offset = get_file_offset_for_position(head, position); - - std::lock_guard lock_guard(file_access_mutex_); - ensure_file_is_at_least_length(file_offset); - fseek(file_, file_offset, SEEK_SET); - fwrite(parsed_track, 1, sizeof(parsed_track), file_); + return (position * 1 + head) * (128 << sector_size) * sectors_per_track; } diff --git a/Storage/Disk/DiskImage/Formats/AcornADF.hpp b/Storage/Disk/DiskImage/Formats/AcornADF.hpp index b48a9a43f..46961f9f7 100644 --- a/Storage/Disk/DiskImage/Formats/AcornADF.hpp +++ b/Storage/Disk/DiskImage/Formats/AcornADF.hpp @@ -9,8 +9,7 @@ #ifndef AcornADF_hpp #define AcornADF_hpp -#include "../DiskImage.hpp" -#include "../../../FileHolder.hpp" +#include "MFMSectorDump.hpp" namespace Storage { namespace Disk { @@ -18,7 +17,7 @@ namespace Disk { /*! Provies a @c Disk containing an ADF disk image — a decoded sector dump of an Acorn ADFS disk. */ -class AcornADF: public DiskImage, public Storage::FileHolder { +class AcornADF: public MFMSectorDump { public: /*! Construct an @c AcornADF containing content from the file with name @c file_name. @@ -34,12 +33,8 @@ class AcornADF: public DiskImage, 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); - std::shared_ptr get_track_at_position(unsigned int head, unsigned int position); private: - std::mutex file_access_mutex_; long get_file_offset_for_position(unsigned int head, unsigned int position); }; diff --git a/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp b/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp new file mode 100644 index 000000000..33f93fa5e --- /dev/null +++ b/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp @@ -0,0 +1,54 @@ +// +// MFMSectorDump.cpp +// Clock Signal +// +// Created by Thomas Harte on 30/09/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#include "MFMSectorDump.hpp" + +#include "Utility/ImplicitSectors.hpp" + +using namespace Storage::Disk; + +MFMSectorDump::MFMSectorDump(const char *file_name) : Storage::FileHolder(file_name) {} + +void MFMSectorDump::set_geometry(int sectors_per_track, uint8_t sector_size, bool is_double_density) { + sectors_per_track_ = sectors_per_track; + sector_size_ = sector_size; + is_double_density_ = is_double_density; +} + +bool MFMSectorDump::get_is_read_only() { + return is_read_only_; +} + +std::shared_ptr MFMSectorDump::get_track_at_position(unsigned int head, unsigned int position) { + uint8_t sectors[(128 << sector_size_)*sectors_per_track_]; + + if(head > 1) return nullptr; + long file_offset = get_file_offset_for_position(head, position); + + { + std::lock_guard lock_guard(file_access_mutex_); + fseek(file_, file_offset, SEEK_SET); + fread(sectors, 1, sizeof(sectors), file_); + } + + return track_for_sectors(sectors, (uint8_t)position, (uint8_t)head, 0, sector_size_, is_double_density_); +} + +void MFMSectorDump::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr &track) { + uint8_t parsed_track[(128 << sector_size_)*sectors_per_track_]; + // Assumption here: sector IDs will run from 0. + decode_sectors(*track, parsed_track, 0, (uint8_t)(sectors_per_track_-1), sector_size_, is_double_density_); + + long file_offset = get_file_offset_for_position(head, position); + + std::lock_guard lock_guard(file_access_mutex_); + ensure_file_is_at_least_length(file_offset); + fseek(file_, file_offset, SEEK_SET); + fwrite(parsed_track, 1, sizeof(parsed_track), file_); + fflush(file_); +} diff --git a/Storage/Disk/DiskImage/Formats/MFMSectorDump.hpp b/Storage/Disk/DiskImage/Formats/MFMSectorDump.hpp new file mode 100644 index 000000000..aee10a307 --- /dev/null +++ b/Storage/Disk/DiskImage/Formats/MFMSectorDump.hpp @@ -0,0 +1,42 @@ +// +// MFMSectorDump.hpp +// Clock Signal +// +// Created by Thomas Harte on 30/09/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#ifndef SectorDump_hpp +#define SectorDump_hpp + +#include "../DiskImage.hpp" +#include "../../../FileHolder.hpp" + +namespace Storage { +namespace Disk { + +/*! + Provies the base for writeable [M]FM disk images that just contain contiguous sector content dumps. +*/ +class MFMSectorDump: public DiskImage, public Storage::FileHolder { + public: + MFMSectorDump(const char *file_name); + void set_geometry(int sectors_per_track, uint8_t sector_size, bool is_double_density); + + bool get_is_read_only(); + void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr &track); + std::shared_ptr get_track_at_position(unsigned int head, unsigned int position); + + private: + std::mutex file_access_mutex_; + virtual long get_file_offset_for_position(unsigned int head, unsigned int position) = 0; + + int sectors_per_track_ = 0; + uint8_t sector_size_ = 0; + bool is_double_density_ = true; +}; + +} +} + +#endif /* SectorDump_hpp */ diff --git a/Storage/Disk/DiskImage/Formats/SSD.cpp b/Storage/Disk/DiskImage/Formats/SSD.cpp index 4763622e2..3dd7d7323 100644 --- a/Storage/Disk/DiskImage/Formats/SSD.cpp +++ b/Storage/Disk/DiskImage/Formats/SSD.cpp @@ -12,14 +12,12 @@ namespace { static const unsigned int sectors_per_track = 10; - static const size_t bytes_per_sector = 256; static const unsigned int sector_size = 1; } using namespace Storage::Disk; -SSD::SSD(const char *file_name) : - Storage::FileHolder(file_name) { +SSD::SSD(const char *file_name) : MFMSectorDump(file_name) { // very loose validation: the file needs to be a multiple of 256 bytes // and not ungainly large @@ -32,6 +30,8 @@ SSD::SSD(const char *file_name) : track_count_ = (unsigned int)(file_stats_.st_size / (256 * 10)); if(track_count_ < 40) track_count_ = 40; else if(track_count_ < 80) track_count_ = 80; + + set_geometry(sectors_per_track, sector_size, false); } unsigned int SSD::get_head_position_count() { @@ -42,37 +42,6 @@ unsigned int SSD::get_head_count() { return head_count_; } -bool SSD::get_is_read_only() { - return is_read_only_; -} - long SSD::get_file_offset_for_position(unsigned int head, unsigned int position) { return (position * head_count_ + head) * 256 * 10; } - -std::shared_ptr SSD::get_track_at_position(unsigned int head, unsigned int position) { - uint8_t sectors[bytes_per_sector*sectors_per_track]; - - if(head >= head_count_) return nullptr; - long file_offset = get_file_offset_for_position(head, position); - - { - std::lock_guard lock_guard(file_access_mutex_); - fseek(file_, file_offset, SEEK_SET); - fread(sectors, 1, sizeof(sectors), file_); - } - - return track_for_sectors(sectors, (uint8_t)position, (uint8_t)head, 0, sector_size, false); -} - -void SSD::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr &track) { - uint8_t parsed_track[bytes_per_sector*sectors_per_track]; - decode_sectors(*track, parsed_track, 0, sectors_per_track-1, sector_size, false); - - long file_offset = get_file_offset_for_position(head, position); - - std::lock_guard lock_guard(file_access_mutex_); - ensure_file_is_at_least_length(file_offset); - fseek(file_, file_offset, SEEK_SET); - fwrite(parsed_track, 1, sizeof(parsed_track), file_); -} diff --git a/Storage/Disk/DiskImage/Formats/SSD.hpp b/Storage/Disk/DiskImage/Formats/SSD.hpp index 4439c1335..0632f29ad 100644 --- a/Storage/Disk/DiskImage/Formats/SSD.hpp +++ b/Storage/Disk/DiskImage/Formats/SSD.hpp @@ -9,8 +9,7 @@ #ifndef SSD_hpp #define SSD_hpp -#include "../DiskImage.hpp" -#include "../../../FileHolder.hpp" +#include "MFMSectorDump.hpp" namespace Storage { namespace Disk { @@ -18,7 +17,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 DiskImage, public Storage::FileHolder { +class SSD: public MFMSectorDump { public: /*! Construct an @c SSD containing content from the file with name @c file_name. @@ -32,15 +31,10 @@ class SSD: public DiskImage, public Storage::FileHolder { ErrorNotSSD, }; - // 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); - std::shared_ptr get_track_at_position(unsigned int head, unsigned int position); private: - std::mutex file_access_mutex_; long get_file_offset_for_position(unsigned int head, unsigned int position); unsigned int head_count_;