From 5b13d3e893e0eb085260b2ec10b544ef1cc755f7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 19 Apr 2024 21:30:15 -0400 Subject: [PATCH 1/4] Attempt the prefetch portion of a pipeline. --- InstructionSets/ARM/Registers.hpp | 9 +- Machines/Acorn/Archimedes/Archimedes.cpp | 101 +++++++++++++++++++---- 2 files changed, 93 insertions(+), 17 deletions(-) diff --git a/InstructionSets/ARM/Registers.hpp b/InstructionSets/ARM/Registers.hpp index 31bab96ab..1e4fcc6ab 100644 --- a/InstructionSets/ARM/Registers.hpp +++ b/InstructionSets/ARM/Registers.hpp @@ -209,6 +209,13 @@ struct Registers { /// Otherwise returns @c false. template bool interrupt() { + if(!would_interrupt()) return false; + exception(); + return true; + } + + template + bool would_interrupt() { switch(type) { case Exception::IRQ: if(interrupt_flags_ & ConditionCode::IRQDisable) { @@ -224,8 +231,6 @@ struct Registers { default: return false; } - - exception(); return true; } diff --git a/Machines/Acorn/Archimedes/Archimedes.cpp b/Machines/Acorn/Archimedes/Archimedes.cpp index 0c33202a9..703c02489 100644 --- a/Machines/Acorn/Archimedes/Archimedes.cpp +++ b/Machines/Acorn/Archimedes/Archimedes.cpp @@ -402,29 +402,57 @@ class ConcreteMachine: executor_.bus.set_rom(roms.find(risc_os)->second); insert_media(target.media); + + fill_pipeline(0); } void update_interrupts() { using Exception = InstructionSet::ARM::Registers::Exception; const int requests = executor_.bus.interrupt_mask(); - if((requests & InterruptRequests::FIQ) && executor_.registers().interrupt()) { + if((requests & InterruptRequests::FIQ) && executor_.registers().would_interrupt()) { + pipeline_.reschedule(Pipeline::SWISubversion::FIQ); return; } - if(requests & InterruptRequests::IRQ) { - executor_.registers().interrupt(); + if((requests & InterruptRequests::IRQ) && executor_.registers().would_interrupt()) { + pipeline_.reschedule(Pipeline::SWISubversion::IRQ); } } void did_set_status() { + // This might have been a change of mode, so... + fill_pipeline(executor_.pc()); update_interrupts(); } void did_set_pc() { + fill_pipeline(executor_.pc()); } bool should_swi(uint32_t) { - return true; + using Exception = InstructionSet::ARM::Registers::Exception; + using SWISubversion = Pipeline::SWISubversion; + + switch(pipeline_.swi_subversion()) { + case Pipeline::SWISubversion::None: + return true; + + case SWISubversion::DataAbort: +// executor_.set_pc(executor_.pc() - 4); + executor_.registers().interrupt(); + break; + case SWISubversion::FIQ: + executor_.set_pc(executor_.pc() - 4); + executor_.registers().interrupt(); + break; + case SWISubversion::IRQ: + executor_.set_pc(executor_.pc() - 4); + executor_.registers().interrupt(); + break; + } + + did_set_pc(); + return false; } void update_clock_rates() { @@ -457,16 +485,7 @@ class ConcreteMachine: int video_divider_ = 1; void tick_cpu() { - uint32_t instruction = 0; - if(!executor_.bus.read(executor_.pc(), instruction, executor_.registers().mode(), false)) { -// logger.info().append("Prefetch abort at %08x; last good was at %08x", executor_.pc(), last_pc); - executor_.prefetch_abort(); - - // TODO: does a double abort cause a reset? - executor_.bus.read(executor_.pc(), instruction, executor_.registers().mode(), false); - } - // TODO: pipeline prefetch? - + const uint32_t instruction = advance_pipeline(executor_.pc() + 8); debugger_.notify(executor_.pc(), instruction, executor_); InstructionSet::ARM::execute(instruction, executor_); } @@ -514,11 +533,63 @@ class ConcreteMachine: return executor_.bus.keyboard().mouse(); } - // MARK: - ARM execution + // MARK: - ARM execution. static constexpr auto arm_model = InstructionSet::ARM::Model::ARMv2; using Executor = InstructionSet::ARM::Executor, ConcreteMachine>; Executor executor_; + void fill_pipeline(uint32_t pc) { + advance_pipeline(pc); + advance_pipeline(pc + 4); + } + + uint32_t advance_pipeline(uint32_t pc) { + uint32_t instruction; + const bool did_read = executor_.bus.read(pc, instruction, executor_.registers().mode(), false); + return pipeline_.exchange( + instruction, + did_read ? Pipeline::SWISubversion::None : Pipeline::SWISubversion::DataAbort); + } + + struct Pipeline { + enum SWISubversion: uint8_t { + None, + DataAbort, + IRQ, + FIQ, + }; + + uint32_t exchange(uint32_t next, SWISubversion subversion) { + const uint32_t result = upcoming_[active_].opcode; + latched_subversion_ = upcoming_[active_].subversion; + + upcoming_[active_].opcode = next; + upcoming_[active_].subversion = subversion; + active_ ^= 1; + + return result; + } + + void reschedule(SWISubversion subversion) { + upcoming_[active_ ^ 1].opcode = 0xef'000000; + upcoming_[active_ ^ 1].subversion = subversion; + } + + SWISubversion swi_subversion() const { + return latched_subversion_; + } + + private: + struct Stage { + uint32_t opcode; + SWISubversion subversion; + }; + Stage upcoming_[2]; + int active_ = 0; + + SWISubversion latched_subversion_; + } pipeline_; + // MARK: - Yucky, temporary junk. HackyDebugger debugger_; }; From 83eac172c9ccbfb80049082c297a830c43d99244 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 19 Apr 2024 21:46:09 -0400 Subject: [PATCH 2/4] Revoke in-pipeline interrupts. I'm unclear on what timing should apply here really. --- InstructionSets/ARM/Registers.hpp | 4 +- Machines/Acorn/Archimedes/Archimedes.cpp | 53 +++++++++++++++--------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/InstructionSets/ARM/Registers.hpp b/InstructionSets/ARM/Registers.hpp index 1e4fcc6ab..17cb5c96b 100644 --- a/InstructionSets/ARM/Registers.hpp +++ b/InstructionSets/ARM/Registers.hpp @@ -209,7 +209,9 @@ struct Registers { /// Otherwise returns @c false. template bool interrupt() { - if(!would_interrupt()) return false; + if(!would_interrupt()) { + return false; + } exception(); return true; } diff --git a/Machines/Acorn/Archimedes/Archimedes.cpp b/Machines/Acorn/Archimedes/Archimedes.cpp index 703c02489..52cc70fd6 100644 --- a/Machines/Acorn/Archimedes/Archimedes.cpp +++ b/Machines/Acorn/Archimedes/Archimedes.cpp @@ -410,13 +410,23 @@ class ConcreteMachine: using Exception = InstructionSet::ARM::Registers::Exception; const int requests = executor_.bus.interrupt_mask(); - if((requests & InterruptRequests::FIQ) && executor_.registers().would_interrupt()) { - pipeline_.reschedule(Pipeline::SWISubversion::FIQ); + if((requests & InterruptRequests::FIQ) && executor_.registers().interrupt()) { + did_set_pc(); return; } - if((requests & InterruptRequests::IRQ) && executor_.registers().would_interrupt()) { - pipeline_.reschedule(Pipeline::SWISubversion::IRQ); + if((requests & InterruptRequests::IRQ) && executor_.registers().interrupt()) { + did_set_pc(); + return; } + +// const int requests = executor_.bus.interrupt_mask(); +// if((requests & InterruptRequests::FIQ) && executor_.registers().would_interrupt()) { +// pipeline_.reschedule(Pipeline::SWISubversion::FIQ); +// return; +// } +// if((requests & InterruptRequests::IRQ) && executor_.registers().would_interrupt()) { +// pipeline_.reschedule(Pipeline::SWISubversion::IRQ); +// } } void did_set_status() { @@ -441,14 +451,14 @@ class ConcreteMachine: // executor_.set_pc(executor_.pc() - 4); executor_.registers().interrupt(); break; - case SWISubversion::FIQ: - executor_.set_pc(executor_.pc() - 4); - executor_.registers().interrupt(); - break; - case SWISubversion::IRQ: - executor_.set_pc(executor_.pc() - 4); - executor_.registers().interrupt(); - break; +// case SWISubversion::FIQ: +// executor_.set_pc(executor_.pc() - 4); +// executor_.registers().interrupt(); +// break; +// case SWISubversion::IRQ: +// executor_.set_pc(executor_.pc() - 4); +// executor_.registers().interrupt(); +// break; } did_set_pc(); @@ -539,6 +549,7 @@ class ConcreteMachine: Executor executor_; void fill_pipeline(uint32_t pc) { +// if(pipeline_.interrupt_next()) return; advance_pipeline(pc); advance_pipeline(pc + 4); } @@ -555,8 +566,8 @@ class ConcreteMachine: enum SWISubversion: uint8_t { None, DataAbort, - IRQ, - FIQ, +// IRQ, +// FIQ, }; uint32_t exchange(uint32_t next, SWISubversion subversion) { @@ -570,19 +581,23 @@ class ConcreteMachine: return result; } - void reschedule(SWISubversion subversion) { - upcoming_[active_ ^ 1].opcode = 0xef'000000; - upcoming_[active_ ^ 1].subversion = subversion; - } +// void reschedule(SWISubversion subversion) { +// upcoming_[active_ ^ 1].opcode = 0xef'000000; +// upcoming_[active_ ^ 1].subversion = subversion; +// } SWISubversion swi_subversion() const { return latched_subversion_; } +// bool interrupt_next() const { +// return upcoming_[active_].subversion == SWISubversion::IRQ || upcoming_[active_].subversion == SWISubversion::FIQ; +// } + private: struct Stage { uint32_t opcode; - SWISubversion subversion; + SWISubversion subversion = SWISubversion::None; }; Stage upcoming_[2]; int active_ = 0; From ea3eef38174265aea7c31c66ed3d567a539e29e6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 19 Apr 2024 22:21:23 -0400 Subject: [PATCH 3/4] Put interrupts into pipeline, without delay. --- InstructionSets/ARM/Registers.hpp | 11 +--- Machines/Acorn/Archimedes/Archimedes.cpp | 68 ++++++++++++------------ 2 files changed, 35 insertions(+), 44 deletions(-) diff --git a/InstructionSets/ARM/Registers.hpp b/InstructionSets/ARM/Registers.hpp index 17cb5c96b..f1c690dd3 100644 --- a/InstructionSets/ARM/Registers.hpp +++ b/InstructionSets/ARM/Registers.hpp @@ -205,17 +205,8 @@ struct Registers { set_pc(uint32_t(type)); } - /// Applies an exception of @c type and returns @c true if: (i) it is IRQ or FIQ; (ii) the processor is currently accepting such interrupts. + /// Returns @c true if: (i) the exception type is IRQ or FIQ; and (ii) the processor is currently accepting such interrupts. /// Otherwise returns @c false. - template - bool interrupt() { - if(!would_interrupt()) { - return false; - } - exception(); - return true; - } - template bool would_interrupt() { switch(type) { diff --git a/Machines/Acorn/Archimedes/Archimedes.cpp b/Machines/Acorn/Archimedes/Archimedes.cpp index 52cc70fd6..f9fe9b5bf 100644 --- a/Machines/Acorn/Archimedes/Archimedes.cpp +++ b/Machines/Acorn/Archimedes/Archimedes.cpp @@ -410,23 +410,13 @@ class ConcreteMachine: using Exception = InstructionSet::ARM::Registers::Exception; const int requests = executor_.bus.interrupt_mask(); - if((requests & InterruptRequests::FIQ) && executor_.registers().interrupt()) { - did_set_pc(); + if((requests & InterruptRequests::FIQ) && executor_.registers().would_interrupt()) { + pipeline_.reschedule(Pipeline::SWISubversion::FIQ); return; } - if((requests & InterruptRequests::IRQ) && executor_.registers().interrupt()) { - did_set_pc(); - return; + if((requests & InterruptRequests::IRQ) && executor_.registers().would_interrupt()) { + pipeline_.reschedule(Pipeline::SWISubversion::IRQ); } - -// const int requests = executor_.bus.interrupt_mask(); -// if((requests & InterruptRequests::FIQ) && executor_.registers().would_interrupt()) { -// pipeline_.reschedule(Pipeline::SWISubversion::FIQ); -// return; -// } -// if((requests & InterruptRequests::IRQ) && executor_.registers().would_interrupt()) { -// pipeline_.reschedule(Pipeline::SWISubversion::IRQ); -// } } void did_set_status() { @@ -449,16 +439,19 @@ class ConcreteMachine: case SWISubversion::DataAbort: // executor_.set_pc(executor_.pc() - 4); - executor_.registers().interrupt(); + executor_.registers().exception(); + break; + + // FIQ and IRQ decrement the PC because their apperance in the pipeline causes + // it to look as though they were fetched, but they weren't. + case SWISubversion::FIQ: + executor_.set_pc(executor_.pc() - 4); + executor_.registers().exception(); + break; + case SWISubversion::IRQ: + executor_.set_pc(executor_.pc() - 4); + executor_.registers().exception(); break; -// case SWISubversion::FIQ: -// executor_.set_pc(executor_.pc() - 4); -// executor_.registers().interrupt(); -// break; -// case SWISubversion::IRQ: -// executor_.set_pc(executor_.pc() - 4); -// executor_.registers().interrupt(); -// break; } did_set_pc(); @@ -549,7 +542,7 @@ class ConcreteMachine: Executor executor_; void fill_pipeline(uint32_t pc) { -// if(pipeline_.interrupt_next()) return; + if(pipeline_.interrupt_next()) return; advance_pipeline(pc); advance_pipeline(pc + 4); } @@ -566,8 +559,8 @@ class ConcreteMachine: enum SWISubversion: uint8_t { None, DataAbort, -// IRQ, -// FIQ, + IRQ, + FIQ, }; uint32_t exchange(uint32_t next, SWISubversion subversion) { @@ -581,18 +574,25 @@ class ConcreteMachine: return result; } -// void reschedule(SWISubversion subversion) { -// upcoming_[active_ ^ 1].opcode = 0xef'000000; -// upcoming_[active_ ^ 1].subversion = subversion; -// } - SWISubversion swi_subversion() const { return latched_subversion_; } -// bool interrupt_next() const { -// return upcoming_[active_].subversion == SWISubversion::IRQ || upcoming_[active_].subversion == SWISubversion::FIQ; -// } + // TODO: one day, possibly: schedule the subversion one slot further into the future + // (i.e. active_ ^ 1) to allow one further instruction to occur as usual before the + // action paplies. That is, if interrupts take effect one instruction later after a flags + // change, which I don't yet know. + // + // In practice I got into a bit of a race condition between interrupt scheduling and + // flags changes, so have backed off for now. + void reschedule(SWISubversion subversion) { + upcoming_[active_].opcode = 0xef'000000; + upcoming_[active_].subversion = subversion; + } + + bool interrupt_next() const { + return upcoming_[active_].subversion == SWISubversion::IRQ || upcoming_[active_].subversion == SWISubversion::FIQ; + } private: struct Stage { From 0775e3ad58d26848c619779f1cf4724da0e9f598 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 19 Apr 2024 22:35:43 -0400 Subject: [PATCH 4/4] This is an 8-bit value. --- Machines/Acorn/Archimedes/Video.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Machines/Acorn/Archimedes/Video.hpp b/Machines/Acorn/Archimedes/Video.hpp index 87ab8cc08..b14f5b544 100644 --- a/Machines/Acorn/Archimedes/Video.hpp +++ b/Machines/Acorn/Archimedes/Video.hpp @@ -117,7 +117,7 @@ struct Video { } break; case 0xc0: - sound_.set_frequency(value & 0x7f); + sound_.set_frequency(value & 0xff); break; default: