1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-25 03:32:01 +00:00

Switch — messily — to a more compact way of indicating sequence.

This commit is contained in:
Thomas Harte 2022-05-03 09:04:54 -04:00
parent 17a2ce0464
commit 1bb809098c
3 changed files with 103 additions and 91 deletions

View File

@ -250,56 +250,37 @@ void Executor<model, BusHandler>::run_for_instructions(int count) {
// Obtain the appropriate sequence. // Obtain the appropriate sequence.
// //
// TODO: make a decision about whether this goes into a fully-decoded Instruction. // TODO: make a decision about whether this goes into a fully-decoded Instruction.
Sequence<model> sequence(instruction.operation); const auto flags = operand_flags<model>(instruction.operation);
// Perform it. // TODO: potential alignment exception, here and in store.
while(!sequence.empty()) { #define fetch_operand(n) \
const auto step = sequence.pop_front(); if(effective_address_[n].requires_fetch) { \
read(instruction.size(), effective_address_[n].value.l, operand_[n]); \
}
switch(step) { if(flags & FetchOp1) { fetch_operand(0); }
default: assert(false); // i.e. TODO if(flags & FetchOp2) { fetch_operand(1); }
case Step::FetchOp1: #undef fetch_operand
case Step::FetchOp2: {
const auto index = int(step) & 1;
// If the operand wasn't indirect, it's already fetched. perform<model>(instruction, operand_[0], operand_[1], status_, *this);
if(!effective_address_[index].requires_fetch) continue;
// TODO: potential bus alignment exception. // TODO: is it worth holding registers as a single block to avoid conditional below?
read(instruction.size(), effective_address_[index].value.l, operand_[index]); #define store_operand(n) \
} break; if(!effective_address_[n].requires_fetch) { \
if(instruction.mode(n) == AddressingMode::DataRegisterDirect) { \
data_[instruction.reg(n)] = operand_[n]; \
} else { \
address_[instruction.reg(n)] = operand_[n]; \
} \
} else { \
write(instruction.size(), effective_address_[n].value.l, operand_[n]); \
}
case Step::Perform: if(flags & StoreOp1) { store_operand(0); }
perform<model>(instruction, operand_[0], operand_[1], status_, *this); if(flags & StoreOp2) { store_operand(1); }
break;
case Step::StoreOp1: #undef store_operand
case Step::StoreOp2: {
const auto index = int(step) & 1;
// If the operand wasn't indirect, store directly to Dn or An.
if(!effective_address_[index].requires_fetch) {
// 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.l, operand_[index]);
} break;
}
}
} }
} }

View File

@ -12,6 +12,7 @@
using namespace InstructionSet::M68k; using namespace InstructionSet::M68k;
/*
template <Step... T> struct Steps { template <Step... T> struct Steps {
static constexpr uint32_t value = 0; static constexpr uint32_t value = 0;
}; };
@ -110,3 +111,4 @@ template class InstructionSet::M68k::Sequence<Model::M68010>;
template class InstructionSet::M68k::Sequence<Model::M68020>; template class InstructionSet::M68k::Sequence<Model::M68020>;
template class InstructionSet::M68k::Sequence<Model::M68030>; template class InstructionSet::M68k::Sequence<Model::M68030>;
template class InstructionSet::M68k::Sequence<Model::M68040>; template class InstructionSet::M68k::Sequence<Model::M68040>;
*/

View File

