2022-05-01 19:10:54 +00:00
|
|
|
//
|
|
|
|
// ExecutorImplementation.hpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 01/05/2022.
|
|
|
|
// Copyright © 2022 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#ifndef InstructionSets_M68k_ExecutorImplementation_hpp
|
|
|
|
#define InstructionSets_M68k_ExecutorImplementation_hpp
|
|
|
|
|
2022-05-01 19:14:12 +00:00
|
|
|
#include "../Perform.hpp"
|
2022-05-01 19:10:54 +00:00
|
|
|
#include <cassert>
|
|
|
|
|
|
|
|
namespace InstructionSet {
|
|
|
|
namespace M68k {
|
|
|
|
|
2022-05-05 19:31:59 +00:00
|
|
|
#define sp() registers_[8 + 7]
|
|
|
|
|
2022-05-01 19:10:54 +00:00
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
Executor<model, BusHandler>::Executor(BusHandler &handler) : bus_handler_(handler) {
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
void Executor<model, BusHandler>::reset() {
|
|
|
|
// Establish: supervisor state, all interrupts blocked.
|
|
|
|
status_.set_status(0b0010'0011'1000'0000);
|
2022-05-03 01:27:58 +00:00
|
|
|
did_update_status();
|
2022-05-01 19:10:54 +00:00
|
|
|
|
|
|
|
// Seed stack pointer and program counter.
|
2022-05-05 19:31:59 +00:00
|
|
|
sp().l = bus_handler_.template read<uint32_t>(0);
|
2022-05-01 19:10:54 +00:00
|
|
|
program_counter_.l = bus_handler_.template read<uint32_t>(4);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
void Executor<model, BusHandler>::read(DataSize size, uint32_t address, CPU::SlicedInt32 &value) {
|
|
|
|
switch(size) {
|
|
|
|
case DataSize::Byte:
|
|
|
|
value.b = bus_handler_.template read<uint8_t>(address);
|
|
|
|
break;
|
|
|
|
case DataSize::Word:
|
|
|
|
value.w = bus_handler_.template read<uint16_t>(address);
|
|
|
|
break;
|
|
|
|
case DataSize::LongWord:
|
|
|
|
value.l = bus_handler_.template read<uint32_t>(address);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
void Executor<model, BusHandler>::write(DataSize size, uint32_t address, CPU::SlicedInt32 value) {
|
|
|
|
switch(size) {
|
|
|
|
case DataSize::Byte:
|
|
|
|
bus_handler_.template write<uint8_t>(address, value.b);
|
|
|
|
break;
|
|
|
|
case DataSize::Word:
|
|
|
|
bus_handler_.template write<uint16_t>(address, value.w);
|
|
|
|
break;
|
|
|
|
case DataSize::LongWord:
|
|
|
|
bus_handler_.template write<uint32_t>(address, value.l);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
template <typename IntT> IntT Executor<model, BusHandler>::read_pc() {
|
|
|
|
const IntT result = bus_handler_.template read<IntT>(program_counter_.l);
|
|
|
|
|
|
|
|
if constexpr (sizeof(IntT) == 4) {
|
|
|
|
program_counter_.l += 4;
|
|
|
|
} else {
|
|
|
|
program_counter_.l += 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
uint32_t Executor<model, BusHandler>::index_8bitdisplacement() {
|
|
|
|
// TODO: if not a 68000, check bit 8 for whether this should be a full extension word;
|
|
|
|
// also include the scale field even if not.
|
|
|
|
const auto extension = read_pc<uint16_t>();
|
|
|
|
const auto offset = int8_t(extension);
|
2022-05-02 19:09:59 +00:00
|
|
|
const int register_index = (extension >> 12) & 7;
|
2022-05-05 19:31:59 +00:00
|
|
|
const uint32_t displacement = registers_[register_index + ((extension >> 12) & 0x08)].l;
|
2022-05-02 19:09:59 +00:00
|
|
|
const uint32_t sized_displacement = (extension & 0x800) ? displacement : int16_t(displacement);
|
|
|
|
return offset + sized_displacement;
|
2022-05-01 19:10:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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:
|
2022-05-05 19:31:59 +00:00
|
|
|
ea.value = registers_[instruction.reg(index)];
|
2022-05-01 19:12:13 +00:00
|
|
|
ea.requires_fetch = false;
|
2022-05-01 19:10:54 +00:00
|
|
|
break;
|
|
|
|
case AddressingMode::AddressRegisterDirect:
|
2022-05-05 19:31:59 +00:00
|
|
|
ea.value = registers_[8 + instruction.reg(index)];
|
2022-05-01 19:12:13 +00:00
|
|
|
ea.requires_fetch = false;
|
2022-05-01 19:10:54 +00:00
|
|
|
break;
|
|
|
|
case AddressingMode::Quick:
|
2022-05-03 15:09:57 +00:00
|
|
|
ea.value.l = quick(opcode, instruction.operation);
|
2022-05-01 19:12:13 +00:00
|
|
|
ea.requires_fetch = false;
|
2022-05-01 19:10:54 +00:00
|
|
|
break;
|
|
|
|
case AddressingMode::ImmediateData:
|
2022-05-06 13:22:38 +00:00
|
|
|
switch(instruction.operand_size()) {
|
2022-05-03 00:17:44 +00:00
|
|
|
case DataSize::Byte:
|
|
|
|
ea.value.l = read_pc<uint16_t>() & 0xff;
|
|
|
|
break;
|
|
|
|
case DataSize::Word:
|
|
|
|
ea.value.l = read_pc<uint16_t>();
|
|
|
|
break;
|
|
|
|
case DataSize::LongWord:
|
|
|
|
ea.value.l = read_pc<uint32_t>();
|
|
|
|
break;
|
|
|
|
}
|
2022-05-01 19:12:13 +00:00
|
|
|
ea.requires_fetch = false;
|
2022-05-01 19:10:54 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Absolute addresses.
|
|
|
|
//
|
|
|
|
case AddressingMode::AbsoluteShort:
|
|
|
|
ea.value.l = int16_t(read_pc<uint16_t>());
|
2022-05-01 19:12:13 +00:00
|
|
|
ea.requires_fetch = true;
|
2022-05-01 19:10:54 +00:00
|
|
|
break;
|
|
|
|
case AddressingMode::AbsoluteLong:
|
|
|
|
ea.value.l = read_pc<uint32_t>();
|
2022-05-01 19:12:13 +00:00
|
|
|
ea.requires_fetch = true;
|
2022-05-01 19:10:54 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Address register indirects.
|
|
|
|
//
|
|
|
|
case AddressingMode::AddressRegisterIndirect:
|
2022-05-05 19:31:59 +00:00
|
|
|
ea.value = registers_[8 + instruction.reg(index)];
|
2022-05-01 19:12:13 +00:00
|
|
|
ea.requires_fetch = true;
|
2022-05-01 19:10:54 +00:00
|
|
|
break;
|
|
|
|
case AddressingMode::AddressRegisterIndirectWithPostincrement: {
|
|
|
|
const auto reg = instruction.reg(index);
|
|
|
|
|
2022-05-05 19:31:59 +00:00
|
|
|
ea.value = registers_[8 + reg];
|
2022-05-01 19:12:13 +00:00
|
|
|
ea.requires_fetch = true;
|
2022-05-01 19:10:54 +00:00
|
|
|
|
2022-05-06 13:22:38 +00:00
|
|
|
switch(instruction.operand_size()) {
|
2022-05-05 19:31:59 +00:00
|
|
|
case DataSize::Byte: registers_[8 + reg].l += byte_increments[reg]; break;
|
|
|
|
case DataSize::Word: registers_[8 + reg].l += 2; break;
|
|
|
|
case DataSize::LongWord: registers_[8 + reg].l += 4; break;
|
2022-05-01 19:10:54 +00:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case AddressingMode::AddressRegisterIndirectWithPredecrement: {
|
|
|
|
const auto reg = instruction.reg(index);
|
|
|
|
|
2022-05-06 13:22:38 +00:00
|
|
|
switch(instruction.operand_size()) {
|
2022-05-05 19:31:59 +00:00
|
|
|
case DataSize::Byte: registers_[8 + reg].l -= byte_increments[reg]; break;
|
|
|
|
case DataSize::Word: registers_[8 + reg].l -= 2; break;
|
|
|
|
case DataSize::LongWord: registers_[8 + reg].l -= 4; break;
|
2022-05-01 19:10:54 +00:00
|
|
|
}
|
|
|
|
|
2022-05-05 19:31:59 +00:00
|
|
|
ea.value = registers_[8 + reg];
|
2022-05-01 19:12:13 +00:00
|
|
|
ea.requires_fetch = true;
|
2022-05-01 19:10:54 +00:00
|
|
|
} break;
|
|
|
|
case AddressingMode::AddressRegisterIndirectWithDisplacement:
|
2022-05-05 19:31:59 +00:00
|
|
|
ea.value.l = registers_[8 + instruction.reg(index)].l + int16_t(read_pc<uint16_t>());
|
2022-05-01 19:12:13 +00:00
|
|
|
ea.requires_fetch = true;
|
2022-05-01 19:10:54 +00:00
|
|
|
break;
|
|
|
|
case AddressingMode::AddressRegisterIndirectWithIndex8bitDisplacement:
|
2022-05-05 19:31:59 +00:00
|
|
|
ea.value.l = registers_[8 + instruction.reg(index)].l + index_8bitdisplacement();
|
2022-05-01 19:12:13 +00:00
|
|
|
ea.requires_fetch = true;
|
2022-05-01 19:10:54 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
//
|
|
|
|
// PC-relative addresses.
|
|
|
|
//
|
|
|
|
// TODO: rephrase these in terms of instruction_address_. Just for security
|
|
|
|
// against whatever mutations the PC has been through already to get to here.
|
|
|
|
//
|
|
|
|
case AddressingMode::ProgramCounterIndirectWithDisplacement:
|
|
|
|
ea.value.l = program_counter_.l + int16_t(read_pc<uint16_t>());
|
2022-05-01 19:12:13 +00:00
|
|
|
ea.requires_fetch = true;
|
2022-05-01 19:10:54 +00:00
|
|
|
break;
|
|
|
|
case AddressingMode::ProgramCounterIndirectWithIndex8bitDisplacement:
|
|
|
|
ea.value.l = program_counter_.l + index_8bitdisplacement();
|
2022-05-01 19:12:13 +00:00
|
|
|
ea.requires_fetch = true;
|
2022-05-01 19:10:54 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
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.
|
|
|
|
instruction_address_ = program_counter_.l;
|
|
|
|
const auto opcode = read_pc<uint16_t>();
|
|
|
|
const Preinstruction instruction = decoder_.decode(opcode);
|
|
|
|
|
2022-05-03 01:27:58 +00:00
|
|
|
if(!status_.is_supervisor_ && instruction.requires_supervisor()) {
|
|
|
|
raise_exception(8);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(instruction.operation == Operation::Undefined) {
|
|
|
|
switch(opcode & 0xf000) {
|
|
|
|
default:
|
|
|
|
raise_exception(4);
|
|
|
|
continue;
|
|
|
|
case 0xa000:
|
|
|
|
raise_exception(10);
|
|
|
|
continue;
|
|
|
|
case 0xf000:
|
|
|
|
raise_exception(11);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2022-05-01 19:10:54 +00:00
|
|
|
|
|
|
|
// Temporary storage.
|
|
|
|
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.
|
|
|
|
//
|
2022-05-01 19:30:03 +00:00
|
|
|
// TODO: much of this work should be performed by a full Decoder,
|
|
|
|
// so that it can be cached.
|
2022-05-01 19:10:54 +00:00
|
|
|
effective_address_[0] = calculate_effective_address(instruction, opcode, 0);
|
|
|
|
effective_address_[1] = calculate_effective_address(instruction, opcode, 1);
|
|
|
|
operand_[0] = effective_address_[0].value;
|
|
|
|
operand_[1] = effective_address_[1].value;
|
|
|
|
|
|
|
|
// Obtain the appropriate sequence.
|
|
|
|
//
|
|
|
|
// TODO: make a decision about whether this goes into a fully-decoded Instruction.
|
2022-05-03 13:04:54 +00:00
|
|
|
const auto flags = operand_flags<model>(instruction.operation);
|
2022-05-01 19:10:54 +00:00
|
|
|
|
2022-05-03 13:04:54 +00:00
|
|
|
// TODO: potential alignment exception, here and in store.
|
2022-05-06 13:22:38 +00:00
|
|
|
#define fetch_operand(n) \
|
|
|
|
if(effective_address_[n].requires_fetch) { \
|
|
|
|
read(instruction.operand_size(), effective_address_[n].value.l, operand_[n]); \
|
2022-05-03 13:04:54 +00:00
|
|
|
}
|
2022-05-01 19:10:54 +00:00
|
|
|
|
2022-05-03 13:04:54 +00:00
|
|
|
if(flags & FetchOp1) { fetch_operand(0); }
|
|
|
|
if(flags & FetchOp2) { fetch_operand(1); }
|
2022-05-01 19:10:54 +00:00
|
|
|
|
2022-05-03 13:04:54 +00:00
|
|
|
#undef fetch_operand
|
2022-05-01 19:10:54 +00:00
|
|
|
|
2022-05-03 13:04:54 +00:00
|
|
|
perform<model>(instruction, operand_[0], operand_[1], status_, *this);
|
2022-05-01 19:10:54 +00:00
|
|
|
|
2022-05-05 19:31:59 +00:00
|
|
|
// TODO: rephrase to avoid conditional below.
|
2022-05-06 13:22:38 +00:00
|
|
|
#define store_operand(n) \
|
|
|
|
if(!effective_address_[n].requires_fetch) { \
|
|
|
|
if(instruction.mode(n) == AddressingMode::DataRegisterDirect) { \
|
|
|
|
registers_[instruction.reg(n)] = operand_[n]; \
|
|
|
|
} else { \
|
|
|
|
registers_[8 + instruction.reg(n)] = operand_[n]; \
|
|
|
|
} \
|
|
|
|
} else { \
|
|
|
|
write(instruction.operand_size(), effective_address_[n].value.l, operand_[n]); \
|
2022-05-03 13:04:54 +00:00
|
|
|
}
|
2022-05-01 19:10:54 +00:00
|
|
|
|
2022-05-03 13:04:54 +00:00
|
|
|
if(flags & StoreOp1) { store_operand(0); }
|
|
|
|
if(flags & StoreOp2) { store_operand(1); }
|
2022-05-01 19:10:54 +00:00
|
|
|
|
2022-05-03 13:04:54 +00:00
|
|
|
#undef store_operand
|
2022-05-01 19:10:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-02 16:57:45 +00:00
|
|
|
// MARK: - State
|
|
|
|
|
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
typename Executor<model, BusHandler>::Registers Executor<model, BusHandler>::get_state() {
|
|
|
|
Registers result;
|
|
|
|
|
|
|
|
for(int c = 0; c < 8; c++) {
|
2022-05-05 19:31:59 +00:00
|
|
|
result.data[c] = registers_[c].l;
|
2022-05-02 16:57:45 +00:00
|
|
|
}
|
|
|
|
for(int c = 0; c < 7; c++) {
|
2022-05-05 19:31:59 +00:00
|
|
|
result.address[c] = registers_[8 + c].l;
|
2022-05-02 16:57:45 +00:00
|
|
|
}
|
|
|
|
result.status = status_.status();
|
|
|
|
result.program_counter = program_counter_.l;
|
|
|
|
|
2022-05-05 19:31:59 +00:00
|
|
|
stack_pointers_[status_.is_supervisor_] = sp();
|
2022-05-02 16:57:45 +00:00
|
|
|
result.user_stack_pointer = stack_pointers_[0].l;
|
|
|
|
result.supervisor_stack_pointer = stack_pointers_[1].l;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
void Executor<model, BusHandler>::set_state(const Registers &state) {
|
|
|
|
for(int c = 0; c < 8; c++) {
|
2022-05-05 19:31:59 +00:00
|
|
|
registers_[c].l = state.data[c];
|
2022-05-02 16:57:45 +00:00
|
|
|
}
|
|
|
|
for(int c = 0; c < 7; c++) {
|
2022-05-05 19:31:59 +00:00
|
|
|
registers_[8 + c].l = state.address[c];
|
2022-05-02 16:57:45 +00:00
|
|
|
}
|
|
|
|
status_.set_status(state.status);
|
|
|
|
program_counter_.l = state.program_counter;
|
|
|
|
|
|
|
|
stack_pointers_[0].l = state.user_stack_pointer;
|
|
|
|
stack_pointers_[1].l = state.supervisor_stack_pointer;
|
2022-05-05 19:31:59 +00:00
|
|
|
sp() = stack_pointers_[status_.is_supervisor_];
|
2022-05-02 16:57:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Flow Control.
|
2022-05-02 11:45:07 +00:00
|
|
|
// TODO: flow control, all below here.
|
|
|
|
|
|
|
|
template <Model model, typename BusHandler>
|
2022-05-03 01:27:58 +00:00
|
|
|
void Executor<model, BusHandler>::raise_exception(int index) {
|
|
|
|
const uint32_t address = index << 2;
|
|
|
|
|
|
|
|
// 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.
|
2022-05-05 19:31:59 +00:00
|
|
|
bus_handler_.template write<uint32_t>(sp().l - 4, instruction_address_);
|
|
|
|
bus_handler_.template write<uint16_t>(sp().l - 6, status);
|
|
|
|
sp().l -= 6;
|
2022-05-03 01:27:58 +00:00
|
|
|
|
|
|
|
// Fetch the new program counter.
|
|
|
|
program_counter_.l = bus_handler_.template read<uint32_t>(address);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
void Executor<model, BusHandler>::did_update_status() {
|
|
|
|
// Shuffle the stack pointers.
|
2022-05-05 19:31:59 +00:00
|
|
|
stack_pointers_[active_stack_pointer_] = sp();
|
|
|
|
sp() = stack_pointers_[status_.is_supervisor_];
|
2022-05-03 01:27:58 +00:00
|
|
|
active_stack_pointer_ = status_.is_supervisor_;
|
|
|
|
}
|
2022-05-02 11:45:07 +00:00
|
|
|
|
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
void Executor<model, BusHandler>::stop() {}
|
|
|
|
|
|
|
|
template <Model model, typename BusHandler>
|
2022-05-03 18:40:51 +00:00
|
|
|
void Executor<model, BusHandler>::set_pc(uint32_t address) {
|
|
|
|
program_counter_.l = address;
|
|
|
|
}
|
2022-05-02 11:45:07 +00:00
|
|
|
|
|
|
|
template <Model model, typename BusHandler>
|
2022-05-03 18:40:51 +00:00
|
|
|
void Executor<model, BusHandler>::add_pc(uint32_t offset) {
|
2022-05-03 19:21:42 +00:00
|
|
|
program_counter_.l = instruction_address_ + offset;
|
2022-05-03 18:40:51 +00:00
|
|
|
}
|
2022-05-02 11:45:07 +00:00
|
|
|
|
2022-05-03 19:32:54 +00:00
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
void Executor<model, BusHandler>::bsr(uint32_t offset) {
|
2022-05-05 19:31:59 +00:00
|
|
|
sp().l -= 4;
|
|
|
|
bus_handler_.template write<uint32_t>(sp().l, program_counter_.l);
|
2022-05-03 19:32:54 +00:00
|
|
|
program_counter_.l = instruction_address_ + offset;
|
|
|
|
}
|
|
|
|
|
2022-05-03 19:49:55 +00:00
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
void Executor<model, BusHandler>::jsr(uint32_t address) {
|
2022-05-05 19:31:59 +00:00
|
|
|
sp().l -= 4;
|
|
|
|
bus_handler_.template write<uint32_t>(sp().l, program_counter_.l);
|
2022-05-03 19:49:55 +00:00
|
|
|
program_counter_.l = address;
|
|
|
|
}
|
|
|
|
|
2022-05-04 12:26:11 +00:00
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
void Executor<model, BusHandler>::link(uint32_t &address, uint32_t offset) {
|
2022-05-05 19:31:59 +00:00
|
|
|
sp().l -= 4;
|
|
|
|
bus_handler_.template write<uint32_t>(sp().l, address);
|
|
|
|
address = sp().l;
|
|
|
|
sp().l += offset;
|
2022-05-04 12:26:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
void Executor<model, BusHandler>::unlink(uint32_t &address) {
|
2022-05-05 19:31:59 +00:00
|
|
|
sp().l = address;
|
|
|
|
address = bus_handler_.template read<uint32_t>(sp().l);
|
|
|
|
sp().l += 4;
|
2022-05-04 12:26:11 +00:00
|
|
|
}
|
|
|
|
|
2022-05-06 15:33:57 +00:00
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
void Executor<model, BusHandler>::pea(uint32_t address) {
|
|
|
|
sp().l -= 4;
|
|
|
|
bus_handler_.template write<uint32_t>(sp().l, address);
|
|
|
|
}
|
|
|
|
|
2022-05-06 16:18:56 +00:00
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
void Executor<model, BusHandler>::tas(uint32_t address) {
|
|
|
|
uint8_t value = bus_handler_.template read<uint8_t>(address);
|
|
|
|
|
|
|
|
status_.overflow_flag_ = status_.carry_flag_ = 0;
|
|
|
|
status_.zero_result_ = value;
|
|
|
|
status_.negative_flag_ = value & 0x80;
|
|
|
|
value |= 0x80;
|
|
|
|
|
|
|
|
bus_handler_.template write<uint8_t>(address, value);
|
|
|
|
}
|
|
|
|
|
2022-05-05 13:26:26 +00:00
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
template <typename IntT>
|
2022-05-05 16:27:36 +00:00
|
|
|
void Executor<model, BusHandler>::movep(Preinstruction instruction, uint32_t source, uint32_t dest) {
|
|
|
|
if(instruction.mode<0>() == AddressingMode::DataRegisterDirect) {
|
2022-05-05 16:42:57 +00:00
|
|
|
// Move register to memory.
|
2022-05-05 16:27:36 +00:00
|
|
|
const uint32_t reg = source;
|
|
|
|
uint32_t address = dest;
|
|
|
|
|
|
|
|
if constexpr (sizeof(IntT) == 4) {
|
|
|
|
bus_handler_.template write<uint8_t>(address, uint8_t(reg >> 24));
|
|
|
|
address += 2;
|
|
|
|
|
|
|
|
bus_handler_.template write<uint8_t>(address, uint8_t(reg >> 16));
|
|
|
|
address += 2;
|
|
|
|
}
|
2022-05-05 13:26:26 +00:00
|
|
|
|
2022-05-05 16:27:36 +00:00
|
|
|
bus_handler_.template write<uint8_t>(address, uint8_t(reg >> 8));
|
2022-05-05 13:26:26 +00:00
|
|
|
address += 2;
|
|
|
|
|
2022-05-05 16:27:36 +00:00
|
|
|
bus_handler_.template write<uint8_t>(address, uint8_t(reg));
|
|
|
|
} else {
|
|
|
|
// Move memory to register.
|
2022-05-05 19:31:59 +00:00
|
|
|
uint32_t ® = registers_[instruction.reg<1>()].l;
|
2022-05-05 16:27:36 +00:00
|
|
|
uint32_t address = source;
|
|
|
|
|
|
|
|
if constexpr (sizeof(IntT) == 4) {
|
|
|
|
reg = bus_handler_.template read<uint8_t>(address) << 24;
|
|
|
|
address += 2;
|
|
|
|
|
2022-05-05 16:37:47 +00:00
|
|
|
reg |= bus_handler_.template read<uint8_t>(address) << 16;
|
2022-05-05 16:27:36 +00:00
|
|
|
address += 2;
|
|
|
|
} else {
|
|
|
|
reg &= 0xffff0000;
|
|
|
|
}
|
2022-05-05 13:26:26 +00:00
|
|
|
|
2022-05-05 16:27:36 +00:00
|
|
|
reg |= bus_handler_.template read<uint8_t>(address) << 8;
|
2022-05-05 13:26:26 +00:00
|
|
|
address += 2;
|
|
|
|
|
2022-05-05 16:27:36 +00:00
|
|
|
reg |= bus_handler_.template read<uint8_t>(address);
|
2022-05-05 13:26:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-05 16:42:57 +00:00
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
template <typename IntT>
|
2022-05-06 13:45:06 +00:00
|
|
|
void Executor<model, BusHandler>::movem_toM(Preinstruction instruction, uint32_t source, uint32_t dest) {
|
|
|
|
// Move registers to memory. This is the only permitted use of the predecrement mode,
|
|
|
|
// which reverses output order.
|
|
|
|
|
|
|
|
if(instruction.mode<1>() == AddressingMode::AddressRegisterIndirectWithPredecrement) {
|
|
|
|
// The structure of the code in the mainline part of the executor is such
|
|
|
|
// that the address register will already have been predecremented before
|
|
|
|
// reaching here, and it'll have been by two bytes per the operand size
|
|
|
|
// rather than according to the instruction size. That's not wanted, so undo it.
|
|
|
|
//
|
|
|
|
// TODO: with the caveat that the 68020+ have different behaviour:
|
|
|
|
//
|
|
|
|
// "For the MC68020, MC68030, MC68040, and CPU32, if the addressing register is also
|
|
|
|
// moved to memory, the value written is the initial register value decremented by the
|
|
|
|
// size of the operation. The MC68000 and MC68010 write the initial register value
|
|
|
|
// (not decremented)."
|
|
|
|
registers_[8 + instruction.reg<1>()].l += 2;
|
2022-05-05 22:51:29 +00:00
|
|
|
|
2022-05-06 13:45:06 +00:00
|
|
|
uint32_t reg = registers_[8 + instruction.reg<1>()].l;
|
|
|
|
int index = 15;
|
2022-05-05 22:51:29 +00:00
|
|
|
|
|
|
|
while(source) {
|
|
|
|
if(source & 1) {
|
2022-05-06 13:45:06 +00:00
|
|
|
reg -= sizeof(IntT);
|
|
|
|
bus_handler_.template write<IntT>(reg, IntT(registers_[index].l));
|
2022-05-05 22:51:29 +00:00
|
|
|
}
|
2022-05-06 13:45:06 +00:00
|
|
|
--index;
|
2022-05-05 22:51:29 +00:00
|
|
|
source >>= 1;
|
|
|
|
}
|
|
|
|
|
2022-05-06 13:45:06 +00:00
|
|
|
registers_[8 + instruction.reg<1>()].l = reg;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int index = 0;
|
|
|
|
while(source) {
|
|
|
|
if(source & 1) {
|
|
|
|
bus_handler_.template write<IntT>(dest, IntT(registers_[index].l));
|
|
|
|
dest += sizeof(IntT);
|
|
|
|
}
|
|
|
|
++index;
|
|
|
|
source >>= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Model model, typename BusHandler>
|
|
|
|
template <typename IntT>
|
|
|
|
void Executor<model, BusHandler>::movem_toR(Preinstruction instruction, uint32_t source, uint32_t dest) {
|
|
|
|
// Move memory to registers.
|
|
|
|
//
|
|
|
|
// A 68000 convention has been broken here; the instruction form is:
|
|
|
|
// MOVEM <ea>, #
|
|
|
|
// ... but the instruction is encoded as [MOVEM] [#] [ea].
|
|
|
|
//
|
|
|
|
// This project's decoder decodes as #, <ea>.
|
|
|
|
int index = 0;
|
|
|
|
while(source) {
|
|
|
|
if(source & 1) {
|
|
|
|
if constexpr (sizeof(IntT) == 2) {
|
|
|
|
registers_[index].l = int16_t(bus_handler_.template read<uint16_t>(dest));
|
|
|
|
} else {
|
|
|
|
registers_[index].l = bus_handler_.template read<uint32_t>(dest);
|
2022-05-06 00:32:21 +00:00
|
|
|
}
|
2022-05-06 13:45:06 +00:00
|
|
|
dest += sizeof(IntT);
|
2022-05-06 00:32:21 +00:00
|
|
|
}
|
2022-05-06 13:45:06 +00:00
|
|
|
++index;
|
|
|
|
source >>= 1;
|
|
|
|
}
|
2022-05-06 00:32:21 +00:00
|
|
|
|
2022-05-06 13:45:06 +00:00
|
|
|
if(instruction.mode<1>() == AddressingMode::AddressRegisterIndirectWithPostincrement) {
|
|
|
|
// "If the effective address is specified by the postincrement mode ...
|
|
|
|
// [i]f the addressing register is also loaded from memory, the memory value is
|
|
|
|
// ignored and the register is written with the postincremented effective address."
|
2022-05-06 01:06:53 +00:00
|
|
|
|
2022-05-06 13:56:01 +00:00
|
|
|
registers_[8 + instruction.reg<1>()].l = dest;
|
2022-05-05 16:42:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-05 19:31:59 +00:00
|
|
|
#undef sp
|
|
|
|
|
2022-05-01 19:10:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* InstructionSets_M68k_ExecutorImplementation_hpp */
|