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 2fc24652f..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 */; }; @@ -1524,6 +1526,9 @@ 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 = ""; }; + 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 = ""; }; @@ -2969,10 +2974,13 @@ 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; sourceTree = ""; @@ -5238,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 */, @@ -5544,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/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; 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/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/Formats/RawSectorDump.hpp b/Storage/MassStorage/Formats/RawSectorDump.hpp new file mode 100644 index 000000000..60d0e7314 --- /dev/null +++ b/Storage/MassStorage/Formats/RawSectorDump.hpp @@ -0,0 +1,55 @@ +// +// 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" + +#include + +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 */ 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) {