mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-09 05:25:01 +00:00
Establish more of the 680x0 executor loop.
This commit is contained in:
@@ -21,24 +21,83 @@ Executor<model, BusHandler>::Executor(BusHandler &handler) : bus_handler_(handle
|
|||||||
|
|
||||||
template <Model model, typename BusHandler>
|
template <Model model, typename BusHandler>
|
||||||
void Executor<model, BusHandler>::reset() {
|
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>
|
template <Model model, typename BusHandler>
|
||||||
void Executor<model, BusHandler>::run_for_instructions(int count) {
|
void Executor<model, BusHandler>::run_for_instructions(int count) {
|
||||||
while(count--) {
|
while(count--) {
|
||||||
// TODO: check interrupt level, trace flag.
|
// TODO: check interrupt level, trace flag.
|
||||||
|
|
||||||
// Read the next instruction.
|
// 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 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;
|
program_counter_.l += 2;
|
||||||
|
|
||||||
// Obtain the appropriate sequence.
|
|
||||||
Sequence<model> sequence(instruction.operation);
|
|
||||||
|
|
||||||
// Temporary storage.
|
// 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.
|
// Perform it.
|
||||||
while(!sequence.empty()) {
|
while(!sequence.empty()) {
|
||||||
@@ -47,9 +106,67 @@ void Executor<model, BusHandler>::run_for_instructions(int count) {
|
|||||||
switch(step) {
|
switch(step) {
|
||||||
default: assert(false); // i.e. TODO
|
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:
|
case Step::Perform:
|
||||||
perform<model>(instruction, source, dest, status_, this);
|
perform<model>(instruction, operand_[0], operand_[1], status_, this);
|
||||||
break;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -42,6 +42,11 @@ template <Model model, typename BusHandler> class Executor {
|
|||||||
Predecoder<model> decoder_;
|
Predecoder<model> decoder_;
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
struct EffectiveAddress {
|
||||||
|
uint32_t value;
|
||||||
|
bool is_address;
|
||||||
|
};
|
||||||
|
EffectiveAddress calculate_effective_address(Preinstruction instruction, uint16_t opcode, int index);
|
||||||
|
|
||||||
// Processor state.
|
// Processor state.
|
||||||
Status status_;
|
Status status_;
|
||||||
|
@@ -356,27 +356,37 @@ class Preinstruction {
|
|||||||
// For one-operand instructions, only argument 0 will
|
// For one-operand instructions, only argument 0 will
|
||||||
// be provided, and will be a source and/or destination as
|
// be provided, and will be a source and/or destination as
|
||||||
// per the semantics of the operation.
|
// 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 {
|
template <int index> AddressingMode mode() const {
|
||||||
if constexpr (index > 1) {
|
if constexpr (index > 1) {
|
||||||
return AddressingMode::None;
|
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 {
|
template <int index> int reg() const {
|
||||||
if constexpr (index > 1) {
|
if constexpr (index > 1) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return operands_[index] >> 5;
|
return reg(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool requires_supervisor() {
|
bool requires_supervisor() const {
|
||||||
return flags_ & 0x80;
|
return flags_ & 0x80;
|
||||||
}
|
}
|
||||||
DataSize size() {
|
DataSize size() const {
|
||||||
return DataSize(flags_ & 0x03);
|
return DataSize(flags_ & 0x03);
|
||||||
}
|
}
|
||||||
Condition condition() {
|
Condition condition() const {
|
||||||
return Condition((flags_ >> 2) & 0x0f);
|
return Condition((flags_ >> 2) & 0x0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -28,7 +28,6 @@ template<Model model> uint32_t Sequence<model>::steps_for(Operation operation) {
|
|||||||
//
|
//
|
||||||
case Operation::LEA:
|
case Operation::LEA:
|
||||||
return Steps<
|
return Steps<
|
||||||
Step::CalcEA1,
|
|
||||||
Step::Perform
|
Step::Perform
|
||||||
>::value;
|
>::value;
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
namespace InstructionSet {
|
namespace InstructionSet {
|
||||||
namespace M68k {
|
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.
|
/// operand 1, and an LSB of 1 for operand 2.
|
||||||
enum class Step {
|
enum class Step {
|
||||||
/// No further steps remain.
|
/// No further steps remain.
|
||||||
@@ -30,10 +30,6 @@ enum class Step {
|
|||||||
StoreOp1,
|
StoreOp1,
|
||||||
/// Store the value of operand 2.
|
/// Store the value of operand 2.
|
||||||
StoreOp2,
|
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
|
/// A catch-all for bus activity that doesn't fit the pattern
|
||||||
/// of fetch/stop operand 1/2, e.g. this opaquely covers almost
|
/// of fetch/stop operand 1/2, e.g. this opaquely covers almost
|
||||||
/// the entirety of MOVEM.
|
/// the entirety of MOVEM.
|
||||||
|
@@ -22,7 +22,7 @@ struct Status {
|
|||||||
int is_supervisor_ = 0; // 1 => processor is in supervisor mode; 0 => it isn't.
|
int is_supervisor_ = 0; // 1 => processor is in supervisor mode; 0 => it isn't.
|
||||||
|
|
||||||
/* b7–b9 */
|
/* 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 */
|
/* b0–b4 */
|
||||||
uint_fast32_t zero_result_ = 0; // The zero flag is set if this value is zero.
|
uint_fast32_t zero_result_ = 0; // The zero flag is set if this value is zero.
|
||||||
|
Reference in New Issue
Block a user