@ -12,64 +12,93 @@
#include "Instruction.hpp" #include "Instruction.hpp"
#include "Model.hpp" #include "Model.hpp"
#include <cassert>
namespace InstructionSet { namespace InstructionSet {
namespace M68k { namespace M68k {
/// Additional guarantees: [Fetch/Store][1/2] have an LSB of 0 for static constexpr uint8_t FetchOp1 = (1 << 0);
/// operand 1, and an LSB of 1 for operand 2. static constexpr uint8_t FetchOp2 = (1 << 1);
enum class Step { static constexpr uint8_t StoreOp1 = (1 << 2);
/// No further steps remain. static constexpr uint8_t StoreOp2 = (1 << 3);
Done,
/// Do the logical operation.
Perform,
/// Fetch the value of operand 1.
FetchOp1,
/// Fetch the value of operand 2.
FetchOp2,
/// Store the value of operand 1.
StoreOp1,
/// Store the value of operand 2.
StoreOp2,
/// 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.
///
/// TODO: list all operations that contain this step,
/// and to cover what activity.
SpecificBusActivity,
Max = SpecificBusActivity /*!
}; Provides a bitfield with a value in the range 015 indicating which of the provided operation's
operands are accessed via standard fetch and store cycles.
/// Indicates the abstract steps necessary to perform an operation, Unusual bus sequences, such as TAS or MOVEM, are not described here.
/// at least as far as that's generic. */
template<Model model> class Sequence { template <Model model, Operation t_operation = Operation::Undefined> uint8_t operand_flags(Operation r_operation = Operation::Undefined) {
public: switch((t_operation != Operation::Undefined) ? t_operation : r_operation) {
Sequence(Operation); default:
assert(false);
/// @returns The next @c Step to perform, or @c Done //
/// if no further steps remain. This step is removed from the // No operands are fetched or stored.
/// list of remaining steps. //
Step pop_front() { case Operation::LEA:
static_assert(int(Step::Max) < 16); case Operation::PEA:
const auto step = Step(steps_ & 15); return 0;
steps_ >>= 4;
return step;
}
/// @returns @c true if no steps other than @c Done remain; //
/// @c false otherwise. // Single-operand read.
bool empty() { //
return !steps_; case Operation::MOVEtoSR: case Operation::MOVEtoCCR: case Operation::MOVEtoUSP:
} case Operation::ORItoSR: case Operation::ORItoCCR:
case Operation::ANDItoSR: case Operation::ANDItoCCR:
case Operation::EORItoSR: case Operation::EORItoCCR:
return FetchOp1;
private: //
uint32_t steps_ = 0; // Single-operand write.
//
case Operation::MOVEfromSR: case Operation::MOVEfromUSP:
return StoreOp1;
uint32_t steps_for(Operation); //
}; // Single-operand read-modify-write.
//
case Operation::NBCD:
case Operation::EXTbtow: case Operation::EXTwtol:
return FetchOp1 | StoreOp1;
//
// Two-operand; read both.
//
case Operation::CMPb: case Operation::CMPw: case Operation::CMPl:
case Operation::CMPAw: case Operation::CMPAl:
return FetchOp1 | FetchOp2;
//
// Two-operand; read source, write dest.
//
case Operation::MOVEb: case Operation::MOVEw: case Operation::MOVEl:
case Operation::MOVEAw: case Operation::MOVEAl:
return FetchOp1 | StoreOp2;
//
// Two-operand; read both, write dest.
//
case Operation::ABCD: case Operation::SBCD:
case Operation::ADDb: case Operation::ADDw: case Operation::ADDl:
case Operation::ADDAw: case Operation::ADDAl:
case Operation::ADDXb: case Operation::ADDXw: case Operation::ADDXl:
case Operation::SUBb: case Operation::SUBw: case Operation::SUBl:
case Operation::SUBAw: case Operation::SUBAl:
case Operation::SUBXb: case Operation::SUBXw: case Operation::SUBXl:
case Operation::ORb: case Operation::ORw: case Operation::ORl:
case Operation::ANDb: case Operation::ANDw: case Operation::ANDl:
case Operation::EORb: case Operation::EORw: case Operation::EORl:
return FetchOp1 | FetchOp2 | StoreOp2;
//
// Two-operand; read both, write both.
//
case Operation::EXG:
return FetchOp1 | FetchOp2 | StoreOp1 | StoreOp2;
}
}
static_assert(sizeof(Sequence<Model::M68000>) == sizeof(uint32_t));
} }
} }