mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-23 03:32:32 +00:00
Merge pull request #251 from TomHarte/HFEWriteable
Makes HFE files writeable
This commit is contained in:
commit
ea5023ac26
@ -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 = "<group>"; };
|
||||
4B1497911EE4B5A800CE2596 /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ZX8081.hpp; path = ZX8081/ZX8081.hpp; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
4B1558BF1F844ECD006E9A97 /* BitReverse.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = BitReverse.hpp; path = Data/BitReverse.hpp; sourceTree = "<group>"; };
|
||||
4B1D08051E0F7A1100763741 /* TimeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TimeTests.mm; sourceTree = "<group>"; };
|
||||
4B1E85731D170228001EF87D /* Typer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Typer.cpp; sourceTree = "<group>"; };
|
||||
4B1E85741D170228001EF87D /* Typer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Typer.hpp; sourceTree = "<group>"; };
|
||||
@ -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 */,
|
||||
|
25
Storage/Data/BitReverse.cpp
Normal file
25
Storage/Data/BitReverse.cpp
Normal file
@ -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<uint8_t> &vector) {
|
||||
for(auto &byte : vector) {
|
||||
byte =
|
||||
static_cast<uint8_t>(
|
||||
((byte & 0x01) << 7) |
|
||||
((byte & 0x02) << 5) |
|
||||
((byte & 0x04) << 3) |
|
||||
((byte & 0x08) << 1) |
|
||||
((byte & 0x10) >> 1) |
|
||||
((byte & 0x20) >> 3) |
|
||||
((byte & 0x40) >> 5) |
|
||||
((byte & 0x80) >> 7)
|
||||
);
|
||||
}
|
||||
}
|
30
Storage/Data/BitReverse.hpp
Normal file
30
Storage/Data/BitReverse.hpp
Normal file
@ -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 <cstdint>
|
||||
#include <vector>
|
||||
|
||||
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<uint8_t> &vector);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* BitReverse_hpp */
|
@ -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<Track> 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;
|
||||
|
@ -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<Track> get_track_at_position(unsigned int head, unsigned int position);
|
||||
|
||||
private:
|
||||
|
@ -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<Track> get_track_at_position(unsigned int head, unsigned int position);
|
||||
|
||||
private:
|
||||
|
@ -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<Track> get_track_at_position(unsigned int head, unsigned int position);
|
||||
using DiskImage::get_is_read_only;
|
||||
|
||||
private:
|
||||
uint8_t number_of_tracks_;
|
||||
|
@ -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<Track> 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<Track> 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<Track> HFE::get_track_at_position(unsigned int head, unsigned int position) {
|
||||
PCMSegment segment;
|
||||
{
|
||||
std::lock_guard<std::mutex> 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> track(new PCMTrack(segment));
|
||||
return track;
|
||||
}
|
||||
|
||||
void HFE::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) {
|
||||
std::unique_lock<std::mutex> 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<uint16_t>(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();
|
||||
}
|
||||
|
@ -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> &track);
|
||||
std::shared_ptr<Track> 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_;
|
||||
|
@ -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<Track> MFMSectorDump::get_track_at_position(unsigned int head, unsigned int position) {
|
||||
uint8_t sectors[(128 << sector_size_)*sectors_per_track_];
|
||||
|
||||
|
@ -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> &track);
|
||||
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
|
||||
|
||||
|
@ -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_) {
|
||||
|
@ -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> &track);
|
||||
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
|
||||
|
||||
|
@ -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_);
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user