mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-23 20:29:42 +00:00
Switch — messily — to a more compact way of indicating sequence.
This commit is contained in:
parent
17a2ce0464
commit
1bb809098c
@ -250,56 +250,37 @@ void Executor<model, BusHandler>::run_for_instructions(int count) {
|
||||
// Obtain the appropriate sequence.
|
||||
//
|
||||
// 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.
|
||||
while(!sequence.empty()) {
|
||||
const auto step = sequence.pop_front();
|
||||
// TODO: potential alignment exception, here and in store.
|
||||
#define fetch_operand(n) \
|
||||
if(effective_address_[n].requires_fetch) { \
|
||||
read(instruction.size(), effective_address_[n].value.l, operand_[n]); \
|
||||
}
|
||||
|
||||
switch(step) {
|
||||
default: assert(false); // i.e. TODO
|
||||
if(flags & FetchOp1) { fetch_operand(0); }
|
||||
if(flags & FetchOp2) { fetch_operand(1); }
|
||||
|
||||
case Step::FetchOp1:
|
||||
case Step::FetchOp2: {
|
||||
const auto index = int(step) & 1;
|
||||
#undef fetch_operand
|
||||
|
||||
// If the operand wasn't indirect, it's already fetched.
|
||||
if(!effective_address_[index].requires_fetch) continue;
|
||||
perform<model>(instruction, operand_[0], operand_[1], status_, *this);
|
||||
|
||||
// TODO: potential bus alignment exception.
|
||||
read(instruction.size(), effective_address_[index].value.l, operand_[index]);
|
||||
} break;
|
||||
// TODO: is it worth holding registers as a single block to avoid conditional below?
|
||||
#define store_operand(n) \
|
||||
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:
|
||||
perform<model>(instruction, operand_[0], operand_[1], status_, *this);
|
||||
break;
|
||||
if(flags & StoreOp1) { store_operand(0); }
|
||||
if(flags & StoreOp2) { store_operand(1); }
|
||||
|
||||
case Step::StoreOp1:
|
||||
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;
|
||||
}
|
||||
}
|
||||
#undef store_operand
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
using namespace InstructionSet::M68k;
|
||||
|
||||
/*
|
||||
template <Step... T> struct Steps {
|
||||
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::M68030>;
|
||||
template class InstructionSet::M68k::Sequence<Model::M68040>;
|
||||
*/
|
||||
|
@ -12,64 +12,93 @@
|
||||
#include "Instruction.hpp"
|
||||
#include "Model.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace InstructionSet {
|
||||
namespace M68k {
|
||||
|
||||
/// 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.
|
||||
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,
|
||||
static constexpr uint8_t FetchOp1 = (1 << 0);
|
||||
static constexpr uint8_t FetchOp2 = (1 << 1);
|
||||
static constexpr uint8_t StoreOp1 = (1 << 2);
|
||||
static constexpr uint8_t StoreOp2 = (1 << 3);
|
||||
|
||||
Max = SpecificBusActivity
|
||||
};
|
||||
/*!
|
||||
Provides a bitfield with a value in the range 0–15 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,
|
||||
/// at least as far as that's generic.
|
||||
template<Model model> class Sequence {
|
||||
public:
|
||||
Sequence(Operation);
|
||||
Unusual bus sequences, such as TAS or MOVEM, are not described here.
|
||||
*/
|
||||
template <Model model, Operation t_operation = Operation::Undefined> uint8_t operand_flags(Operation r_operation = Operation::Undefined) {
|
||||
switch((t_operation != Operation::Undefined) ? t_operation : r_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
|
||||
/// list of remaining steps.
|
||||
Step pop_front() {
|
||||
static_assert(int(Step::Max) < 16);
|
||||
const auto step = Step(steps_ & 15);
|
||||
steps_ >>= 4;
|
||||
return step;
|
||||
}
|
||||
//
|
||||
// No operands are fetched or stored.
|
||||
//
|
||||
case Operation::LEA:
|
||||
case Operation::PEA:
|
||||
return 0;
|
||||
|
||||
/// @returns @c true if no steps other than @c Done remain;
|
||||
/// @c false otherwise.
|
||||
bool empty() {
|
||||
return !steps_;
|
||||
}
|
||||
//
|
||||
// Single-operand read.
|
||||
//
|
||||
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));
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user