mirror of
				https://github.com/TomHarte/CLK.git
				synced 2025-10-31 20:16:07 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			132 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			132 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //
 | |
| //  HFE.cpp
 | |
| //  Clock Signal
 | |
| //
 | |
| //  Created by Thomas Harte on 17/08/2017.
 | |
| //  Copyright 2017 Thomas Harte. All rights reserved.
 | |
| //
 | |
| 
 | |
| #include "HFE.hpp"
 | |
| 
 | |
| #include "../../Track/PCMTrack.hpp"
 | |
| #include "../../Track/TrackSerialiser.hpp"
 | |
| #include "../../../Data/BitReverse.hpp"
 | |
| 
 | |
| using namespace Storage::Disk;
 | |
| 
 | |
| HFE::HFE(const std::string &file_name) :
 | |
| 		file_(file_name) {
 | |
| 	if(!file_.check_signature("HXCPICFE")) throw Error::InvalidFormat;
 | |
| 
 | |
| 	if(file_.get8()) throw Error::UnknownVersion;
 | |
| 	track_count_ = file_.get8();
 | |
| 	head_count_ = file_.get8();
 | |
| 
 | |
| 	file_.seek(7, SEEK_CUR);
 | |
| 	track_list_offset_ = long(file_.get16le()) << 9;
 | |
| }
 | |
| 
 | |
| HeadPosition HFE::get_maximum_head_position() {
 | |
| 	return HeadPosition(track_count_);
 | |
| }
 | |
| 
 | |
| int HFE::get_head_count() {
 | |
| 	return head_count_;
 | |
| }
 | |
| 
 | |
| /*!
 | |
| 	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(Track::Address address) {
 | |
| 	// Get track position and length from the lookup table; data is then always interleaved
 | |
| 	// based on an assumption of two heads.
 | |
| 	file_.seek(track_list_offset_ + address.position.as_int() * 4, SEEK_SET);
 | |
| 
 | |
| 	long track_offset = 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.
 | |
| 
 | |
| 	file_.seek(track_offset, SEEK_SET);
 | |
| 	if(address.head) file_.seek(256, SEEK_CUR);
 | |
| 
 | |
| 	return track_length / 2;	// Divide by two to give the track length for a single side.
 | |
| }
 | |
| 
 | |
| std::shared_ptr<Track> HFE::get_track_at_position(Track::Address address) {
 | |
| 	PCMSegment segment;
 | |
| 	{
 | |
| 		std::lock_guard lock_guard(file_.get_file_access_mutex());
 | |
| 		uint16_t track_length = seek_track(address);
 | |
| 
 | |
| 		segment.data.resize(track_length * 8);
 | |
| 
 | |
| 		// 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.
 | |
| 		uint16_t c = 0;
 | |
| 		while(c < track_length) {
 | |
| 			// Decide how many bytes of at most 256 to read, and read them.
 | |
| 			uint16_t length = uint16_t(std::min(256, track_length - c));
 | |
| 			std::vector<uint8_t> section = file_.read(length);
 | |
| 
 | |
| 			// Push those into the PCMSegment. In HFE the least-significant bit is
 | |
| 			// serialised first. TODO: move this logic to PCMSegment.
 | |
| 			for(uint16_t byte = 0; byte < length; ++byte) {
 | |
| 				const size_t base = size_t(c + byte) << 3;
 | |
| 				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).
 | |
| 			c += length;
 | |
| 			file_.seek(256, SEEK_CUR);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return std::make_shared<PCMTrack>(segment);
 | |
| }
 | |
| 
 | |
| void HFE::set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tracks) {
 | |
| 	for(auto &track : tracks) {
 | |
| 		std::unique_lock lock_guard(file_.get_file_access_mutex());
 | |
| 		uint16_t track_length = seek_track(track.first);
 | |
| 		lock_guard.unlock();
 | |
| 
 | |
| 		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(uint16_t(byte_segment.size()), track_length);
 | |
| 
 | |
| 		lock_guard.lock();
 | |
| 		seek_track(track.first);
 | |
| 
 | |
| 		uint16_t c = 0;
 | |
| 		while(c < data_length) {
 | |
| 			uint16_t length = uint16_t(std::min(256, data_length - c));
 | |
| 			file_.write(&byte_segment[c], length);
 | |
| 			c += length;
 | |
| 			file_.seek(256, SEEK_CUR);
 | |
| 		}
 | |
| 		lock_guard.unlock();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool HFE::get_is_read_only() {
 | |
| 	return file_.get_is_known_read_only();
 | |
| }
 |