2017-08-18 01:48:48 +00:00
|
|
|
//
|
|
|
|
// HFE.cpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 17/08/2017.
|
2018-05-13 19:19:52 +00:00
|
|
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
2017-08-18 01:48:48 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
#include "HFE.hpp"
|
|
|
|
|
2017-09-23 02:39:23 +00:00
|
|
|
#include "../../Track/PCMTrack.hpp"
|
2017-10-04 01:24:20 +00:00
|
|
|
#include "../../Track/TrackSerialiser.hpp"
|
2017-10-03 23:12:45 +00:00
|
|
|
#include "../../../Data/BitReverse.hpp"
|
2017-08-18 02:20:02 +00:00
|
|
|
|
2017-08-18 01:48:48 +00:00
|
|
|
using namespace Storage::Disk;
|
|
|
|
|
2018-04-06 21:42:24 +00:00
|
|
|
HFE::HFE(const std::string &file_name) :
|
2017-11-03 02:32:00 +00:00
|
|
|
file_(file_name) {
|
2018-04-28 03:18:45 +00:00
|
|
|
if(!file_.check_signature("HXCPICFE")) throw Error::InvalidFormat;
|
2017-08-18 02:20:02 +00:00
|
|
|
|
2018-04-28 03:18:45 +00:00
|
|
|
if(file_.get8()) throw Error::UnknownVersion;
|
2017-11-03 02:32:00 +00:00
|
|
|
track_count_ = file_.get8();
|
|
|
|
head_count_ = file_.get8();
|
2017-08-18 02:20:02 +00:00
|
|
|
|
2017-11-03 02:32:00 +00:00
|
|
|
file_.seek(7, SEEK_CUR);
|
|
|
|
track_list_offset_ = static_cast<long>(file_.get16le()) << 9;
|
2017-08-18 01:48:48 +00:00
|
|
|
}
|
|
|
|
|
2018-05-07 03:17:36 +00:00
|
|
|
HeadPosition HFE::get_maximum_head_position() {
|
|
|
|
return HeadPosition(track_count_);
|
2017-08-18 01:48:48 +00:00
|
|
|
}
|
|
|
|
|
2017-10-07 01:45:12 +00:00
|
|
|
int HFE::get_head_count() {
|
2017-08-18 01:48:48 +00:00
|
|
|
return head_count_;
|
|
|
|
}
|
|
|
|
|
2017-10-04 00:33:55 +00:00
|
|
|
/*!
|
|
|
|
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.
|
|
|
|
*/
|
2017-10-07 01:45:12 +00:00
|
|
|
uint16_t HFE::seek_track(Track::Address address) {
|
2017-08-18 02:20:02 +00:00
|
|
|
// Get track position and length from the lookup table; data is then always interleaved
|
|
|
|
// based on an assumption of two heads.
|
2018-05-07 03:17:36 +00:00
|
|
|
file_.seek(track_list_offset_ + address.position.as_int() * 4, SEEK_SET);
|
2017-08-18 02:20:02 +00:00
|
|
|
|
2018-07-01 16:05:41 +00:00
|
|
|
long track_offset = static_cast<long>(file_.get16le()) << 9; // Track offset, in units of 512 bytes.
|
|
|
|
uint16_t track_length = file_.get16le(); // Track length, in bytes, containing both the front and back track.
|
2017-08-18 02:20:02 +00:00
|
|
|
|
2017-11-03 02:32:00 +00:00
|
|
|
file_.seek(track_offset, SEEK_SET);
|
|
|
|
if(address.head) file_.seek(256, SEEK_CUR);
|
2017-08-18 02:20:02 +00:00
|
|
|
|
2018-07-01 16:05:41 +00:00
|
|
|
return track_length / 2; // Divide by two to give the track length for a single side.
|
2017-10-04 00:33:55 +00:00
|
|
|
}
|
|
|
|
|
2017-10-07 01:45:12 +00:00
|
|
|
std::shared_ptr<Track> HFE::get_track_at_position(Track::Address address) {
|
2017-08-18 02:20:02 +00:00
|
|
|
PCMSegment segment;
|
2017-10-04 01:24:20 +00:00
|
|
|
{
|
2017-11-03 02:32:00 +00:00
|
|
|
std::lock_guard<std::mutex> lock_guard(file_.get_file_access_mutex());
|
2017-10-07 01:45:12 +00:00
|
|
|
uint16_t track_length = seek_track(address);
|
2017-10-04 01:24:20 +00:00
|
|
|
|
2018-07-01 16:05:41 +00:00
|
|
|
segment.data.resize(track_length * 8);
|
2017-10-04 01:24:20 +00:00
|
|
|
|
2018-07-01 16:05:41 +00:00
|
|
|
// HFE tracks are stored as 256 bytes for side 1, then 256 bytes for side 2,
|
|
|
|
// then 256 bytes for side 1, then 256 bytes for side 2, etc, until the final
|
|
|
|
// 512-byte segment which will contain less than the full 256 bytes.
|
|
|
|
//
|
|
|
|
// seek_track will have advanced an extra initial 256 bytes if the address
|
|
|
|
// refers to side 2, so the loop below can act ass though it were definitely
|
|
|
|
// dealing with side 1.
|
2017-10-04 01:24:20 +00:00
|
|
|
uint16_t c = 0;
|
|
|
|
while(c < track_length) {
|
2018-07-01 16:05:41 +00:00
|
|
|
// Decide how many bytes of at most 256 to read, and read them.
|
2017-10-22 01:50:53 +00:00
|
|
|
uint16_t length = static_cast<uint16_t>(std::min(256, track_length - c));
|
2018-07-01 16:05:41 +00:00
|
|
|
std::vector<uint8_t> section = file_.read(length);
|
|
|
|
|
|
|
|
// Push those into the PCMSegment. In HFE the least-significant bit is
|
2018-07-01 19:58:56 +00:00
|
|
|
// serialised first. TODO: move this logic to PCMSegment.
|
2018-07-01 16:05:41 +00:00
|
|
|
for(uint16_t byte = 0; byte < length; ++byte) {
|
2018-07-01 19:58:56 +00:00
|
|
|
const size_t base = static_cast<size_t>(c + byte) << 3;
|
2018-07-01 16:05:41 +00:00
|
|
|
segment.data[base + 0] = !!(section[byte] & 0x01);
|
|
|
|
segment.data[base + 1] = !!(section[byte] & 0x02);
|
|
|
|
segment.data[base + 2] = !!(section[byte] & 0x04);
|
|
|
|
segment.data[base + 3] = !!(section[byte] & 0x08);
|
|
|
|
segment.data[base + 4] = !!(section[byte] & 0x10);
|
|
|
|
segment.data[base + 5] = !!(section[byte] & 0x20);
|
|
|
|
segment.data[base + 6] = !!(section[byte] & 0x40);
|
|
|
|
segment.data[base + 7] = !!(section[byte] & 0x80);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Advance the target pointer, and skip the next 256 bytes of the file
|
|
|
|
// (which will be for the other side of the disk).
|
2017-10-04 01:24:20 +00:00
|
|
|
c += length;
|
2017-11-03 02:32:00 +00:00
|
|
|
file_.seek(256, SEEK_CUR);
|
2017-10-04 01:24:20 +00:00
|
|
|
}
|
2017-08-18 02:20:02 +00:00
|
|
|
}
|
|
|
|
|
2018-05-01 02:08:51 +00:00
|
|
|
return std::make_shared<PCMTrack>(segment);
|
2017-08-18 01:48:48 +00:00
|
|
|
}
|
2017-09-23 00:28:11 +00:00
|
|
|
|
2017-10-07 23:37:36 +00:00
|
|
|
void HFE::set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tracks) {
|
|
|
|
for(auto &track : tracks) {
|
2017-11-03 02:32:00 +00:00
|
|
|
std::unique_lock<std::mutex> lock_guard(file_.get_file_access_mutex());
|
2017-10-07 23:37:36 +00:00
|
|
|
uint16_t track_length = seek_track(track.first);
|
|
|
|
lock_guard.unlock();
|
2017-10-04 01:24:20 +00:00
|
|
|
|
2018-07-01 16:05:41 +00:00
|
|
|
const PCMSegment segment = Storage::Disk::track_serialisation(*track.second, Storage::Time(1, track_length * 8));
|
|
|
|
|
|
|
|
// Convert the segment into a byte encoding, LSB first.
|
|
|
|
std::vector<uint8_t> byte_segment = segment.byte_data(false);
|
|
|
|
uint16_t data_length = std::min(static_cast<uint16_t>(byte_segment.size()), track_length);
|
2017-10-04 01:24:20 +00:00
|
|
|
|
2017-10-07 23:37:36 +00:00
|
|
|
lock_guard.lock();
|
|
|
|
seek_track(track.first);
|
2017-10-04 01:24:20 +00:00
|
|
|
|
2017-10-07 23:37:36 +00:00
|
|
|
uint16_t c = 0;
|
|
|
|
while(c < data_length) {
|
2017-10-22 01:50:53 +00:00
|
|
|
uint16_t length = static_cast<uint16_t>(std::min(256, data_length - c));
|
2018-07-01 16:05:41 +00:00
|
|
|
file_.write(&byte_segment[c], length);
|
2017-10-07 23:37:36 +00:00
|
|
|
c += length;
|
2017-11-03 02:32:00 +00:00
|
|
|
file_.seek(256, SEEK_CUR);
|
2017-10-07 23:37:36 +00:00
|
|
|
}
|
|
|
|
lock_guard.unlock();
|
2017-10-04 01:24:20 +00:00
|
|
|
}
|
2017-10-04 00:33:55 +00:00
|
|
|
}
|
2017-11-03 02:32:00 +00:00
|
|
|
|
|
|
|
bool HFE::get_is_read_only() {
|
|
|
|
return file_.get_is_known_read_only();
|
|
|
|
}
|