From 50375fb373f6f57ac6ff6d83ac523637e2d3483d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 23 Jun 2017 20:18:19 -0400 Subject: [PATCH 1/6] Ensured tape position is unaffected if the attempt at loading quickly fails. --- Machines/ZX8081/ZX8081.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 3d955c268..9bec922da 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -124,7 +124,8 @@ int Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { case CPU::Z80::PartialMachineCycle::ReadOpcodeStart: case CPU::Z80::PartialMachineCycle::ReadOpcodeWait: // Check for use of the fast tape hack. - if(use_fast_tape_hack_ && address == tape_trap_address_) { + if(use_fast_tape_hack_ && address == tape_trap_address_ && tape_player_.has_tape()) { + Storage::Time time = tape_player_.get_tape()->get_current_time(); int next_byte = parser_.get_next_byte(tape_player_.get_tape()); if(next_byte != -1) { uint16_t hl = get_value_of_register(CPU::Z80::Register::HL); @@ -132,6 +133,8 @@ int Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { *cycle.value = 0x00; set_value_of_register(CPU::Z80::Register::ProgramCounter, tape_return_address_ - 1); return 0; + } else { + tape_player_.get_tape()->seek(time); } } is_opcode_read = true; From a72a2e0a1af0da6d461dcc7568b4470670e9b7c9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 23 Jun 2017 20:21:37 -0400 Subject: [PATCH 2/6] Ensured tape doesn't proceed of its own volition when in fast-loading mode. --- Machines/ZX8081/ZX8081.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 9bec922da..7c18fa392 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -53,7 +53,7 @@ int Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { } if(is_zx81_) horizontal_counter_ %= 207; - tape_player_.run_for_cycles(cycle.length); + if(!use_fast_tape_hack_) tape_player_.run_for_cycles(cycle.length); if(nmi_is_enabled_ && !get_halt_line() && get_non_maskable_interrupt_line()) { set_wait_line(true); From a53011f77816442b42178e5d917305a90761fa35 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 6 Jul 2017 22:31:12 -0400 Subject: [PATCH 3/6] Extended intro and outro length because right now I'm racing this myself. Can return to normal once tape motor control is implemented. --- Storage/Tape/Formats/ZX80O81P.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Storage/Tape/Formats/ZX80O81P.cpp b/Storage/Tape/Formats/ZX80O81P.cpp index 53b64222b..e1f969217 100644 --- a/Storage/Tape/Formats/ZX80O81P.cpp +++ b/Storage/Tape/Formats/ZX80O81P.cpp @@ -54,7 +54,7 @@ Tape::Pulse ZX80O81P::virtual_get_next_pulse() { // Start with 1 second of silence. if(!is_past_silence_ || has_finished_data()) { pulse.type = Pulse::Type::Low; - pulse.length.length = 5; + pulse.length.length = 10; pulse.length.clock_rate = 1; is_past_silence_ = true; has_ended_final_byte_ = has_finished_data(); From 84d0e9b4cd25fe093c1bccbb5b206f57a6a07cbc Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 6 Jul 2017 22:31:45 -0400 Subject: [PATCH 4/6] Accept a pulse that begins exactly on seek_time as being found while seeking. --- Storage/Tape/Tape.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Storage/Tape/Tape.cpp b/Storage/Tape/Tape.cpp index b5907a078..3fef2973e 100644 --- a/Storage/Tape/Tape.cpp +++ b/Storage/Tape/Tape.cpp @@ -22,7 +22,7 @@ TapePlayer::TapePlayer(unsigned int input_clock_rate) : void Storage::Tape::Tape::seek(Time &seek_time) { current_time_.set_zero(); next_time_.set_zero(); - while(next_time_ < seek_time) get_next_pulse(); + while(next_time_ <= seek_time) get_next_pulse(); } void Storage::Tape::Tape::reset() { From 2f42874fd370b586d8d87c74b8a6def8c6129af1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 6 Jul 2017 22:33:03 -0400 Subject: [PATCH 5/6] Another fix to deal with real-time fighting: allow 8 and 18 pulses to be recognised as 1s and 0s. That's because the hand-off from ROM routines to parsing may occur very shortly before the first pulse of a valid sequence, making it look like there's a ghost. A cleaner solution needs to be found, probably revolving around allowing parsers to be attached to tapes and therefore to run constantly. --- Storage/Tape/Parsers/ZX8081.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Storage/Tape/Parsers/ZX8081.cpp b/Storage/Tape/Parsers/ZX8081.cpp index dba32dc28..f4678dc6f 100644 --- a/Storage/Tape/Parsers/ZX8081.cpp +++ b/Storage/Tape/Parsers/ZX8081.cpp @@ -79,10 +79,10 @@ void Parser::inspect_waves(const std::vector &waves) { // the gap that follows the bit due to the simplified "high is high, everything else is low" // logic applied to pulse detection. So those two things will merge. Meaning we're looking for // 17 and/or 7 pulses. - int gaps_to_swallow = (int)wave_offset + ((waves[number_of_pulses + wave_offset] == WaveType::Gap) ? 1 : 0); + size_t gaps_to_swallow = wave_offset + ((waves[number_of_pulses + wave_offset] == WaveType::Gap) ? 1 : 0); switch(number_of_pulses) { - case 17: push_symbol(SymbolType::One, 17 + gaps_to_swallow); break; - case 7: push_symbol(SymbolType::Zero, 7 + gaps_to_swallow); break; + case 18: case 17: push_symbol(SymbolType::One, (int)(number_of_pulses + gaps_to_swallow)); break; + case 8: case 7: push_symbol(SymbolType::Zero, (int)(number_of_pulses + gaps_to_swallow)); break; default: push_symbol(SymbolType::Unrecognised, 1); break; } } From a3684545b5647a9fcfb824f71bccdcbb46809d59 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 6 Jul 2017 22:33:54 -0400 Subject: [PATCH 6/6] Added a block on the tape motor for a short period after each time the ROM routine is intercepted for a substituted byte read. To reduce the collision between fast tape and real tape loading. --- Machines/ZX8081/ZX8081.cpp | 14 ++++++++++++-- Machines/ZX8081/ZX8081.hpp | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 7c18fa392..cf3c24ed0 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -22,7 +22,8 @@ Machine::Machine() : hsync_(false), nmi_is_enabled_(false), tape_player_(ZX8081ClockRate), - use_fast_tape_hack_(false) { + use_fast_tape_hack_(false), + tape_advance_delay_(0) { set_clock_rate(ZX8081ClockRate); tape_player_.set_motor_control(true); clear_all_keys(); @@ -53,7 +54,11 @@ int Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { } if(is_zx81_) horizontal_counter_ %= 207; - if(!use_fast_tape_hack_) tape_player_.run_for_cycles(cycle.length); + if(!tape_advance_delay_) { + tape_player_.run_for_cycles(cycle.length); + } else { + tape_advance_delay_ = std::max(tape_advance_delay_ - cycle.length, 0); + } if(nmi_is_enabled_ && !get_halt_line() && get_non_maskable_interrupt_line()) { set_wait_line(true); @@ -132,6 +137,11 @@ int Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { ram_[hl & ram_mask_] = (uint8_t)next_byte; *cycle.value = 0x00; set_value_of_register(CPU::Z80::Register::ProgramCounter, tape_return_address_ - 1); + + // Assume that having read one byte quickly, we're probably going to be asked to read + // another shortly. Therefore, temporarily disable the tape motor for 1000 cycles in order + // to avoid fighting with real time. This is a stop-gap fix. + tape_advance_delay_ = 1000; return 0; } else { tape_player_.get_tape()->seek(time); diff --git a/Machines/ZX8081/ZX8081.hpp b/Machines/ZX8081/ZX8081.hpp index 9bd9044a5..d07ed461a 100644 --- a/Machines/ZX8081/ZX8081.hpp +++ b/Machines/ZX8081/ZX8081.hpp @@ -94,6 +94,7 @@ class Machine: uint8_t latched_video_byte_; bool use_fast_tape_hack_; + int tape_advance_delay_; }; }