mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-24 12:30:17 +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.
|
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>;
|
||||||
|
*/
|
||||||
|
@ -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 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,
|
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));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user