From ab8e1fdcbfaa60290c3ee03e72f58678dbdaec06 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 10 May 2022 16:20:30 -0400 Subject: [PATCH] Take a swing at access faults and address errors. --- InstructionSets/M68k/ExceptionVectors.hpp | 50 ++++++++++++ InstructionSets/M68k/Executor.hpp | 7 ++ .../Implementation/ExecutorImplementation.hpp | 80 +++++++++++++++---- .../Clock Signal.xcodeproj/project.pbxproj | 2 + 4 files changed, 125 insertions(+), 14 deletions(-) create mode 100644 InstructionSets/M68k/ExceptionVectors.hpp diff --git a/InstructionSets/M68k/ExceptionVectors.hpp b/InstructionSets/M68k/ExceptionVectors.hpp new file mode 100644 index 000000000..83fbf2552 --- /dev/null +++ b/InstructionSets/M68k/ExceptionVectors.hpp @@ -0,0 +1,50 @@ +// +// ExceptionVectors.hpp +// Clock Signal +// +// Created by Thomas Harte on 10/05/2022. +// Copyright © 2022 Thomas Harte. All rights reserved. +// + +#ifndef InstructionSets_M68k_ExceptionVectors_hpp +#define InstructionSets_M68k_ExceptionVectors_hpp + +namespace InstructionSet { +namespace M68k { + +enum Exception { + InitialStackPointer = 0, + InitialProgramCounter = 1, + AccessFault = 2, + AddressError = 3, + IllegalInstruction = 4, + IntegerDivideByZero = 5, + CHK = 6, + TRAPV = 7, + PrivilegeViolation = 8, + Trace = 9, + Line1010 = 10, + Line1111 = 11, + CoprocessorProtocolViolation = 13, + FormatError = 14, + UninitialisedInterrupt = 15, + SpuriousInterrupt = 24, + InterruptAutovectorBase = 25, + TrapBase = 32, + FPBranchOrSetOnUnorderedCondition = 48, + FPInexactResult = 49, + FPDivideByZero = 50, + FPUnderflow = 51, + FPOperandError = 52, + FPOverflow = 53, + FPSignallingNAN = 54, + FPUnimplementedDataType = 55, + MMUConfigurationError = 56, + MMUIllegalOperationError = 57, + MMUAccessLevelViolationError = 58, +}; + +} +} + +#endif /* InstructionSets_M68k_ExceptionVectors_hpp */ diff --git a/InstructionSets/M68k/Executor.hpp b/InstructionSets/M68k/Executor.hpp index 543b05919..fc409ebf7 100644 --- a/InstructionSets/M68k/Executor.hpp +++ b/InstructionSets/M68k/Executor.hpp @@ -46,6 +46,10 @@ template class Executor: public NullFlowContr /// will not necessarily take effect immediately when signalled. void run_for_instructions(int); + /// Call this at any time to interrupt processing with a bus error; + /// the function code and address must be provided. + void signal_bus_error(FunctionCode, uint32_t address); + // Flow control; Cf. Perform.hpp. template void raise_exception(int); @@ -83,6 +87,8 @@ template class Executor: public NullFlowContr void set_state(const Registers &); private: + void run(int &); + BusHandler &bus_handler_; Predecoder decoder_; @@ -108,6 +114,7 @@ template class Executor: public NullFlowContr CPU::SlicedInt32 registers_[16]; // D0–D8, followed by A0–A8. CPU::SlicedInt32 stack_pointers_[2]; uint32_t instruction_address_; + uint16_t instruction_opcode_; int active_stack_pointer_ = 0; // A lookup table to ensure that A7 is adjusted by 2 rather than 1 in diff --git a/InstructionSets/M68k/Implementation/ExecutorImplementation.hpp b/InstructionSets/M68k/Implementation/ExecutorImplementation.hpp index bcf61c4d1..56759d738 100644 --- a/InstructionSets/M68k/Implementation/ExecutorImplementation.hpp +++ b/InstructionSets/M68k/Implementation/ExecutorImplementation.hpp @@ -10,6 +10,8 @@ #define InstructionSets_M68k_ExecutorImplementation_hpp #include "../Perform.hpp" +#include "../ExceptionVectors.hpp" + #include namespace InstructionSet { @@ -19,6 +21,9 @@ namespace M68k { #define Dn(x) registers_[x] #define sp An(7) +#define AccessException(code, address, vector) \ + uint64_t(((vector) << 8) | uint64_t(code) | ((address) << 16)) + template Executor::Executor(BusHandler &handler) : bus_handler_(handler) { reset(); @@ -31,23 +36,31 @@ void Executor::reset() { did_update_status(); // Seed stack pointer and program counter. - sp.l = read(0); + sp.l = read(0) & 0xffff'fffe; program_counter_.l = read(4); } template template IntT Executor::read(uint32_t address, bool is_from_pc) { - // TODO: check for an alignment exception, both here and in write. - // + const auto code = FunctionCode((status_.is_supervisor_ << 2) | 1 << int(is_from_pc)); + if(model == Model::M68000 && sizeof(IntT) > 1 && address & 1) { + throw AccessException(code, address, Exception::AddressError | (int(is_from_pc) << 3) | (1 << 4)); + } + // TODO: omit generation of the FunctionCode if the BusHandler doesn't receive it. - return bus_handler_.template read(address, FunctionCode((status_.is_supervisor_ << 2) | 1 << int(is_from_pc))); + return bus_handler_.template read(address, code); } template template void Executor::write(uint32_t address, IntT value) { - bus_handler_.template write(address, value, FunctionCode((status_.is_supervisor_ << 2) | 1)); + const auto code = FunctionCode((status_.is_supervisor_ << 2) | 1); + if(model == Model::M68000 && sizeof(IntT) > 1 && address & 1) { + throw AccessException(code, address, Exception::AddressError); + } + + bus_handler_.template write(address, value, code); } template @@ -209,31 +222,69 @@ typename Executor::EffectiveAddress Executor +void Executor::signal_bus_error(FunctionCode code, uint32_t address) { + throw AccessException(code, address, Exception::AccessFault); +} template void Executor::run_for_instructions(int count) { + while(count) { + try { + run(count); + } catch (uint64_t exception) { + // Unpack the exception; this is the converse of the AccessException macro. + const int vector_address = (exception >> 6) & 0xfc; + const uint16_t code = uint16_t(exception & 0xff); + const uint32_t faulting_address = uint32_t(exception >> 16); + + // Grab the status to store, then switch into supervisor mode. + const uint16_t status = status_.status(); + status_.is_supervisor_ = 1; + did_update_status(); + + // Push status and the program counter at instruction start. + write(sp.l - 14, code); + write(sp.l - 12, faulting_address); + write(sp.l - 8, instruction_opcode_); + write(sp.l - 6, status); + write(sp.l - 4, instruction_address_); + sp.l -= 14; + + // Fetch the new program counter; reset on a double fault. + try { + program_counter_.l = read(vector_address); + } catch (uint64_t) { + reset(); + } + } + } +} + +template +void Executor::run(int &count) { while(count--) { // TODO: check interrupt level, trace flag. // Read the next instruction. instruction_address_ = program_counter_.l; - const auto opcode = read_pc(); - const Preinstruction instruction = decoder_.decode(opcode); + instruction_opcode_ = read_pc(); + const Preinstruction instruction = decoder_.decode(instruction_opcode_); if(!status_.is_supervisor_ && instruction.requires_supervisor()) { - raise_exception(8); + raise_exception(Exception::PrivilegeViolation); continue; } if(instruction.operation == Operation::Undefined) { - switch(opcode & 0xf000) { + switch(instruction_opcode_ & 0xf000) { default: - raise_exception(4); + raise_exception(Exception::IllegalInstruction); continue; case 0xa000: - raise_exception(10); + raise_exception(Exception::Line1010); continue; case 0xf000: - raise_exception(11); + raise_exception(Exception::Line1111); continue; } } @@ -249,8 +300,8 @@ void Executor::run_for_instructions(int count) { // // TODO: much of this work should be performed by a full Decoder, // so that it can be cached. - effective_address_[0] = calculate_effective_address(instruction, opcode, 0); - effective_address_[1] = calculate_effective_address(instruction, opcode, 1); + effective_address_[0] = calculate_effective_address(instruction, instruction_opcode_, 0); + effective_address_[1] = calculate_effective_address(instruction, instruction_opcode_, 1); operand_[0] = effective_address_[0].value; operand_[1] = effective_address_[1].value; @@ -578,6 +629,7 @@ void Executor::movem_toR(Preinstruction instruction, uint32_t #undef sp #undef Dn #undef An +#undef AccessException } } diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index da91fc9bc..9dd005d94 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1480,6 +1480,7 @@ 4B7C6819275196E8001671EC /* MouseJoystick.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MouseJoystick.hpp; sourceTree = ""; }; 4B7C681C2751A104001671EC /* Bitplanes.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Bitplanes.cpp; sourceTree = ""; }; 4B7C681D2751A104001671EC /* Bitplanes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Bitplanes.hpp; sourceTree = ""; }; + 4B7C79FE282AFA9B002D6C0B /* ExceptionVectors.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ExceptionVectors.hpp; sourceTree = ""; }; 4B7F188C2154825D00388727 /* MasterSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MasterSystem.cpp; sourceTree = ""; }; 4B7F188D2154825D00388727 /* MasterSystem.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MasterSystem.hpp; sourceTree = ""; }; 4B7F1895215486A100388727 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = ""; }; @@ -3196,6 +3197,7 @@ children = ( 4B79629F2819681F008130F9 /* Decoder.cpp */, 4B79629E2819681F008130F9 /* Decoder.hpp */, + 4B7C79FE282AFA9B002D6C0B /* ExceptionVectors.hpp */, 4BB5B99C281C805300522DA9 /* Executor.hpp */, 4B79629C2819681F008130F9 /* Instruction.hpp */, 4B79629D2819681F008130F9 /* Model.hpp */,