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/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.cpp b/Storage/Disk/DiskImage/Formats/HFE.cpp index e6ad0f07f..529a46c81 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.cpp +++ b/Storage/Disk/DiskImage/Formats/HFE.cpp @@ -9,6 +9,8 @@ #include "HFE.hpp" #include "../../Track/PCMTrack.hpp" +#include "../../Track/TrackSerialiser.hpp" +#include "../../../Data/BitReverse.hpp" using namespace Storage::Disk; @@ -35,7 +37,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); @@ -46,38 +55,53 @@ 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); - PCMSegment segment; - uint16_t side_length = track_length / 2; - segment.data.resize(side_length); - segment.number_of_bits = side_length * 8; + return track_length / 2; +} - 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); +std::shared_ptr HFE::get_track_at_position(unsigned int head, unsigned int position) { + PCMSegment segment; + { + std::lock_guard lock_guard(file_access_mutex_); + uint16_t track_length = seek_track(head, position); + + 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 // 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; } +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(); +} diff --git a/Storage/Disk/DiskImage/Formats/HFE.hpp b/Storage/Disk/DiskImage/Formats/HFE.hpp index 24676daf7..5ca1446f4 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.hpp +++ b/Storage/Disk/DiskImage/Formats/HFE.hpp @@ -36,9 +36,12 @@ 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; + 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_; 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_; }; }