From 35705c53458f83995e8810ea1a0e5a1b45ea6099 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 Oct 2017 19:12:45 -0400 Subject: [PATCH 1/4] Factors out bit reversing from the HFE class. --- .../Clock Signal.xcodeproj/project.pbxproj | 6 ++++ Storage/Data/BitReverse.cpp | 25 ++++++++++++++++ Storage/Data/BitReverse.hpp | 30 +++++++++++++++++++ Storage/Disk/DiskImage/Formats/HFE.cpp | 17 ++--------- 4 files changed, 63 insertions(+), 15 deletions(-) create mode 100644 Storage/Data/BitReverse.cpp create mode 100644 Storage/Data/BitReverse.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 3e198b866..f15c35951 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 4B14978F1EE4B4D200CE2596 /* CSZX8081.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B14978E1EE4B4D200CE2596 /* CSZX8081.mm */; }; 4B1497921EE4B5A800CE2596 /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1497901EE4B5A800CE2596 /* ZX8081.cpp */; }; 4B1497981EE4B97F00CE2596 /* ZX8081Options.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B1497961EE4B97F00CE2596 /* ZX8081Options.xib */; }; + 4B1558C01F844ECD006E9A97 /* BitReverse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1558BE1F844ECD006E9A97 /* BitReverse.cpp */; }; 4B1D08061E0F7A1100763741 /* TimeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D08051E0F7A1100763741 /* TimeTests.mm */; }; 4B1E85751D170228001EF87D /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E85731D170228001EF87D /* Typer.cpp */; }; 4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E85801D176468001EF87D /* 6532Tests.swift */; }; @@ -504,6 +505,8 @@ 4B1497901EE4B5A800CE2596 /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZX8081.cpp; path = ZX8081/ZX8081.cpp; sourceTree = ""; }; 4B1497911EE4B5A800CE2596 /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ZX8081.hpp; path = ZX8081/ZX8081.hpp; sourceTree = ""; }; 4B1497971EE4B97F00CE2596 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/ZX8081Options.xib"; sourceTree = SOURCE_ROOT; }; + 4B1558BE1F844ECD006E9A97 /* BitReverse.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = BitReverse.cpp; path = Data/BitReverse.cpp; sourceTree = ""; }; + 4B1558BF1F844ECD006E9A97 /* BitReverse.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = BitReverse.hpp; path = Data/BitReverse.hpp; sourceTree = ""; }; 4B1D08051E0F7A1100763741 /* TimeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TimeTests.mm; sourceTree = ""; }; 4B1E85731D170228001EF87D /* Typer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Typer.cpp; sourceTree = ""; }; 4B1E85741D170228001EF87D /* Typer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Typer.hpp; sourceTree = ""; }; @@ -1741,8 +1744,10 @@ 4B8805F81DCFF6CD003085B1 /* Data */ = { isa = PBXGroup; children = ( + 4B1558BE1F844ECD006E9A97 /* BitReverse.cpp */, 4B8805F51DCFF6C9003085B1 /* Commodore.cpp */, 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */, + 4B1558BF1F844ECD006E9A97 /* BitReverse.hpp */, 4B8805F61DCFF6C9003085B1 /* Commodore.hpp */, 4BA0F68D1EEA0E8400E9489E /* ZX8081.hpp */, ); @@ -2917,6 +2922,7 @@ 4B4518A31F75FD1C00926311 /* HFE.cpp in Sources */, 4B8378E21F336920005CA9E4 /* CharacterMapper.cpp in Sources */, 4B4518A11F75FD1C00926311 /* D64.cpp in Sources */, + 4B1558C01F844ECD006E9A97 /* BitReverse.cpp in Sources */, 4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */, 4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */, 4B8334951F5E25B60097E338 /* C1540.cpp in Sources */, diff --git a/Storage/Data/BitReverse.cpp b/Storage/Data/BitReverse.cpp new file mode 100644 index 000000000..2f14ffdc5 --- /dev/null +++ b/Storage/Data/BitReverse.cpp @@ -0,0 +1,25 @@ +// +// BitReverse.cpp +// Clock Signal +// +// Created by Thomas Harte on 03/10/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#include "BitReverse.hpp" + +void Storage::Data::BitReverse::reverse(std::vector &vector) { + for(auto &byte : vector) { + byte = + static_cast( + ((byte & 0x01) << 7) | + ((byte & 0x02) << 5) | + ((byte & 0x04) << 3) | + ((byte & 0x08) << 1) | + ((byte & 0x10) >> 1) | + ((byte & 0x20) >> 3) | + ((byte & 0x40) >> 5) | + ((byte & 0x80) >> 7) + ); + } +} diff --git a/Storage/Data/BitReverse.hpp b/Storage/Data/BitReverse.hpp new file mode 100644 index 000000000..6f54a35ab --- /dev/null +++ b/Storage/Data/BitReverse.hpp @@ -0,0 +1,30 @@ +// +// BitReverse.hpp +// Clock Signal +// +// Created by Thomas Harte on 03/10/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#ifndef BitReverse_hpp +#define BitReverse_hpp + +#include +#include + +namespace Storage { +namespace Data { +namespace BitReverse { + +/*! + Reverses the order of the bits in every byte of the vector — + bit 7 exchanges with bit 0, bit 6 exchanges with bit 1, 5 with 2, + and 4 with 3. +*/ +void reverse(std::vector &vector); + +} +} +} + +#endif /* BitReverse_hpp */ diff --git a/Storage/Disk/DiskImage/Formats/HFE.cpp b/Storage/Disk/DiskImage/Formats/HFE.cpp index e6ad0f07f..535ac0741 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.cpp +++ b/Storage/Disk/DiskImage/Formats/HFE.cpp @@ -9,6 +9,7 @@ #include "HFE.hpp" #include "../../Track/PCMTrack.hpp" +#include "../../../Data/BitReverse.hpp" using namespace Storage::Disk; @@ -61,21 +62,7 @@ std::shared_ptr HFE::get_track_at_position(unsigned int head, unsigned in // Flip bytes; HFE's preference is that the least-significant bit // is serialised first, but PCMTrack posts the most-significant first. - for(size_t i = 0; i < segment.data.size(); i++) { - uint8_t original = segment.data[i]; - uint8_t flipped_byte = - (uint8_t)( - ((original & 0x01) << 7) | - ((original & 0x02) << 5) | - ((original & 0x04) << 3) | - ((original & 0x08) << 1) | - ((original & 0x10) >> 1) | - ((original & 0x20) >> 3) | - ((original & 0x40) >> 5) | - ((original & 0x80) >> 7) - ); - segment.data[i] = flipped_byte; - } + Storage::Data::BitReverse::reverse(segment.data); std::shared_ptr track(new PCMTrack(segment)); return track; From 7b01c1bee6605054372df09bb63a5700e622493a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 Oct 2017 19:36:06 -0400 Subject: [PATCH 2/4] Revokes direct visibility of is_read_only_ to subclasses of FileHolder. --- Storage/Disk/DiskImage/Formats/CPCDSK.cpp | 5 ----- Storage/Disk/DiskImage/Formats/CPCDSK.hpp | 2 +- Storage/Disk/DiskImage/Formats/D64.hpp | 1 + Storage/Disk/DiskImage/Formats/G64.hpp | 1 + Storage/Disk/DiskImage/Formats/HFE.hpp | 1 + Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp | 4 ---- Storage/Disk/DiskImage/Formats/MFMSectorDump.hpp | 2 +- Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp | 4 ---- Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp | 2 +- Storage/FileHolder.cpp | 4 ++++ Storage/FileHolder.hpp | 8 +++++++- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/CPCDSK.cpp b/Storage/Disk/DiskImage/Formats/CPCDSK.cpp index b9a6c0cf8..81b57cd01 100644 --- a/Storage/Disk/DiskImage/Formats/CPCDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/CPCDSK.cpp @@ -45,11 +45,6 @@ unsigned int CPCDSK::get_head_count() { return head_count_; } -bool CPCDSK::get_is_read_only() { - // TODO: allow writing. - return false; -} - std::shared_ptr 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; diff --git a/Storage/Disk/DiskImage/Formats/CPCDSK.hpp b/Storage/Disk/DiskImage/Formats/CPCDSK.hpp index af8725259..c095f7c49 100644 --- a/Storage/Disk/DiskImage/Formats/CPCDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/CPCDSK.hpp @@ -37,7 +37,7 @@ class CPCDSK: public DiskImage, public Storage::FileHolder { // implemented to satisfy @c Disk unsigned int get_head_position_count(); unsigned int get_head_count(); - bool get_is_read_only(); + using DiskImage::get_is_read_only; std::shared_ptr get_track_at_position(unsigned int head, unsigned int position); private: diff --git a/Storage/Disk/DiskImage/Formats/D64.hpp b/Storage/Disk/DiskImage/Formats/D64.hpp index ea8236fc0..5854b0394 100644 --- a/Storage/Disk/DiskImage/Formats/D64.hpp +++ b/Storage/Disk/DiskImage/Formats/D64.hpp @@ -34,6 +34,7 @@ class D64: public DiskImage, public Storage::FileHolder { // implemented to satisfy @c Disk unsigned int get_head_position_count(); + using DiskImage::get_is_read_only; std::shared_ptr get_track_at_position(unsigned int head, unsigned int position); private: diff --git a/Storage/Disk/DiskImage/Formats/G64.hpp b/Storage/Disk/DiskImage/Formats/G64.hpp index ca8273106..5e56a9f81 100644 --- a/Storage/Disk/DiskImage/Formats/G64.hpp +++ b/Storage/Disk/DiskImage/Formats/G64.hpp @@ -38,6 +38,7 @@ class G64: public DiskImage, public Storage::FileHolder { // implemented to satisfy @c Disk unsigned int get_head_position_count(); std::shared_ptr get_track_at_position(unsigned int head, unsigned int position); + using DiskImage::get_is_read_only; private: uint8_t number_of_tracks_; diff --git a/Storage/Disk/DiskImage/Formats/HFE.hpp b/Storage/Disk/DiskImage/Formats/HFE.hpp index 24676daf7..6371bfe97 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.hpp +++ b/Storage/Disk/DiskImage/Formats/HFE.hpp @@ -36,6 +36,7 @@ class HFE: public DiskImage, public Storage::FileHolder { // implemented to satisfy @c Disk unsigned int get_head_position_count(); unsigned int get_head_count(); + using Storage::FileHolder::get_is_read_only; std::shared_ptr get_track_at_position(unsigned int head, unsigned int position); private: diff --git a/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp b/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp index 33f93fa5e..db49dd36b 100644 --- a/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp +++ b/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp @@ -20,10 +20,6 @@ void MFMSectorDump::set_geometry(int sectors_per_track, uint8_t sector_size, boo 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_]; diff --git a/Storage/Disk/DiskImage/Formats/MFMSectorDump.hpp b/Storage/Disk/DiskImage/Formats/MFMSectorDump.hpp index aee10a307..9ee2c46cf 100644 --- a/Storage/Disk/DiskImage/Formats/MFMSectorDump.hpp +++ b/Storage/Disk/DiskImage/Formats/MFMSectorDump.hpp @@ -23,7 +23,7 @@ class MFMSectorDump: public DiskImage, public Storage::FileHolder { 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(); + using Storage::FileHolder::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); diff --git a/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp b/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp index db7264138..a8ad8feb2 100644 --- a/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp @@ -37,10 +37,6 @@ unsigned int OricMFMDSK::get_head_count() { return head_count_; } -bool OricMFMDSK::get_is_read_only() { - return is_read_only_; -} - long OricMFMDSK::get_file_offset_for_position(unsigned int head, unsigned int position) { long seek_offset = 0; switch(geometry_type_) { diff --git a/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp b/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp index c98ad3490..757baba21 100644 --- a/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp @@ -34,7 +34,7 @@ class OricMFMDSK: public DiskImage, public Storage::FileHolder { // implemented to satisfy @c Disk unsigned int get_head_position_count(); unsigned int get_head_count(); - bool get_is_read_only(); + using Storage::FileHolder::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); diff --git a/Storage/FileHolder.cpp b/Storage/FileHolder.cpp index 6aa66094d..c369dc1fd 100644 --- a/Storage/FileHolder.cpp +++ b/Storage/FileHolder.cpp @@ -77,6 +77,10 @@ uint16_t FileHolder::fgetc16be() { return result; } +bool FileHolder::get_is_read_only() { + return is_read_only_; +} + void FileHolder::ensure_file_is_at_least_length(long length) { fseek(file_, 0, SEEK_END); long bytes_to_write = length - ftell(file_); diff --git a/Storage/FileHolder.hpp b/Storage/FileHolder.hpp index d68ca2544..47544f6bc 100644 --- a/Storage/FileHolder.hpp +++ b/Storage/FileHolder.hpp @@ -79,6 +79,11 @@ class FileHolder { */ void ensure_file_is_at_least_length(long length); + /*! + @returns @c true if this file is read-only; @c false otherwise. + */ + bool get_is_read_only(); + class BitStream { public: BitStream(FILE *f, bool lsb_first) : @@ -124,10 +129,11 @@ class FileHolder { FILE *file_; struct stat file_stats_; - bool is_read_only_; std::mutex file_access_mutex_; const std::string name_; + private: + bool is_read_only_; }; } From 1cc85615d50e1a8375451507eeb94a7003d77499 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 Oct 2017 20:33:55 -0400 Subject: [PATCH 3/4] Factors HFE track seeking out from the track fetching method. --- Storage/Disk/DiskImage/Formats/HFE.cpp | 19 +++++++++++++++++-- Storage/Disk/DiskImage/Formats/HFE.hpp | 2 ++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/HFE.cpp b/Storage/Disk/DiskImage/Formats/HFE.cpp index 535ac0741..ea33940d5 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.cpp +++ b/Storage/Disk/DiskImage/Formats/HFE.cpp @@ -36,7 +36,14 @@ unsigned int HFE::get_head_count() { return head_count_; } -std::shared_ptr HFE::get_track_at_position(unsigned int head, unsigned int position) { +/*! + Seeks to the beginning of the track at @c position underneath @c head, + returning its length in bytes. + + To read the track, start from the current file position, read 256 bytes, + skip 256 bytes, read 256 bytes, skip 256 bytes, etc. +*/ +uint16_t HFE::seek_track(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); @@ -47,8 +54,14 @@ std::shared_ptr HFE::get_track_at_position(unsigned int head, unsigned in fseek(file_, track_offset, SEEK_SET); if(head) fseek(file_, 256, SEEK_CUR); + return track_length / 2; +} + +std::shared_ptr HFE::get_track_at_position(unsigned int head, unsigned int position) { + uint16_t track_length = seek_track(head, position); + PCMSegment segment; - uint16_t side_length = track_length / 2; + uint16_t side_length = track_length; segment.data.resize(side_length); segment.number_of_bits = side_length * 8; @@ -68,3 +81,5 @@ std::shared_ptr HFE::get_track_at_position(unsigned int head, unsigned in return track; } +void HFE::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr &track) { +} diff --git a/Storage/Disk/DiskImage/Formats/HFE.hpp b/Storage/Disk/DiskImage/Formats/HFE.hpp index 6371bfe97..5ca1446f4 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.hpp +++ b/Storage/Disk/DiskImage/Formats/HFE.hpp @@ -37,9 +37,11 @@ class HFE: public DiskImage, public Storage::FileHolder { unsigned int get_head_position_count(); unsigned int get_head_count(); using Storage::FileHolder::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: + uint16_t seek_track(unsigned int head, unsigned int position); unsigned int head_count_; unsigned int track_count_; From 0fb363ea0e5c597470113dc67f4ce096eebf21c0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 Oct 2017 21:24:20 -0400 Subject: [PATCH 4/4] Adds writing support for HFEs. --- Storage/Disk/DiskImage/Formats/HFE.cpp | 44 +++++++++++++++++++------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/HFE.cpp b/Storage/Disk/DiskImage/Formats/HFE.cpp index ea33940d5..529a46c81 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.cpp +++ b/Storage/Disk/DiskImage/Formats/HFE.cpp @@ -9,6 +9,7 @@ #include "HFE.hpp" #include "../../Track/PCMTrack.hpp" +#include "../../Track/TrackSerialiser.hpp" #include "../../../Data/BitReverse.hpp" using namespace Storage::Disk; @@ -58,19 +59,21 @@ uint16_t HFE::seek_track(unsigned int head, unsigned int position) { } std::shared_ptr HFE::get_track_at_position(unsigned int head, unsigned int position) { - uint16_t track_length = seek_track(head, position); - PCMSegment segment; - uint16_t side_length = track_length; - segment.data.resize(side_length); - segment.number_of_bits = side_length * 8; + { + std::lock_guard lock_guard(file_access_mutex_); + uint16_t track_length = seek_track(head, position); - uint16_t c = 0; - while(c < side_length) { - uint16_t length = (uint16_t)std::min(256, side_length - c); - fread(&segment.data[c], 1, length, file_); - c += length; - fseek(file_, 256, SEEK_CUR); + segment.data.resize(track_length); + segment.number_of_bits = track_length * 8; + + uint16_t c = 0; + while(c < track_length) { + uint16_t length = (uint16_t)std::min(256, track_length - c); + fread(&segment.data[c], 1, length, file_); + c += length; + fseek(file_, 256, SEEK_CUR); + } } // Flip bytes; HFE's preference is that the least-significant bit @@ -82,4 +85,23 @@ std::shared_ptr HFE::get_track_at_position(unsigned int head, unsigned in } void HFE::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr &track) { + std::unique_lock lock_guard(file_access_mutex_); + uint16_t track_length = seek_track(head, position); + lock_guard.unlock(); + + PCMSegment segment = Storage::Disk::track_serialisation(*track, Storage::Time(1, track_length * 8)); + Storage::Data::BitReverse::reverse(segment.data); + uint16_t data_length = std::min(static_cast(segment.data.size()), track_length); + + lock_guard.lock(); + seek_track(head, position); + + uint16_t c = 0; + while(c < data_length) { + uint16_t length = (uint16_t)std::min(256, data_length - c); + fwrite(&segment.data[c], 1, length, file_); + c += length; + fseek(file_, 256, SEEK_CUR); + } + lock_guard.unlock(); }