1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-02-20 14:29:11 +00:00

Separate stateful serialisation from tapes.

This commit is contained in:
Thomas Harte 2025-01-17 16:39:21 -05:00
parent 2f546842a7
commit 58d3fdc1c2
54 changed files with 436 additions and 315 deletions

View File

@ -77,8 +77,8 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(
// If there are any tapes, attempt to get data from the first.
if(!media.tapes.empty()) {
std::shared_ptr<Storage::Tape::Tape> tape = media.tapes.front();
std::vector<File> files = GetFiles(tape);
tape->reset();
auto serialiser = tape->serialiser();
std::vector<File> files = GetFiles(*serialiser);
// continue if there are any files
if(!files.empty()) {

View File

@ -16,7 +16,7 @@
using namespace Analyser::Static::Acorn;
static std::unique_ptr<File::Chunk> GetNextChunk(
const std::shared_ptr<Storage::Tape::Tape> &tape,
Storage::Tape::TapeSerialiser &serialiser,
Storage::Tape::Acorn::Parser &parser
) {
auto new_chunk = std::make_unique<File::Chunk>();
@ -24,16 +24,16 @@ static std::unique_ptr<File::Chunk> GetNextChunk(
// TODO: move this into the parser
const auto shift = [&] {
shift_register = (shift_register >> 1) | (parser.get_next_bit(tape) << 9);
shift_register = (shift_register >> 1) | (parser.get_next_bit(serialiser) << 9);
};
// find next area of high tone
while(!tape->is_at_end() && (shift_register != 0x3ff)) {
while(!serialiser.is_at_end() && (shift_register != 0x3ff)) {
shift();
}
// find next 0x2a (swallowing stop bit)
while(!tape->is_at_end() && (shift_register != 0x254)) {
while(!serialiser.is_at_end() && (shift_register != 0x254)) {
shift();
}
@ -43,8 +43,8 @@ static std::unique_ptr<File::Chunk> GetNextChunk(
// read out name
char name[11];
std::size_t name_ptr = 0;
while(!tape->is_at_end() && name_ptr < sizeof(name)) {
name[name_ptr] = char(parser.get_next_byte(tape));
while(!serialiser.is_at_end() && name_ptr < sizeof(name)) {
name[name_ptr] = char(parser.get_next_byte(serialiser));
if(!name[name_ptr]) break;
++name_ptr;
}
@ -52,15 +52,15 @@ static std::unique_ptr<File::Chunk> GetNextChunk(
new_chunk->name = name;
// addresses
new_chunk->load_address = uint32_t(parser.get_next_word(tape));
new_chunk->execution_address = uint32_t(parser.get_next_word(tape));
new_chunk->block_number = uint16_t(parser.get_next_short(tape));
new_chunk->block_length = uint16_t(parser.get_next_short(tape));
new_chunk->block_flag = uint8_t(parser.get_next_byte(tape));
new_chunk->next_address = uint32_t(parser.get_next_word(tape));
new_chunk->load_address = uint32_t(parser.get_next_word(serialiser));
new_chunk->execution_address = uint32_t(parser.get_next_word(serialiser));
new_chunk->block_number = uint16_t(parser.get_next_short(serialiser));
new_chunk->block_length = uint16_t(parser.get_next_short(serialiser));
new_chunk->block_flag = uint8_t(parser.get_next_byte(serialiser));
new_chunk->next_address = uint32_t(parser.get_next_word(serialiser));
const uint16_t calculated_header_crc = parser.get_crc();
uint16_t stored_header_crc = uint16_t(parser.get_next_short(tape));
uint16_t stored_header_crc = uint16_t(parser.get_next_short(serialiser));
stored_header_crc = uint16_t((stored_header_crc >> 8) | (stored_header_crc << 8));
new_chunk->header_crc_matched = stored_header_crc == calculated_header_crc;
@ -69,12 +69,12 @@ static std::unique_ptr<File::Chunk> GetNextChunk(
parser.reset_crc();
new_chunk->data.reserve(new_chunk->block_length);
for(int c = 0; c < new_chunk->block_length; c++) {
new_chunk->data.push_back(uint8_t(parser.get_next_byte(tape)));
new_chunk->data.push_back(uint8_t(parser.get_next_byte(serialiser)));
}
if(new_chunk->block_length && !(new_chunk->block_flag&0x40)) {
uint16_t calculated_data_crc = parser.get_crc();
uint16_t stored_data_crc = uint16_t(parser.get_next_short(tape));
uint16_t stored_data_crc = uint16_t(parser.get_next_short(serialiser));
stored_data_crc = uint16_t((stored_data_crc >> 8) | (stored_data_crc << 8));
new_chunk->data_crc_matched = stored_data_crc == calculated_data_crc;
} else {
@ -127,13 +127,13 @@ static std::unique_ptr<File> GetNextFile(std::deque<File::Chunk> &chunks) {
return file;
}
std::vector<File> Analyser::Static::Acorn::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
std::vector<File> Analyser::Static::Acorn::GetFiles(Storage::Tape::TapeSerialiser &serialiser) {
Storage::Tape::Acorn::Parser parser;
// populate chunk list
std::deque<File::Chunk> chunk_list;
while(!tape->is_at_end()) {
std::unique_ptr<File::Chunk> chunk = GetNextChunk(tape, parser);
while(!serialiser.is_at_end()) {
std::unique_ptr<File::Chunk> chunk = GetNextChunk(serialiser, parser);
if(chunk) {
chunk_list.push_back(*chunk);
}

View File

@ -15,6 +15,6 @@
namespace Analyser::Static::Acorn {
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &);
std::vector<File> GetFiles(Storage::Tape::TapeSerialiser &);
}

View File

@ -185,7 +185,7 @@ bool CheckBootSector(
return false;
}
bool IsAmstradTape(const std::shared_ptr<Storage::Tape::Tape> &tape) {
bool IsAmstradTape(Storage::Tape::TapeSerialiser &serialiser) {
// Limited sophistication here; look for a CPC-style file header, that is
// any Spectrum-esque block with a synchronisation character of 0x2c.
//
@ -194,7 +194,7 @@ bool IsAmstradTape(const std::shared_ptr<Storage::Tape::Tape> &tape) {
Parser parser(Parser::MachineType::AmstradCPC);
while(true) {
const auto block = parser.find_block(tape);
const auto block = parser.find_block(serialiser);
if(!block) break;
if(block->type == 0x2c) {
@ -222,7 +222,8 @@ Analyser::Static::TargetList Analyser::Static::AmstradCPC::GetTargets(
if(!media.tapes.empty()) {
bool has_cpc_tape = false;
for(auto &tape: media.tapes) {
has_cpc_tape |= IsAmstradTape(tape);
const auto serialiser = tape->serialiser();
has_cpc_tape |= IsAmstradTape(*serialiser);
}
if(has_cpc_tape) {

View File

@ -212,8 +212,8 @@ Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(
// Find all valid Commodore files on tapes.
for(auto &tape : media.tapes) {
std::vector<File> tape_files = GetFiles(tape);
tape->reset();
auto serialiser = tape->serialiser();
std::vector<File> tape_files = GetFiles(*serialiser);
if(!tape_files.empty()) {
files.insert(
files.end(),

View File

@ -12,15 +12,15 @@
using namespace Analyser::Static::Commodore;
std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
std::vector<File> Analyser::Static::Commodore::GetFiles(Storage::Tape::TapeSerialiser &serialiser) {
Storage::Tape::Commodore::Parser parser;
std::vector<File> file_list;
std::unique_ptr<Storage::Tape::Commodore::Header> header = parser.get_next_header(tape);
std::unique_ptr<Storage::Tape::Commodore::Header> header = parser.get_next_header(serialiser);
while(!tape->is_at_end()) {
while(!serialiser.is_at_end()) {
if(!header) {
header = parser.get_next_header(tape);
header = parser.get_next_header(serialiser);
continue;
}
@ -34,8 +34,8 @@ std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<St
new_file.type = File::DataSequence;
new_file.data.swap(header->data);
while(!tape->is_at_end()) {
header = parser.get_next_header(tape);
while(!serialiser.is_at_end()) {
header = parser.get_next_header(serialiser);
if(!header) continue;
if(header->type != Storage::Tape::Commodore::Header::DataBlock) break;
std::copy(header->data.begin(), header->data.end(), std::back_inserter(new_file.data));
@ -45,7 +45,7 @@ std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<St
case Storage::Tape::Commodore::Header::RelocatableProgram:
case Storage::Tape::Commodore::Header::NonRelocatableProgram: {
std::unique_ptr<Storage::Tape::Commodore::Data> data = parser.get_next_data(tape);
std::unique_ptr<Storage::Tape::Commodore::Data> data = parser.get_next_data(serialiser);
if(data) {
File &new_file = file_list.emplace_back();
new_file.name = header->name;
@ -58,12 +58,12 @@ std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<St
? File::RelocatableProgram : File::NonRelocatableProgram;
}
header = parser.get_next_header(tape);
header = parser.get_next_header(serialiser);
}
break;
default:
header = parser.get_next_header(tape);
header = parser.get_next_header(serialiser);
break;
}
}

View File

@ -13,6 +13,6 @@
namespace Analyser::Static::Commodore {
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &);
std::vector<File> GetFiles(Storage::Tape::TapeSerialiser &);
}

View File

@ -34,7 +34,7 @@ std::vector<File> Analyser::Static::MSX::GetFiles(const std::shared_ptr<Storage:
using Parser = Storage::Tape::MSX::Parser;
// Get all recognisable files from the tape.
while(!tape->is_at_end()) {
while(!tape_player.is_at_end()) {
// Try to locate and measure a header.
std::unique_ptr<Parser::FileSpeed> file_speed = Parser::find_header(tape_player);
if(!file_speed) continue;

View File

@ -194,8 +194,8 @@ Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(
int basic11_votes = 0;
for(auto &tape : media.tapes) {
std::vector<File> tape_files = GetFiles(tape);
tape->reset();
auto serialiser = tape->serialiser();
std::vector<File> tape_files = GetFiles(*serialiser);
if(!tape_files.empty()) {
for(const auto &file : tape_files) {
if(file.data_type == File::MachineCode) {

View File

@ -11,57 +11,57 @@
using namespace Analyser::Static::Oric;
std::vector<File> Analyser::Static::Oric::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
std::vector<File> Analyser::Static::Oric::GetFiles(Storage::Tape::TapeSerialiser &serialiser) {
std::vector<File> files;
Storage::Tape::Oric::Parser parser;
while(!tape->is_at_end()) {
while(!serialiser.is_at_end()) {
// sync to next lead-in, check that it's one of three 0x16s
bool is_fast = parser.sync_and_get_encoding_speed(tape);
bool is_fast = parser.sync_and_get_encoding_speed(serialiser);
int next_bytes[2];
next_bytes[0] = parser.get_next_byte(tape, is_fast);
next_bytes[1] = parser.get_next_byte(tape, is_fast);
next_bytes[0] = parser.get_next_byte(serialiser, is_fast);
next_bytes[1] = parser.get_next_byte(serialiser, is_fast);
if(next_bytes[0] != 0x16 || next_bytes[1] != 0x16) continue;
// get the first byte that isn't a 0x16, check it was a 0x24
int byte = 0x16;
while(!tape->is_at_end() && byte == 0x16) {
byte = parser.get_next_byte(tape, is_fast);
while(!serialiser.is_at_end() && byte == 0x16) {
byte = parser.get_next_byte(serialiser, is_fast);
}
if(byte != 0x24) continue;
// skip two empty bytes
parser.get_next_byte(tape, is_fast);
parser.get_next_byte(tape, is_fast);
parser.get_next_byte(serialiser, is_fast);
parser.get_next_byte(serialiser, is_fast);
// get data and launch types
File new_file;
switch(parser.get_next_byte(tape, is_fast)) {
switch(parser.get_next_byte(serialiser, is_fast)) {
case 0x00: new_file.data_type = File::ProgramType::BASIC; break;
case 0x80: new_file.data_type = File::ProgramType::MachineCode; break;
default: new_file.data_type = File::ProgramType::None; break;
}
switch(parser.get_next_byte(tape, is_fast)) {
switch(parser.get_next_byte(serialiser, is_fast)) {
case 0x80: new_file.launch_type = File::ProgramType::BASIC; break;
case 0xc7: new_file.launch_type = File::ProgramType::MachineCode; break;
default: new_file.launch_type = File::ProgramType::None; break;
}
// read end and start addresses
new_file.ending_address = uint16_t(parser.get_next_byte(tape, is_fast) << 8);
new_file.ending_address |= uint16_t(parser.get_next_byte(tape, is_fast));
new_file.starting_address = uint16_t(parser.get_next_byte(tape, is_fast) << 8);
new_file.starting_address |= uint16_t(parser.get_next_byte(tape, is_fast));
new_file.ending_address = uint16_t(parser.get_next_byte(serialiser, is_fast) << 8);
new_file.ending_address |= uint16_t(parser.get_next_byte(serialiser, is_fast));
new_file.starting_address = uint16_t(parser.get_next_byte(serialiser, is_fast) << 8);
new_file.starting_address |= uint16_t(parser.get_next_byte(serialiser, is_fast));
// skip an empty byte
parser.get_next_byte(tape, is_fast);
parser.get_next_byte(serialiser, is_fast);
// read file name, up to 16 characters and null terminated
char file_name[17];
int name_pos = 0;
while(name_pos < 16) {
file_name[name_pos] = char(parser.get_next_byte(tape, is_fast));
file_name[name_pos] = char(parser.get_next_byte(serialiser, is_fast));
if(!file_name[name_pos]) break;
name_pos++;
}
@ -72,11 +72,11 @@ std::vector<File> Analyser::Static::Oric::GetFiles(const std::shared_ptr<Storage
std::size_t body_length = new_file.ending_address - new_file.starting_address + 1;
new_file.data.reserve(body_length);
for(std::size_t c = 0; c < body_length; c++) {
new_file.data.push_back(uint8_t(parser.get_next_byte(tape, is_fast)));
new_file.data.push_back(uint8_t(parser.get_next_byte(serialiser, is_fast)));
}
// only one validation check: was there enough tape?
if(!tape->is_at_end()) {
if(!serialiser.is_at_end()) {
files.push_back(new_file);
}
}

View File

@ -28,6 +28,6 @@ struct File {
std::vector<uint8_t> data;
};
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &);
std::vector<File> GetFiles(Storage::Tape::TapeSerialiser &);
}

View File

@ -385,13 +385,6 @@ TargetList Analyser::Static::GetTargets(const std::string &file_name) {
append(TargetPlatform::ZX8081, ZX8081::GetTargets);
append(TargetPlatform::ZXSpectrum, ZXSpectrum::GetTargets);
// Reset any tapes to their initial position.
for(const auto &target : targets) {
for(auto &tape : target->media.tapes) {
tape->reset();
}
}
// Sort by initial confidence. Use a stable sort in case any of the machine-specific analysers
// picked their insertion order carefully.
std::stable_sort(targets.begin(), targets.end(),

View File

@ -14,12 +14,12 @@
#include "Target.hpp"
#include "../../../Storage/Tape/Parsers/ZX8081.hpp"
static std::vector<Storage::Data::ZX8081::File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
static std::vector<Storage::Data::ZX8081::File> GetFiles(Storage::Tape::TapeSerialiser &serialiser) {
std::vector<Storage::Data::ZX8081::File> files;
Storage::Tape::ZX8081::Parser parser;
while(!tape->is_at_end()) {
std::shared_ptr<Storage::Data::ZX8081::File> next_file = parser.get_next_file(tape);
while(!serialiser.is_at_end()) {
std::shared_ptr<Storage::Data::ZX8081::File> next_file = parser.get_next_file(serialiser);
if(next_file != nullptr) {
files.push_back(*next_file);
}
@ -36,8 +36,8 @@ Analyser::Static::TargetList Analyser::Static::ZX8081::GetTargets(
) {
TargetList destination;
if(!media.tapes.empty()) {
std::vector<Storage::Data::ZX8081::File> files = GetFiles(media.tapes.front());
media.tapes.front()->reset();
const auto serialiser = media.tapes.front()->serialiser();
std::vector<Storage::Data::ZX8081::File> files = GetFiles(*serialiser);
if(!files.empty()) {
Target *const target = new Target;
destination.push_back(std::unique_ptr<::Analyser::Static::Target>(target));

View File

@ -18,7 +18,7 @@
namespace {
bool IsSpectrumTape(const std::shared_ptr<Storage::Tape::Tape> &tape) {
bool IsSpectrumTape(Storage::Tape::TapeSerialiser &tape) {
using Parser = Storage::Tape::ZXSpectrum::Parser;
Parser parser(Parser::MachineType::ZXSpectrum);
@ -116,7 +116,8 @@ Analyser::Static::TargetList Analyser::Static::ZXSpectrum::GetTargets(
if(!media.tapes.empty()) {
bool has_spectrum_tape = false;
for(auto &tape: media.tapes) {
has_spectrum_tape |= IsSpectrumTape(tape);
auto serialiser = tape->serialiser();
has_spectrum_tape |= IsSpectrumTape(*serialiser);
}
if(has_spectrum_tape) {

View File

@ -454,7 +454,7 @@ public:
int cycles_left_while_plausibly_in_data = 50;
tape_.clear_interrupts(Interrupt::ReceiveDataFull);
while(!tape_.tape()->is_at_end()) {
while(!tape_.serialiser()->is_at_end()) {
tape_.run_for_input_pulse();
--cycles_left_while_plausibly_in_data;
if(!cycles_left_while_plausibly_in_data) fast_load_is_in_data_ = false;

View File

@ -900,7 +900,7 @@ public:
// preceding symbol and be a short way into the pulse that should determine the
// first bit of this byte.
parser.process_pulse(tape_player_.current_pulse());
const auto byte = parser.get_byte(tape_player_.tape());
const auto byte = parser.get_byte(*tape_player_.serialiser());
auto flags = z80_.value_of(CPU::Z80::Register::Flags);
if(byte) {

View File

@ -214,6 +214,7 @@ public:
c1541_ = std::make_unique<C1540::Machine>(C1540::Personality::C1541, roms);
c1541_->set_serial_bus(serial_bus_);
Serial::attach(serial_port_, serial_bus_);
c1541_->run_for(Cycles(2000000));
}
tape_player_ = std::make_unique<Storage::Tape::BinaryTapePlayer>(clock);
@ -299,6 +300,33 @@ public:
serial_port_.set_output(Serial::Line::Attention, Serial::LineLevel(~output & 0x04));
}
} else if(address < 0xfd00 || address >= 0xff40) {
if(use_fast_tape_hack_ && operation == CPU::MOS6502Esque::BusOperation::ReadOpcode && address == 0xe5fd) {
// TODO:
//
// ; read a dipole from tape (and then RTS)
// ;
// ; if c=1 then error
// ; else if v=1 then short
// ; else if n=0 then long
// ; else word
// ; end
// ; end
// ; end
// Compare with:
//
// dsamp1 *=*+2 ;time constant for x cell sample 07B8
// dsamp2 *=*+2 ;time constant for y cell sample
// zcell *=*+2 ;time constant for z cell verify
// const uint8_t dsamp1 = map_.read(0x7b8);
// const uint8_t dsamp2 = map_.read(0x7b9);
// const uint8_t zcell = map_.read(0x7ba);
//
//
// printf("rddipl: %d / %d / %d\n", dsamp1, dsamp2, zcell);
}
if(is_read(operation)) {
*value = map_.read(address);
} else {
@ -384,8 +412,8 @@ public:
const uint8_t joystick_mask =
0xff &
((joystick_mask_ & 0x02) ? 0xff : (joystick(1).mask() | 0x40)) &
((joystick_mask_ & 0x04) ? 0xff : (joystick(0).mask() | 0x80));
((joystick_mask_ & 0x02) ? 0xff : (joystick(0).mask() | 0x40)) &
((joystick_mask_ & 0x04) ? 0xff : (joystick(1).mask() | 0x80));
*value = keyboard_input & joystick_mask;
} break;
@ -551,10 +579,12 @@ private:
map_.page<PagerSide::Read, 0x8000, 16384>(basic_.data());
map_.page<PagerSide::Read, 0xc000, 16384>(kernel_.data());
rom_is_paged_ = true;
set_use_fast_tape();
}
void page_cpu_ram() {
map_.page<PagerSide::Read, 0x8000, 32768>(&ram_[0x8000]);
rom_is_paged_ = false;
set_use_fast_tape();
}
bool rom_is_paged_ = false;
@ -659,10 +689,14 @@ private:
std::unique_ptr<Storage::Tape::BinaryTapePlayer> tape_player_;
bool play_button_ = false;
bool allow_fast_tape_hack_ = false; // TODO: implement fast-tape hack.
void set_use_fast_tape() {}
bool use_fast_tape_hack_ = false;
void set_use_fast_tape() {
use_fast_tape_hack_ = allow_fast_tape_hack_ && tape_player_->motor_control() && rom_is_paged_;
}
void update_tape_motor() {
const auto output = io_output_ | ~io_direction_;
tape_player_->set_motor_control(play_button_ && (~output & 0x08));
set_use_fast_tape();
}
uint8_t io_direction_ = 0x00, io_output_ = 0x00;

View File

@ -522,9 +522,9 @@ public:
// Address 0xf7b2 contains a JSR to 0xf8c0 that will fill the tape buffer with the next header.
// So cancel that via a double NOP and fill in the next header programmatically.
Storage::Tape::Commodore::Parser parser;
std::unique_ptr<Storage::Tape::Commodore::Header> header = parser.get_next_header(tape_->tape());
std::unique_ptr<Storage::Tape::Commodore::Header> header = parser.get_next_header(*tape_->serialiser());
const auto tape_position = tape_->tape()->offset();
const auto tape_position = tape_->serialiser()->offset();
if(header) {
// serialise to wherever b2:b3 points
const uint16_t tape_buffer_pointer = uint16_t(ram_[0xb2]) | uint16_t(ram_[0xb3] << 8);
@ -533,7 +533,7 @@ public:
logger.info().append("Found header");
} else {
// no header found, so pretend this hack never interceded
tape_->tape()->set_offset(tape_position);
tape_->serialiser()->set_offset(tape_position);
hold_tape_ = false;
logger.info().append("Didn't find header");
}
@ -547,8 +547,8 @@ public:
uint8_t x = uint8_t(m6502_.value_of(CPU::MOS6502::Register::X));
if(x == 0xe) {
Storage::Tape::Commodore::Parser parser;
const auto tape_position = tape_->tape()->offset();
const std::unique_ptr<Storage::Tape::Commodore::Data> data = parser.get_next_data(tape_->tape());
const auto tape_position = tape_->serialiser()->offset();
const std::unique_ptr<Storage::Tape::Commodore::Data> data = parser.get_next_data(*tape_->serialiser());
if(data) {
uint16_t start_address, end_address;
start_address = uint16_t(ram_[0xc1] | (ram_[0xc2] << 8));
@ -578,7 +578,7 @@ public:
hold_tape_ = true;
logger.info().append("Found data");
} else {
tape_->tape()->set_offset(tape_position);
tape_->serialiser()->set_offset(tape_position);
hold_tape_ = false;
logger.info().append("Didn't find data");
}

View File

@ -168,7 +168,7 @@ class TapePlayer: public Storage::Tape::BinaryTapePlayer {
@returns The next byte from the tape.
*/
uint8_t get_next_byte(bool use_fast_encoding) {
return uint8_t(parser_.get_next_byte(tape(), use_fast_encoding));
return uint8_t(parser_.get_next_byte(*serialiser(), use_fast_encoding));
}
private:
@ -469,7 +469,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface, CPU::MOS
use_fast_tape_hack_ &&
operation == CPU::MOS6502::BusOperation::ReadOpcode &&
tape_player_.has_tape() &&
!tape_player_.tape()->is_at_end()) {
!tape_player_.serialiser()->is_at_end()) {
uint8_t next_byte = tape_player_.get_next_byte(!ram_[tape_speed_address_]);
m6502_.set_value_of(CPU::MOS6502Esque::A, next_byte);

View File

@ -232,8 +232,8 @@ template<bool is_zx81> class ConcreteMachine:
case CPU::Z80::PartialMachineCycle::ReadOpcode:
// Check for use of the fast tape hack.
if(use_fast_tape_hack_ && address == tape_trap_address_) {
const uint64_t prior_offset = tape_player_.tape()->offset();
const int next_byte = parser_.get_next_byte(tape_player_.tape());
const uint64_t prior_offset = tape_player_.serialiser()->offset();
const int next_byte = parser_.get_next_byte(*tape_player_.serialiser());
if(next_byte != -1) {
const uint16_t hl = z80_.value_of(CPU::Z80::Register::HL);
ram_[hl & ram_mask_] = uint8_t(next_byte);
@ -246,7 +246,7 @@ template<bool is_zx81> class ConcreteMachine:
tape_advance_delay_ = 1000;
return 0;
} else {
tape_player_.tape()->set_offset(prior_offset);
tape_player_.serialiser()->set_offset(prior_offset);
}
}

View File

@ -921,7 +921,7 @@ template<Model model> class ConcreteMachine:
if(!(flags & 1)) return false;
const uint8_t block_type = uint8_t(z80_.value_of(Register::ADash));
const auto block = parser.find_block(tape_player_.tape());
const auto block = parser.find_block(*tape_player_.serialiser());
if(!block || block_type != (*block).type) return false;
uint16_t length = z80_.value_of(Register::DE);
@ -930,7 +930,7 @@ template<Model model> class ConcreteMachine:
flags = 0x93;
uint8_t parity = 0x00;
while(length--) {
auto next = parser.get_byte(tape_player_.tape());
auto next = parser.get_byte(*tape_player_.serialiser());
if(!next) {
flags &= ~1;
break;
@ -941,7 +941,7 @@ template<Model model> class ConcreteMachine:
++target;
}
auto stored_parity = parser.get_byte(tape_player_.tape());
auto stored_parity = parser.get_byte(*tape_player_.serialiser());
if(!stored_parity) {
flags &= ~1;
} else {

View File

@ -59,7 +59,13 @@ namespace {
const uint8_t ascii_signature[] = TenX(0xea);
}
CAS::CAS(const std::string &file_name) : Tape(serialiser_), serialiser_(file_name) {}
CAS::CAS(const std::string &file_name) : file_name_(file_name) {
format_serialiser();
}
std::unique_ptr<FormatSerialiser> CAS::format_serialiser() const {
return std::make_unique<Serialiser>(file_name_);
}
CAS::Serialiser::Serialiser(const std::string &file_name) {
Storage::FileHolder file(file_name, FileHolder::FileMode::Read);

View File

@ -34,7 +34,8 @@ public:
};
private:
struct Serialiser: public TapeSerialiser {
std::unique_ptr<FormatSerialiser> format_serialiser() const override;
struct Serialiser: public FormatSerialiser {
Serialiser(const std::string &file_name);
private:
@ -65,7 +66,8 @@ private:
} phase_ = Phase::Header;
std::size_t distance_into_phase_ = 0;
std::size_t distance_into_bit_ = 0;
} serialiser_;
};
std::string file_name_;
};
}

View File

@ -14,14 +14,7 @@
using namespace Storage::Tape;
CSW::CSW(const std::string &file_name) : Tape(serialiser_), serialiser_(file_name) {}
CSW::CSW(const std::vector<uint8_t> &&data, CompressionType type, bool initial_level, uint32_t sampling_rate) :
Tape(serialiser_),
serialiser_(std::move(data), type, initial_level, sampling_rate) {}
CSW::Serialiser::Serialiser(const std::string &file_name) : source_data_pointer_(0) {
CSW::CSW(const std::string &file_name) {
Storage::FileHolder file(file_name, FileHolder::FileMode::Read);
if(file.stats().st_size < 0x20) throw ErrorNotCSW;
@ -42,21 +35,22 @@ CSW::Serialiser::Serialiser(const std::string &file_name) : source_data_pointer_
// The header now diverges based on version.
uint32_t number_of_waves = 0;
CompressionType compression_type;
if(major_version == 1) {
pulse_.length.clock_rate = file.get16le();
if(file.get8() != 1) throw ErrorNotCSW;
compression_type_ = CompressionType::RLE;
compression_type = CompressionType::RLE;
pulse_.type = (file.get8() & 1) ? Pulse::High : Pulse::Low;
file.seek(0x20, SEEK_SET);
} else {
pulse_.length.clock_rate = file.get32le();
number_of_waves = file.get32le();
number_of_waves = file.get32le(); // TODO: is this still useful?
switch(file.get8()) {
case 1: compression_type_ = CompressionType::RLE; break;
case 2: compression_type_ = CompressionType::ZRLE; break;
case 1: compression_type = CompressionType::RLE; break;
case 2: compression_type = CompressionType::ZRLE; break;
default: throw ErrorNotCSW;
}
@ -73,36 +67,42 @@ CSW::Serialiser::Serialiser(const std::string &file_name) : source_data_pointer_
file_data.resize(remaining_data);
file.read(file_data.data(), remaining_data);
if(compression_type_ == CompressionType::ZRLE) {
set_data(std::move(file_data), compression_type);
}
CSW::CSW(std::vector<uint8_t> &&data, CompressionType type, bool initial_level, uint32_t sampling_rate) {
set_data(std::move(data), type);
pulse_.length.clock_rate = sampling_rate;
pulse_.type = initial_level ? Pulse::Type::High : Pulse::Type::Low;
}
void CSW::set_data(std::vector<uint8_t> &&data, CompressionType type) {
// TODO: compression types.
if(type == CompressionType::ZRLE) {
// The only clue given by CSW as to the output size in bytes is that there will be
// number_of_waves waves. Waves are usually one byte, but may be five. So this code
// is pessimistic.
source_data_.resize(size_t(number_of_waves) * 5);
// source_data_.resize(size_t(number_of_waves) * 5);
// uncompress will tell how many compressed bytes there actually were, so use its
// modification of output_length to throw away all the memory that isn't actually
// needed.
uLongf output_length = uLongf(number_of_waves * 5);
uncompress(source_data_.data(), &output_length, file_data.data(), file_data.size());
source_data_.resize(std::size_t(output_length));
// uLongf output_length = uLongf(number_of_waves * 5);
// uncompress(source_data_.data(), &output_length, file_data.data(), file_data.size());
// source_data_.resize(std::size_t(output_length));
} else {
source_data_ = std::move(file_data);
source_data_ = std::move(data);
}
invert_pulse();
}
CSW::Serialiser::Serialiser(
const std::vector<uint8_t> &&data,
const CompressionType compression_type,
const bool initial_level,
const uint32_t sampling_rate
) : compression_type_(compression_type) {
pulse_.length.clock_rate = sampling_rate;
pulse_.type = initial_level ? Pulse::High : Pulse::Low;
source_data_ = std::move(data);
std::unique_ptr<FormatSerialiser> CSW::format_serialiser() const {
return std::make_unique<Serialiser>(source_data_, pulse_);
}
CSW::Serialiser::Serialiser(const std::vector<uint8_t> &data, Pulse pulse) : pulse_(pulse), source_data_(data) {}
uint8_t CSW::Serialiser::get_next_byte() {
if(source_data_pointer_ == source_data_.size()) return 0xff;

View File

@ -36,33 +36,35 @@ public:
/*!
Constructs a @c CSW containing content as specified. Does not throw.
*/
CSW(const std::vector<uint8_t> &&data, CompressionType, bool initial_level, uint32_t sampling_rate);
CSW(std::vector<uint8_t> &&data, CompressionType, bool initial_level, uint32_t sampling_rate);
enum {
ErrorNotCSW
};
private:
struct Serialiser: public TapeSerialiser {
Serialiser(const std::string &file_name);
Serialiser(const std::vector<uint8_t> &&data, CompressionType, bool initial_level, uint32_t sampling_rate);
void set_data(std::vector<uint8_t> &&data, CompressionType type);
std::unique_ptr<FormatSerialiser> format_serialiser() const override;
struct Serialiser: public FormatSerialiser {
Serialiser(const std::vector<uint8_t> &data, Pulse);
private:
// implemented to satisfy @c Tape
// implemented to satisfy @c FormatSerialiser
bool is_at_end() const override;
void reset() override;
Pulse next_pulse() override;
Pulse pulse_;
CompressionType compression_type_;
uint8_t get_next_byte();
uint32_t get_next_int32le();
void invert_pulse();
std::vector<uint8_t> source_data_;
std::size_t source_data_pointer_;
} serialiser_;
Pulse pulse_;
const std::vector<uint8_t> &source_data_;
std::size_t source_data_pointer_ = 0;
};
std::vector<uint8_t> source_data_;
Pulse pulse_;
};
}

View File

@ -12,7 +12,14 @@
using namespace Storage::Tape;
CommodoreTAP::CommodoreTAP(const std::string &file_name) : Tape(serialiser_), serialiser_(file_name) {}
CommodoreTAP::CommodoreTAP(const std::string &file_name) : file_name_(file_name) {
Serialiser test(file_name);
target_platforms_ = test.target_platforms();
}
std::unique_ptr<FormatSerialiser> CommodoreTAP::format_serialiser() const {
return std::make_unique<Serialiser>(file_name_);
}
CommodoreTAP::Serialiser::Serialiser(const std::string &file_name) :
file_(file_name, FileHolder::FileMode::Read)
@ -106,7 +113,7 @@ Storage::Tape::Pulse CommodoreTAP::Serialiser::next_pulse() {
// MARK: - TargetPlatform::Distinguisher
TargetPlatform::Type CommodoreTAP::target_platforms() {
return serialiser_.target_platforms();
return target_platforms_;
}
TargetPlatform::Type CommodoreTAP::Serialiser::target_platforms() {

View File

@ -36,8 +36,9 @@ public:
private:
TargetPlatform::Type target_platforms() override;
std::unique_ptr<FormatSerialiser> format_serialiser() const override;
struct Serialiser: public TapeSerialiser {
struct Serialiser: public FormatSerialiser {
Serialiser(const std::string &file_name);
TargetPlatform::Type target_platforms();
@ -76,7 +77,9 @@ private:
Pulse current_pulse_;
bool is_at_end_ = false;
} serialiser_;
};
std::string file_name_;
TargetPlatform::Type target_platforms_;
};
}

View File

@ -12,8 +12,13 @@
using namespace Storage::Tape;
OricTAP::OricTAP(const std::string &file_name) : Tape(serialiser_), serialiser_(file_name) {}
OricTAP::OricTAP(const std::string &file_name) : file_name_(file_name) {
format_serialiser();
}
std::unique_ptr<FormatSerialiser> OricTAP::format_serialiser() const {
return std::make_unique<Serialiser>(file_name_);
}
OricTAP::Serialiser::Serialiser(const std::string &file_name) :
file_(file_name, FileHolder::FileMode::Read)

View File

@ -33,7 +33,9 @@ public:
};
private:
struct Serialiser: public TapeSerialiser {
std::unique_ptr<FormatSerialiser> format_serialiser() const override;
struct Serialiser: public FormatSerialiser {
Serialiser(const std::string &file_name);
private:
@ -53,7 +55,8 @@ private:
} phase_, next_phase_;
int phase_counter_;
uint16_t data_end_address_, data_start_address_;
} serialiser_;
};
std::string file_name_;
};
}

View File

@ -21,7 +21,13 @@ Log::Logger<Log::Source::TZX> logger;
}
TZX::TZX(const std::string &file_name) : Tape(serialiser_), serialiser_(file_name) {}
TZX::TZX(const std::string &file_name) : file_name_(file_name) {
format_serialiser();
}
std::unique_ptr<FormatSerialiser> TZX::format_serialiser() const {
return std::make_unique<Serialiser>(file_name_);
}
TZX::Serialiser::Serialiser(const std::string &file_name) :
file_(file_name, FileHolder::FileMode::Read),
@ -113,14 +119,15 @@ void TZX::Serialiser::get_csw_recording_block() {
std::vector<uint8_t> raw_block = file_.read(block_length - 10);
CSW csw(
const CSW csw(
std::move(raw_block),
(compression_type == 2) ? CSW::CompressionType::ZRLE : CSW::CompressionType::RLE,
current_level_,
sampling_rate
);
while(!csw.is_at_end()) {
Pulse next_pulse = csw.next_pulse();
auto serialiser = csw.serialiser();
while(!serialiser->is_at_end()) {
Pulse next_pulse = serialiser->next_pulse();
current_level_ = (next_pulse.type == Pulse::High);
push_back(next_pulse);
}

View File

@ -32,6 +32,8 @@ public:
};
private:
std::unique_ptr<FormatSerialiser> format_serialiser() const override;
struct Serialiser: public PulseQueuedSerialiser {
Serialiser(const std::string &file_name);
@ -104,7 +106,8 @@ private:
void post_gap(unsigned int milliseconds);
void post_pulse(const Storage::Time &time);
} serialiser_;
};
std::string file_name_;
};
}

View File

@ -48,7 +48,13 @@
using namespace Storage::Tape;
PRG::PRG(const std::string &file_name) : Tape(serialiser_), serialiser_(file_name) {}
PRG::PRG(const std::string &file_name) : file_name_(file_name) {
format_serialiser();
}
std::unique_ptr<FormatSerialiser> PRG::format_serialiser() const {
return std::make_unique<Serialiser>(file_name_);
}
PRG::Serialiser::Serialiser(const std::string &file_name) :
file_(file_name, FileHolder::FileMode::Read),
@ -66,10 +72,6 @@ PRG::Serialiser::Serialiser(const std::string &file_name) :
throw ErrorBadFormat;
}
void PRG::set_target_platforms(TargetPlatform::Type type) {
serialiser_.set_target_platforms(type);
}
void PRG::Serialiser::set_target_platforms(TargetPlatform::Type type) {
timings_ = Timings(type & TargetPlatform::Type::Plus4);
}

View File

@ -20,7 +20,7 @@ namespace Storage::Tape {
/*!
Provides a @c Tape containing a .PRG, which is a direct local file.
*/
class PRG: public Tape, public TargetPlatform::Recipient {
class PRG: public Tape {
public:
/*!
Constructs a @c T64 containing content from the file with name @c file_name, of type @c type.
@ -35,11 +35,11 @@ public:
};
private:
void set_target_platforms(TargetPlatform::Type) override;
std::unique_ptr<FormatSerialiser> format_serialiser() const override;
struct Serialiser: public TapeSerialiser {
struct Serialiser: public FormatSerialiser, public TargetPlatform::Recipient {
Serialiser(const std::string &file_name);
void set_target_platforms(TargetPlatform::Type);
void set_target_platforms(TargetPlatform::Type) override;
private:
bool is_at_end() const override;
Pulse next_pulse() override;
@ -86,7 +86,8 @@ private:
unsigned int one_length;
unsigned int marker_length;
} timings_;
} serialiser_;
};
std::string file_name_;
};
}

View File

@ -75,7 +75,14 @@ int gzget32(gzFile file) {
using namespace Storage::Tape;
UEF::UEF(const std::string &file_name) : Tape(serialiser_), serialiser_(file_name) {}
UEF::UEF(const std::string &file_name) : file_name_(file_name) {
Serialiser test_serialiser(file_name);
target_platform_ = test_serialiser.target_platforms();
}
std::unique_ptr<FormatSerialiser> UEF::format_serialiser() const {
return std::make_unique<Serialiser>(file_name_);
}
UEF::Serialiser::Serialiser(const std::string &file_name) {
file_ = gzopen(file_name.c_str(), "rb");
@ -324,7 +331,7 @@ void UEF::Serialiser::queue_bit(const int bit) {
// MARK: - TargetPlatform::Distinguisher
TargetPlatform::Type UEF::target_platforms() {
return serialiser_.target_platforms();
return target_platform_;
}
TargetPlatform::Type UEF::Serialiser::target_platforms() {

View File

@ -36,6 +36,7 @@ public:
private:
TargetPlatform::Type target_platforms() override;
std::unique_ptr<FormatSerialiser> format_serialiser() const override;
struct Serialiser: public PulseQueuedSerialiser {
Serialiser(const std::string &file_name);
@ -76,7 +77,9 @@ private:
void queue_bit(int bit);
void queue_implicit_byte(uint8_t byte);
} serialiser_;
};
std::string file_name_;
TargetPlatform::Type target_platform_;
};
}

View File

@ -11,7 +11,14 @@
using namespace Storage::Tape;
ZX80O81P::ZX80O81P(const std::string &file_name) : Tape(serialiser_), serialiser_(file_name) {}
ZX80O81P::ZX80O81P(const std::string &file_name) : file_name_(file_name) {
Serialiser test_serialiser(file_name);
target_platforms_ = test_serialiser.target_platforms();
}
std::unique_ptr<FormatSerialiser> ZX80O81P::format_serialiser() const {
return std::make_unique<Serialiser>(file_name_);
}
ZX80O81P::Serialiser::Serialiser(const std::string &file_name) {
Storage::FileHolder file(file_name, FileHolder::FileMode::Read);
@ -106,9 +113,9 @@ Pulse ZX80O81P::Serialiser::next_pulse() {
}
TargetPlatform::Type ZX80O81P::target_platforms() {
return serialiser_.target_platform_type();
return target_platforms_;
}
TargetPlatform::Type ZX80O81P::Serialiser::target_platform_type() {
TargetPlatform::Type ZX80O81P::Serialiser::target_platforms() {
return platform_type_;
}

View File

@ -38,10 +38,11 @@ public:
private:
// TargetPlatform::TypeDistinguisher.
TargetPlatform::Type target_platforms() override;
std::unique_ptr<FormatSerialiser> format_serialiser() const override;
struct Serialiser: public TapeSerialiser {
struct Serialiser: public FormatSerialiser {
Serialiser(const std::string &file_name);
TargetPlatform::Type target_platform_type();
TargetPlatform::Type target_platforms();
private:
bool is_at_end() const override;
@ -59,7 +60,9 @@ private:
std::vector<uint8_t> data_;
std::size_t data_pointer_;
} serialiser_;
};
std::string file_name_;
TargetPlatform::Type target_platforms_;
};
}

View File

@ -18,7 +18,13 @@ using namespace Storage::Tape;
https://sinclair.wiki.zxnet.co.uk/wiki/TAP_format
*/
ZXSpectrumTAP::ZXSpectrumTAP(const std::string &file_name) : Tape(serialiser_), serialiser_(file_name) {}
ZXSpectrumTAP::ZXSpectrumTAP(const std::string &file_name) : file_name_(file_name) {
format_serialiser();
}
std::unique_ptr<FormatSerialiser> ZXSpectrumTAP::format_serialiser() const {
return std::make_unique<Serialiser>(file_name_);
}
ZXSpectrumTAP::Serialiser::Serialiser(const std::string &file_name) :
file_(file_name, FileHolder::FileMode::Read)

View File

@ -34,7 +34,9 @@ public:
};
private:
struct Serialiser: public TapeSerialiser {
std::unique_ptr<FormatSerialiser> format_serialiser() const override;
struct Serialiser: public FormatSerialiser {
Serialiser(const std::string &file_name);
private:
Storage::FileHolder file_;
@ -54,7 +56,8 @@ private:
bool is_at_end() const override;
void reset() override;
Pulse next_pulse() override;
} serialiser_;
};
std::string file_name_;
};
}

View File

@ -18,13 +18,13 @@ Parser::Parser(): crc_(0x1021) {
shifter_.set_delegate(this);
}
int Parser::get_next_bit(const std::shared_ptr<Storage::Tape::Tape> &tape) {
const SymbolType symbol = get_next_symbol(tape);
int Parser::get_next_bit(Storage::Tape::TapeSerialiser &serialiser) {
const SymbolType symbol = get_next_symbol(serialiser);
return (symbol == SymbolType::One) ? 1 : 0;
}
int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape) {
if(get_next_bit(tape)) {
int Parser::get_next_byte(Storage::Tape::TapeSerialiser &serialiser) {
if(get_next_bit(serialiser)) {
set_error_flag();
return -1;
}
@ -32,9 +32,9 @@ int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape) {
int value = 0;
int c = 8;
while(c--) {
value = (value >> 1) | (get_next_bit(tape) << 7);
value = (value >> 1) | (get_next_bit(serialiser) << 7);
}
if(!get_next_bit(tape)) {
if(!get_next_bit(serialiser)) {
set_error_flag();
return -1;
}
@ -42,15 +42,15 @@ int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape) {
return value;
}
unsigned int Parser::get_next_short(const std::shared_ptr<Storage::Tape::Tape> &tape) {
unsigned int result = unsigned(get_next_byte(tape));
result |= unsigned(get_next_byte(tape)) << 8;
unsigned int Parser::get_next_short(Storage::Tape::TapeSerialiser &serialiser) {
unsigned int result = unsigned(get_next_byte(serialiser));
result |= unsigned(get_next_byte(serialiser)) << 8;
return result;
}
unsigned int Parser::get_next_word(const std::shared_ptr<Storage::Tape::Tape> &tape) {
unsigned int result = get_next_short(tape);
result |= get_next_short(tape) << 8;
unsigned int Parser::get_next_word(Storage::Tape::TapeSerialiser &serialiser) {
unsigned int result = get_next_short(serialiser);
result |= get_next_short(serialiser) << 8;
return result;
}

View File

@ -46,10 +46,10 @@ class Parser: public Storage::Tape::Parser<SymbolType>, public Shifter::Delegate
public:
Parser();
int get_next_bit(const std::shared_ptr<Storage::Tape::Tape> &);
int get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &);
unsigned int get_next_short(const std::shared_ptr<Storage::Tape::Tape> &);
unsigned int get_next_word(const std::shared_ptr<Storage::Tape::Tape> &);
int get_next_bit(Storage::Tape::TapeSerialiser &);
int get_next_byte(Storage::Tape::TapeSerialiser &);
unsigned int get_next_short(Storage::Tape::TapeSerialiser &);
unsigned int get_next_word(Storage::Tape::TapeSerialiser &);
void reset_crc();
uint16_t get_crc() const;

View File

@ -20,10 +20,10 @@ Parser::Parser() :
Advances to the next block on the tape, treating it as a header, then consumes, parses, and returns it.
Returns @c nullptr if any wave-encoding level errors are encountered.
*/
std::unique_ptr<Header> Parser::get_next_header(const std::shared_ptr<Storage::Tape::Tape> &tape) {
std::unique_ptr<Header> Parser::get_next_header(Storage::Tape::TapeSerialiser &serialiser) {
return duplicate_match<Header>(
get_next_header_body(tape, true),
get_next_header_body(tape, false)
get_next_header_body(serialiser, true),
get_next_header_body(serialiser, false)
);
}
@ -31,10 +31,10 @@ std::unique_ptr<Header> Parser::get_next_header(const std::shared_ptr<Storage::T
Advances to the next block on the tape, treating it as data, then consumes, parses, and returns it.
Returns @c nullptr if any wave-encoding level errors are encountered.
*/
std::unique_ptr<Data> Parser::get_next_data(const std::shared_ptr<Storage::Tape::Tape> &tape) {
std::unique_ptr<Data> Parser::get_next_data(Storage::Tape::TapeSerialiser &serialiser) {
return duplicate_match<Data>(
get_next_data_body(tape, true),
get_next_data_body(tape, false)
get_next_data_body(serialiser, true),
get_next_data_body(serialiser, false)
);
}
@ -43,7 +43,10 @@ std::unique_ptr<Data> Parser::get_next_data(const std::shared_ptr<Storage::Tape:
including setting the duplicate_matched flag.
*/
template<class ObjectType>
std::unique_ptr<ObjectType> Parser::duplicate_match(std::unique_ptr<ObjectType> first_copy, std::unique_ptr<ObjectType> second_copy) {
std::unique_ptr<ObjectType> Parser::duplicate_match(
std::unique_ptr<ObjectType> first_copy,
std::unique_ptr<ObjectType> second_copy
) {
// if only one copy was parsed successfully, return it
if(!first_copy) return second_copy;
if(!second_copy) return first_copy;
@ -64,19 +67,19 @@ template<class ObjectType>
return std::move(*copy_to_return);
}
std::unique_ptr<Header> Parser::get_next_header_body(const std::shared_ptr<Storage::Tape::Tape> &tape, bool is_original) {
std::unique_ptr<Header> Parser::get_next_header_body(Storage::Tape::TapeSerialiser &serialiser, bool is_original) {
auto header = std::make_unique<Header>();
reset_error_flag();
// find and proceed beyond lead-in tone
proceed_to_symbol(tape, SymbolType::LeadIn);
proceed_to_symbol(serialiser, SymbolType::LeadIn);
// look for landing zone
proceed_to_landing_zone(tape, is_original);
proceed_to_landing_zone(serialiser, is_original);
reset_parity_byte();
// get header type
const uint8_t header_type = get_next_byte(tape);
const uint8_t header_type = get_next_byte(serialiser);
switch(header_type) {
default: header->type = Header::Unknown; break;
case 0x01: header->type = Header::RelocatableProgram; break;
@ -89,11 +92,11 @@ std::unique_ptr<Header> Parser::get_next_header_body(const std::shared_ptr<Stora
// grab rest of data
header->data.reserve(191);
for(std::size_t c = 0; c < 191; c++) {
header->data.push_back(get_next_byte(tape));
header->data.push_back(get_next_byte(serialiser));
}
const uint8_t parity_byte = get_parity_byte();
header->parity_was_valid = get_next_byte(tape) == parity_byte;
header->parity_was_valid = get_next_byte(serialiser) == parity_byte;
// parse if this is not pure data
if(header->type != Header::DataBlock) {
@ -125,20 +128,20 @@ void Header::serialise(uint8_t *target, [[maybe_unused]] uint16_t length) {
std::memcpy(&target[1], data.data(), 191);
}
std::unique_ptr<Data> Parser::get_next_data_body(const std::shared_ptr<Storage::Tape::Tape> &tape, bool is_original) {
std::unique_ptr<Data> Parser::get_next_data_body(Storage::Tape::TapeSerialiser &serialiser, bool is_original) {
auto data = std::make_unique<Data>();
reset_error_flag();
// find and proceed beyond lead-in tone to the next landing zone
proceed_to_symbol(tape, SymbolType::LeadIn);
proceed_to_landing_zone(tape, is_original);
proceed_to_symbol(serialiser, SymbolType::LeadIn);
proceed_to_landing_zone(serialiser, is_original);
reset_parity_byte();
// accumulate until the next non-word marker is hit
while(!tape->is_at_end()) {
const SymbolType start_symbol = get_next_symbol(tape);
while(!serialiser.is_at_end()) {
const SymbolType start_symbol = get_next_symbol(serialiser);
if(start_symbol != SymbolType::Word) break;
data->data.push_back(get_next_byte_contents(tape));
data->data.push_back(get_next_byte_contents(serialiser));
}
// the above has reead the parity byte to the end of the data; if it matched the calculated parity it'll now be zero
@ -154,11 +157,11 @@ std::unique_ptr<Data> Parser::get_next_data_body(const std::shared_ptr<Storage::
/*!
Finds and completes the next landing zone.
*/
void Parser::proceed_to_landing_zone(const std::shared_ptr<Storage::Tape::Tape> &tape, bool is_original) {
void Parser::proceed_to_landing_zone(Storage::Tape::TapeSerialiser &serialiser, bool is_original) {
uint8_t landing_zone[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
while(!tape->is_at_end()) {
while(!serialiser.is_at_end()) {
memmove(landing_zone, &landing_zone[1], sizeof(uint8_t) * 8);
landing_zone[8] = get_next_byte(tape);
landing_zone[8] = get_next_byte(serialiser);
bool is_landing_zone = true;
for(int c = 0; c < 9; c++) {
@ -174,8 +177,8 @@ void Parser::proceed_to_landing_zone(const std::shared_ptr<Storage::Tape::Tape>
/*!
Swallows the next byte; sets the error flag if it is not equal to @c value.
*/
void Parser::expect_byte(const std::shared_ptr<Storage::Tape::Tape> &tape, uint8_t value) {
const uint8_t next_byte = get_next_byte(tape);
void Parser::expect_byte(Storage::Tape::TapeSerialiser &serialiser, uint8_t value) {
const uint8_t next_byte = get_next_byte(serialiser);
if(next_byte != value) set_error_flag();
}
@ -186,9 +189,9 @@ void Parser::add_parity_byte(uint8_t byte) { parity_byte_ ^= byte; }
/*!
Proceeds to the next word marker then returns the result of @c get_next_byte_contents.
*/
uint8_t Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape) {
proceed_to_symbol(tape, SymbolType::Word);
return get_next_byte_contents(tape);
uint8_t Parser::get_next_byte(Storage::Tape::TapeSerialiser &serialiser) {
proceed_to_symbol(serialiser, SymbolType::Word);
return get_next_byte_contents(serialiser);
}
/*!
@ -196,11 +199,11 @@ uint8_t Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape)
Returns a byte composed of the first eight of those as bits; sets the error flag if any symbol is not
::One and not ::Zero, or if the ninth bit is not equal to the odd parity of the other eight.
*/
uint8_t Parser::get_next_byte_contents(const std::shared_ptr<Storage::Tape::Tape> &tape) {
uint8_t Parser::get_next_byte_contents(Storage::Tape::TapeSerialiser &serialiser) {
int byte_plus_parity = 0;
int c = 9;
while(c--) {
const SymbolType next_symbol = get_next_symbol(tape);
const SymbolType next_symbol = get_next_symbol(serialiser);
if((next_symbol != SymbolType::One) && (next_symbol != SymbolType::Zero)) set_error_flag();
byte_plus_parity = (byte_plus_parity >> 1) | (((next_symbol == SymbolType::One) ? 1 : 0) << 8);
}
@ -219,9 +222,9 @@ uint8_t Parser::get_next_byte_contents(const std::shared_ptr<Storage::Tape::Tape
/*!
Returns the result of two consecutive @c get_next_byte calls, arranged in little-endian format.
*/
uint16_t Parser::get_next_short(const std::shared_ptr<Storage::Tape::Tape> &tape) {
uint16_t value = get_next_byte(tape);
value |= get_next_byte(tape) << 8;
uint16_t Parser::get_next_short(Storage::Tape::TapeSerialiser &serialiser) {
uint16_t value = get_next_byte(serialiser);
value |= get_next_byte(serialiser) << 8;
return value;
}

View File

@ -61,13 +61,13 @@ public:
Advances to the next block on the tape, treating it as a header, then consumes, parses, and returns it.
Returns @c nullptr if any wave-encoding level errors are encountered.
*/
std::unique_ptr<Header> get_next_header(const std::shared_ptr<Storage::Tape::Tape> &tape);
std::unique_ptr<Header> get_next_header(Storage::Tape::TapeSerialiser &);
/*!
Advances to the next block on the tape, treating it as data, then consumes, parses, and returns it.
Returns @c nullptr if any wave-encoding level errors are encountered.
*/
std::unique_ptr<Data> get_next_data(const std::shared_ptr<Storage::Tape::Tape> &tape);
std::unique_ptr<Data> get_next_data(Storage::Tape::TapeSerialiser &);
private:
/*!
@ -75,20 +75,21 @@ private:
including setting the duplicate_matched flag.
*/
template<class ObjectType>
std::unique_ptr<ObjectType> duplicate_match(std::unique_ptr<ObjectType> first_copy, std::unique_ptr<ObjectType> second_copy);
std::unique_ptr<ObjectType> duplicate_match(
std::unique_ptr<ObjectType> first_copy, std::unique_ptr<ObjectType> second_copy);
std::unique_ptr<Header> get_next_header_body(const std::shared_ptr<Storage::Tape::Tape> &tape, bool is_original);
std::unique_ptr<Data> get_next_data_body(const std::shared_ptr<Storage::Tape::Tape> &tape, bool is_original);
std::unique_ptr<Header> get_next_header_body(Storage::Tape::TapeSerialiser &, bool is_original);
std::unique_ptr<Data> get_next_data_body(Storage::Tape::TapeSerialiser &, bool is_original);
/*!
Finds and completes the next landing zone.
*/
void proceed_to_landing_zone(const std::shared_ptr<Storage::Tape::Tape> &tape, bool is_original);
void proceed_to_landing_zone(Storage::Tape::TapeSerialiser &, bool is_original);
/*!
Swallows the next byte; sets the error flag if it is not equal to @c value.
*/
void expect_byte(const std::shared_ptr<Storage::Tape::Tape> &tape, uint8_t value);
void expect_byte(Storage::Tape::TapeSerialiser &, uint8_t value);
uint8_t parity_byte_ = 0;
void reset_parity_byte();
@ -98,19 +99,19 @@ private:
/*!
Proceeds to the next word marker then returns the result of @c get_next_byte_contents.
*/
uint8_t get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape);
uint8_t get_next_byte(Storage::Tape::TapeSerialiser &);
/*!
Reads the next nine symbols and applies a binary test to each to differentiate between ::One and not-::One.
Returns a byte composed of the first eight of those as bits; sets the error flag if any symbol is not
::One and not ::Zero, or if the ninth bit is not equal to the odd parity of the other eight.
*/
uint8_t get_next_byte_contents(const std::shared_ptr<Storage::Tape::Tape> &tape);
uint8_t get_next_byte_contents(Storage::Tape::TapeSerialiser &);
/*!
Returns the result of two consecutive @c get_next_byte calls, arranged in little-endian format.
*/
uint16_t get_next_short(const std::shared_ptr<Storage::Tape::Tape> &tape);
uint16_t get_next_short(Storage::Tape::TapeSerialiser &);
/*!
Per the contract with Analyser::Static::TapeParser; sums time across pulses. If this pulse

View File

@ -25,7 +25,7 @@ std::unique_ptr<Parser::FileSpeed> Parser::find_header(Storage::Tape::BinaryTape
float low = std::numeric_limits<float>::max();
float high = std::numeric_limits<float>::min();
int samples = 0;
while(!tape_player.tape()->is_at_end()) {
while(!tape_player.is_at_end()) {
float next_length = 0.0f;
do {
next_length += float(tape_player.get_cycles_until_next_event()) / float(tape_player.get_input_clock_rate());
@ -43,14 +43,14 @@ std::unique_ptr<Parser::FileSpeed> Parser::find_header(Storage::Tape::BinaryTape
if(samples == 1111*2) break; // Cycles are read, not half-cycles.
}
if(tape_player.tape()->is_at_end()) return nullptr;
if(tape_player.is_at_end()) return nullptr;
/*
"The next 256 cycles are then read (1B34H) and averaged to determine the cassette HI cycle length."
*/
float total_length = 0.0f;
samples = 512;
while(!tape_player.tape()->is_at_end()) {
while(!tape_player.is_at_end()) {
total_length += float(tape_player.get_cycles_until_next_event()) / float(tape_player.get_input_clock_rate());
if(tape_player.input() != last_level) {
samples--;
@ -60,7 +60,7 @@ std::unique_ptr<Parser::FileSpeed> Parser::find_header(Storage::Tape::BinaryTape
tape_player.run_for_input_pulse();
}
if(tape_player.tape()->is_at_end()) return nullptr;
if(tape_player.is_at_end()) return nullptr;
/*
This figure is multiplied by 1.5 and placed in LOWLIM where it defines the minimum acceptable length
@ -103,7 +103,7 @@ int Parser::get_byte(const FileSpeed &speed, Storage::Tape::BinaryTapePlayer &ta
*/
const float minimum_start_bit_duration = float(speed.minimum_start_bit_duration) * 0.00001145f * 0.5f;
int input = 0;
while(!tape_player.tape()->is_at_end()) {
while(!tape_player.is_at_end()) {
// Find next transition.
bool level = tape_player.input();
float duration = 0.0;
@ -132,11 +132,11 @@ int Parser::get_byte(const FileSpeed &speed, Storage::Tape::BinaryTapePlayer &ta
);
int bits_left = 8;
bool level = tape_player.input();
while(!tape_player.tape()->is_at_end() && bits_left--) {
while(!tape_player.is_at_end() && bits_left--) {
// Count number of transitions within cycles_per_window.
int transitions = 0;
int cycles_remaining = cycles_per_window;
while(!tape_player.tape()->is_at_end() && cycles_remaining) {
while(!tape_player.is_at_end() && cycles_remaining) {
const int cycles_until_next_event = int(tape_player.get_cycles_until_next_event());
const int cycles_to_run_for = std::min(cycles_until_next_event, cycles_remaining);
@ -149,7 +149,7 @@ int Parser::get_byte(const FileSpeed &speed, Storage::Tape::BinaryTapePlayer &ta
}
}
if(tape_player.tape()->is_at_end()) return -1;
if(tape_player.is_at_end()) return -1;
int next_bit = 0;
switch(transitions) {
@ -170,7 +170,7 @@ int Parser::get_byte(const FileSpeed &speed, Storage::Tape::BinaryTapePlayer &ta
transition count two more."
*/
int required_transitions = 2 - (transitions&1);
while(!tape_player.tape()->is_at_end()) {
while(!tape_player.is_at_end()) {
tape_player.run_for_input_pulse();
if(level != tape_player.input()) {
level = tape_player.input();
@ -179,7 +179,7 @@ int Parser::get_byte(const FileSpeed &speed, Storage::Tape::BinaryTapePlayer &ta
}
}
if(tape_player.tape()->is_at_end()) return -1;
if(tape_player.is_at_end()) return -1;
}
return result;
}

View File

@ -10,27 +10,27 @@
using namespace Storage::Tape::Oric;
int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape, bool use_fast_encoding) {
int Parser::get_next_byte(Storage::Tape::TapeSerialiser &serialiser, bool use_fast_encoding) {
detection_mode_ = use_fast_encoding ? FastZero : SlowZero;
cycle_length_ = 0.0f;
int result = 0;
int bit_count = 0;
while(bit_count < 11 && !tape->is_at_end()) {
SymbolType symbol = get_next_symbol(tape);
while(bit_count < 11 && !serialiser.is_at_end()) {
SymbolType symbol = get_next_symbol(serialiser);
if(!bit_count && symbol != SymbolType::Zero) continue;
detection_mode_ = use_fast_encoding ? FastData : SlowData;
result |= ((symbol == SymbolType::One) ? 1 : 0) << bit_count;
bit_count++;
}
// TODO: check parity?
return tape->is_at_end() ? -1 : ((result >> 1)&0xff);
return serialiser.is_at_end() ? -1 : ((result >> 1)&0xff);
}
bool Parser::sync_and_get_encoding_speed(const std::shared_ptr<Storage::Tape::Tape> &tape) {
bool Parser::sync_and_get_encoding_speed(Storage::Tape::TapeSerialiser &serialiser) {
detection_mode_ = Sync;
while(!tape->is_at_end()) {
const SymbolType symbol = get_next_symbol(tape);
while(!serialiser.is_at_end()) {
const SymbolType symbol = get_next_symbol(serialiser);
switch(symbol) {
case SymbolType::FoundSlow: return false;
case SymbolType::FoundFast: return true;

View File

@ -25,8 +25,8 @@ enum class SymbolType {
class Parser: public Storage::Tape::PulseClassificationParser<WaveType, SymbolType> {
public:
int get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape, bool use_fast_encoding);
bool sync_and_get_encoding_speed(const std::shared_ptr<Storage::Tape::Tape> &tape);
int get_next_byte(Storage::Tape::TapeSerialiser &, bool use_fast_encoding);
bool sync_and_get_encoding_speed(Storage::Tape::TapeSerialiser &);
private:
void process_pulse(const Storage::Tape::Pulse &pulse) override;

View File

@ -162,22 +162,22 @@ void Parser::inspect_waves(const std::vector<Storage::Tape::ZXSpectrum::WaveType
}
}
std::optional<Block> Parser::find_block(const std::shared_ptr<Storage::Tape::Tape> &tape) {
std::optional<Block> Parser::find_block(Storage::Tape::TapeSerialiser &serialiser) {
// Decide whether to kick off a speed detection phase.
if(should_detect_speed()) {
speed_phase_ = SpeedDetectionPhase::WaitingForGap;
}
// Find pilot tone.
proceed_to_symbol(tape, SymbolType::Pilot);
if(is_at_end(tape)) return std::nullopt;
proceed_to_symbol(serialiser, SymbolType::Pilot);
if(is_at_end(serialiser)) return std::nullopt;
// Find sync bit.
proceed_to_symbol(tape, SymbolType::Zero);
if(is_at_end(tape)) return std::nullopt;
proceed_to_symbol(serialiser, SymbolType::Zero);
if(is_at_end(serialiser)) return std::nullopt;
// Read marker byte.
const auto type = get_byte(tape);
const auto type = get_byte(serialiser);
if(!type) return std::nullopt;
// That succeeded.
@ -187,11 +187,11 @@ std::optional<Block> Parser::find_block(const std::shared_ptr<Storage::Tape::Tap
return block;
}
std::vector<uint8_t> Parser::get_block_body(const std::shared_ptr<Storage::Tape::Tape> &tape) {
std::vector<uint8_t> Parser::get_block_body(Storage::Tape::TapeSerialiser &serialiser) {
std::vector<uint8_t> result;
while(true) {
const auto next_byte = get_byte(tape);
const auto next_byte = get_byte(serialiser);
if(!next_byte) break;
result.push_back(*next_byte);
}
@ -203,10 +203,10 @@ void Parser::seed_checksum(uint8_t value) {
checksum_ = value;
}
std::optional<uint8_t> Parser::get_byte(const std::shared_ptr<Storage::Tape::Tape> &tape) {
std::optional<uint8_t> Parser::get_byte(Storage::Tape::TapeSerialiser &serialiser) {
uint8_t result = 0;
for(int c = 0; c < 8; c++) {
const SymbolType symbol = get_next_symbol(tape);
const SymbolType symbol = get_next_symbol(serialiser);
if(symbol != SymbolType::One && symbol != SymbolType::Zero) return std::nullopt;
result = uint8_t((result << 1) | (symbol == SymbolType::One));
}

View File

@ -72,19 +72,19 @@ public:
in Spectrum-world this seems to be called the flag byte. This call can therefore be followed up with one
of the get_ methods.
*/
std::optional<Block> find_block(const std::shared_ptr<Storage::Tape::Tape> &tape);
std::optional<Block> find_block(Storage::Tape::TapeSerialiser &);
/*!
Reads the contents of the rest of this block, until the next gap.
*/
std::vector<uint8_t> get_block_body(const std::shared_ptr<Storage::Tape::Tape> &tape);
std::vector<uint8_t> get_block_body(Storage::Tape::TapeSerialiser &);
/*!
Reads a single byte from the tape, if there is one left, updating the internal checksum.
The checksum is computed as an exclusive OR of all bytes read.
*/
std::optional<uint8_t> get_byte(const std::shared_ptr<Storage::Tape::Tape> &tape);
std::optional<uint8_t> get_byte(Storage::Tape::TapeSerialiser &);
/*!
Seeds the internal checksum.

View File

@ -27,11 +27,11 @@ public:
Asks the parser to continue taking pulses from the tape until either the subclass next declares a symbol
or the tape runs out, returning the most-recently declared symbol.
*/
SymbolType get_next_symbol(const std::shared_ptr<Storage::Tape::Tape> &tape) {
while(!has_next_symbol_ && !tape->is_at_end()) {
process_pulse(tape->next_pulse());
SymbolType get_next_symbol(Storage::Tape::TapeSerialiser &serialiser) {
while(!has_next_symbol_ && !serialiser.is_at_end()) {
process_pulse(serialiser.next_pulse());
}
if(!has_next_symbol_ && tape->is_at_end()) mark_end();
if(!has_next_symbol_ && serialiser.is_at_end()) mark_end();
has_next_symbol_ = false;
return next_symbol_;
}
@ -50,17 +50,17 @@ public:
/*!
@returns `true` if there is no data left on the tape and the WaveType queue has been exhausted; `false` otherwise.
*/
bool is_at_end(const std::shared_ptr<Storage::Tape::Tape> &tape) {
return tape->is_at_end() && !has_next_symbol_;
bool is_at_end(Storage::Tape::TapeSerialiser &serialiser) {
return serialiser.is_at_end() && !has_next_symbol_;
}
/*!
Swallows symbols until it reaches the first instance of the required symbol, swallows that
and returns.
*/
void proceed_to_symbol(const std::shared_ptr<Storage::Tape::Tape> &tape, SymbolType required_symbol) {
while(!is_at_end(tape)) {
const SymbolType symbol = get_next_symbol(tape);
void proceed_to_symbol(Storage::Tape::TapeSerialiser &serialiser, SymbolType required_symbol) {
while(!is_at_end(serialiser)) {
const SymbolType symbol = get_next_symbol(serialiser);
if(symbol == required_symbol) return;
}
}

View File

@ -89,13 +89,13 @@ void Parser::inspect_waves(const std::vector<WaveType> &waves) {
}
}
int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape) {
int Parser::get_next_byte(Storage::Tape::TapeSerialiser &serialiser) {
int c = 8;
int result = 0;
while(c) {
if(is_at_end(tape)) return -1;
if(is_at_end(serialiser)) return -1;
SymbolType symbol = get_next_symbol(tape);
SymbolType symbol = get_next_symbol(serialiser);
if(symbol != SymbolType::One && symbol != SymbolType::Zero) {
if(c == 8) continue;
return_symbol(symbol);
@ -108,30 +108,30 @@ int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape) {
return result;
}
std::shared_ptr<std::vector<uint8_t>> Parser::get_next_file_data(const std::shared_ptr<Storage::Tape::Tape> &tape) {
if(is_at_end(tape)) return nullptr;
SymbolType symbol = get_next_symbol(tape);
std::shared_ptr<std::vector<uint8_t>> Parser::get_next_file_data(Storage::Tape::TapeSerialiser &serialiser) {
if(is_at_end(serialiser)) return nullptr;
SymbolType symbol = get_next_symbol(serialiser);
if(symbol != SymbolType::FileGap) {
return nullptr;
}
while((symbol == SymbolType::FileGap || symbol == SymbolType::Unrecognised) && !is_at_end(tape)) {
symbol = get_next_symbol(tape);
while((symbol == SymbolType::FileGap || symbol == SymbolType::Unrecognised) && !is_at_end(serialiser)) {
symbol = get_next_symbol(serialiser);
}
if(is_at_end(tape)) return nullptr;
if(is_at_end(serialiser)) return nullptr;
return_symbol(symbol);
auto result = std::make_shared<std::vector<uint8_t>>();
int byte;
while(!is_at_end(tape)) {
byte = get_next_byte(tape);
while(!is_at_end(serialiser)) {
byte = get_next_byte(serialiser);
if(byte == -1) return result;
result->push_back(uint8_t(byte));
}
return result;
}
std::shared_ptr<Storage::Data::ZX8081::File> Parser::get_next_file(const std::shared_ptr<Storage::Tape::Tape> &tape) {
std::shared_ptr<std::vector<uint8_t>> file_data = get_next_file_data(tape);
std::shared_ptr<Storage::Data::ZX8081::File> Parser::get_next_file(Storage::Tape::TapeSerialiser &serialiser) {
std::shared_ptr<std::vector<uint8_t>> file_data = get_next_file_data(serialiser);
if(!file_data) {
return nullptr;
}

View File

@ -33,14 +33,14 @@ public:
/*!
Reads and combines the next eight bits. Returns -1 if any errors are encountered.
*/
int get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &);
int get_next_byte(Storage::Tape::TapeSerialiser &);
/*!
Waits for a long gap, reads all the bytes between that and the next long gap, then
attempts to parse those as a valid ZX80 or ZX81 file. If no file is found,
returns nullptr.
*/
std::shared_ptr<Storage::Data::ZX8081::File> get_next_file(const std::shared_ptr<Storage::Tape::Tape> &);
std::shared_ptr<Storage::Data::ZX8081::File> get_next_file(Storage::Tape::TapeSerialiser &);
private:
bool pulse_was_high_;
@ -51,7 +51,7 @@ private:
void mark_end() override;
void inspect_waves(const std::vector<WaveType> &waves) override;
std::shared_ptr<std::vector<uint8_t>> get_next_file_data(const std::shared_ptr<Storage::Tape::Tape> &);
std::shared_ptr<std::vector<uint8_t>> get_next_file_data(Storage::Tape::TapeSerialiser &);
};
}

View File

@ -23,7 +23,7 @@ namespace Storage::Tape {
anything there, and otherwise calls @c push_next_pulses() which is
virtual, giving subclasses a chance to provide the next batch of pulses.
*/
class PulseQueuedSerialiser: public TapeSerialiser {
class PulseQueuedSerialiser: public FormatSerialiser {
public:
void emplace_back(Pulse::Type, Time);
void push_back(Pulse);

View File

@ -16,11 +16,15 @@ TapePlayer::TapePlayer(const int input_clock_rate) :
TimedEventLoop(input_clock_rate)
{}
Tape::Tape(TapeSerialiser &serialiser) : serialiser_(serialiser) {}
TapeSerialiser::TapeSerialiser(std::unique_ptr<FormatSerialiser> &&serialiser) : serialiser_(serialiser) {}
std::unique_ptr<TapeSerialiser> Tape::serialiser() const {
return std::make_unique<TapeSerialiser>(format_serialiser());
}
// MARK: - Seeking
void Storage::Tape::Tape::seek(const Time seek_time) {
void TapeSerialiser::seek(const Time seek_time) {
Time next_time(0);
reset();
while(next_time <= seek_time) {
@ -29,7 +33,7 @@ void Storage::Tape::Tape::seek(const Time seek_time) {
}
}
Storage::Time Tape::current_time() {
Storage::Time TapeSerialiser::current_time() {
Time time(0);
uint64_t steps = offset();
reset();
@ -40,22 +44,22 @@ Storage::Time Tape::current_time() {
return time;
}
void Storage::Tape::Tape::reset() {
void TapeSerialiser::reset() {
offset_ = 0;
serialiser_.reset();
}
Pulse Tape::next_pulse() {
pulse_ = serialiser_.next_pulse();
Pulse TapeSerialiser::next_pulse() {
pulse_ = serialiser_->next_pulse();
offset_++;
return pulse_;
}
uint64_t Tape::offset() const {
uint64_t TapeSerialiser::offset() const {
return offset_;
}
void Tape::set_offset(uint64_t offset) {
void TapeSerialiser::set_offset(uint64_t offset) {
if(offset == offset_) return;
if(offset < offset_) {
reset();
@ -64,26 +68,30 @@ void Tape::set_offset(uint64_t offset) {
while(offset--) next_pulse();
}
bool Tape::is_at_end() const {
return serialiser_.is_at_end();
bool TapeSerialiser::is_at_end() const {
return serialiser_->is_at_end();
}
// MARK: - Player
ClockingHint::Preference TapePlayer::preferred_clocking() const {
return (!tape_ || tape_->is_at_end()) ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime;
return (!tape_ || serialiser_->is_at_end()) ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime;
}
void TapePlayer::set_tape(std::shared_ptr<Storage::Tape::Tape> tape) {
tape_ = tape;
serialiser_ = tape->serialiser();
reset_timer();
next_pulse();
update_clocking_observer();
}
std::shared_ptr<Storage::Tape::Tape> TapePlayer::tape() {
return tape_;
bool TapePlayer::is_at_end() const {
return serialiser_->is_at_end();
}
TapeSerialiser *TapePlayer::serialiser() {
return serialiser_.get();
}
bool TapePlayer::has_tape() const {
@ -93,8 +101,8 @@ bool TapePlayer::has_tape() const {
void TapePlayer::next_pulse() {
// get the new pulse
if(tape_) {
current_pulse_ = tape_->next_pulse();
if(tape_->is_at_end()) update_clocking_observer();
current_pulse_ = serialiser_->next_pulse();
if(serialiser_->is_at_end()) update_clocking_observer();
} else {
current_pulse_.length.length = 1;
current_pulse_.length.clock_rate = 1;

View File

@ -33,22 +33,18 @@ struct Pulse {
/*!
Provdes the means for tape serialiserion.
*/
class TapeSerialiser {
class FormatSerialiser {
public:
virtual ~FormatSerialiser() = default;
virtual Pulse next_pulse() = 0;
virtual void reset() = 0;
virtual bool is_at_end() const = 0;
};
/*!
Models a tape as a sequence of pulses, each pulse being of arbitrary length and described
by their relationship with zero:
- high pulses exit from zero upward before returning to it;
- low pulses exit from zero downward before returning to it;
- zero pulses run along zero.
*/
class Tape {
class TapeSerialiser {
public:
TapeSerialiser(std::unique_ptr<FormatSerialiser> &&);
virtual ~TapeSerialiser() = default;
/*!
If at the start of the tape returns the first stored pulse. Otherwise advances past
@ -86,13 +82,25 @@ public:
*/
void seek(Time);
Tape(TapeSerialiser &);
virtual ~Tape() = default;
private:
uint64_t offset_;
uint64_t offset_{};
Pulse pulse_;
TapeSerialiser &serialiser_;
std::unique_ptr<FormatSerialiser> &serialiser_;
};
/*!
Models a tape as a sequence of pulses, each pulse being of arbitrary length and described
by their relationship with zero:
- high pulses exit from zero upward before returning to it;
- low pulses exit from zero downward before returning to it;
- zero pulses run along zero.
*/
class Tape {
public:
std::unique_ptr<TapeSerialiser> serialiser() const;
protected:
virtual std::unique_ptr<FormatSerialiser> format_serialiser() const = 0;
};
/*!
@ -109,7 +117,8 @@ public:
void set_tape(std::shared_ptr<Storage::Tape::Tape>);
bool has_tape() const;
std::shared_ptr<Storage::Tape::Tape> tape();
bool is_at_end() const;
TapeSerialiser *serialiser();
void run_for(Cycles);
void run_for_input_pulse();
@ -127,6 +136,7 @@ private:
inline void next_pulse();
std::shared_ptr<Storage::Tape::Tape> tape_;
std::unique_ptr<TapeSerialiser> serialiser_;
Pulse current_pulse_;
};