diff --git a/InstructionSets/M68k/Executor.cpp b/InstructionSets/M68k/Executor.cpp index 242da4a07..926271cc0 100644 --- a/InstructionSets/M68k/Executor.cpp +++ b/InstructionSets/M68k/Executor.cpp @@ -21,24 +21,83 @@ Executor<model, BusHandler>::Executor(BusHandler &handler) : bus_handler_(handle template <Model model, typename BusHandler> void Executor<model, BusHandler>::reset() { - // TODO. + // Establish: supervisor state, all interrupts blocked. + status_.set_status(0b0010'0011'1000'0000); + + // Seed stack pointer and program counter. + data_[7] = bus_handler_.template read<uint32_t>(0); + program_counter_.l = bus_handler_.template read<uint32_t>(4); } +template <Model model, typename BusHandler> +typename Executor<model, BusHandler>::EffectiveAddress Executor<model, BusHandler>::calculate_effective_address(Preinstruction instruction, uint16_t opcode, int index) { + EffectiveAddress ea; + switch(instruction.mode(index)) { + case AddressingMode::None: + // Permit an uninitialised effective address to be returned; + // this value shouldn't be used. + break; + + // + // Operands that don't have effective addresses, which are returned as values. + // + case AddressingMode::DataRegisterDirect: + ea.value = data_[instruction.reg(index)]; + ea.is_address = false; + break; + case AddressingMode::AddressRegisterDirect: + ea.value = address_[instruction.reg(index)]; + ea.is_address = false; + break; + case AddressingMode::Quick: + ea.value = quick(instruction.operation, opcode); + ea.is_address = false; + break; + + // + // Operands that are effective addresses. + // + + default: + // TODO. + assert(false); + break; + } + + return ea; +} + + template <Model model, typename BusHandler> void Executor<model, BusHandler>::run_for_instructions(int count) { while(count--) { // TODO: check interrupt level, trace flag. // Read the next instruction. - const Preinstruction instruction = decoder_.decode(bus_handler_.template read<uint16_t>(program_counter_.l)); const auto instruction_address = program_counter_.l; + const uint16_t opcode = bus_handler_.template read<uint16_t>(program_counter_.l); + const Preinstruction instruction = decoder_.decode(opcode); program_counter_.l += 2; - // Obtain the appropriate sequence. - Sequence<model> sequence(instruction.operation); - // Temporary storage. - CPU::SlicedInt32 source, dest; + CPU::SlicedInt32 operand_[2]; + EffectiveAddress effective_address_[2]; + + // Calculate effective addresses; copy 'addresses' into the + // operands by default both: (i) because they might be values, + // rather than addresses; and (ii) then they'll be there for use + // by LEA and PEA. + // + // TODO: 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); + operand_[0].l = effective_address_[0].value; + operand_[1].l = effective_address_[1].value; + + // Obtain the appropriate sequence. + // + // TODO: make a decision about whether this goes into a fully-decoded Instruction. + Sequence<model> sequence(instruction.operation); // Perform it. while(!sequence.empty()) { @@ -47,9 +106,67 @@ void Executor<model, BusHandler>::run_for_instructions(int count) { switch(step) { default: assert(false); // i.e. TODO + case Step::FetchOp1: + case Step::FetchOp2: { + const auto index = int(step) & 1; + + // If the operand wasn't indirect, it's already fetched. + if(!effective_address_[index].is_address) continue; + + // TODO: potential bus alignment exception. + + switch(instruction.size()) { + case DataSize::Byte: + operand_[index].l = bus_handler_.template read<uint8_t>(effective_address_[index].value); + break; + case DataSize::Word: + operand_[index].l = bus_handler_.template read<uint16_t>(effective_address_[index].value); + break; + case DataSize::LongWord: + operand_[index].l = bus_handler_.template read<uint32_t>(effective_address_[index].value); + break; + } + } break; + case Step::Perform: - perform<model>(instruction, source, dest, status_, this); + perform<model>(instruction, operand_[0], operand_[1], status_, this); break; + + case Step::StoreOp1: + case Step::StoreOp2: { + const auto index = int(step) & 1; + + // If the operand wasn't indirect, it's already fetched. + if(!effective_address_[index].is_address) { + // This must be either address or data register indirect. + assert( + instruction.mode(index) == AddressingMode::DataRegisterDirect || + instruction.mode(index) == AddressingMode::AddressRegisterDirect); + + // TODO: is it worth holding registers as a single block to avoid this conditional? + if(instruction.mode(index) == AddressingMode::DataRegisterDirect) { + data_[instruction.reg(index)] = operand_[index]; + } else { + address_[instruction.reg(index)] = operand_[index]; + } + + break; + } + + // TODO: potential bus alignment exception. + + switch(instruction.size()) { + case DataSize::Byte: + bus_handler_.write(effective_address_[index].value, operand_[index].b); + break; + case DataSize::Word: + bus_handler_.write(effective_address_[index].value, operand_[index].w); + break; + case DataSize::LongWord: + bus_handler_.write(effective_address_[index].value, operand_[index].l); + break; + } + } break; } } } diff --git a/InstructionSets/M68k/Executor.hpp b/InstructionSets/M68k/Executor.hpp index b9a28ebec..52ae19ec7 100644 --- a/InstructionSets/M68k/Executor.hpp +++ b/InstructionSets/M68k/Executor.hpp @@ -42,6 +42,11 @@ template <Model model, typename BusHandler> class Executor { Predecoder<model> decoder_; void reset(); + struct EffectiveAddress { + uint32_t value; + bool is_address; + }; + EffectiveAddress calculate_effective_address(Preinstruction instruction, uint16_t opcode, int index); // Processor state. Status status_; diff --git a/InstructionSets/M68k/Instruction.hpp b/InstructionSets/M68k/Instruction.hpp index 252fb5e77..4e9236eda 100644 --- a/InstructionSets/M68k/Instruction.hpp +++ b/InstructionSets/M68k/Instruction.hpp @@ -356,27 +356,37 @@ class Preinstruction { // For one-operand instructions, only argument 0 will // be provided, and will be a source and/or destination as // per the semantics of the operation. + // + // The versions templated on index do a range check; + // if using the runtime versions then results for indices + // other than 0 and 1 are undefined. + AddressingMode mode(int index) const { + return AddressingMode(operands_[index] & 0x1f); + } template <int index> AddressingMode mode() const { if constexpr (index > 1) { return AddressingMode::None; } - return AddressingMode(operands_[index] & 0x1f); + return mode(index); + } + int reg(int index) const { + return operands_[index] >> 5; } template <int index> int reg() const { if constexpr (index > 1) { return 0; } - return operands_[index] >> 5; + return reg(index); } - bool requires_supervisor() { + bool requires_supervisor() const { return flags_ & 0x80; } - DataSize size() { + DataSize size() const { return DataSize(flags_ & 0x03); } - Condition condition() { + Condition condition() const { return Condition((flags_ >> 2) & 0x0f); } diff --git a/InstructionSets/M68k/Sequence.cpp b/InstructionSets/M68k/Sequence.cpp index 07ec25bb4..ef95f9c37 100644 --- a/InstructionSets/M68k/Sequence.cpp +++ b/InstructionSets/M68k/Sequence.cpp @@ -28,7 +28,6 @@ template<Model model> uint32_t Sequence<model>::steps_for(Operation operation) { // case Operation::LEA: return Steps< - Step::CalcEA1, Step::Perform >::value; diff --git a/InstructionSets/M68k/Sequence.hpp b/InstructionSets/M68k/Sequence.hpp index 2c011cf31..922150654 100644 --- a/InstructionSets/M68k/Sequence.hpp +++ b/InstructionSets/M68k/Sequence.hpp @@ -15,7 +15,7 @@ namespace InstructionSet { namespace M68k { -/// Additional guarantees: [Fetch/Store/CalcEA][1/2] have an LSB of 0 for +/// Additional guarantees: [Fetch/Store][1/2] have an LSB of 0 for /// operand 1, and an LSB of 1 for operand 2. enum class Step { /// No further steps remain. @@ -30,10 +30,6 @@ enum class Step { StoreOp1, /// Store the value of operand 2. StoreOp2, - /// Calculate effective address of operand 1. - CalcEA1, - /// Calculate effective address of operand 2. - CalcEA2, /// A catch-all for bus activity that doesn't fit the pattern /// of fetch/stop operand 1/2, e.g. this opaquely covers almost /// the entirety of MOVEM. diff --git a/InstructionSets/M68k/Status.hpp b/InstructionSets/M68k/Status.hpp index 8e7652762..f28489088 100644 --- a/InstructionSets/M68k/Status.hpp +++ b/InstructionSets/M68k/Status.hpp @@ -22,7 +22,7 @@ struct Status { int is_supervisor_ = 0; // 1 => processor is in supervisor mode; 0 => it isn't. /* b7–b9 */ - int interrupt_level_ = 0; // The direct integer value of thee current interrupt level. + int interrupt_level_ = 0; // The direct integer value of the current interrupt level. /* b0–b4 */ uint_fast32_t zero_result_ = 0; // The zero flag is set if this value is zero.