mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-13 00:25:26 +00:00
Factored out from the UEF implementation the concept of being a tape that has a queue of pending pulses and manages that queue.
This commit is contained in:
@@ -54,6 +54,7 @@
|
|||||||
4B3BF5B01F146265005B6C36 /* CSW.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BF5AE1F146264005B6C36 /* CSW.cpp */; };
|
4B3BF5B01F146265005B6C36 /* CSW.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BF5AE1F146264005B6C36 /* CSW.cpp */; };
|
||||||
4B3F1B461E0388D200DB26EE /* PCMPatchedTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F1B441E0388D200DB26EE /* PCMPatchedTrack.cpp */; };
|
4B3F1B461E0388D200DB26EE /* PCMPatchedTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F1B441E0388D200DB26EE /* PCMPatchedTrack.cpp */; };
|
||||||
4B448E811F1C45A00009ABD6 /* TZX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B448E7F1F1C45A00009ABD6 /* TZX.cpp */; };
|
4B448E811F1C45A00009ABD6 /* TZX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B448E7F1F1C45A00009ABD6 /* TZX.cpp */; };
|
||||||
|
4B448E841F1C4C480009ABD6 /* PulseQueuedTape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B448E821F1C4C480009ABD6 /* PulseQueuedTape.cpp */; };
|
||||||
4B44EBF51DC987AF00A7820C /* AllSuiteA.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */; };
|
4B44EBF51DC987AF00A7820C /* AllSuiteA.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */; };
|
||||||
4B44EBF71DC9883B00A7820C /* 6502_functional_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */; };
|
4B44EBF71DC9883B00A7820C /* 6502_functional_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */; };
|
||||||
4B44EBF91DC9898E00A7820C /* BCDTEST_beeb in Resources */ = {isa = PBXBuildFile; fileRef = 4B44EBF81DC9898E00A7820C /* BCDTEST_beeb */; };
|
4B44EBF91DC9898E00A7820C /* BCDTEST_beeb in Resources */ = {isa = PBXBuildFile; fileRef = 4B44EBF81DC9898E00A7820C /* BCDTEST_beeb */; };
|
||||||
@@ -547,6 +548,8 @@
|
|||||||
4B3F1B451E0388D200DB26EE /* PCMPatchedTrack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCMPatchedTrack.hpp; sourceTree = "<group>"; };
|
4B3F1B451E0388D200DB26EE /* PCMPatchedTrack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCMPatchedTrack.hpp; sourceTree = "<group>"; };
|
||||||
4B448E7F1F1C45A00009ABD6 /* TZX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TZX.cpp; sourceTree = "<group>"; };
|
4B448E7F1F1C45A00009ABD6 /* TZX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TZX.cpp; sourceTree = "<group>"; };
|
||||||
4B448E801F1C45A00009ABD6 /* TZX.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TZX.hpp; sourceTree = "<group>"; };
|
4B448E801F1C45A00009ABD6 /* TZX.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TZX.hpp; sourceTree = "<group>"; };
|
||||||
|
4B448E821F1C4C480009ABD6 /* PulseQueuedTape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PulseQueuedTape.cpp; sourceTree = "<group>"; };
|
||||||
|
4B448E831F1C4C480009ABD6 /* PulseQueuedTape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PulseQueuedTape.hpp; sourceTree = "<group>"; };
|
||||||
4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = AllSuiteA.bin; path = AllSuiteA/AllSuiteA.bin; sourceTree = "<group>"; };
|
4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = AllSuiteA.bin; path = AllSuiteA/AllSuiteA.bin; sourceTree = "<group>"; };
|
||||||
4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = 6502_functional_test.bin; path = "Klaus Dormann/6502_functional_test.bin"; sourceTree = "<group>"; };
|
4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = 6502_functional_test.bin; path = "Klaus Dormann/6502_functional_test.bin"; sourceTree = "<group>"; };
|
||||||
4B44EBF81DC9898E00A7820C /* BCDTEST_beeb */ = {isa = PBXFileReference; lastKnownFileType = file; name = BCDTEST_beeb; path = BCDTest/BCDTEST_beeb; sourceTree = "<group>"; };
|
4B44EBF81DC9898E00A7820C /* BCDTEST_beeb */ = {isa = PBXFileReference; lastKnownFileType = file; name = BCDTEST_beeb; path = BCDTest/BCDTEST_beeb; sourceTree = "<group>"; };
|
||||||
@@ -1391,6 +1394,8 @@
|
|||||||
children = (
|
children = (
|
||||||
4B69FB411C4D941400B5F0AA /* Formats */,
|
4B69FB411C4D941400B5F0AA /* Formats */,
|
||||||
4B8805F11DCFC9A2003085B1 /* Parsers */,
|
4B8805F11DCFC9A2003085B1 /* Parsers */,
|
||||||
|
4B448E821F1C4C480009ABD6 /* PulseQueuedTape.cpp */,
|
||||||
|
4B448E831F1C4C480009ABD6 /* PulseQueuedTape.hpp */,
|
||||||
4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */,
|
4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */,
|
||||||
4B69FB3C1C4D908A00B5F0AA /* Tape.hpp */,
|
4B69FB3C1C4D908A00B5F0AA /* Tape.hpp */,
|
||||||
);
|
);
|
||||||
@@ -2580,6 +2585,7 @@
|
|||||||
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */,
|
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */,
|
||||||
4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */,
|
4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */,
|
||||||
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */,
|
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */,
|
||||||
|
4B448E841F1C4C480009ABD6 /* PulseQueuedTape.cpp in Sources */,
|
||||||
4BD4A8CD1E077E8A0020D856 /* PCMSegment.cpp in Sources */,
|
4BD4A8CD1E077E8A0020D856 /* PCMSegment.cpp in Sources */,
|
||||||
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */,
|
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */,
|
||||||
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
|
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
|
||||||
|
@@ -14,8 +14,7 @@
|
|||||||
|
|
||||||
#pragma mark - ZLib extensions
|
#pragma mark - ZLib extensions
|
||||||
|
|
||||||
static float gzgetfloat(gzFile file)
|
static float gzgetfloat(gzFile file) {
|
||||||
{
|
|
||||||
uint8_t bytes[4];
|
uint8_t bytes[4];
|
||||||
gzread(file, bytes, 4);
|
gzread(file, bytes, 4);
|
||||||
|
|
||||||
@@ -42,30 +41,26 @@ static float gzgetfloat(gzFile file)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t gzget8(gzFile file)
|
static uint8_t gzget8(gzFile file) {
|
||||||
{
|
|
||||||
// This is a workaround for gzgetc, which seems to be broken in ZLib 1.2.8.
|
// This is a workaround for gzgetc, which seems to be broken in ZLib 1.2.8.
|
||||||
uint8_t result;
|
uint8_t result;
|
||||||
gzread(file, &result, 1);
|
gzread(file, &result, 1);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gzget16(gzFile file)
|
static int gzget16(gzFile file) {
|
||||||
{
|
|
||||||
uint8_t bytes[2];
|
uint8_t bytes[2];
|
||||||
gzread(file, bytes, 2);
|
gzread(file, bytes, 2);
|
||||||
return bytes[0] | (bytes[1] << 8);
|
return bytes[0] | (bytes[1] << 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gzget24(gzFile file)
|
static int gzget24(gzFile file) {
|
||||||
{
|
|
||||||
uint8_t bytes[3];
|
uint8_t bytes[3];
|
||||||
gzread(file, bytes, 3);
|
gzread(file, bytes, 3);
|
||||||
return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16);
|
return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gzget32(gzFile file)
|
static int gzget32(gzFile file) {
|
||||||
{
|
|
||||||
uint8_t bytes[4];
|
uint8_t bytes[4];
|
||||||
gzread(file, bytes, 4);
|
gzread(file, bytes, 4);
|
||||||
return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
|
return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
|
||||||
@@ -75,28 +70,21 @@ using namespace Storage::Tape;
|
|||||||
|
|
||||||
UEF::UEF(const char *file_name) :
|
UEF::UEF(const char *file_name) :
|
||||||
time_base_(1200),
|
time_base_(1200),
|
||||||
is_at_end_(false),
|
is_300_baud_(false) {
|
||||||
pulse_pointer_(0),
|
|
||||||
is_300_baud_(false)
|
|
||||||
{
|
|
||||||
file_ = gzopen(file_name, "rb");
|
file_ = gzopen(file_name, "rb");
|
||||||
|
|
||||||
char identifier[10];
|
char identifier[10];
|
||||||
int bytes_read = gzread(file_, identifier, 10);
|
int bytes_read = gzread(file_, identifier, 10);
|
||||||
if(bytes_read < 10 || strcmp(identifier, "UEF File!"))
|
if(bytes_read < 10 || strcmp(identifier, "UEF File!")) {
|
||||||
{
|
|
||||||
throw ErrorNotUEF;
|
throw ErrorNotUEF;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t version[2];
|
uint8_t version[2];
|
||||||
gzread(file_, version, 2);
|
gzread(file_, version, 2);
|
||||||
|
|
||||||
if(version[1] > 0 || version[0] > 10)
|
if(version[1] > 0 || version[0] > 10) {
|
||||||
{
|
|
||||||
throw ErrorNotUEF;
|
throw ErrorNotUEF;
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_next_tape_chunk();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UEF::~UEF()
|
UEF::~UEF()
|
||||||
@@ -106,47 +94,16 @@ UEF::~UEF()
|
|||||||
|
|
||||||
#pragma mark - Public methods
|
#pragma mark - Public methods
|
||||||
|
|
||||||
void UEF::virtual_reset()
|
void UEF::virtual_reset() {
|
||||||
{
|
|
||||||
gzseek(file_, 12, SEEK_SET);
|
gzseek(file_, 12, SEEK_SET);
|
||||||
is_at_end_ = false;
|
set_is_at_end(false);
|
||||||
parse_next_tape_chunk();
|
clear();
|
||||||
}
|
|
||||||
|
|
||||||
bool UEF::is_at_end()
|
|
||||||
{
|
|
||||||
return is_at_end_;
|
|
||||||
}
|
|
||||||
|
|
||||||
Storage::Tape::Tape::Pulse UEF::virtual_get_next_pulse()
|
|
||||||
{
|
|
||||||
Pulse next_pulse;
|
|
||||||
|
|
||||||
if(is_at_end_)
|
|
||||||
{
|
|
||||||
next_pulse.type = Pulse::Zero;
|
|
||||||
next_pulse.length.length = time_base_ * 4;
|
|
||||||
next_pulse.length.clock_rate = time_base_ * 4;
|
|
||||||
return next_pulse;
|
|
||||||
}
|
|
||||||
|
|
||||||
next_pulse = queued_pulses_[pulse_pointer_];
|
|
||||||
pulse_pointer_++;
|
|
||||||
if(pulse_pointer_ == queued_pulses_.size())
|
|
||||||
{
|
|
||||||
queued_pulses_.clear();
|
|
||||||
pulse_pointer_ = 0;
|
|
||||||
parse_next_tape_chunk();
|
|
||||||
}
|
|
||||||
return next_pulse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Chunk navigator
|
#pragma mark - Chunk navigator
|
||||||
|
|
||||||
void UEF::parse_next_tape_chunk()
|
void UEF::get_next_pulses() {
|
||||||
{
|
while(empty()) {
|
||||||
while(queued_pulses_.empty())
|
|
||||||
{
|
|
||||||
// read chunk details
|
// read chunk details
|
||||||
uint16_t chunk_id = (uint16_t)gzget16(file_);
|
uint16_t chunk_id = (uint16_t)gzget16(file_);
|
||||||
uint32_t chunk_length = (uint32_t)gzget32(file_);
|
uint32_t chunk_length = (uint32_t)gzget32(file_);
|
||||||
@@ -154,14 +111,12 @@ void UEF::parse_next_tape_chunk()
|
|||||||
// figure out where the next chunk will start
|
// figure out where the next chunk will start
|
||||||
z_off_t start_of_next_chunk = gztell(file_) + chunk_length;
|
z_off_t start_of_next_chunk = gztell(file_) + chunk_length;
|
||||||
|
|
||||||
if(gzeof(file_))
|
if(gzeof(file_)) {
|
||||||
{
|
set_is_at_end(true);
|
||||||
is_at_end_ = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(chunk_id)
|
switch(chunk_id) {
|
||||||
{
|
|
||||||
case 0x0100: queue_implicit_bit_pattern(chunk_length); break;
|
case 0x0100: queue_implicit_bit_pattern(chunk_length); break;
|
||||||
case 0x0102: queue_explicit_bit_pattern(chunk_length); break;
|
case 0x0102: queue_explicit_bit_pattern(chunk_length); break;
|
||||||
case 0x0112: queue_integer_gap(); break;
|
case 0x0112: queue_integer_gap(); break;
|
||||||
@@ -173,16 +128,15 @@ void UEF::parse_next_tape_chunk()
|
|||||||
case 0x0114: queue_security_cycles(); break;
|
case 0x0114: queue_security_cycles(); break;
|
||||||
case 0x0104: queue_defined_data(chunk_length); break;
|
case 0x0104: queue_defined_data(chunk_length); break;
|
||||||
|
|
||||||
case 0x0113: // change of base rate
|
// change of base rate
|
||||||
{
|
case 0x0113: {
|
||||||
// TODO: something smarter than just converting this to an int
|
// TODO: something smarter than just converting this to an int
|
||||||
float new_time_base = gzgetfloat(file_);
|
float new_time_base = gzgetfloat(file_);
|
||||||
time_base_ = (unsigned int)roundf(new_time_base);
|
time_base_ = (unsigned int)roundf(new_time_base);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0117:
|
case 0x0117: {
|
||||||
{
|
|
||||||
int baud_rate = gzget16(file_);
|
int baud_rate = gzget16(file_);
|
||||||
is_300_baud_ = (baud_rate == 300);
|
is_300_baud_ = (baud_rate == 300);
|
||||||
}
|
}
|
||||||
@@ -199,16 +153,14 @@ void UEF::parse_next_tape_chunk()
|
|||||||
|
|
||||||
#pragma mark - Chunk parsers
|
#pragma mark - Chunk parsers
|
||||||
|
|
||||||
void UEF::queue_implicit_bit_pattern(uint32_t length)
|
void UEF::queue_implicit_bit_pattern(uint32_t length) {
|
||||||
{
|
|
||||||
while(length--)
|
while(length--)
|
||||||
{
|
{
|
||||||
queue_implicit_byte(gzget8(file_));
|
queue_implicit_byte(gzget8(file_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UEF::queue_explicit_bit_pattern(uint32_t length)
|
void UEF::queue_explicit_bit_pattern(uint32_t length) {
|
||||||
{
|
|
||||||
size_t length_in_bits = (length << 3) - (size_t)gzget8(file_);
|
size_t length_in_bits = (length << 3) - (size_t)gzget8(file_);
|
||||||
uint8_t current_byte = 0;
|
uint8_t current_byte = 0;
|
||||||
for(size_t bit = 0; bit < length_in_bits; bit++)
|
for(size_t bit = 0; bit < length_in_bits; bit++)
|
||||||
@@ -219,31 +171,27 @@ void UEF::queue_explicit_bit_pattern(uint32_t length)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UEF::queue_integer_gap()
|
void UEF::queue_integer_gap() {
|
||||||
{
|
|
||||||
Time duration;
|
Time duration;
|
||||||
duration.length = (unsigned int)gzget16(file_);
|
duration.length = (unsigned int)gzget16(file_);
|
||||||
duration.clock_rate = time_base_;
|
duration.clock_rate = time_base_;
|
||||||
queued_pulses_.emplace_back(Pulse::Zero, duration);
|
emplace_back(Pulse::Zero, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UEF::queue_floating_point_gap()
|
void UEF::queue_floating_point_gap() {
|
||||||
{
|
|
||||||
float length = gzgetfloat(file_);
|
float length = gzgetfloat(file_);
|
||||||
Time duration;
|
Time duration;
|
||||||
duration.length = (unsigned int)(length * 4000000);
|
duration.length = (unsigned int)(length * 4000000);
|
||||||
duration.clock_rate = 4000000;
|
duration.clock_rate = 4000000;
|
||||||
queued_pulses_.emplace_back(Pulse::Zero, duration);
|
emplace_back(Pulse::Zero, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UEF::queue_carrier_tone()
|
void UEF::queue_carrier_tone() {
|
||||||
{
|
|
||||||
unsigned int number_of_cycles = (unsigned int)gzget16(file_);
|
unsigned int number_of_cycles = (unsigned int)gzget16(file_);
|
||||||
while(number_of_cycles--) queue_bit(1);
|
while(number_of_cycles--) queue_bit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UEF::queue_carrier_tone_with_dummy()
|
void UEF::queue_carrier_tone_with_dummy() {
|
||||||
{
|
|
||||||
unsigned int pre_cycles = (unsigned int)gzget16(file_);
|
unsigned int pre_cycles = (unsigned int)gzget16(file_);
|
||||||
unsigned int post_cycles = (unsigned int)gzget16(file_);
|
unsigned int post_cycles = (unsigned int)gzget16(file_);
|
||||||
while(pre_cycles--) queue_bit(1);
|
while(pre_cycles--) queue_bit(1);
|
||||||
@@ -251,15 +199,13 @@ void UEF::queue_carrier_tone_with_dummy()
|
|||||||
while(post_cycles--) queue_bit(1);
|
while(post_cycles--) queue_bit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UEF::queue_security_cycles()
|
void UEF::queue_security_cycles() {
|
||||||
{
|
|
||||||
int number_of_cycles = gzget24(file_);
|
int number_of_cycles = gzget24(file_);
|
||||||
bool first_is_pulse = gzget8(file_) == 'P';
|
bool first_is_pulse = gzget8(file_) == 'P';
|
||||||
bool last_is_pulse = gzget8(file_) == 'P';
|
bool last_is_pulse = gzget8(file_) == 'P';
|
||||||
|
|
||||||
uint8_t current_byte = 0;
|
uint8_t current_byte = 0;
|
||||||
for(int cycle = 0; cycle < number_of_cycles; cycle++)
|
for(int cycle = 0; cycle < number_of_cycles; cycle++) {
|
||||||
{
|
|
||||||
if(!(cycle&7)) current_byte = gzget8(file_);
|
if(!(cycle&7)) current_byte = gzget8(file_);
|
||||||
int bit = (current_byte >> 7);
|
int bit = (current_byte >> 7);
|
||||||
current_byte <<= 1;
|
current_byte <<= 1;
|
||||||
@@ -268,24 +214,18 @@ void UEF::queue_security_cycles()
|
|||||||
duration.length = bit ? 1 : 2;
|
duration.length = bit ? 1 : 2;
|
||||||
duration.clock_rate = time_base_ * 4;
|
duration.clock_rate = time_base_ * 4;
|
||||||
|
|
||||||
if(!cycle && first_is_pulse)
|
if(!cycle && first_is_pulse) {
|
||||||
{
|
emplace_back(Pulse::High, duration);
|
||||||
queued_pulses_.emplace_back(Pulse::High, duration);
|
} else if(cycle == number_of_cycles-1 && last_is_pulse) {
|
||||||
}
|
emplace_back(Pulse::Low, duration);
|
||||||
else if(cycle == number_of_cycles-1 && last_is_pulse)
|
} else {
|
||||||
{
|
emplace_back(Pulse::Low, duration);
|
||||||
queued_pulses_.emplace_back(Pulse::Low, duration);
|
emplace_back(Pulse::High, duration);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
queued_pulses_.emplace_back(Pulse::Low, duration);
|
|
||||||
queued_pulses_.emplace_back(Pulse::High, duration);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UEF::queue_defined_data(uint32_t length)
|
void UEF::queue_defined_data(uint32_t length) {
|
||||||
{
|
|
||||||
if(length < 3) return;
|
if(length < 3) return;
|
||||||
|
|
||||||
int bits_per_packet = gzget8(file_);
|
int bits_per_packet = gzget8(file_);
|
||||||
@@ -296,8 +236,7 @@ void UEF::queue_defined_data(uint32_t length)
|
|||||||
number_of_stop_bits = abs(number_of_stop_bits);
|
number_of_stop_bits = abs(number_of_stop_bits);
|
||||||
|
|
||||||
length -= 3;
|
length -= 3;
|
||||||
while(length--)
|
while(length--) {
|
||||||
{
|
|
||||||
uint8_t byte = gzget8(file_);
|
uint8_t byte = gzget8(file_);
|
||||||
|
|
||||||
uint8_t parity_value = byte;
|
uint8_t parity_value = byte;
|
||||||
@@ -307,14 +246,12 @@ void UEF::queue_defined_data(uint32_t length)
|
|||||||
|
|
||||||
queue_bit(0);
|
queue_bit(0);
|
||||||
int c = bits_per_packet;
|
int c = bits_per_packet;
|
||||||
while(c--)
|
while(c--) {
|
||||||
{
|
|
||||||
queue_bit(byte&1);
|
queue_bit(byte&1);
|
||||||
byte >>= 1;
|
byte >>= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(parity_type)
|
switch(parity_type) {
|
||||||
{
|
|
||||||
default: break;
|
default: break;
|
||||||
case 'E': queue_bit(parity_value&1); break;
|
case 'E': queue_bit(parity_value&1); break;
|
||||||
case 'O': queue_bit((parity_value&1) ^ 1); break;
|
case 'O': queue_bit((parity_value&1) ^ 1); break;
|
||||||
@@ -322,45 +259,38 @@ void UEF::queue_defined_data(uint32_t length)
|
|||||||
|
|
||||||
int stop_bits = number_of_stop_bits;
|
int stop_bits = number_of_stop_bits;
|
||||||
while(stop_bits--) queue_bit(1);
|
while(stop_bits--) queue_bit(1);
|
||||||
if(has_extra_stop_wave)
|
if(has_extra_stop_wave) {
|
||||||
{
|
|
||||||
Time duration;
|
Time duration;
|
||||||
duration.length = 1;
|
duration.length = 1;
|
||||||
duration.clock_rate = time_base_ * 4;
|
duration.clock_rate = time_base_ * 4;
|
||||||
queued_pulses_.emplace_back(Pulse::Low, duration);
|
emplace_back(Pulse::Low, duration);
|
||||||
queued_pulses_.emplace_back(Pulse::High, duration);
|
emplace_back(Pulse::High, duration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Queuing helpers
|
#pragma mark - Queuing helpers
|
||||||
|
|
||||||
void UEF::queue_implicit_byte(uint8_t byte)
|
void UEF::queue_implicit_byte(uint8_t byte) {
|
||||||
{
|
|
||||||
queue_bit(0);
|
queue_bit(0);
|
||||||
int c = 8;
|
int c = 8;
|
||||||
while(c--)
|
while(c--) {
|
||||||
{
|
|
||||||
queue_bit(byte&1);
|
queue_bit(byte&1);
|
||||||
byte >>= 1;
|
byte >>= 1;
|
||||||
}
|
}
|
||||||
queue_bit(1);
|
queue_bit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UEF::queue_bit(int bit)
|
void UEF::queue_bit(int bit) {
|
||||||
{
|
|
||||||
int number_of_cycles;
|
int number_of_cycles;
|
||||||
Time duration;
|
Time duration;
|
||||||
duration.clock_rate = time_base_ * 4;
|
duration.clock_rate = time_base_ * 4;
|
||||||
|
|
||||||
if(bit)
|
if(bit) {
|
||||||
{
|
|
||||||
// encode high-frequency waves
|
// encode high-frequency waves
|
||||||
duration.length = 1;
|
duration.length = 1;
|
||||||
number_of_cycles = 2;
|
number_of_cycles = 2;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// encode low-frequency waves
|
// encode low-frequency waves
|
||||||
duration.length = 2;
|
duration.length = 2;
|
||||||
number_of_cycles = 1;
|
number_of_cycles = 1;
|
||||||
@@ -368,9 +298,8 @@ void UEF::queue_bit(int bit)
|
|||||||
|
|
||||||
if(is_300_baud_) number_of_cycles *= 4;
|
if(is_300_baud_) number_of_cycles *= 4;
|
||||||
|
|
||||||
while(number_of_cycles--)
|
while(number_of_cycles--) {
|
||||||
{
|
emplace_back(Pulse::Low, duration);
|
||||||
queued_pulses_.emplace_back(Pulse::Low, duration);
|
emplace_back(Pulse::High, duration);
|
||||||
queued_pulses_.emplace_back(Pulse::High, duration);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#ifndef TapeUEF_hpp
|
#ifndef TapeUEF_hpp
|
||||||
#define TapeUEF_hpp
|
#define TapeUEF_hpp
|
||||||
|
|
||||||
#include "../Tape.hpp"
|
#include "../PulseQueuedTape.hpp"
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -20,7 +20,7 @@ namespace Tape {
|
|||||||
/*!
|
/*!
|
||||||
Provides a @c Tape containing a UEF tape image, a slightly-convoluted description of pulses.
|
Provides a @c Tape containing a UEF tape image, a slightly-convoluted description of pulses.
|
||||||
*/
|
*/
|
||||||
class UEF : public Tape {
|
class UEF : public PulseQueuedTape {
|
||||||
public:
|
public:
|
||||||
/*!
|
/*!
|
||||||
Constructs a @c UEF containing content from the file with name @c file_name.
|
Constructs a @c UEF containing content from the file with name @c file_name.
|
||||||
@@ -34,22 +34,14 @@ class UEF : public Tape {
|
|||||||
ErrorNotUEF
|
ErrorNotUEF
|
||||||
};
|
};
|
||||||
|
|
||||||
// implemented to satisfy @c Tape
|
|
||||||
bool is_at_end();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void virtual_reset();
|
void virtual_reset();
|
||||||
Pulse virtual_get_next_pulse();
|
|
||||||
|
|
||||||
gzFile file_;
|
gzFile file_;
|
||||||
unsigned int time_base_;
|
unsigned int time_base_;
|
||||||
bool is_at_end_;
|
|
||||||
bool is_300_baud_;
|
bool is_300_baud_;
|
||||||
|
|
||||||
std::vector<Pulse> queued_pulses_;
|
void get_next_pulses();
|
||||||
size_t pulse_pointer_;
|
|
||||||
|
|
||||||
void parse_next_tape_chunk();
|
|
||||||
|
|
||||||
void queue_implicit_bit_pattern(uint32_t length);
|
void queue_implicit_bit_pattern(uint32_t length);
|
||||||
void queue_explicit_bit_pattern(uint32_t length);
|
void queue_explicit_bit_pattern(uint32_t length);
|
||||||
|
61
Storage/Tape/PulseQueuedTape.cpp
Normal file
61
Storage/Tape/PulseQueuedTape.cpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
//
|
||||||
|
// PulseQueuedTape.cpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 16/07/2017.
|
||||||
|
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "PulseQueuedTape.hpp"
|
||||||
|
|
||||||
|
using namespace Storage::Tape;
|
||||||
|
|
||||||
|
PulseQueuedTape::PulseQueuedTape() : pulse_pointer_(0) {}
|
||||||
|
|
||||||
|
bool PulseQueuedTape::is_at_end() {
|
||||||
|
return is_at_end_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PulseQueuedTape::set_is_at_end(bool is_at_end) {
|
||||||
|
is_at_end_ = is_at_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PulseQueuedTape::clear() {
|
||||||
|
queued_pulses_.clear();
|
||||||
|
pulse_pointer_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PulseQueuedTape::empty() {
|
||||||
|
return queued_pulses_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PulseQueuedTape::emplace_back(Tape::Pulse::Type type, Time length) {
|
||||||
|
queued_pulses_.emplace_back(type, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tape::Pulse PulseQueuedTape::silence() {
|
||||||
|
Pulse silence;
|
||||||
|
silence.type = Pulse::Zero;
|
||||||
|
silence.length.length = 1;
|
||||||
|
silence.length.clock_rate = 1;
|
||||||
|
return silence;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tape::Pulse PulseQueuedTape::virtual_get_next_pulse() {
|
||||||
|
if(is_at_end_) {
|
||||||
|
return silence();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pulse_pointer_ == queued_pulses_.size()) {
|
||||||
|
clear();
|
||||||
|
get_next_pulses();
|
||||||
|
|
||||||
|
if(is_at_end_ || pulse_pointer_ == queued_pulses_.size()) {
|
||||||
|
return silence();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t read_pointer = pulse_pointer_;
|
||||||
|
pulse_pointer_++;
|
||||||
|
return queued_pulses_[read_pointer];
|
||||||
|
}
|
53
Storage/Tape/PulseQueuedTape.hpp
Normal file
53
Storage/Tape/PulseQueuedTape.hpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// PulseQueuedTape.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 16/07/2017.
|
||||||
|
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef PulseQueuedTape_hpp
|
||||||
|
#define PulseQueuedTape_hpp
|
||||||
|
|
||||||
|
#include "Tape.hpp"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Storage {
|
||||||
|
namespace Tape {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Provides a @c Tape with a queue of upcoming pulses and an is-at-end flag.
|
||||||
|
|
||||||
|
If is-at-end is set then get_next_pulse() returns a second of silence and
|
||||||
|
is_at_end() returns true.
|
||||||
|
|
||||||
|
Otherwise get_next_pulse() returns something from the pulse queue if there is
|
||||||
|
anything there, and otherwise calls get_next_pulses(). get_next_pulses() is
|
||||||
|
virtual, giving subclasses a chance to provide the next batch of pulses.
|
||||||
|
*/
|
||||||
|
class PulseQueuedTape: public Tape {
|
||||||
|
public:
|
||||||
|
PulseQueuedTape();
|
||||||
|
bool is_at_end();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void emplace_back(Tape::Pulse::Type type, Time length);
|
||||||
|
void clear();
|
||||||
|
bool empty();
|
||||||
|
|
||||||
|
void set_is_at_end(bool);
|
||||||
|
virtual void get_next_pulses() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Pulse virtual_get_next_pulse();
|
||||||
|
Pulse silence();
|
||||||
|
|
||||||
|
std::vector<Pulse> queued_pulses_;
|
||||||
|
size_t pulse_pointer_;
|
||||||
|
bool is_at_end_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* PulseQueuedTape_hpp */
|
Reference in New Issue
Block a user