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:
parent
2f546842a7
commit
58d3fdc1c2
@ -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()) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 &);
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 &);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 &);
|
||||
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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));
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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_;
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 &);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user