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

Accept that a uint8_t isn't always going to be large enough; split decoding by minimum processor.

This commit is contained in:
Thomas Harte 2022-10-25 09:50:19 -04:00
parent 8c670d2105
commit 38c531fd5a
3 changed files with 233 additions and 93 deletions

View File

@ -71,7 +71,7 @@ uint32_t operand_mask(AddressingMode mode1, AddressingMode mode2) {
/// immediate operand.
template <Model model>
constexpr Operation Predecoder<model>::operation(OpT op) {
if(op <= OpT(Operation::Max)) {
if(op <= OpMax) {
return Operation(op);
}
@ -140,7 +140,7 @@ constexpr Operation Predecoder<model>::operation(OpT op) {
}
template <Model model>
template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
template <typename Predecoder<model>::OpT op> uint32_t Predecoder<model>::invalid_operands() {
constexpr auto Dn = Mask< AddressingMode::DataRegisterDirect >::value;
constexpr auto An = Mask< AddressingMode::AddressRegisterDirect >::value;
constexpr auto Ind = Mask< AddressingMode::AddressRegisterIndirect >::value;
@ -174,12 +174,15 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
//
// Control [flow] addressing modes.
//
static constexpr auto ControlAddressingModes = Ind | d16An | d8AnXn | XXXw | XXXl | d16PC | d8PCXn;
static constexpr auto ControlAddressingModes = Ind | d16An | d8AnXn | XXXw | XXXl | d16PC | d8PCXn;
//
// An invalid response, used as the default case.
//
static constexpr auto InvalidOperands = uint32_t(~0);
switch(op) {
// By default, disallow all operands (including 'None'). This should catch any
// opcodes that are unmapped below.
default: return uint32_t(~0);
default: break;
case OpT(Operation::ABCD):
case OpT(Operation::ADDXb): case OpT(Operation::ADDXw): case OpT(Operation::ADDXl):
@ -280,8 +283,7 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
>::value;
case OpT(Operation::ANDItoCCR): case OpT(Operation::ANDItoSR):
case OpT(Operation::Bccw): case OpT(Operation::Bccl):
case OpT(Operation::BSRl): case OpT(Operation::BSRw):
case OpT(Operation::Bccw): case OpT(Operation::BSRw):
case OpT(Operation::EORItoCCR): case OpT(Operation::EORItoSR):
case OpT(Operation::ORItoCCR): case OpT(Operation::ORItoSR):
case OpT(Operation::STOP):
@ -317,7 +319,6 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
case OpT(Operation::Bccb):
case OpT(Operation::BSRb):
case OpT(Operation::TRAP):
case OpT(Operation::BKPT):
return ~OneOperandMask<
Quick
>::value;
@ -337,25 +338,6 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
AllModesNoAn
>::value;
case OpT(Operation::BFCHG): case OpT(Operation::BFCLR): case OpT(Operation::BFSET):
case OpT(Operation::BFINS):
return ~TwoOperandMask<
Ext,
Dn | Ind | d16An | d8AnXn | XXXw | XXXl
>::value;
case OpT(Operation::BFTST): case OpT(Operation::BFFFO): case OpT(Operation::BFEXTU):
case OpT(Operation::BFEXTS):
return ~TwoOperandMask<
Ext,
Dn | Ind | d16An | d8AnXn | XXXw | XXXl | d16PC | d8PCXn
>::value;
case OpT(Operation::PACK):
return ~OneOperandMask<
Imm
>::value;
case CMPIb: case CMPIl: case CMPIw:
if constexpr (model == Model::M68000) {
return ~TwoOperandMask<
@ -442,16 +424,6 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
Dn
>::value;
case OpT(Operation::RTD):
return ~OneOperandMask<
Imm
>::value;
case OpT(Operation::CALLM):
return ~OneOperandMask<
ControlAddressingModes
>::value;
case OpT(Operation::JMP):
case OpT(Operation::JSR):
case OpT(Operation::PEA):
@ -479,11 +451,6 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
case OpT(Operation::RESET):
return ~NoOperandMask::value;
case OpT(Operation::RTM):
return ~OneOperandMask<
An | Dn
>::value;
case OpT(Operation::UNLINK):
case OpT(Operation::MOVEtoUSP):
case OpT(Operation::MOVEfromUSP):
@ -515,11 +482,78 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
d16An
>::value;
}
//
// 68010 additions.
//
if constexpr (model <= Model::M68010) {
return InvalidOperands;
}
switch(op) {
default: break;
case OpT(Operation::BKPT):
return ~OneOperandMask<
Quick
>::value;
case OpT(Operation::RTD):
return ~OneOperandMask<
Imm
>::value;
}
//
// 68020 additions.
//
if constexpr (model <= Model::M68020) {
return InvalidOperands;
}
switch(op) {
default: break;
case OpT(Operation::Bccl): case OpT(Operation::BSRl):
return ~OneOperandMask<
Imm
>::value;
case OpT(Operation::RTM):
return ~OneOperandMask<
An | Dn
>::value;
case OpT(Operation::CALLM):
return ~OneOperandMask<
ControlAddressingModes
>::value;
case OpT(Operation::BFCHG): case OpT(Operation::BFCLR): case OpT(Operation::BFSET):
case OpT(Operation::BFINS):
return ~TwoOperandMask<
Ext,
Dn | Ind | d16An | d8AnXn | XXXw | XXXl
>::value;
case OpT(Operation::BFTST): case OpT(Operation::BFFFO): case OpT(Operation::BFEXTU):
case OpT(Operation::BFEXTS):
return ~TwoOperandMask<
Ext,
Dn | Ind | d16An | d8AnXn | XXXw | XXXl | d16PC | d8PCXn
>::value;
case OpT(Operation::PACK):
return ~OneOperandMask<
Imm
>::value;
}
return InvalidOperands;
}
/// Provides a post-decoding validation step — primarily ensures that the prima facie addressing modes are supported by the operation.
template <Model model>
template <uint8_t op, bool validate> Preinstruction Predecoder<model>::validated(
template <typename Predecoder<model>::OpT op, bool validate> Preinstruction Predecoder<model>::validated(
AddressingMode op1_mode, int op1_reg,
AddressingMode op2_mode, int op2_reg,
Condition condition
@ -554,7 +588,7 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::validated
/// Decodes the fields within an instruction and constructs a `Preinstruction`, given that the operation has already been
/// decoded. Optionally applies validation
template <Model model>
template <uint8_t op, bool validate> Preinstruction Predecoder<model>::decode(uint16_t instruction) {
template <typename Predecoder<model>::OpT op, bool validate> Preinstruction Predecoder<model>::decode(uint16_t instruction) {
// Fields used pervasively below.
//
// Underlying assumption: the compiler will discard whatever of these
@ -564,7 +598,11 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::decode(ui
const auto opmode = (instruction >> 6) & 7;
const auto data_register = (instruction >> 9) & 7;
//
// Operations common to all processors.
//
switch(op) {
default: break;
//
// MARK: ABCD, SBCD, ADDX.
@ -648,24 +686,24 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::decode(ui
combined_mode(ea_mode, ea_register), ea_register);
//
// MARK: STOP, ANDItoCCR, ANDItoSR, EORItoCCR, EORItoSR, ORItoCCR, ORItoSR, BSRl, BSRw
// MARK: STOP, ANDItoCCR, ANDItoSR, EORItoCCR, EORItoSR, ORItoCCR, ORItoSR, BSRw
//
// Operand is an immedate; destination/source (if any) is implied by the operation,
// e.g. ORItoSR has a destination of SR.
//
case OpT(Operation::STOP):
case OpT(Operation::BSRl): case OpT(Operation::BSRw):
case OpT(Operation::BSRw):
case OpT(Operation::ORItoSR): case OpT(Operation::ORItoCCR):
case OpT(Operation::ANDItoSR): case OpT(Operation::ANDItoCCR):
case OpT(Operation::EORItoSR): case OpT(Operation::EORItoCCR):
return validated<op, validate>(AddressingMode::ImmediateData);
//
// MARK: Bccl, Bccw
// MARK: Bccw
//
// Operand is an immedate; b8b11 are a condition code.
//
case OpT(Operation::Bccl): case OpT(Operation::Bccw):
case OpT(Operation::Bccw):
return validated<op, validate>(
AddressingMode::ImmediateData, 0,
AddressingMode::None, 0,
@ -683,7 +721,7 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::decode(ui
AddressingMode::DataRegisterDirect, data_register);
//
// MARK: EXG.
// MARK: EXG
//
// b0b2: register Ry (data or address, address if exchange is address <-> data);
// b9b11: register Rx (data or address, data if exchange is address <-> data);
@ -716,17 +754,6 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::decode(ui
combined_mode(ea_mode, ea_register), ea_register,
AddressingMode::DataRegisterDirect, data_register);
//
// MARK: DIVSl.
//
// b0b2 and b3b5: source effective address.
// Plus an immediate word operand
//
case OpT(Operation::DIVSl):
return validated<op, validate>(
AddressingMode::ExtensionWord, 0,
combined_mode(ea_mode, ea_register), ea_register);
//
// MARK: LEA
//
@ -851,11 +878,10 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::decode(ui
combined_mode(ea_mode, ea_register), ea_register);
//
// MARK: TRAP, BCCb, BSRb, BKPT
// MARK: TRAP, BCCb, BSRb
//
// No further operands decoded, but one is somewhere in the opcode.
//
case OpT(Operation::BKPT):
case OpT(Operation::TRAP):
case OpT(Operation::BSRb):
return validated<op, validate>(AddressingMode::Quick);
@ -936,6 +962,70 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::decode(ui
return validated<op, validate>(
AddressingMode::AddressRegisterIndirectWithPostincrement, ea_register,
AddressingMode::AddressRegisterIndirectWithPostincrement, data_register);
}
//
// 68010 additions.
//
if constexpr (model < Model::M68010) {
assert(false);
}
switch(op) {
default: break;
//
// MARK: BKPT
//
// No further operands decoded, but one is somewhere in the opcode.
//
case OpT(Operation::BKPT):
return validated<op, validate>(AddressingMode::Quick);
//
// MARK: RTD
//
case OpT(Operation::RTD):
return validated<op, validate>(AddressingMode::ImmediateData);
}
//
// 68020 additions.
//
if constexpr (model < Model::M68020) {
assert(false);
}
switch(op) {
default: break;
//
// MARK: BSRl
//
// Operand is an immedate.
//
case OpT(Operation::BSRl):
return validated<op, validate>(AddressingMode::ImmediateData);
//
// MARK: Bccl
//
// Operand is an immedate; b8b11 are a condition code.
//
case OpT(Operation::Bccl):
return validated<op, validate>(
AddressingMode::ImmediateData, 0,
AddressingMode::None, 0,
Condition((instruction >> 8) & 0xf));
//
// MARK: DIVSl.
//
// b0b2 and b3b5: source effective address.
// Plus an immediate word operand
//
case OpT(Operation::DIVSl):
return validated<op, validate>(
AddressingMode::ExtensionWord, 0,
combined_mode(ea_mode, ea_register), ea_register);
//
// MARK: BFCHG, BFTST, BFFFO, BFEXTU, BFEXTS, BFCLR, BFSET, BFINS
@ -961,12 +1051,6 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::decode(ui
AddressingMode::ImmediateData, 0,
combined_mode(ea_mode, ea_register), ea_register);
//
// MARK: RTD
//
case OpT(Operation::RTD):
return validated<op, validate>(AddressingMode::ImmediateData);
//
// MARK: RTM
//
@ -996,14 +1080,11 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::decode(ui
// MARK: DIVl
//
//
//
// MARK: Impossible error case.
//
default:
// Should be unreachable.
assert(false);
// TODO.
}
// Should be unreachable.
assert(false);
}
// MARK: - Page decoders.
@ -1151,7 +1232,6 @@ Preinstruction Predecoder<model>::decode3(uint16_t instruction) {
case 0x040: Decode(Op::MOVEAw);
default: Decode(Op::MOVEw);
}
// Decode(Op::MOVEw);
}
template <Model model>

View File

@ -50,7 +50,30 @@ template <Model model> class Predecoder {
Preinstruction decodeE(uint16_t instruction);
Preinstruction decodeF(uint16_t instruction);
using OpT = uint8_t;
template <Model> struct OperationProperties {};
template <> struct OperationProperties<Model::M68000> {
using OpT = uint8_t;
static constexpr OpT OpMax = OpT(Operation::Max68000);
};
template <> struct OperationProperties<Model::M68010> {
using OpT = uint8_t;
static constexpr OpT OpMax = OpT(Operation::Max68010);
};
template <> struct OperationProperties<Model::M68020> {
using OpT = uint16_t;
static constexpr OpT OpMax = OpT(Operation::Max68020);
};
template <> struct OperationProperties<Model::M68030> {
using OpT = uint16_t;
static constexpr OpT OpMax = OpT(Operation::Max68030);
};
template <> struct OperationProperties<Model::M68040> {
using OpT = uint16_t;
static constexpr OpT OpMax = OpT(Operation::Max68040);
};
using OpT = typename OperationProperties<model>::OpT;
static constexpr OpT OpMax = OperationProperties<model>::OpMax;
// Specific instruction decoders.
template <OpT operation, bool validate = true> Preinstruction decode(uint16_t instruction);
@ -59,14 +82,14 @@ template <Model model> class Predecoder {
AddressingMode op2_mode = AddressingMode::None, int op2_reg = 0,
Condition condition = Condition::True
);
template <uint8_t op> uint32_t invalid_operands();
template <OpT operation> uint32_t invalid_operands();
// Extended operation list; collapses into a single byte enough information to
// know both the type of operation and how to decode the operands. Most of the
// time that's knowable from the Operation alone, hence the rather awkward
// extension of @c Operation.
enum ExtendedOperation: OpT {
MOVEPtoRl = uint8_t(Operation::Max) + 1, MOVEPtoRw,
MOVEPtoRl = OperationProperties<model>::OpMax + 1, MOVEPtoRw,
MOVEPtoMl, MOVEPtoMw,
MOVEQ,
@ -87,8 +110,6 @@ template <Model model> class Predecoder {
CMPMb, CMPMw, CMPMl,
MOVEq,
ADDtoMb, ADDtoMw, ADDtoMl,
ADDtoRb, ADDtoRw, ADDtoRl,

View File

@ -105,12 +105,19 @@ enum class Operation: uint8_t {
STOP, RESET,
//
// 68010 additions.
//
BKPT, RTD,
//
// 68020 additions.
//
BKPT, TRAPcc,
CALLM, RTD, RTM,
TRAPcc,
CALLM, RTM,
BFCHG, BFCLR,
BFEXTS, BFEXTU,
@ -140,10 +147,31 @@ enum class Operation: uint8_t {
MOVEfromCCR,
MOVEC, MOVES,
//
// 68030 additions.
//
PFLUSH, PFLUSHA,
PLOADR, PLOADW,
PMOVE, PMOVEFD,
PTESTR, PTESTW,
//
// 68040 additions.
//
// TODO: the big addition of the 68040 is incorporation of the FPU; should I make decoding of those instructions
// dependent upon a 68040 being selected, or should I offer a separate decoder in order to support systems with
// a coprocessor?
//
// Introspection.
//
Max = MOVES
Max68000 = RESET,
Max68010 = RTD,
Max68020 = MOVES,
Max68030 = PTESTW,
Max68040 = PTESTW,
};
const char *to_string(Operation op);
@ -365,15 +393,26 @@ class Preinstruction {
return operands_[index] & 0xf;
}
/// @returns @c true if this instruction requires supervisor privileges; @c false otherwise.
bool requires_supervisor() const {
return flags_ & Flags::IsSupervisor;
}
bool requires_extension_word() const {
return flags_ & Flags::RequiresExtensionWord;
/// @returns @c true if this instruction will require further fetching than can be encoded in a
/// @c Preinstruction. In practice this means it is one of a very small quantity of 68020+
/// instructions; those that can rationalise extension words into one of the two operands will
/// do so. Use the free function @c extension_words(instruction.operation) to
/// look up the number of additional words required.
///
/// (specifically affected, at least: PACK, UNPK, CAS, CAS2)
bool requires_further_extension() const {
return flags_ & Flags::RequiresFurtherExtension;
}
/// @returns The @c DataSize used for operands of this instruction, i.e. byte, word or longword.
DataSize operand_size() const {
return DataSize((flags_ & Flags::SizeMask) >> Flags::SizeShift);
}
/// @returns The condition code evaluated by this instruction if applicable. If this instruction is not
/// conditional, the result is undefined.
Condition condition() const {
return Condition((flags_ & Flags::ConditionMask) >> Flags::ConditionShift);
}
@ -390,7 +429,7 @@ class Preinstruction {
AddressingMode op1_mode, int op1_reg,
AddressingMode op2_mode, int op2_reg,
bool is_supervisor,
bool requires_extension_word,
bool requires_further_extension,
DataSize size,
Condition condition) : operation(operation)
{
@ -398,17 +437,17 @@ class Preinstruction {
operands_[1] = uint8_t((uint8_t(op2_mode) << 3) | op2_reg);
flags_ = uint8_t(
(is_supervisor ? Flags::IsSupervisor : 0x00) |
(requires_extension_word ? Flags::RequiresExtensionWord : 0x00) |
(requires_further_extension ? Flags::RequiresFurtherExtension : 0x00) |
(int(condition) << Flags::ConditionShift) |
(int(size) << Flags::SizeShift)
);
}
struct Flags {
static constexpr uint8_t IsSupervisor = 0b1000'0000;
static constexpr uint8_t RequiresExtensionWord = 0b0100'0000;
static constexpr uint8_t ConditionMask = 0b0011'1100;
static constexpr uint8_t SizeMask = 0b0000'0011;
static constexpr uint8_t IsSupervisor = 0b1000'0000;
static constexpr uint8_t RequiresFurtherExtension = 0b0100'0000;
static constexpr uint8_t ConditionMask = 0b0011'1100;
static constexpr uint8_t SizeMask = 0b0000'0011;
static constexpr int ConditionShift = 2;
static constexpr int SizeShift = 0;