diff --git a/InstructionSets/ARM/Executor.hpp b/InstructionSets/ARM/Executor.hpp index 4e96addc3..b867fe82c 100644 --- a/InstructionSets/ARM/Executor.hpp +++ b/InstructionSets/ARM/Executor.hpp @@ -29,11 +29,21 @@ DestinationT read_bus(SourceT value) { } } +struct NullStatusHandler { + void did_set_status() {} +}; + /// A class compatible with the @c OperationMapper definition of a scheduler which applies all actions /// immediately, updating either a set of @c Registers or using the templated @c MemoryT to access /// memory. No hooks are currently provided for applying realistic timing. -template +/// +/// If a StatusObserverT is specified, it'll receive calls to @c did_set_status() following every direct +/// write to the status bits — i.e. any change that can affect mode or interrupt flags. +template struct Executor { + template + Executor(StatusObserverT &observer, Args &&...args) : status_observer_(observer), bus(std::forward(args)...) {} + template Executor(Args &&...args) : bus(std::forward(args)...) {} @@ -193,6 +203,7 @@ struct Executor { // PSR flags which are not protected by virtue of the processor mode" if(fields.destination() == 15) { registers_.set_status(conditions); + status_observer_.did_set_status(); } else { // Set N and Z in a unified way. registers_.set_nz(conditions); @@ -546,6 +557,7 @@ struct Executor { registers_.set_pc(pc_proxy); if constexpr (flags.load_psr()) { registers_.set_status(pc_proxy); + status_observer_.did_set_status(); } } } @@ -601,6 +613,12 @@ struct Executor { MemoryT bus; private: + using StatusObserverTStorage = + typename std::conditional< + std::is_same_v, + StatusObserverT, + StatusObserverT &>::type; + StatusObserverTStorage status_observer_; Registers registers_; static bool is_invalid_address(uint32_t address) { @@ -613,8 +631,8 @@ private: /// Executes the instruction @c instruction which should have been fetched from @c executor.pc(), /// modifying @c executor. -template -void execute(uint32_t instruction, Executor &executor) { +template +void execute(uint32_t instruction, Executor &executor) { executor.set_pc(executor.pc() + 4); dispatch(instruction, executor); } diff --git a/Machines/Acorn/Archimedes/Archimedes.cpp b/Machines/Acorn/Archimedes/Archimedes.cpp index a95a00528..8fe046e60 100644 --- a/Machines/Acorn/Archimedes/Archimedes.cpp +++ b/Machines/Acorn/Archimedes/Archimedes.cpp @@ -390,7 +390,7 @@ class ConcreteMachine: ConcreteMachine( const Analyser::Static::Target &target, const ROMMachine::ROMFetcher &rom_fetcher - ) : executor_(*this, *this) { + ) : executor_(*this, *this, *this) { set_clock_rate(ClockRate); constexpr ROM::Name risc_os = ROM::Name::AcornRISCOS311; @@ -416,6 +416,10 @@ class ConcreteMachine: } } + void did_set_status() { + update_interrupts(); + } + void update_clock_rates() { video_divider_ = executor_.bus.video().clock_divider(); } @@ -501,7 +505,7 @@ class ConcreteMachine: // MARK: - ARM execution static constexpr auto arm_model = InstructionSet::ARM::Model::ARMv2; - using Executor = InstructionSet::ARM::Executor>; + using Executor = InstructionSet::ARM::Executor, ConcreteMachine>; Executor executor_; // MARK: - Yucky, temporary junk.