From d8d8d00985de7da2f6b254de7c06a33ec314c962 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 27 Apr 2026 10:06:30 -0400 Subject: [PATCH] Attempt high-level version of Loriciels loader. --- Machines/AmstradCPC/AmstradCPC.cpp | 2 +- Machines/MSX/MSX.cpp | 5 +- Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp | 4 +- .../MO/FastTapeSchemes/LoricielsBactron.hpp | 56 +++++++++++++++---- Machines/Thomson/MO/MO.cpp | 7 +++ Storage/Tape/Tape.cpp | 10 ++++ Storage/Tape/Tape.hpp | 2 + 7 files changed, 70 insertions(+), 16 deletions(-) diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index cff71b28c..9acbf32ad 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -893,7 +893,7 @@ public: // TODO (in the player, not here): adapt it to accept an input clock rate and // run_for as HalfCycles. - if(!tape_player_is_sleeping_) tape_player_.run_for(cycle.length.get()); + if(!tape_player_is_sleeping_) tape_player_.run_for(cycle.length.reduce()); // Pump the AY. ay_.run_for(cycle.length); diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 2c14d2670..46df1666b 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -773,8 +773,9 @@ public: } } - if(!tape_player_is_sleeping_) - tape_player_.run_for(cycle.length.as()); + if(!tape_player_is_sleeping_) { + tape_player_.run_for(cycle.length.reduce()); + } return addition; } diff --git a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp index fed4fb2d4..48e9ed403 100644 --- a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp +++ b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp @@ -621,7 +621,7 @@ public: } private: - void advance(HalfCycles duration) { + void advance(const HalfCycles duration) { time_since_audio_update_ += duration; video_ += duration; @@ -629,7 +629,7 @@ private: z80_.set_interrupt_line(video_.last_valid()->get_interrupt_line(), video_.last_sequence_point_overrun()); } - if(!tape_player_is_sleeping_) tape_player_.run_for(duration.get()); + if(!tape_player_is_sleeping_) tape_player_.run_for(Cycles(duration.get())); // Update automatic tape motor control, if enabled; if it's been // 0.5 seconds since software last possibly polled the tape, stop it. diff --git a/Machines/Thomson/MO/FastTapeSchemes/LoricielsBactron.hpp b/Machines/Thomson/MO/FastTapeSchemes/LoricielsBactron.hpp index d8c04efe3..7db86a285 100644 --- a/Machines/Thomson/MO/FastTapeSchemes/LoricielsBactron.hpp +++ b/Machines/Thomson/MO/FastTapeSchemes/LoricielsBactron.hpp @@ -18,14 +18,14 @@ namespace Thomson::FastLoader { Sampling code is: - Z35CA LDA ,U ;35CA: A6 C4 '..' + Z35CA LDA ,U ;35CA: A6 C4 '..' LSRA ;35CC: 44 'D' STA ,-S ;35CD: A7 E2 '..' - STA ,S ;35CF: A7 E4 '..' + STA ,S ;35CF: A7 E4 '..' ; [S], [S+1] = tape in b6 LDA ,U ;35D1: A6 C4 '..' LSRA ;35D3: 44 'D' ADDA ,S ;35D4: AB E4 '..' - STA ,S ;35D6: A7 E4 '..' + STA ,S ;35D6: A7 E4 '..' ; [S] = two-bit tape average in b6:b7 LDA ,U ;35D8: A6 C4 '..' LSRA ;35DA: 44 'D' ADDA ,S+ ;35DB: AB E0 '..' @@ -34,11 +34,26 @@ namespace Thomson::FastLoader { Outer loop is: - Z35AD BSR Z35CA ;35AD: 8D 1B '..' - BPL Z35AD ;35AF: 2A FC '*.' ; loop until edge? - LDX #M0041 ;35B1: 8E 00 41 '..A' - BSR Z35C4 ;35B4: 8D 0E '..' ; fixed delay + Z35AD BSR Z35CA ;35AD: 8D 1B '..' + BPL Z35AD ;35AF: 2A FC '*.' ; loop until sample differs from 3507 + LDX #M0041 ;35B1: 8E 00 41 '..A' ; bit length + BSR Z35C4 ;35B4: 8D 0E '..' ; fixed delay: 7µs + [as per 35C4] BSR Z35CA ;35B6: 8D 12 '..' ; sample again for bit + BPL Z35BF ;35B8: 2A 05 '*.' ; BPL -> 43 = COMA, which sets carry + COM M3507 ;35BA: 73 35 07 's5.' + CLRA ;35BD: 4F 'O' ; clear carry + BRN Z3603 ;35BE: 21 43 '!C' + ROL M3508 ;35C0: 79 35 08 'y5.' + RTS ;35C3: 39 '9' + + Delay loop is: + + Z35C4 DEX ;35C4: 30 1F '0.' ; 5 µs + BNE Z35C4 ;35C6: 26 FC '&.' ; 3 µs + CLRA ;35C8: 4F 'O' ; 2 µs + RTS ;35C9: 39 '9' ; 5 µs + + i.e. length is 8*X + 7 µs. */ struct LoricielsBactron: public Loader { @@ -60,7 +75,6 @@ struct LoricielsBactron: public Loader { memory.iterator(address - 2) ) ) { - // TODO: use 'outer' for validation. switch(address) { case 0x35cc: return 0x35ad; default: @@ -75,7 +89,7 @@ struct LoricielsBactron: public Loader { const uint16_t address, MemoryAccess &memory, CPU::M6809::Registers &, - Storage::Tape::BinaryTapePlayer &, + Storage::Tape::BinaryTapePlayer &player, Storage::Tape::TapeSerialiser & ) override { // Check that this is still the expected routine. @@ -88,9 +102,29 @@ struct LoricielsBactron: public Loader { return std::make_pair(TrapAction::None, false); } - // TODO. + player.run_for(Storage::Time(14 + 8*0x41, 1'000'000)); - return std::make_pair(TrapAction::None, false); + // Sample current level. + const bool start_level = memory[0x3507] & 0x80; + + // Proceed to next edge; time can be skipped here. + while(player.input() == start_level) { + player.complete_pulse(); + } + + // Wait for 14 + 8*(0x41) µs. + player.run_for(Storage::Time(14 + 8*0x41, 1'000'000)); + + // Sample current level to determine bit, shift and complement as appropriate. + const bool final_level = player.input(); + memory[0x3508] <<= 1; + if(final_level == start_level) { + memory[0x3508] |= 1; + } else { + memory[0x3507] ^= 0xff; + } + + return std::make_pair(TrapAction::RTS, true); } }; diff --git a/Machines/Thomson/MO/MO.cpp b/Machines/Thomson/MO/MO.cpp index 6fa25cc63..da38472ad 100644 --- a/Machines/Thomson/MO/MO.cpp +++ b/Machines/Thomson/MO/MO.cpp @@ -128,6 +128,8 @@ struct ConcreteMachine: audio_queue_.lock_flush(); } + uint16_t previous_pc = 0; + template < CPU::M6809::BusPhase bus_phase, CPU::M6809::LIC lic, @@ -250,6 +252,11 @@ struct ConcreteMachine: value = memory_.read(address); if constexpr (lic == CPU::M6809::LIC::InstructionFetch) { + if(address == 0x35ad && previous_pc < 0x35ad) { + printf("[%d] 3507: %02x; 3508: %02x\n", tape_player_.serialiser()->offset(), memory_[0x3507], memory_[0x3508]); + } + previous_pc = address; + if( allow_fast_tape_loading_ && tape_player_.motor_control() && diff --git a/Storage/Tape/Tape.cpp b/Storage/Tape/Tape.cpp index a8a683dc8..a128f2ccb 100644 --- a/Storage/Tape/Tape.cpp +++ b/Storage/Tape/Tape.cpp @@ -132,6 +132,12 @@ void TapePlayer::run_for(const Cycles cycles) { } } +void TapePlayer::run_for(const Time time) { + run_for(Cycles( + (get_input_clock_rate() * time.length) / time.clock_rate + )); +} + void TapePlayer::run_for_input_pulse() { jump_to_next_event(); } @@ -200,6 +206,10 @@ void BinaryTapePlayer::run_for(const Cycles cycles) { if(motor_is_running_) TapePlayer::run_for(cycles); } +void BinaryTapePlayer::run_for(const Time time) { + if(motor_is_running_) TapePlayer::run_for(time); +} + void BinaryTapePlayer::set_delegate(Delegate *const delegate) { delegate_ = delegate; } diff --git a/Storage/Tape/Tape.hpp b/Storage/Tape/Tape.hpp index 2b47373bb..21b4aae15 100644 --- a/Storage/Tape/Tape.hpp +++ b/Storage/Tape/Tape.hpp @@ -129,6 +129,7 @@ public: TapeSerialiser *serialiser(); void run_for(Cycles); + void run_for(Time); // Converts the Time to an integral number of cycles; some precision is lost. void run_for_input_pulse(); ClockingHint::Preference preferred_clocking() const override; @@ -166,6 +167,7 @@ public: bool input() const; void run_for(Cycles); + void run_for(Time); // Converts the Time to an integral number of cycles; some precision is lost. struct Delegate { virtual void tape_did_change_input(BinaryTapePlayer &) = 0;