From 1a60ced61bcaf60b44ad236194cb22a024c037a8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 25 Aug 2019 23:03:54 -0400 Subject: [PATCH] Starts trying to deal with creating a whole volume from merely a partition. --- Machines/Apple/Macintosh/Macintosh.cpp | 5 ++ .../Clock Signal.xcodeproj/project.pbxproj | 8 ++ .../xcschemes/Clock Signal.xcscheme | 2 - .../MassStorage/Encodings/MacintoshVolume.cpp | 64 ++++++++++++++ .../MassStorage/Encodings/MacintoshVolume.hpp | 87 +++++++++++++++++++ Storage/MassStorage/Formats/HFV.cpp | 26 ++++-- Storage/MassStorage/Formats/HFV.hpp | 14 ++- 7 files changed, 195 insertions(+), 11 deletions(-) create mode 100644 Storage/MassStorage/Encodings/MacintoshVolume.cpp create mode 100644 Storage/MassStorage/Encodings/MacintoshVolume.hpp diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index b81786d85..fee1ad738 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -39,6 +39,7 @@ #include "../../../Storage/MassStorage/SCSI/SCSI.hpp" #include "../../../Storage/MassStorage/SCSI/DirectAccessDevice.hpp" +#include "../../../Storage/MassStorage/Encodings/MacintoshVolume.hpp" #include "../../../Analyser/Static/Macintosh/Target.hpp" @@ -454,6 +455,10 @@ template class ConcreteMachin // TODO: allow this only at machine startup. if(!media.mass_storage_devices.empty()) { + const auto volume = dynamic_cast(media.mass_storage_devices.front().get()); + if(volume) { + volume->set_drive_type(Storage::MassStorage::Encodings::Macintosh::DriveType::SCSI); + } hard_drive_->set_storage(media.mass_storage_devices.front()); } diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 8926909d8..2e10c1604 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -229,6 +229,8 @@ 4B7136911F789C93008B8ED9 /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B71368F1F789C93008B8ED9 /* SegmentParser.cpp */; }; 4B74CF812312FA9C00500CE8 /* HFV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF802312FA9C00500CE8 /* HFV.cpp */; }; 4B74CF822312FA9C00500CE8 /* HFV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF802312FA9C00500CE8 /* HFV.cpp */; }; + 4B74CF85231370BC00500CE8 /* MacintoshVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF83231370BC00500CE8 /* MacintoshVolume.cpp */; }; + 4B74CF86231370BC00500CE8 /* MacintoshVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF83231370BC00500CE8 /* MacintoshVolume.cpp */; }; 4B7913CC1DFCD80E00175A82 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7913CA1DFCD80E00175A82 /* Video.cpp */; }; 4B79A5011FC913C900EEDAD5 /* MSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B79A4FF1FC913C900EEDAD5 /* MSX.cpp */; }; 4B79E4441E3AF38600141F11 /* cassette.png in Resources */ = {isa = PBXBuildFile; fileRef = 4B79E4411E3AF38600141F11 /* cassette.png */; }; @@ -988,6 +990,8 @@ 4B7136901F789C93008B8ED9 /* SegmentParser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SegmentParser.hpp; sourceTree = ""; }; 4B74CF7F2312FA9C00500CE8 /* HFV.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = HFV.hpp; sourceTree = ""; }; 4B74CF802312FA9C00500CE8 /* HFV.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HFV.cpp; sourceTree = ""; }; + 4B74CF83231370BC00500CE8 /* MacintoshVolume.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MacintoshVolume.cpp; path = Encodings/MacintoshVolume.cpp; sourceTree = ""; }; + 4B74CF84231370BC00500CE8 /* MacintoshVolume.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = MacintoshVolume.hpp; path = Encodings/MacintoshVolume.hpp; sourceTree = ""; }; 4B77069C1EC904570053B588 /* Z80.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Z80.hpp; path = Z80/Z80.hpp; sourceTree = ""; }; 4B770A961FE9EE770026DC70 /* CompoundSource.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CompoundSource.hpp; sourceTree = ""; }; 4B7913CA1DFCD80E00175A82 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Video.cpp; path = Electron/Video.cpp; sourceTree = ""; }; @@ -2266,6 +2270,8 @@ 4B6AAEA3230E3E1D0078E864 /* MassStorageDevice.hpp */, 4B74CF7E2312FA9C00500CE8 /* Formats */, 4B6AAEA5230E40250078E864 /* SCSI */, + 4B74CF83231370BC00500CE8 /* MacintoshVolume.cpp */, + 4B74CF84231370BC00500CE8 /* MacintoshVolume.hpp */, ); path = MassStorage; sourceTree = ""; @@ -4083,6 +4089,7 @@ 4B055ACD1FAE9B030060FFFF /* Keyboard.cpp in Sources */, 4B055AB21FAE860F0060FFFF /* CommodoreTAP.cpp in Sources */, 4B055ADF1FAE9B4C0060FFFF /* IRQDelegatePortHandler.cpp in Sources */, + 4B74CF86231370BC00500CE8 /* MacintoshVolume.cpp in Sources */, 4BD424E02193B5340097291A /* TextureTarget.cpp in Sources */, 4B055AB51FAE860F0060FFFF /* TapePRG.cpp in Sources */, 4B055AE01FAE9B660060FFFF /* CRT.cpp in Sources */, @@ -4197,6 +4204,7 @@ 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */, 4B3BF5B01F146265005B6C36 /* CSW.cpp in Sources */, 4BCE0060227D39AB000CA200 /* Video.cpp in Sources */, + 4B74CF85231370BC00500CE8 /* MacintoshVolume.cpp in Sources */, 4B4518A51F75FD1C00926311 /* SSD.cpp in Sources */, 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */, 4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index a52587e0e..0cd11b1be 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -72,9 +72,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - enableAddressSanitizer = "YES" enableASanStackUseAfterReturn = "YES" - enableUBSanitizer = "YES" disableMainThreadChecker = "YES" launchStyle = "0" useCustomWorkingDirectory = "NO" diff --git a/Storage/MassStorage/Encodings/MacintoshVolume.cpp b/Storage/MassStorage/Encodings/MacintoshVolume.cpp new file mode 100644 index 000000000..fcf6b7be4 --- /dev/null +++ b/Storage/MassStorage/Encodings/MacintoshVolume.cpp @@ -0,0 +1,64 @@ +// +// MacintoshVolume.cpp +// Clock Signal +// +// Created by Thomas Harte on 25/08/2019. +// Copyright © 2019 Thomas Harte. All rights reserved. +// + +#include "MacintoshVolume.hpp" + +using namespace Storage::MassStorage::Encodings::Macintosh; + +void Mapper::set_drive_type(DriveType drive_type, size_t number_of_blocks) { + drive_type_ = drive_type; + number_of_blocks_ = number_of_blocks; +} + +size_t Mapper::get_number_of_blocks() { + return number_of_blocks_ + 5; +} + +ssize_t Mapper::to_source_address(size_t address) { + /* + Reserve one block at the start of the device + for a partition map. + + TODO: and four or so more for a driver? + */ + return ssize_t(address) - 5; +} + +std::vector Mapper::convert_source_block(ssize_t source_address, std::vector source_data) { + switch(source_address) { + case -5: { + uint32_t total_device_blocks = uint32_t(number_of_blocks_ + 5); + + /* The driver descriptor. */ + std::vector driver_description = { + 0x45, 0x52, /* device signature */ + 0x02, 0x00, /* block size, in bytes */ + + uint8_t(total_device_blocks >> 24), + uint8_t(total_device_blocks >> 16), + uint8_t(total_device_blocks >> 8), + uint8_t(total_device_blocks), + /* number of blocks on device */ + + 0x00, 0x00, /* reserved */ + 0x00, 0x00, /* reserved */ + 0x00, 0x00, /* reserved */ + + 0x00, 0x01, /* number of device descriptor entries */ + 0x00, 0x01, /* first device descriptor's starting block */ + 0x00, 0x04, /* size of device driver */ + 0x00, 0x01, /* operating system (MacOS = 1) */ + }; + driver_description.resize(512); + + return driver_description; + } + + default: return source_data; + } +} diff --git a/Storage/MassStorage/Encodings/MacintoshVolume.hpp b/Storage/MassStorage/Encodings/MacintoshVolume.hpp new file mode 100644 index 000000000..185c658c0 --- /dev/null +++ b/Storage/MassStorage/Encodings/MacintoshVolume.hpp @@ -0,0 +1,87 @@ +// +// MacintoshVolume.hpp +// Clock Signal +// +// Created by Thomas Harte on 25/08/2019. +// Copyright © 2019 Thomas Harte. All rights reserved. +// + +#ifndef MacintoshVolume_hpp +#define MacintoshVolume_hpp + +#include +#include + +namespace Storage { +namespace MassStorage { +namespace Encodings { +namespace Macintosh { + +enum class DriveType { + SCSI +}; + +/*! + On the Macintosh life is slightly complicated by Apple's + decision to include device drivers on mass storage drives + themselves — therefore a mass-storage device that is + connected by SCSI will have different preliminary data on it + than the same volume connected by ATA or as an HD20. + + Mass storage devices that respond to @c Volume can be made + to provide the proper whole-volume encoding necessary to + impersonate different types of Macintosh drive. +*/ +class Volume { + public: + virtual void set_drive_type(DriveType type) = 0; +}; + +/*! + A Mapper can used by a mass-storage device that knows the + contents of an HFS or MFS partition to provide the conversion + necessary to a particular type of drive. +*/ +class Mapper { + public: + /*! + Sets the drive type to map to and the number of blocks in the underlying partition. + */ + void set_drive_type(DriveType, size_t number_of_blocks); + + /*! + Maps from a mass-storage device address to an address + in the underlying [H/M]FS partition. + */ + ssize_t to_source_address(size_t address); + + /*! + Converts from a source data block to one properly encoded for the drive type. + + Expected usage: + + const size_t source_address = mapper.to_source_address(unit_address); + if(is_in_range_for_partition(source_address)) { + return mapper.convert_source_block(source_address, get_block_contents(source_address)); + } else { + return mapper.convert_source_block(source_address); + } + */ + std::vector convert_source_block(ssize_t source_address, std::vector source_data = {}); + + /*! + @returns The total number of blocks on the entire volume. + */ + size_t get_number_of_blocks(); + + private: + DriveType drive_type_; + size_t number_of_blocks_; +}; + +} +} +} +} + +#endif /* MacintoshVolume_hpp */ diff --git a/Storage/MassStorage/Formats/HFV.cpp b/Storage/MassStorage/Formats/HFV.cpp index 5b617ad1e..774195e1b 100644 --- a/Storage/MassStorage/Formats/HFV.cpp +++ b/Storage/MassStorage/Formats/HFV.cpp @@ -23,17 +23,29 @@ size_t HFV::get_block_size() { } size_t HFV::get_number_of_blocks() { - return size_t(file_.stats().st_size) / get_block_size(); + return mapper_.get_number_of_blocks(); } std::vector HFV::get_block(size_t address) { - const long file_offset = long(get_block_size() * address); - file_.seek(file_offset, SEEK_SET); - return file_.read(get_block_size()); + const auto source_address = mapper_.to_source_address(address); + if(source_address >= 0 && size_t(source_address)*get_block_size() < size_t(file_.stats().st_size)) { + const long file_offset = long(get_block_size()) * long(source_address); + file_.seek(file_offset, SEEK_SET); + return mapper_.convert_source_block(source_address, file_.read(get_block_size())); + } else { + return mapper_.convert_source_block(source_address); + } } void HFV::set_block(size_t address, const std::vector &contents) { - const long file_offset = long(get_block_size() * address); - file_.seek(file_offset, SEEK_SET); - file_.write(contents); + const auto source_address = mapper_.to_source_address(address); + if(source_address >= 0 && size_t(source_address)*get_block_size() < size_t(file_.stats().st_size)) { + const long file_offset = long(get_block_size()) * long(source_address); + file_.seek(file_offset, SEEK_SET); + file_.write(contents); + } +} + +void HFV::set_drive_type(Encodings::Macintosh::DriveType drive_type) { + mapper_.set_drive_type(drive_type, size_t(file_.stats().st_size) / get_block_size()); } diff --git a/Storage/MassStorage/Formats/HFV.hpp b/Storage/MassStorage/Formats/HFV.hpp index a44a29bdc..1d8c85b2a 100644 --- a/Storage/MassStorage/Formats/HFV.hpp +++ b/Storage/MassStorage/Formats/HFV.hpp @@ -11,26 +11,36 @@ #include "../MassStorageDevice.hpp" #include "../../FileHolder.hpp" +#include "../Encodings/MacintoshVolume.hpp" namespace Storage { namespace MassStorage { /*! Provides a @c MassStorageDevice containing an HFV image, which is a sector dump of - a Macintosh drive that is not the correct size to be a floppy disk. + the HFS volume of a Macintosh drive that is not the correct size to be a floppy disk. */ -class HFV: public MassStorageDevice { +class HFV: public MassStorageDevice, public Encodings::Macintosh::Volume { public: + /*! + Constructs an HFV with the contents of the file named @c file_name. + Raises an exception if the file name doesn't appear to identify a valid + Macintosh mass storage image. + */ HFV(const std::string &file_name); private: FileHolder file_; + Encodings::Macintosh::Mapper mapper_; + /* 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; + /* Encodings::Macintosh::Volume overrides. */ + void set_drive_type(Encodings::Macintosh::DriveType) final; }; }