From 20c814a4dd90cbb4debb65c01eb9161ebac42c73 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 1 May 2021 21:10:46 -0400 Subject: [PATCH 1/4] Factors out boilerplate around full-device sector images. --- .../Clock Signal.xcodeproj/project.pbxproj | 2 + Storage/MassStorage/Formats/DAT.cpp | 33 +++--------- Storage/MassStorage/Formats/DAT.hpp | 14 +---- Storage/MassStorage/Formats/RawSectorDump.hpp | 53 +++++++++++++++++++ 4 files changed, 64 insertions(+), 38 deletions(-) create mode 100644 Storage/MassStorage/Formats/RawSectorDump.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 2fc24652f..e32353f5f 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1524,6 +1524,7 @@ 4B9378E322A199C600973513 /* Audio.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Audio.hpp; sourceTree = ""; }; 4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZX8081OptionsPanel.swift; sourceTree = ""; }; 4B961408222760E0001A7BF2 /* Screenshot.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Screenshot.hpp; sourceTree = ""; }; + 4B96F7CB263E30B00092AEE1 /* RawSectorDump.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = RawSectorDump.hpp; sourceTree = ""; }; 4B98A05C1FFAD3F600ADF63B /* CSROMFetcher.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CSROMFetcher.hpp; sourceTree = ""; }; 4B98A05D1FFAD3F600ADF63B /* CSROMFetcher.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CSROMFetcher.mm; sourceTree = ""; }; 4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MSXStaticAnalyserTests.mm; sourceTree = ""; }; @@ -2973,6 +2974,7 @@ 4B74CF802312FA9C00500CE8 /* HFV.cpp */, 4BE8EB6425C750B50040BC40 /* DAT.cpp */, 4BE8EB6525C750B50040BC40 /* DAT.hpp */, + 4B96F7CB263E30B00092AEE1 /* RawSectorDump.hpp */, ); path = Formats; sourceTree = ""; diff --git a/Storage/MassStorage/Formats/DAT.cpp b/Storage/MassStorage/Formats/DAT.cpp index ceb00c7ab..d5853dfd6 100644 --- a/Storage/MassStorage/Formats/DAT.cpp +++ b/Storage/MassStorage/Formats/DAT.cpp @@ -10,32 +10,13 @@ using namespace Storage::MassStorage; -DAT::DAT(const std::string &file_name) : file_(file_name) { - // Is the file a multiple of 256 bytes in size? - const auto file_size = file_.stats().st_size; - if(file_size & 255) throw std::exception(); - - // Does it contain the 'Hugo' signature? - file_.seek(0x201, SEEK_SET); - if(!file_.check_signature("Hugo")) { +DAT::DAT(const std::string &file_name) : RawSectorDump(file_name) { + // Does the third sector contain the 'Hugo' signature? + const auto sector3 = get_block(2); + if(sector3.size() != 256) { + throw std::exception(); + } + if(sector3[1] != 'H' || sector3[2] != 'u' || sector3[3] != 'g' || sector3[4] != 'o') { throw std::exception(); } } - -size_t DAT::get_block_size() { - return 256; -} - -size_t DAT::get_number_of_blocks() { - return size_t(file_.stats().st_size) / 256; -} - -std::vector DAT::get_block(size_t address) { - file_.seek(long(address * 256), SEEK_SET); - return file_.read(256); -} - -void DAT::set_block(size_t address, const std::vector &contents) { - file_.seek(long(address * 256), SEEK_SET); - file_.write(contents); -} diff --git a/Storage/MassStorage/Formats/DAT.hpp b/Storage/MassStorage/Formats/DAT.hpp index 7ec0350e4..a47a537c5 100644 --- a/Storage/MassStorage/Formats/DAT.hpp +++ b/Storage/MassStorage/Formats/DAT.hpp @@ -9,8 +9,7 @@ #ifndef MassStorage_DAT_hpp #define MassStorage_DAT_hpp -#include "../MassStorageDevice.hpp" -#include "../../FileHolder.hpp" +#include "RawSectorDump.hpp" namespace Storage { namespace MassStorage { @@ -20,18 +19,9 @@ namespace MassStorage { sector dump of an ADFS volume. It will be validated for an ADFS catalogue and communicate in 256-byte blocks. */ -class DAT: public MassStorageDevice { +class DAT: public RawSectorDump<256> { public: DAT(const std::string &file_name); - - private: - FileHolder file_; - - /* MassStorageDevices overrides. */ - size_t get_block_size() final; - size_t get_number_of_blocks() final; - std::vector get_block(size_t address) final; - void set_block(size_t address, const std::vector &) final; }; } diff --git a/Storage/MassStorage/Formats/RawSectorDump.hpp b/Storage/MassStorage/Formats/RawSectorDump.hpp new file mode 100644 index 000000000..b75340459 --- /dev/null +++ b/Storage/MassStorage/Formats/RawSectorDump.hpp @@ -0,0 +1,53 @@ +// +// RawSectorDump.hpp +// Clock Signal +// +// Created by Thomas Harte on 01/05/2021. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#ifndef RawSectorDump_h +#define RawSectorDump_h + +#include "../MassStorageDevice.hpp" +#include "../../FileHolder.hpp" + +namespace Storage { +namespace MassStorage { + +template class RawSectorDump: public MassStorageDevice { + public: + RawSectorDump(const std::string &file_name) : file_(file_name) { + // Is the file a multiple of sector_size bytes in size? + const auto file_size = size_t(file_.stats().st_size); + if(file_size % sector_size) throw std::exception(); + } + + /* MassStorageDevices overrides. */ + size_t get_block_size() final { + return sector_size; + } + + size_t get_number_of_blocks() final { + return size_t(file_.stats().st_size) / sector_size; + } + + std::vector get_block(size_t address) final { + file_.seek(long(address * sector_size), SEEK_SET); + return file_.read(sector_size); + } + + void set_block(size_t address, const std::vector &contents) final { + assert(contents.size() == sector_size); + file_.seek(long(address * sector_size), SEEK_SET); + file_.write(contents); + } + + private: + FileHolder file_; +}; + +} +} + +#endif /* RawSectorDump_h */ From bfb2f79cff138803086ea0f297b6adc54eae118c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 10 May 2021 21:33:40 -0400 Subject: [PATCH 2/4] That's two learning curves. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0aecf6fe1..07c16a566 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![Clock Signal Application Icon](READMEImages/Icon.png) # Clock Signal -Clock Signal ('CLK') is an emulator for tourists that seeks to be invisible. Users directly launch classic software, avoiding the learning curve associated with emulators and with classic machines. +Clock Signal ('CLK') is an emulator for tourists that seeks to be invisible. Users directly launch classic software, avoiding the learning curves associated with emulators and with classic machines. This emulator seeks to offer: * single-click load of any piece of source media for any supported platform; From 50ea56e908d5d80955c8abe0a9ca58a3399f0ff2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 13 May 2021 19:06:00 -0400 Subject: [PATCH 3/4] Adds support for Macintosh SCSI device images. This is now in addition to the single-partition images previously supported. --- Analyser/Static/StaticAnalyser.cpp | 4 ++- .../Clock Signal.xcodeproj/project.pbxproj | 12 ++++++-- Storage/MassStorage/Formats/DSK.cpp | 23 ++++++++++++++ Storage/MassStorage/Formats/DSK.hpp | 30 +++++++++++++++++++ Storage/MassStorage/Formats/HFV.cpp | 5 +++- .../MassStorage/SCSI/DirectAccessDevice.cpp | 2 +- 6 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 Storage/MassStorage/Formats/DSK.cpp create mode 100644 Storage/MassStorage/Formats/DSK.hpp diff --git a/Analyser/Static/StaticAnalyser.cpp b/Analyser/Static/StaticAnalyser.cpp index 5a0a7996d..589f3f206 100644 --- a/Analyser/Static/StaticAnalyser.cpp +++ b/Analyser/Static/StaticAnalyser.cpp @@ -55,6 +55,7 @@ // Mass Storage Devices (i.e. usually, hard disks) #include "../../Storage/MassStorage/Formats/DAT.hpp" +#include "../../Storage/MassStorage/Formats/DSK.hpp" #include "../../Storage/MassStorage/Formats/HFV.hpp" // State Snapshots @@ -144,7 +145,8 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: TargetPlatform::AmstradCPC | TargetPlatform::Oric | TargetPlatform::ZXSpectrum) // DSK (Amstrad CPC, etc) Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::DiskII) // DSK (Apple II) Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::Macintosh) // DSK (Macintosh, floppy disk) - Format("dsk", result.mass_storage_devices, MassStorage::HFV, TargetPlatform::Macintosh) // DSK (Macintosh, hard disk) + Format("dsk", result.mass_storage_devices, MassStorage::HFV, TargetPlatform::Macintosh) // DSK (Macintosh, hard disk, single volume image) + Format("dsk", result.mass_storage_devices, MassStorage::DSK, TargetPlatform::Macintosh) // DSK (Macintosh, hard disk, full device image) Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::MSX) // DSK (MSX) Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::Oric) // DSK (Oric) Format("g64", result.disks, Disk::DiskImageHolder, TargetPlatform::Commodore) // G64 diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index e32353f5f..bde5d0985 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -551,6 +551,8 @@ 4B92E26B234AE35100CD6D1B /* MFP68901.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B92E268234AE35000CD6D1B /* MFP68901.cpp */; }; 4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */; }; 4B9378E422A199C600973513 /* Audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9378E222A199C600973513 /* Audio.cpp */; }; + 4B96F7CE263E33B10092AEE1 /* DSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96F7CC263E33B10092AEE1 /* DSK.cpp */; }; + 4B96F7CF263E33B10092AEE1 /* DSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96F7CC263E33B10092AEE1 /* DSK.cpp */; }; 4B98A05E1FFAD3F600ADF63B /* CSROMFetcher.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B98A05D1FFAD3F600ADF63B /* CSROMFetcher.mm */; }; 4B98A05F1FFAD62400ADF63B /* CSROMFetcher.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B98A05D1FFAD3F600ADF63B /* CSROMFetcher.mm */; }; 4B98A0611FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */; }; @@ -1525,6 +1527,8 @@ 4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZX8081OptionsPanel.swift; sourceTree = ""; }; 4B961408222760E0001A7BF2 /* Screenshot.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Screenshot.hpp; sourceTree = ""; }; 4B96F7CB263E30B00092AEE1 /* RawSectorDump.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = RawSectorDump.hpp; sourceTree = ""; }; + 4B96F7CC263E33B10092AEE1 /* DSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DSK.cpp; sourceTree = ""; }; + 4B96F7CD263E33B10092AEE1 /* DSK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DSK.hpp; sourceTree = ""; }; 4B98A05C1FFAD3F600ADF63B /* CSROMFetcher.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CSROMFetcher.hpp; sourceTree = ""; }; 4B98A05D1FFAD3F600ADF63B /* CSROMFetcher.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CSROMFetcher.mm; sourceTree = ""; }; 4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MSXStaticAnalyserTests.mm; sourceTree = ""; }; @@ -2970,10 +2974,12 @@ 4B74CF7E2312FA9C00500CE8 /* Formats */ = { isa = PBXGroup; children = ( - 4B74CF7F2312FA9C00500CE8 /* HFV.hpp */, - 4B74CF802312FA9C00500CE8 /* HFV.cpp */, 4BE8EB6425C750B50040BC40 /* DAT.cpp */, 4BE8EB6525C750B50040BC40 /* DAT.hpp */, + 4B96F7CC263E33B10092AEE1 /* DSK.cpp */, + 4B96F7CD263E33B10092AEE1 /* DSK.hpp */, + 4B74CF802312FA9C00500CE8 /* HFV.cpp */, + 4B74CF7F2312FA9C00500CE8 /* HFV.hpp */, 4B96F7CB263E30B00092AEE1 /* RawSectorDump.hpp */, ); path = Formats; @@ -5240,6 +5246,7 @@ 4B1B58F7246CC4E8009C171E /* State.cpp in Sources */, 4B0ACC03237756F6008902D0 /* Line.cpp in Sources */, 4B055AB11FAE86070060FFFF /* Tape.cpp in Sources */, + 4B96F7CF263E33B10092AEE1 /* DSK.cpp in Sources */, 4B2B946626377C0200E7097C /* SZX.cpp in Sources */, 4BEDA43225B3C700000C2DBD /* Executor.cpp in Sources */, 4BC1317B2346DF2B00E4FF3D /* MSA.cpp in Sources */, @@ -5546,6 +5553,7 @@ 4B89453E201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4BF8D4D5251C11DD00BBE21B /* 65816Storage.cpp in Sources */, 4B0ACC2823775819008902D0 /* DMAController.cpp in Sources */, + 4B96F7CE263E33B10092AEE1 /* DSK.cpp in Sources */, 4BC131702346DE5000E4FF3D /* StaticAnalyser.cpp in Sources */, 4B37EE821D7345A6006A09A4 /* BinaryDump.cpp in Sources */, 4BCE0053227CE8CA000CA200 /* AppleII.cpp in Sources */, diff --git a/Storage/MassStorage/Formats/DSK.cpp b/Storage/MassStorage/Formats/DSK.cpp new file mode 100644 index 000000000..231855546 --- /dev/null +++ b/Storage/MassStorage/Formats/DSK.cpp @@ -0,0 +1,23 @@ +// +// DSK.cpp +// Clock Signal +// +// Created by Thomas Harte on 01/05/2021. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#include "DSK.hpp" + +using namespace Storage::MassStorage; + +DSK::DSK(const std::string &file_name) : RawSectorDump(file_name) { + // Minimum validation: check the first sector for a device signature, + // with 512-byte blocks. + const auto sector = get_block(0); + if(sector.size() != 512) { + throw std::exception(); + } + if(sector[0] != 0x45 || sector[1] != 0x52 || sector[2] != 0x02 || sector[3] != 0x00) { + throw std::exception(); + } +} diff --git a/Storage/MassStorage/Formats/DSK.hpp b/Storage/MassStorage/Formats/DSK.hpp new file mode 100644 index 000000000..bdf1b53cc --- /dev/null +++ b/Storage/MassStorage/Formats/DSK.hpp @@ -0,0 +1,30 @@ +// +// DSK.hpp +// Clock Signal +// +// Created by Thomas Harte on 01/05/2021. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#ifndef MassStorage_DSK_hpp +#define MassStorage_DSK_hpp + +#include "RawSectorDump.hpp" + +namespace Storage { +namespace MassStorage { + +/*! + Provides a @c MassStorageDevice containing a Macintosh DSK image, which is just a + sector dump of an entire HFS drive. It will be validated for an Apple-style partition map and communicate + in 512-byte blocks. +*/ +class DSK: public RawSectorDump<512> { + public: + DSK(const std::string &file_name); +}; + +} +} + +#endif /* MassStorage_DSK_hpp */ diff --git a/Storage/MassStorage/Formats/HFV.cpp b/Storage/MassStorage/Formats/HFV.cpp index 3e454e892..f4e3b9ae0 100644 --- a/Storage/MassStorage/Formats/HFV.cpp +++ b/Storage/MassStorage/Formats/HFV.cpp @@ -15,7 +15,10 @@ HFV::HFV(const std::string &file_name) : file_(file_name) { const auto file_size = file_.stats().st_size; if(file_size & 511 || file_size <= 800*1024) throw std::exception(); - // TODO: check filing system for MFS, HFS or HFS+. + // Is this an HFS volume? + // TODO: check filing system for MFS or HFS+. + const auto prefix = file_.read(2); + if(prefix[0] != 'L' || prefix[1] != 'K') throw std::exception(); } size_t HFV::get_block_size() { diff --git a/Storage/MassStorage/SCSI/DirectAccessDevice.cpp b/Storage/MassStorage/SCSI/DirectAccessDevice.cpp index f6944d2f0..a6d99ccc2 100644 --- a/Storage/MassStorage/SCSI/DirectAccessDevice.cpp +++ b/Storage/MassStorage/SCSI/DirectAccessDevice.cpp @@ -19,7 +19,7 @@ bool DirectAccessDevice::read(const Target::CommandState &state, Target::Respond if(!device_) return false; const auto specs = state.read_write_specs(); - LOG("Read: " << specs.number_of_blocks << " from " << specs.address); + LOG("Read: " << std::dec << specs.number_of_blocks << " from " << specs.address); std::vector output = device_->get_block(specs.address); for(uint32_t offset = 1; offset < specs.number_of_blocks; ++offset) { From ceae81a33281706ce1f5ab4ed85093e140e830b9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 13 May 2021 19:11:19 -0400 Subject: [PATCH 4/4] Add missing header. --- Storage/MassStorage/Formats/RawSectorDump.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Storage/MassStorage/Formats/RawSectorDump.hpp b/Storage/MassStorage/Formats/RawSectorDump.hpp index b75340459..60d0e7314 100644 --- a/Storage/MassStorage/Formats/RawSectorDump.hpp +++ b/Storage/MassStorage/Formats/RawSectorDump.hpp @@ -12,6 +12,8 @@ #include "../MassStorageDevice.hpp" #include "../../FileHolder.hpp" +#include + namespace Storage { namespace MassStorage {