1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Attempt to round out addressing modes, shift to a header, as per templating on BusHandler.

This commit is contained in:
Thomas Harte 2022-05-01 15:10:54 -04:00
parent 0b19bbff8d
commit 6b073c6067
5 changed files with 301 additions and 185 deletions

View File

@ -8,182 +8,3 @@
#include "Executor.hpp"
#include "Perform.hpp"
#include <cassert>
using namespace InstructionSet::M68k;
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);
// 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>
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>
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.l = data_[instruction.reg(index)];
ea.is_address = false;
break;
case AddressingMode::AddressRegisterDirect:
ea.value.l = address_[instruction.reg(index)];
ea.is_address = false;
break;
case AddressingMode::Quick:
ea.value.l = quick(instruction.operation, opcode);
ea.is_address = false;
break;
case AddressingMode::ImmediateData:
read(instruction.size(), program_counter_.l, ea.value.l);
program_counter_.l += (instruction.size() == DataSize::LongWord) ? 4 : 2;
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 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;
// TODO: check privilege level.
// 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.
//
// 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] = 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.
Sequence<model> sequence(instruction.operation);
// Perform it.
while(!sequence.empty()) {
const auto step = sequence.pop_front();
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.
read(instruction.size(), effective_address_[index].value, operand_[index]);
} break;
case Step::Perform:
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.
write(instruction.size(), effective_address_[index].value, operand_[index]);
} break;
}
}
}
}

View File

@ -50,15 +50,26 @@ template <Model model, typename BusHandler> class Executor {
void read(DataSize size, uint32_t address, CPU::SlicedInt32 &value);
void write(DataSize size, uint32_t address, CPU::SlicedInt32 value);
template <typename IntT> IntT read_pc();
uint32_t index_8bitdisplacement();
// Processor state.
Status status_;
CPU::SlicedInt32 program_counter_;
CPU::SlicedInt32 data_[8], address_[8];
CPU::SlicedInt32 stack_pointers_[2];
uint32_t instruction_address_;
// A lookup table to ensure that A7 is adjusted by 2 rather than 1 in
// postincrement and predecrement mode.
static constexpr uint32_t byte_increments[] = {
1, 1, 1, 1, 1, 1, 1, 2
};
};
}
}
#include "Implementation/ExecutorImplementation.hpp"
#endif /* InstructionSets_M68k_Executor_hpp */

View File

@ -0,0 +1,282 @@
//
// 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
#include "Perform.hpp"
#include <cassert>
namespace InstructionSet {
namespace M68k {
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);
// 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>
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);
const int register_index = (extension >> 11) & 7;
const uint32_t displacement = (extension & 0x8000) ? address_[register_index].l : data_[register_index].l;
return offset + (extension & 0x800) ? displacement : uint16_t(displacement);
}
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.l = data_[instruction.reg(index)];
ea.is_address = false;
break;
case AddressingMode::AddressRegisterDirect:
ea.value.l = address_[instruction.reg(index)];
ea.is_address = false;
break;
case AddressingMode::Quick:
ea.value.l = quick(instruction.operation, opcode);
ea.is_address = false;
break;
case AddressingMode::ImmediateData:
read(instruction.size(), program_counter_.l, ea.value.l);
program_counter_.l += (instruction.size() == DataSize::LongWord) ? 4 : 2;
ea.is_address = false;
break;
//
// Absolute addresses.
//
case AddressingMode::AbsoluteShort:
ea.value.l = int16_t(read_pc<uint16_t>());
ea.is_address = true;
break;
case AddressingMode::AbsoluteLong:
ea.value.l = read_pc<uint32_t>();
ea.is_address = true;
break;
//
// Address register indirects.
//
case AddressingMode::AddressRegisterIndirect:
ea.value.l = address_[instruction.reg(index)];
ea.is_address = true;
break;
case AddressingMode::AddressRegisterIndirectWithPostincrement: {
const auto reg = instruction.reg(index);
ea.value.l = address_[reg];
ea.is_address = true;
switch(instruction.size()) {
case DataSize::Byte: address_[reg] += byte_increments[reg]; break;
case DataSize::Word: address_[reg] += 2; break;
case DataSize::LongWord: address_[reg] += 4; break;
}
} break;
case AddressingMode::AddressRegisterIndirectWithPredecrement: {
const auto reg = instruction.reg(index);
switch(instruction.size()) {
case DataSize::Byte: address_[reg] -= byte_increments[reg]; break;
case DataSize::Word: address_[reg] -= 2; break;
case DataSize::LongWord: address_[reg] -= 4; break;
}
ea.value.l = address_[reg];
ea.is_address = true;
} break;
case AddressingMode::AddressRegisterIndirectWithDisplacement:
ea.value.l = address_[instruction.reg(index)] + int16_t(read_pc<uint16_t>());
ea.is_address = true;
break;
case AddressingMode::AddressRegisterIndirectWithIndex8bitDisplacement:
ea.value.l = address_[instruction.reg(index)] + index_8bitdisplacement();
ea.is_address = true;
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>());
ea.is_address = true;
break;
case AddressingMode::ProgramCounterIndirectWithIndex8bitDisplacement:
ea.value.l = program_counter_.l + index_8bitdisplacement();
ea.is_address = true;
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);
program_counter_.l += 2;
// TODO: check privilege level.
// 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.
//
// 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] = 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.
Sequence<model> sequence(instruction.operation);
// Perform it.
while(!sequence.empty()) {
const auto step = sequence.pop_front();
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.
read(instruction.size(), effective_address_[index].value, operand_[index]);
} break;
case Step::Perform:
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.
write(instruction.size(), effective_address_[index].value, operand_[index]);
} break;
}
}
}
}
}
}
#endif /* InstructionSets_M68k_ExecutorImplementation_hpp */

