diff --git a/Machines/Acorn/BBCMicro/BBCMicro.cpp b/Machines/Acorn/BBCMicro/BBCMicro.cpp index 93b33b37d..97f70bf98 100644 --- a/Machines/Acorn/BBCMicro/BBCMicro.cpp +++ b/Machines/Acorn/BBCMicro/BBCMicro.cpp @@ -899,7 +899,7 @@ public: // The WD1770 is nominally clocked at 8Mhz. wd1770_.run_for(duration * 4); } - if constexpr (tube_processor != TubeProcessor::None) { + if constexpr (requires {tube_.processor;}) { tube_.processor.run_for(duration); } @@ -961,17 +961,15 @@ public: } } } else if(address >= 0xfee0 && address < 0xfee8) { - if constexpr (tube_processor == TubeProcessor::None) { + if constexpr (requires {tube_.ula;}) { if constexpr (is_read(operation)) { - value = address == 0xfee0 ? 0xfe : 0xff; + value = tube_.ula.host_read(address); + } else { + tube_.ula.host_write(address, value); } } else { if constexpr (is_read(operation)) { - const uint8_t result = tube_.ula.host_read(address); - value = result; - } else { - tube_.ula.host_write(address, value); - tube_.processor.set_reset(tube_.ula.parasite_reset()); + value = address == 0xfee0 ? 0xfe : 0xff; } } } else if(address >= 0xfe08 && address < 0xfe10) { @@ -1108,9 +1106,8 @@ private: void set_reset(const bool reset) { m6502_.template set(reset); - if constexpr (tube_processor != TubeProcessor::None) { + if constexpr (requires {tube_.ula;}) { tube_.ula.set_reset(reset); - tube_.processor.set_reset(reset); } } @@ -1219,7 +1216,7 @@ private: void update_irq_line() { const bool tube_irq = [&] { - if constexpr (tube_processor != TubeProcessor::None) { + if constexpr (requires {tube_.ula;}) { return tube_.ula.has_host_irq(); } else { return false; @@ -1274,9 +1271,10 @@ private: Tube tube_; public: - void set_host_tube_irq() { update_irq_line(); } - void set_parasite_tube_irq() { tube_.processor.set_irq(); } - void set_parasite_tube_nmi() { tube_.processor.set_nmi(); } + void set_host_tube_irq(bool) { update_irq_line(); } + void set_parasite_tube_irq(const bool active) { tube_.processor.set_irq(active); } + void set_parasite_tube_nmi(const bool active) { tube_.processor.set_nmi(active); } + void set_parasite_reset(const bool active) { tube_.processor.set_reset(active); } }; } diff --git a/Machines/Acorn/Tube/FIFO.hpp b/Machines/Acorn/Tube/FIFO.hpp index a4336543b..6fc50b5cf 100644 --- a/Machines/Acorn/Tube/FIFO.hpp +++ b/Machines/Acorn/Tube/FIFO.hpp @@ -42,12 +42,16 @@ struct FIFO { uint8_t read() { const uint8_t result = buffer_[read_ % length]; if(write_ != read_) ++read_; + if(write_ == read_) { + ula_.fifo_is_empty(mask_); + } return result; } /// Empties the FIFO. void reset() { read_ = write_ = 0; + ula_.fifo_is_empty(mask_); } private: diff --git a/Machines/Acorn/Tube/Tube6502.hpp b/Machines/Acorn/Tube/Tube6502.hpp index 386546066..dbf58cc3d 100644 --- a/Machines/Acorn/Tube/Tube6502.hpp +++ b/Machines/Acorn/Tube/Tube6502.hpp @@ -38,9 +38,7 @@ public: if(address >= 0xfef8 && address < 0xff00) { rom_visible_ = false; if constexpr (is_read(operation)) { - const uint8_t result = ula_.parasite_read(address); - value = result; - update_interrupts(); + value = ula_.parasite_read(address); } else { ula_.parasite_write(address, value); } @@ -55,20 +53,14 @@ public: return Cycles(1); } - void set_irq() { m6502_.template set(true); } - void set_nmi() { m6502_.template set(true); } + void set_irq(const bool active) { m6502_.template set(active); } + void set_nmi(const bool active) { m6502_.template set(active); } void set_reset(const bool reset) { m6502_.template set(reset); rom_visible_ |= reset; - update_interrupts(); } private: - void update_interrupts() { - m6502_.template set(ula_.has_parasite_irq()); - m6502_.template set(ula_.has_parasite_nmi()); - } - uint8_t rom_[2048]; uint8_t ram_[65536]; Cycles cycles_modulo_; diff --git a/Machines/Acorn/Tube/TubeZ80.hpp b/Machines/Acorn/Tube/TubeZ80.hpp index 45e5bd082..ef616a3a5 100644 --- a/Machines/Acorn/Tube/TubeZ80.hpp +++ b/Machines/Acorn/Tube/TubeZ80.hpp @@ -31,12 +31,11 @@ public: z80_.run_for(cycles * 3); } - void set_irq() { z80_.set_interrupt_line(true); } - void set_nmi() { z80_.set_non_maskable_interrupt_line(true); } + void set_irq(const bool active) { z80_.set_interrupt_line(active); } + void set_nmi(const bool active) { z80_.set_non_maskable_interrupt_line(active); } void set_reset(const bool reset) { z80_.set_reset_line(reset); rom_visible_ |= reset; - update_interrupts(); } HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { @@ -71,7 +70,6 @@ public: case CPU::Z80::PartialMachineCycle::Input: *cycle.value = ula_.parasite_read(address); - update_interrupts(); break; case CPU::Z80::PartialMachineCycle::Output: @@ -85,10 +83,6 @@ public: } private: - void update_interrupts() { - z80_.set_interrupt_line(ula_.has_parasite_irq()); - z80_.set_non_maskable_interrupt_line(ula_.has_parasite_nmi()); - } CPU::Z80::Processor z80_; bool rom_visible_ = true; diff --git a/Machines/Acorn/Tube/ULA.hpp b/Machines/Acorn/Tube/ULA.hpp index d73ffa3d1..d67740576 100644 --- a/Machines/Acorn/Tube/ULA.hpp +++ b/Machines/Acorn/Tube/ULA.hpp @@ -29,24 +29,15 @@ struct ULA { to_host4_(*this, 0x01) {} - /// @returns @c true if the parasite's reset line should be active. - bool parasite_reset() const { - return flags_ & 0x20; - } - /// Call-in for the FIFOs; indicates that a FIFO just went from empty to not-empty, /// which might cause an interrupt elsewhere depending on the mask and on whether /// that interrupt is enabled. void fifo_has_data(const uint8_t mask) { - if(!(flags_ & mask)) return; + apply_fifo_mask(mask, 0xff); + } - switch(mask) { - default: __builtin_unreachable(); - case 0x01: host_.set_host_tube_irq(); break; - case 0x02: - case 0x04: host_.set_parasite_tube_irq(); break; - case 0x08: host_.set_parasite_tube_nmi(); break; - } + void fifo_is_empty(const uint8_t mask) { + apply_fifo_mask(0x00, ~mask); } bool has_host_irq() const { @@ -115,9 +106,14 @@ struct ULA { } void set_reset(const bool reset) { - // This attempts the software - if(!reset && reset_) { -// flags_ = 0x01; + if(reset_ == reset) { + return; + } + + // This is a software approximtion of holding the reset state for as long + // as it is signalled. + if(!reset) { + flags_ = 0x01; to_parasite1_.reset(); to_parasite2_.reset(); to_parasite3_.reset(); @@ -128,20 +124,53 @@ struct ULA { to_host4_.reset(); } reset_ = reset; + + update_parasite_reset(); } private: + void signal_changes(const uint8_t changes) { + if(changes & 0x01) { + host_.set_host_tube_irq(interrupt_sources_ & 0x01); + } + if(changes & 0x06) { + host_.set_parasite_tube_irq(interrupt_sources_ & 0x06); + } + if(changes & 0x08) { + host_.set_parasite_tube_nmi(interrupt_sources_ & 0x08); + } + } + + uint8_t signalling_fifos() const { + return interrupt_sources_ & flags_; + } + + void apply_fifo_mask(const uint8_t or_, const uint8_t and_) { + const auto signalling = signalling_fifos(); + interrupt_sources_ = (interrupt_sources_ | or_) & and_; + signal_changes(signalling_fifos() ^ signalling); + } + + void update_parasite_reset() { + host_.set_parasite_reset((flags_ & 0x20) || reset_); + } + uint8_t status() const { return flags_; } void set_status(const uint8_t value) { + const auto signalling = signalling_fifos(); const uint8_t bits = value & 0x3f; if(value & 0x80) { flags_ |= bits; } else { flags_ &= ~bits; } + signal_changes(signalling_fifos() ^ signalling); + if(value & 0x20) { + update_parasite_reset(); + } // TODO: understand meaning of bits 4 and 6. } @@ -149,6 +178,7 @@ private: HostT &host_; uint8_t flags_ = 0x01; bool reset_ = false; + uint8_t interrupt_sources_ = 0x00; FIFO<1, ULA> to_parasite1_; FIFO<1, ULA> to_parasite2_;