diff --git a/InstructionSets/ARM/Disassembler.hpp b/InstructionSets/ARM/Disassembler.hpp index 6f744e426..5dfc4b5ee 100644 --- a/InstructionSets/ARM/Disassembler.hpp +++ b/InstructionSets/ARM/Disassembler.hpp @@ -255,7 +255,7 @@ struct Disassembler { template void perform(CoprocessorDataOperation) {} template void perform(CoprocessorDataTransfer) {} - void software_interrupt() { + void software_interrupt(SoftwareInterrupt) { instruction_.operation = Instruction::Operation::SWI; } void unknown() { diff --git a/InstructionSets/ARM/Executor.hpp b/InstructionSets/ARM/Executor.hpp index 20ccad951..16c947d65 100644 --- a/InstructionSets/ARM/Executor.hpp +++ b/InstructionSets/ARM/Executor.hpp @@ -29,20 +29,28 @@ DestinationT read_bus(SourceT value) { } } -struct NullStatusHandler { +struct NullControlFlowHandler { + /// Indicates that a potential pipeline-affecting status flag change occurred, + /// i.e. a change to processor mode or interrupt flags. void did_set_status() {} + + /// Indicates that the PC was altered by the instruction. + void did_set_pc() {} + + /// Provides notification that an SWI is about to happen along with the option of skipping it; this gives handlers the + /// chance to substitute a high-level reimplementation of the service call. + bool should_swi([[maybe_unused]] uint32_t comment) { return true; } }; /// 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. /// -/// 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 +/// If a ControlFlowHandlerT is specified, it'll receive calls as defined in the NullControlFlowHandler above. +template struct Executor { template - Executor(StatusObserverT &observer, Args &&...args) : bus(std::forward(args)...), status_observer_(observer) {} + Executor(ControlFlowHandlerT &handler, Args &&...args) : bus(std::forward(args)...), control_flow_handler_(handler) {} template Executor(Args &&...args) : bus(std::forward(args)...) {} @@ -194,7 +202,7 @@ struct Executor { } if(!is_comparison(flags.operation()) && fields.destination() == 15) { - registers_.set_pc(pc_proxy); + set_pc(pc_proxy); } if constexpr (flags.set_condition_codes()) { // "When Rd is R15 and the S flag in the instruction is set, the PSR is overwritten by the @@ -202,8 +210,7 @@ struct Executor { // normally produce a result (CMP, CMN, TST, TEQ) ... the result will be used to update those // 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(); + set_status(conditions); } else { // Set N and Z in a unified way. registers_.set_nz(conditions); @@ -249,7 +256,7 @@ struct Executor { if constexpr (flags.operation() == BranchFlags::Operation::BL) { registers_[14] = registers_.pc_status(0); } - registers_.set_pc(registers_.pc(4) + branch.offset()); + set_pc(registers_.pc(4) + branch.offset()); } template void perform(SingleDataTransfer transfer) { @@ -288,7 +295,7 @@ struct Executor { // Check for an address exception. if(is_invalid_address(address)) { - registers_.exception(); + exception(); return; } @@ -322,7 +329,7 @@ struct Executor { } if(!did_write) { - registers_.exception(); + exception(); return; } } else { @@ -352,12 +359,12 @@ struct Executor { } if(!did_read) { - registers_.exception(); + exception(); return; } if(transfer.destination() == 15) { - registers_.set_pc(value); + set_pc(value); } else { registers_[transfer.destination()] = value; } @@ -368,7 +375,7 @@ struct Executor { // So if this is a load, don't allow write back to overwrite what was loaded. if(flags.operation() == SingleDataTransferFlags::Operation::STR || transfer.base() != transfer.destination()) { if(transfer.base() == 15) { - registers_.set_pc(offsetted_address); + set_pc(offsetted_address); } else { registers_[transfer.base()] = offsetted_address; } @@ -548,37 +555,38 @@ struct Executor { // Finally throw an exception if necessary. if(address_error) { - registers_.exception(); + exception(); } else if(!accesses_succeeded) { - registers_.exception(); + exception(); } else { // If this was an LDM to R15 then apply it appropriately. if(is_ldm && list & (1 << 15)) { - registers_.set_pc(pc_proxy); + set_pc(pc_proxy); if constexpr (flags.load_psr()) { - registers_.set_status(pc_proxy); - status_observer_.did_set_status(); + set_status(pc_proxy); } } } } - void software_interrupt() { - registers_.exception(); + void software_interrupt(SoftwareInterrupt swi) { + if(control_flow_handler_.should_swi(swi.comment())) { + exception(); + } } void unknown() { - registers_.exception(); + exception(); } // Act as if no coprocessors present. template void perform(CoprocessorRegisterTransfer) { - registers_.exception(); + exception(); } template void perform(CoprocessorDataOperation) { - registers_.exception(); + exception(); } template void perform(CoprocessorDataTransfer) { - registers_.exception(); + exception(); } /// @returns The current registers state. @@ -594,13 +602,19 @@ struct Executor { /// Indicates a prefetch abort exception. void prefetch_abort() { - registers_.exception(); + exception(); } /// Sets the expected address of the instruction after whichever is about to be executed. /// So it's PC+4 compared to most other systems. + /// + /// By default this is not forwarded to the control-flow handler. + template void set_pc(uint32_t pc) { registers_.set_pc(pc); + if constexpr (notify) { + control_flow_handler_.did_set_pc(); + } } /// @returns The address of the instruction that should be fetched next. So as execution of each instruction @@ -613,12 +627,23 @@ struct Executor { MemoryT bus; private: - using StatusObserverTStorage = + template + void exception() { + registers_.exception(); + control_flow_handler_.did_set_pc(); + } + + void set_status(uint32_t status) { + registers_.set_status(status); + control_flow_handler_.did_set_status(); + } + + using ControlFlowHandlerTStorage = typename std::conditional< - std::is_same_v, - StatusObserverT, - StatusObserverT &>::type; - StatusObserverTStorage status_observer_; + std::is_same_v, + ControlFlowHandlerT, + ControlFlowHandlerT &>::type; + ControlFlowHandlerTStorage control_flow_handler_; Registers registers_; static bool is_invalid_address(uint32_t address) { diff --git a/InstructionSets/ARM/OperationMapper.hpp b/InstructionSets/ARM/OperationMapper.hpp index 08dbf411c..c136abbbe 100644 --- a/InstructionSets/ARM/OperationMapper.hpp +++ b/InstructionSets/ARM/OperationMapper.hpp @@ -388,6 +388,19 @@ private: uint8_t flags_; }; +// +// Software interrupt. +// +struct SoftwareInterrupt { + constexpr SoftwareInterrupt(uint32_t opcode) noexcept : opcode_(opcode) {} + + /// The 24-bit comment field, often decoded by the receiver of an SWI. + uint32_t comment() const { return opcode_ & 0xff'ffff; } + +private: + uint32_t opcode_; +}; + struct CoprocessorDataTransfer { constexpr CoprocessorDataTransfer(uint32_t opcode) noexcept : opcode_(opcode) {} @@ -461,7 +474,7 @@ struct OperationMapper { // Software interreupt; cf. p.35. if constexpr (((partial >> 24) & 0b1111) == 0b1111) { - scheduler.software_interrupt(); + scheduler.software_interrupt(SoftwareInterrupt(instruction)); return; } @@ -517,7 +530,7 @@ struct SampleScheduler { template void perform(CoprocessorDataTransfer); // Irregular operations. - void software_interrupt(); + void software_interrupt(SoftwareInterrupt); void unknown(); }; diff --git a/Machines/Acorn/Archimedes/Archimedes.cpp b/Machines/Acorn/Archimedes/Archimedes.cpp index b352ddb89..0c33202a9 100644 --- a/Machines/Acorn/Archimedes/Archimedes.cpp +++ b/Machines/Acorn/Archimedes/Archimedes.cpp @@ -420,6 +420,13 @@ class ConcreteMachine: update_interrupts(); } + void did_set_pc() { + } + + bool should_swi(uint32_t) { + return true; + } + void update_clock_rates() { video_divider_ = executor_.bus.video().clock_divider(); }