View File

@ -303,23 +303,23 @@ enum class AddressingMode: uint8_t {
AddressRegisterIndirectWithDisplacement = 0b00'101,
/// (d8, An, Xn)
AddressRegisterIndirectWithIndex8bitDisplacement = 0b00'110,
/// (bd, An, Xn)
/// (bd, An, Xn) [68020+]
AddressRegisterIndirectWithIndexBaseDisplacement = 0b10'000,
/// ([bd, An, Xn], od)
/// ([bd, An, Xn], od) [68020+]
MemoryIndirectPostindexed = 0b10'001,
/// ([bd, An], Xn, od)
/// ([bd, An], Xn, od) [68020+]
MemoryIndirectPreindexed = 0b10'010,
/// (d16, PC)
ProgramCounterIndirectWithDisplacement = 0b01'010,
/// (d8, PC, Xn)
ProgramCounterIndirectWithIndex8bitDisplacement = 0b01'011,
/// (bd, PC, Xn)
/// (bd, PC, Xn) [68020+]
ProgramCounterIndirectWithIndexBaseDisplacement = 0b10'011,
/// ([bd, PC, Xn], od)
/// ([bd, PC, Xn], od) [68020+]
ProgramCounterMemoryIndirectPostindexed = 0b10'100,
/// ([bc, PC], Xn, od)
/// ([bc, PC], Xn, od) [68020+]
ProgramCounterMemoryIndirectPreindexed = 0b10'101,
/// (xxx).W

View File

@ -1943,6 +1943,7 @@
4BB5B99A281B244400522DA9 /* PerformImplementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PerformImplementation.hpp; sourceTree = "<group>"; };
4BB5B99B281C805300522DA9 /* Executor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Executor.cpp; sourceTree = "<group>"; };
4BB5B99C281C805300522DA9 /* Executor.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Executor.hpp; sourceTree = "<group>"; };
4BB5B99F281F121200522DA9 /* ExecutorImplementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ExecutorImplementation.hpp; sourceTree = "<group>"; };
4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimedEventLoop.cpp; sourceTree = "<group>"; };
4BB697CA1D4B6D3E00248BDF /* TimedEventLoop.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TimedEventLoop.hpp; sourceTree = "<group>"; };
4BB697CC1D4BA44400248BDF /* CommodoreGCR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommodoreGCR.cpp; path = Encodings/CommodoreGCR.cpp; sourceTree = "<group>"; };
@ -4092,6 +4093,7 @@
isa = PBXGroup;
children = (
4BB5B99A281B244400522DA9 /* PerformImplementation.hpp */,
4BB5B99F281F121200522DA9 /* ExecutorImplementation.hpp */,
);
path = Implementation;
sourceTree = "<group>";