mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-13 22:32:03 +00:00
Merge pull request #1099 from TomHarte/68020
Expand 68k decoder to the 68020.
This commit is contained in:
commit
c8a82933bc
@ -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;
|
||||
@ -154,6 +154,7 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
|
||||
constexpr auto d8PCXn = Mask< AddressingMode::ProgramCounterIndirectWithIndex8bitDisplacement >::value;
|
||||
constexpr auto Imm = Mask< AddressingMode::ImmediateData >::value;
|
||||
constexpr auto Quick = Mask< AddressingMode::Quick >::value;
|
||||
constexpr auto Ext = Mask< AddressingMode::ExtensionWord >::value;
|
||||
|
||||
// A few recurring combinations; terminology is directly from
|
||||
// the Programmers' Reference Manual.
|
||||
@ -161,7 +162,7 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
|
||||
//
|
||||
// All modes: the complete set (other than Quick).
|
||||
//
|
||||
static constexpr auto AllModes = Dn | An | Ind | PostInc | PreDec | d16An | d8AnXn | XXXw | XXXl | d16PC | d8PCXn | Imm;
|
||||
static constexpr auto AllModes = Dn | An | Ind | PostInc | PreDec | d16An | d8AnXn | XXXw | XXXl | Imm | d16PC | d8PCXn;
|
||||
static constexpr auto AllModesNoAn = AllModes & ~An;
|
||||
|
||||
//
|
||||
@ -173,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):
|
||||
@ -191,11 +195,11 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
|
||||
|
||||
case ADDtoRb:
|
||||
case ANDtoRb: case ANDtoRw: case ANDtoRl:
|
||||
case OpT(Operation::CHK):
|
||||
case OpT(Operation::CHKw):
|
||||
case OpT(Operation::CMPb):
|
||||
case OpT(Operation::DIVU): case OpT(Operation::DIVS):
|
||||
case OpT(Operation::DIVUw): case OpT(Operation::DIVSw):
|
||||
case ORtoRb: case ORtoRw: case ORtoRl:
|
||||
case OpT(Operation::MULU): case OpT(Operation::MULS):
|
||||
case OpT(Operation::MULUw): case OpT(Operation::MULSw):
|
||||
case SUBtoRb:
|
||||
return ~TwoOperandMask<
|
||||
AllModesNoAn,
|
||||
@ -279,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):
|
||||
@ -362,7 +365,7 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
|
||||
>::value;
|
||||
|
||||
case OpT(Operation::TSTb):
|
||||
if constexpr (model == Model::M68000) {
|
||||
if constexpr (model < Model::M68020) {
|
||||
return ~OneOperandMask<
|
||||
AlterableAddressingModesNoAn
|
||||
>::value;
|
||||
@ -375,7 +378,7 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
|
||||
>::value;
|
||||
|
||||
case OpT(Operation::TSTw): case OpT(Operation::TSTl):
|
||||
if constexpr (model == Model::M68000) {
|
||||
if constexpr (model < Model::M68020) {
|
||||
return ~OneOperandMask<
|
||||
AlterableAddressingModesNoAn
|
||||
>::value;
|
||||
@ -457,13 +460,13 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
|
||||
|
||||
case OpT(Operation::MOVEMtoMw): case OpT(Operation::MOVEMtoMl):
|
||||
return ~TwoOperandMask<
|
||||
Imm,
|
||||
Ext,
|
||||
Ind | PreDec | d16An | d8AnXn | XXXw | XXXl
|
||||
>::value;
|
||||
|
||||
case OpT(Operation::MOVEMtoRw): case OpT(Operation::MOVEMtoRl):
|
||||
return ~TwoOperandMask<
|
||||
Imm,
|
||||
Ext,
|
||||
Ind | PostInc | d16An | d8AnXn | XXXw | XXXl | d16PC | d8PCXn
|
||||
>::value;
|
||||
|
||||
@ -479,14 +482,149 @@ 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;
|
||||
|
||||
case OpT(Operation::MOVEfromCCR):
|
||||
return ~OneOperandMask<
|
||||
AlterableAddressingModesNoAn
|
||||
>::value;
|
||||
|
||||
case OpT(Operation::MOVEfromC): case OpT(Operation::MOVEtoC):
|
||||
return ~OneOperandMask<
|
||||
Ext
|
||||
>::value;
|
||||
|
||||
case OpT(Operation::MOVESb): case OpT(Operation::MOVESw): case OpT(Operation::MOVESl):
|
||||
return ~TwoOperandMask<
|
||||
Ext,
|
||||
Ind | PostInc | PreDec | d16An | d8AnXn | XXXw | XXXl
|
||||
>::value;
|
||||
}
|
||||
|
||||
//
|
||||
// 68020 additions.
|
||||
//
|
||||
if constexpr (model < Model::M68020) {
|
||||
return InvalidOperands;
|
||||
}
|
||||
switch(op) {
|
||||
default: break;
|
||||
|
||||
case OpT(Operation::TRAPcc):
|
||||
return ~TwoOperandMask<
|
||||
Ext | NoOperand,
|
||||
Ext | NoOperand
|
||||
>::value;
|
||||
|
||||
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): case OpT(Operation::UNPK):
|
||||
return ~TwoOperandMask<
|
||||
Dn | PreDec,
|
||||
Dn | PreDec
|
||||
>::value;
|
||||
|
||||
case OpT(Operation::CASb): case OpT(Operation::CASw): case OpT(Operation::CASl):
|
||||
return ~TwoOperandMask<
|
||||
Ext,
|
||||
Ind | PostInc | PreDec | d16An | d8AnXn | XXXw | XXXl
|
||||
>::value;
|
||||
|
||||
case OpT(Operation::CAS2w): case OpT(Operation::CAS2l):
|
||||
return ~TwoOperandMask<
|
||||
Ext,
|
||||
Ext
|
||||
>::value;
|
||||
|
||||
case OpT(Operation::CHKorCMP2b):
|
||||
case OpT(Operation::CHKorCMP2w):
|
||||
case OpT(Operation::CHKorCMP2l):
|
||||
return ~TwoOperandMask<
|
||||
Ext,
|
||||
ControlAddressingModes
|
||||
>::value;
|
||||
|
||||
case OpT(Operation::LINKl):
|
||||
return ~TwoOperandMask<
|
||||
An,
|
||||
Imm
|
||||
>::value;
|
||||
|
||||
case OpT(Operation::CHKl):
|
||||
return ~TwoOperandMask<
|
||||
AllModesNoAn,
|
||||
Dn
|
||||
>::value;
|
||||
|
||||
case OpT(Operation::EXTbtol):
|
||||
return ~OneOperandMask<
|
||||
Dn
|
||||
>::value;
|
||||
|
||||
case OpT(Operation::DIVSorDIVUl):
|
||||
case OpT(Operation::MULSorMULUl):
|
||||
return ~TwoOperandMask<
|
||||
Ext,
|
||||
AllModesNoAn
|
||||
>::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
|
||||
Condition condition,
|
||||
int additional_extension_words
|
||||
) {
|
||||
constexpr auto operation = Predecoder<model>::operation(op);
|
||||
|
||||
@ -496,6 +634,7 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::validated
|
||||
op1_mode, op1_reg,
|
||||
op2_mode, op2_reg,
|
||||
requires_supervisor<model>(operation),
|
||||
additional_extension_words,
|
||||
operand_size(operation),
|
||||
condition);
|
||||
}
|
||||
@ -509,6 +648,7 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::validated
|
||||
op1_mode, op1_reg,
|
||||
op2_mode, op2_reg,
|
||||
requires_supervisor<model>(operation),
|
||||
additional_extension_words,
|
||||
operand_size(operation),
|
||||
condition);
|
||||
}
|
||||
@ -516,7 +656,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
|
||||
@ -526,7 +666,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.
|
||||
@ -610,42 +754,42 @@ 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; b8–b11 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,
|
||||
Condition((instruction >> 8) & 0xf));
|
||||
|
||||
//
|
||||
// MARK: CHK
|
||||
// MARK: CHKw
|
||||
//
|
||||
// Implicitly: destination is a register;
|
||||
// b0–b2 and b3–b5: source effective address.
|
||||
//
|
||||
case OpT(Operation::CHK):
|
||||
case OpT(Operation::CHKw):
|
||||
return validated<op, validate>(
|
||||
combined_mode(ea_mode, ea_register), ea_register,
|
||||
AddressingMode::DataRegisterDirect, data_register);
|
||||
|
||||
//
|
||||
// MARK: EXG.
|
||||
// MARK: EXG
|
||||
//
|
||||
// b0–b2: register Ry (data or address, address if exchange is address <-> data);
|
||||
// b9–b11: register Rx (data or address, data if exchange is address <-> data);
|
||||
@ -672,8 +816,8 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::decode(ui
|
||||
// b9–b11: destination data register;
|
||||
// b0–b2 and b3–b5: source effective address.
|
||||
//
|
||||
case OpT(Operation::DIVU): case OpT(Operation::DIVS):
|
||||
case OpT(Operation::MULU): case OpT(Operation::MULS):
|
||||
case OpT(Operation::DIVUw): case OpT(Operation::DIVSw):
|
||||
case OpT(Operation::MULUw): case OpT(Operation::MULSw):
|
||||
return validated<op, validate>(
|
||||
combined_mode(ea_mode, ea_register), ea_register,
|
||||
AddressingMode::DataRegisterDirect, data_register);
|
||||
@ -798,13 +942,13 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::decode(ui
|
||||
case OpT(Operation::MOVEMtoMl): case OpT(Operation::MOVEMtoMw):
|
||||
case OpT(Operation::MOVEMtoRl): case OpT(Operation::MOVEMtoRw):
|
||||
return validated<op, validate>(
|
||||
AddressingMode::ImmediateData, 0,
|
||||
AddressingMode::ExtensionWord, 0,
|
||||
combined_mode(ea_mode, ea_register), ea_register);
|
||||
|
||||
//
|
||||
// MARK: TRAP, BCCb, BSRb
|
||||
//
|
||||
// No further operands decoded, but note that one is somewhere in the opcode.
|
||||
// No further operands decoded, but one is somewhere in the opcode.
|
||||
//
|
||||
case OpT(Operation::TRAP):
|
||||
case OpT(Operation::BSRb):
|
||||
@ -858,6 +1002,7 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::decode(ui
|
||||
// b0–b2: a register to shift (the source here, for consistency with the memory operations);
|
||||
// b8: 0 => b9–b11 are a direct count of bits to shift; 1 => b9–b11 identify a register containing the shift count;
|
||||
// b9–b11: either a quick value or a register.
|
||||
//
|
||||
case OpT(Operation::ASRb): case OpT(Operation::ASRw): case OpT(Operation::ASRl):
|
||||
case OpT(Operation::LSRb): case OpT(Operation::LSRw): case OpT(Operation::LSRl):
|
||||
case OpT(Operation::ROXRb): case OpT(Operation::ROXRw): case OpT(Operation::ROXRl):
|
||||
@ -885,19 +1030,247 @@ 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: Impossible error case.
|
||||
// MARK: BKPT
|
||||
//
|
||||
default:
|
||||
// Should be unreachable.
|
||||
assert(false);
|
||||
// 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);
|
||||
|
||||
//
|
||||
// MARK: MOVE from CCR.
|
||||
//
|
||||
case OpT(Operation::MOVEfromCCR):
|
||||
return validated<op, validate>(combined_mode(ea_mode, ea_register), ea_register);
|
||||
|
||||
//
|
||||
// MARK: MOVE to/from C.
|
||||
//
|
||||
// No further information in the instruction, but an extension word is required.
|
||||
//
|
||||
case OpT(Operation::MOVEfromC): case OpT(Operation::MOVEtoC):
|
||||
return validated<op, validate>(AddressingMode::ExtensionWord);
|
||||
|
||||
//
|
||||
// MARK: MOVES
|
||||
//
|
||||
// b0–b2 and b3–b5: effective address;
|
||||
// also an extension word is present to dictate a further register and a direction of transfer.
|
||||
//
|
||||
case OpT(Operation::MOVESb): case OpT(Operation::MOVESw): case OpT(Operation::MOVESl):
|
||||
return validated<op, validate>(
|
||||
AddressingMode::ExtensionWord, 0,
|
||||
combined_mode(ea_mode, ea_register), ea_register);
|
||||
}
|
||||
|
||||
//
|
||||
// 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; b8–b11 are a condition code.
|
||||
//
|
||||
case OpT(Operation::Bccl):
|
||||
return validated<op, validate>(
|
||||
AddressingMode::ImmediateData, 0,
|
||||
AddressingMode::None, 0,
|
||||
Condition((instruction >> 8) & 0xf));
|
||||
|
||||
//
|
||||
// MARK: DIVSl.
|
||||
//
|
||||
// b0–b2 and b3–b5: source effective address.
|
||||
// Plus an immediate word operand
|
||||
//
|
||||
case OpT(Operation::DIVSorDIVUl):
|
||||
case OpT(Operation::MULSorMULUl):
|
||||
return validated<op, validate>(
|
||||
AddressingMode::ExtensionWord, 0,
|
||||
combined_mode(ea_mode, ea_register), ea_register);
|
||||
|
||||
//
|
||||
// MARK: BFCHG, BFTST, BFFFO, BFEXTU, BFEXTS, BFCLR, BFSET, BFINS
|
||||
//
|
||||
// b0–b2 and b3–b5: an effective address.
|
||||
// There is also an immedate operand describing an offset and width.
|
||||
//
|
||||
case OpT(Operation::BFCHG): case OpT(Operation::BFTST): case OpT(Operation::BFFFO):
|
||||
case OpT(Operation::BFEXTU): case OpT(Operation::BFEXTS): case OpT(Operation::BFCLR):
|
||||
case OpT(Operation::BFSET): case OpT(Operation::BFINS):
|
||||
return validated<op, validate>(
|
||||
AddressingMode::ExtensionWord, 0,
|
||||
combined_mode(ea_mode, ea_register), ea_register);
|
||||
|
||||
//
|
||||
// MARK: CALLM
|
||||
//
|
||||
// b0–b2 and b3–b5: an effective address.
|
||||
// There is also an immedate operand providing argument count.
|
||||
//
|
||||
case OpT(Operation::CALLM):
|
||||
return validated<op, validate>(
|
||||
AddressingMode::ImmediateData, 0,
|
||||
combined_mode(ea_mode, ea_register), ea_register);
|
||||
|
||||
//
|
||||
// MARK: RTM
|
||||
//
|
||||
// b0–b2: a register number;
|
||||
// b3: address/data register selection.
|
||||
//
|
||||
case OpT(Operation::RTM): {
|
||||
const auto addressing_mode = (instruction & 8) ?
|
||||
AddressingMode::AddressRegisterDirect : AddressingMode::DataRegisterDirect;
|
||||
|
||||
return validated<op, validate>(addressing_mode, ea_register);
|
||||
}
|
||||
|
||||
//
|
||||
// MARK: TRAPcc
|
||||
//
|
||||
// Has 0, 1 or 2 following words, neither of which contributes to operation.
|
||||
//
|
||||
case OpT(Operation::TRAPcc): {
|
||||
switch(instruction & 7) {
|
||||
default: return Preinstruction();
|
||||
|
||||
// No extension.
|
||||
case 4: return validated<op, validate>();
|
||||
|
||||
// Word-sized extension.
|
||||
case 2: return validated<op, validate>(AddressingMode::ExtensionWord);
|
||||
|
||||
// DWord-sized extension (which is encoded as two extension operands).
|
||||
case 3:
|
||||
return validated<op, validate>(
|
||||
AddressingMode::ExtensionWord, 0,
|
||||
AddressingMode::ExtensionWord, 0);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// MARK: PACK, UNPK
|
||||
//
|
||||
// b9–b11: Rx (destination)
|
||||
// b0–b2: Ry (source)
|
||||
// b3: 1 => operation is memory-to-memory; 0 => register-to-register.
|
||||
// This instruction is also followed by a 16-bit adjustment extension.
|
||||
//
|
||||
case OpT(Operation::PACK):
|
||||
case OpT(Operation::UNPK): {
|
||||
const auto addressing_mode = (instruction & 8) ?
|
||||
AddressingMode::AddressRegisterIndirectWithPredecrement : AddressingMode::DataRegisterDirect;
|
||||
|
||||
return validated<op, validate>(
|
||||
addressing_mode, ea_register,
|
||||
addressing_mode, data_register,
|
||||
Condition::True, 1);
|
||||
}
|
||||
|
||||
//
|
||||
// MARK: CAS
|
||||
//
|
||||
// b0–b2 and b3–b5: an effective address.
|
||||
// There is also an immedate operand describing relevant registers.
|
||||
//
|
||||
case OpT(Operation::CASb): case OpT(Operation::CASw): case OpT(Operation::CASl):
|
||||
case OpT(Operation::CHKorCMP2b):
|
||||
case OpT(Operation::CHKorCMP2w):
|
||||
case OpT(Operation::CHKorCMP2l):
|
||||
return validated<op, validate>(
|
||||
AddressingMode::ExtensionWord, 0,
|
||||
combined_mode(ea_mode, ea_register), ea_register);
|
||||
|
||||
//
|
||||
// MARK: CAS2
|
||||
//
|
||||
// b0–b2 and b3–b5: an effective address.
|
||||
// There is also an immedate operand describing relevant registers.
|
||||
//
|
||||
case OpT(Operation::CAS2w): case OpT(Operation::CAS2l):
|
||||
return validated<op, validate>(
|
||||
AddressingMode::ExtensionWord, 0,
|
||||
AddressingMode::ExtensionWord, 0);
|
||||
|
||||
//
|
||||
// MARK: LINKl
|
||||
//
|
||||
// b0–b2: 'source' address register;
|
||||
// Implicitly: 'destination' is an immediate.
|
||||
//
|
||||
case OpT(Operation::LINKl):
|
||||
return validated<op, validate>(
|
||||
AddressingMode::AddressRegisterDirect, ea_register,
|
||||
AddressingMode::ImmediateData, 0);
|
||||
|
||||
//
|
||||
// MARK: CHKl
|
||||
//
|
||||
// Implicitly: destination is a register;
|
||||
// b0–b2 and b3–b5: source effective address.
|
||||
//
|
||||
case OpT(Operation::CHKl):
|
||||
return validated<op, validate>(
|
||||
combined_mode(ea_mode, ea_register), ea_register,
|
||||
AddressingMode::DataRegisterDirect, data_register);
|
||||
|
||||
//
|
||||
// MARK: EXTbtol
|
||||
//
|
||||
// b0–b2: a data register.
|
||||
//
|
||||
case OpT(Operation::EXTbtol):
|
||||
return validated<op, validate>(AddressingMode::DataRegisterDirect, ea_register);
|
||||
|
||||
//
|
||||
// MARK: DIVl
|
||||
//
|
||||
//
|
||||
// TODO.
|
||||
}
|
||||
|
||||
// Should be unreachable.
|
||||
assert(false);
|
||||
return Preinstruction(); // To appease GCC during development.
|
||||
}
|
||||
|
||||
// MARK: - Page decoders.
|
||||
|
||||
#define Decode(y) return decode<OpT(y)>(instruction)
|
||||
#define DecodeReq(x, y) if constexpr (x) Decode(y); break;
|
||||
|
||||
template <Model model>
|
||||
Preinstruction Predecoder<model>::decode0(uint16_t instruction) {
|
||||
@ -911,6 +1284,16 @@ Preinstruction Predecoder<model>::decode0(uint16_t instruction) {
|
||||
case 0xa3c: Decode(Op::EORItoCCR); // 4-104 (p208)
|
||||
case 0xa7c: Decode(Op::EORItoSR); // 6-10 (p464)
|
||||
|
||||
// 4-68 (p172)
|
||||
case 0xcfc: DecodeReq(model >= Model::M68020, Op::CAS2w);
|
||||
case 0xefc: DecodeReq(model >= Model::M68020, Op::CAS2l);
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
switch(instruction & 0xff0) {
|
||||
case 0x6c0: DecodeReq(model == Model::M68020, Op::RTM); // 4-167 (p271)
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
@ -957,6 +1340,25 @@ Preinstruction Predecoder<model>::decode0(uint16_t instruction) {
|
||||
case 0xc40: Decode(CMPIw);
|
||||
case 0xc80: Decode(CMPIl);
|
||||
|
||||
// 4-64 (p168)
|
||||
case 0x6c0: DecodeReq(model == Model::M68020, Op::CALLM);
|
||||
|
||||
// 4-67 (p171)
|
||||
case 0xac0: DecodeReq(model >= Model::M68020, Op::CASb);
|
||||
case 0xcc0: DecodeReq(model >= Model::M68020, Op::CASw);
|
||||
case 0xec0: DecodeReq(model >= Model::M68020, Op::CASl);
|
||||
|
||||
// 4-83 (p187) [CMP2] and 4-72 (p176) [CHK2];
|
||||
// the two are distinguished by a bit in the extension word.
|
||||
case 0x0c0: DecodeReq(model >= Model::M68020, Op::CHKorCMP2b);
|
||||
case 0x2c0: DecodeReq(model >= Model::M68020, Op::CHKorCMP2b);
|
||||
case 0x4c0: DecodeReq(model >= Model::M68020, Op::CHKorCMP2b);
|
||||
|
||||
// 6-24 (p478)
|
||||
case 0xe00: DecodeReq(model >= Model::M68010, Op::MOVESb);
|
||||
case 0xe40: DecodeReq(model >= Model::M68010, Op::MOVESw);
|
||||
case 0xe80: DecodeReq(model >= Model::M68010, Op::MOVESl);
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
@ -1011,7 +1413,6 @@ Preinstruction Predecoder<model>::decode3(uint16_t instruction) {
|
||||
case 0x040: Decode(Op::MOVEAw);
|
||||
default: Decode(Op::MOVEw);
|
||||
}
|
||||
// Decode(Op::MOVEw);
|
||||
}
|
||||
|
||||
template <Model model>
|
||||
@ -1023,20 +1424,31 @@ Preinstruction Predecoder<model>::decode4(uint16_t instruction) {
|
||||
case 0xe71: Decode(Op::NOP); // 4-147 (p251)
|
||||
case 0xe72: Decode(Op::STOP); // 6-85 (p539)
|
||||
case 0xe73: Decode(Op::RTE); // 6-84 (p538)
|
||||
case 0xe74: DecodeReq(model >= Model::M68010, Op::RTD); // 4-166 (p270)
|
||||
case 0xe75: Decode(Op::RTS); // 4-169 (p273)
|
||||
case 0xe76: Decode(Op::TRAPV); // 4-191 (p295)
|
||||
case 0xe77: Decode(Op::RTR); // 4-168 (p272)
|
||||
case 0xe7a: DecodeReq(model >= Model::M68010, Op::MOVEtoC); // 6-22 (p476)
|
||||
case 0xe7b: DecodeReq(model >= Model::M68010, Op::MOVEfromC); // 6-22 (p476)
|
||||
default: break;
|
||||
}
|
||||
|
||||
switch(instruction & 0xff8) {
|
||||
case 0x840: Decode(Op::SWAP); // 4-185 (p289)
|
||||
case 0x880: Decode(Op::EXTbtow); // 4-106 (p210)
|
||||
case 0x8c0: Decode(Op::EXTwtol); // 4-106 (p210)
|
||||
case 0xe50: Decode(Op::LINKw); // 4-111 (p215)
|
||||
case 0x848: DecodeReq(model >= Model::M68010, Op::BKPT); // 4-54 (p158)
|
||||
case 0xe58: Decode(Op::UNLINK); // 4-194 (p298)
|
||||
case 0xe60: Decode(Op::MOVEtoUSP); // 6-21 (p475)
|
||||
case 0xe68: Decode(Op::MOVEfromUSP); // 6-21 (p475)
|
||||
|
||||
// 4-106 (p210)
|
||||
case 0x880: Decode(Op::EXTbtow);
|
||||
case 0x8c0: Decode(Op::EXTwtol);
|
||||
case 0x9c0: DecodeReq(model >= Model::M68020, Op::EXTbtol);
|
||||
|
||||
// 4-111 (p215)
|
||||
case 0x808: DecodeReq(model >= Model::M68020, Op::LINKl);
|
||||
case 0xe50: Decode(Op::LINKw);
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
@ -1101,12 +1513,25 @@ Preinstruction Predecoder<model>::decode4(uint16_t instruction) {
|
||||
// 4-108 (p212)
|
||||
case 0xec0: Decode(Op::JMP);
|
||||
|
||||
// 4-94 (p198) [DIVS.l]; 4-98 (p202) [DIVU.l]
|
||||
case 0xc40: DecodeReq(model >= Model::M68020, Op::DIVSorDIVUl);
|
||||
|
||||
// 4-137 (p241) [MULS.l]; 4-140 (p244) [MULU.l]
|
||||
case 0xc00: DecodeReq(model >= Model::M68020, Op::MULSorMULUl);
|
||||
|
||||
// 4-121 (p225)
|
||||
case 0x2c0: DecodeReq(model >= Model::M68010, Op::MOVEfromCCR);
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
switch(instruction & 0x1c0) {
|
||||
case 0x1c0: Decode(Op::LEA); // 4-110 (p214)
|
||||
case 0x180: Decode(Op::CHK); // 4-69 (p173)
|
||||
|
||||
// 4-69 (p173)
|
||||
case 0x180: Decode(Op::CHKw);
|
||||
case 0x100: DecodeReq(model >= Model::M68020, Op::CHKl);
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
@ -1165,12 +1590,23 @@ Preinstruction Predecoder<model>::decode5(uint16_t instruction) {
|
||||
default: break;
|
||||
}
|
||||
|
||||
switch(instruction & 0x0ff) {
|
||||
// 4-173 (p276)
|
||||
case 0x0f8: case 0x0f9: Decode(Op::Scc);
|
||||
|
||||
// 4-189 (p294)
|
||||
case 0x0fa: case 0x0fb: case 0x0fc:
|
||||
DecodeReq(model >= Model::M68020, Op::TRAPcc);
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
switch(instruction & 0x0f8) {
|
||||
// 4-173 (p276)
|
||||
case 0x0c0:
|
||||
case 0x0d0: case 0x0d8:
|
||||
case 0x0e0: case 0x0e8:
|
||||
case 0x0f0: case 0x0f8: Decode(Op::Scc);
|
||||
case 0x0f0: Decode(Op::Scc);
|
||||
|
||||
// 4-91 (p195)
|
||||
case 0x0c8: Decode(Op::DBcc);
|
||||
@ -1226,12 +1662,17 @@ template <Model model>
|
||||
Preinstruction Predecoder<model>::decode8(uint16_t instruction) {
|
||||
using Op = Operation;
|
||||
|
||||
// 4-171 (p275)
|
||||
if((instruction & 0x1f0) == 0x100) Decode(Op::SBCD);
|
||||
switch(instruction & 0x1f0) {
|
||||
case 0x100: Decode(Op::SBCD); // 4-171 (p275)
|
||||
case 0x140: DecodeReq(model >= Model::M68020, Op::PACK); // 4-157 (p261)
|
||||
case 0x180: DecodeReq(model >= Model::M68020, Op::UNPK); // 4-196 (p300)
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
switch(instruction & 0x1c0) {
|
||||
case 0x0c0: Decode(Op::DIVU); // 4-97 (p201)
|
||||
case 0x1c0: Decode(Op::DIVS); // 4-93 (p197)
|
||||
case 0x0c0: Decode(Op::DIVUw); // 4-97 (p201)
|
||||
case 0x1c0: Decode(Op::DIVSw); // 4-93 (p197)
|
||||
|
||||
// 4-150 (p254)
|
||||
case 0x000: Decode(ORtoRb);
|
||||
@ -1336,8 +1777,8 @@ Preinstruction Predecoder<model>::decodeC(uint16_t instruction) {
|
||||
}
|
||||
|
||||
switch(instruction & 0x1c0) {
|
||||
case 0x0c0: Decode(Op::MULU); // 4-139 (p243)
|
||||
case 0x1c0: Decode(Op::MULS); // 4-136 (p240)
|
||||
case 0x0c0: Decode(Op::MULUw); // 4-139 (p243)
|
||||
case 0x1c0: Decode(Op::MULSw); // 4-136 (p240)
|
||||
|
||||
// 4-15 (p119)
|
||||
case 0x000: Decode(ANDtoRb);
|
||||
@ -1399,6 +1840,15 @@ Preinstruction Predecoder<model>::decodeE(uint16_t instruction) {
|
||||
case 0x6c0: Decode(Op::RORm); // 4-160 (p264)
|
||||
case 0x7c0: Decode(Op::ROLm); // 4-160 (p264)
|
||||
|
||||
case 0x8c0: DecodeReq(model >= Model::M68020, Op::BFTST); // 4-51 (p155)
|
||||
case 0x9c0: DecodeReq(model >= Model::M68020, Op::BFEXTU); // 4-40 (p144)
|
||||
case 0xac0: DecodeReq(model >= Model::M68020, Op::BFCHG); // 4-33 (p137)
|
||||
case 0xbc0: DecodeReq(model >= Model::M68020, Op::BFEXTS); // 4-37 (p141)
|
||||
case 0xcc0: DecodeReq(model >= Model::M68020, Op::BFCLR); // 4-35 (p139)
|
||||
case 0xdc0: DecodeReq(model >= Model::M68020, Op::BFFFO); // 4-43 (p147) [though the given opcode is wrong; listed same as BFEXTU]
|
||||
case 0xec0: DecodeReq(model >= Model::M68020, Op::BFSET); // 4-49 (p153)
|
||||
case 0xfc0: DecodeReq(model >= Model::M68020, Op::BFINS); // 4-46 (p150)
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
@ -1455,6 +1905,7 @@ Preinstruction Predecoder<model>::decodeF(uint16_t) {
|
||||
}
|
||||
|
||||
#undef Decode
|
||||
#undef DecodeRef
|
||||
|
||||
// MARK: - Main decoder.
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "Instruction.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "../../Numeric/Sizes.hpp"
|
||||
|
||||
namespace InstructionSet {
|
||||
namespace M68k {
|
||||
@ -50,23 +51,32 @@ template <Model model> class Predecoder {
|
||||
Preinstruction decodeE(uint16_t instruction);
|
||||
Preinstruction decodeF(uint16_t instruction);
|
||||
|
||||
using OpT = uint8_t;
|
||||
// Yuckiness here: 67 is a count of the number of things contained below in
|
||||
// ExtendedOperation; this acts to ensure ExtendedOperation is the minimum
|
||||
// integer size large enough to hold all actual operations plus the ephemeral
|
||||
// ones used here. Intention is to support table-based decoding, which will mean
|
||||
// making those integers less ephemeral, hence the desire to pick a minimum size.
|
||||
using OpT = typename MinIntTypeValue<
|
||||
uint64_t(OperationMax<model>::value) + 67
|
||||
>::type;
|
||||
static constexpr auto OpMax = OpT(OperationMax<model>::value);
|
||||
|
||||
// Specific instruction decoders.
|
||||
template <OpT operation, bool validate = true> Preinstruction decode(uint16_t instruction);
|
||||
template <OpT operation, bool validate> Preinstruction validated(
|
||||
AddressingMode op1_mode = AddressingMode::None, int op1_reg = 0,
|
||||
AddressingMode op2_mode = AddressingMode::None, int op2_reg = 0,
|
||||
Condition condition = Condition::True
|
||||
Condition condition = Condition::True,
|
||||
int further_extension_words = 0
|
||||
);
|
||||
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 = OpMax + 1, MOVEPtoRw,
|
||||
MOVEPtoMl, MOVEPtoMw,
|
||||
|
||||
MOVEQ,
|
||||
@ -87,8 +97,6 @@ template <Model model> class Predecoder {
|
||||
|
||||
CMPMb, CMPMw, CMPMl,
|
||||
|
||||
MOVEq,
|
||||
|
||||
ADDtoMb, ADDtoMw, ADDtoMl,
|
||||
ADDtoRb, ADDtoRw, ADDtoRl,
|
||||
|
||||
|
@ -19,13 +19,29 @@ template <Model model, Operation t_operation> constexpr uint8_t operand_flags(Op
|
||||
|
||||
//
|
||||
// No operands are fetched or stored.
|
||||
// (which means that source and destination will appear as their effective addresses)
|
||||
//
|
||||
// (which means that source and destination, if they exist,
|
||||
// should be supplied as their effective addresses)
|
||||
//
|
||||
case Operation::PEA:
|
||||
case Operation::JMP: case Operation::JSR:
|
||||
case Operation::MOVEPw: case Operation::MOVEPl:
|
||||
case Operation::TAS:
|
||||
case Operation::RTR: case Operation::RTS: case Operation::RTE:
|
||||
case Operation::RTM:
|
||||
case Operation::RTD:
|
||||
case Operation::TRAP: case Operation::RESET: case Operation::NOP:
|
||||
case Operation::STOP: case Operation::TRAPV: case Operation::BKPT:
|
||||
case Operation::TRAPcc:
|
||||
case Operation::CASb: case Operation::CASw: case Operation::CASl:
|
||||
case Operation::CAS2w: case Operation::CAS2l:
|
||||
return 0;
|
||||
|
||||
//
|
||||
// Operand fetch/store status isn't certain just from the operation; this means
|
||||
// that further content from an extension word will be required.
|
||||
//
|
||||
case Operation::MOVESb: case Operation::MOVESw: case Operation::MOVESl:
|
||||
return 0;
|
||||
|
||||
//
|
||||
@ -40,12 +56,17 @@ template <Model model, Operation t_operation> constexpr uint8_t operand_flags(Op
|
||||
case Operation::TSTb: case Operation::TSTw: case Operation::TSTl:
|
||||
case Operation::MOVEMtoMw: case Operation::MOVEMtoMl:
|
||||
case Operation::MOVEMtoRw: case Operation::MOVEMtoRl:
|
||||
case Operation::MOVEtoC:
|
||||
case Operation::CALLM:
|
||||
case Operation::CHKorCMP2b: case Operation::CHKorCMP2w: case Operation::CHKorCMP2l:
|
||||
return FetchOp1;
|
||||
|
||||
//
|
||||
// Single-operand write.
|
||||
//
|
||||
case Operation::MOVEfromUSP:
|
||||
case Operation::MOVEfromCCR:
|
||||
case Operation::MOVEfromC:
|
||||
return StoreOp1;
|
||||
|
||||
//
|
||||
@ -55,7 +76,7 @@ template <Model model, Operation t_operation> constexpr uint8_t operand_flags(Op
|
||||
case Operation::NOTb: case Operation::NOTw: case Operation::NOTl:
|
||||
case Operation::NEGb: case Operation::NEGw: case Operation::NEGl:
|
||||
case Operation::NEGXb: case Operation::NEGXw: case Operation::NEGXl:
|
||||
case Operation::EXTbtow: case Operation::EXTwtol:
|
||||
case Operation::EXTbtow: case Operation::EXTwtol: case Operation::EXTbtol:
|
||||
case Operation::SWAP:
|
||||
case Operation::UNLINK:
|
||||
case Operation::ASLm: case Operation::ASRm:
|
||||
@ -81,9 +102,13 @@ template <Model model, Operation t_operation> constexpr uint8_t operand_flags(Op
|
||||
//
|
||||
case Operation::CMPb: case Operation::CMPw: case Operation::CMPl:
|
||||
case Operation::CMPAw: case Operation::CMPAl:
|
||||
case Operation::CHK:
|
||||
case Operation::CHKw: case Operation::CHKl:
|
||||
case Operation::BTST:
|
||||
case Operation::LINKw:
|
||||
case Operation::LINKw: case Operation::LINKl:
|
||||
case Operation::BFTST: case Operation::BFFFO:
|
||||
case Operation::BFEXTU: case Operation::BFEXTS:
|
||||
case Operation::DIVSorDIVUl:
|
||||
case Operation::MULSorMULUl:
|
||||
return FetchOp1 | FetchOp2;
|
||||
|
||||
//
|
||||
@ -91,6 +116,7 @@ template <Model model, Operation t_operation> constexpr uint8_t operand_flags(Op
|
||||
//
|
||||
case Operation::MOVEb: case Operation::MOVEw: case Operation::MOVEl:
|
||||
case Operation::MOVEAw: case Operation::MOVEAl:
|
||||
case Operation::PACK: case Operation::UNPK:
|
||||
return FetchOp1 | StoreOp2;
|
||||
|
||||
//
|
||||
@ -106,8 +132,8 @@ template <Model model, Operation t_operation> constexpr uint8_t operand_flags(Op
|
||||
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:
|
||||
case Operation::DIVU: case Operation::DIVS:
|
||||
case Operation::MULU: case Operation::MULS:
|
||||
case Operation::DIVUw: case Operation::DIVSw:
|
||||
case Operation::MULUw: case Operation::MULSw:
|
||||
case Operation::ASLb: case Operation::ASLw: case Operation::ASLl:
|
||||
case Operation::ASRb: case Operation::ASRw: case Operation::ASRl:
|
||||
case Operation::LSLb: case Operation::LSLw: case Operation::LSLl:
|
||||
@ -118,6 +144,8 @@ template <Model model, Operation t_operation> constexpr uint8_t operand_flags(Op
|
||||
case Operation::ROXRb: case Operation::ROXRw: case Operation::ROXRl:
|
||||
case Operation::BCHG:
|
||||
case Operation::BCLR: case Operation::BSET:
|
||||
case Operation::BFCHG: case Operation::BFCLR: case Operation::BFSET:
|
||||
case Operation::BFINS:
|
||||
return FetchOp1 | FetchOp2 | StoreOp2;
|
||||
|
||||
//
|
||||
|
@ -22,14 +22,19 @@ constexpr DataSize operand_size(Operation r_operation) {
|
||||
case Operation::STOP:
|
||||
case Operation::RESET:
|
||||
case Operation::RTE: case Operation::RTR:
|
||||
case Operation::RTD:
|
||||
case Operation::TRAP:
|
||||
case Operation::TRAPV:
|
||||
case Operation::TRAPcc:
|
||||
case Operation::BKPT:
|
||||
|
||||
case Operation::ABCD: case Operation::SBCD:
|
||||
case Operation::NBCD:
|
||||
case Operation::ADDb: case Operation::ADDXb:
|
||||
case Operation::SUBb: case Operation::SUBXb:
|
||||
case Operation::MOVEb:
|
||||
case Operation::MOVESb:
|
||||
case Operation::MOVEfromCCR:
|
||||
case Operation::ORItoCCR:
|
||||
case Operation::ANDItoCCR:
|
||||
case Operation::EORItoCCR:
|
||||
@ -53,12 +58,13 @@ constexpr DataSize operand_size(Operation r_operation) {
|
||||
case Operation::ADDXw: case Operation::SUBw:
|
||||
case Operation::SUBAw: case Operation::SUBXw:
|
||||
case Operation::MOVEw: case Operation::MOVEAw:
|
||||
case Operation::MOVESw:
|
||||
case Operation::ORItoSR:
|
||||
case Operation::ANDItoSR:
|
||||
case Operation::EORItoSR:
|
||||
case Operation::MOVEtoSR:
|
||||
case Operation::MOVEfromSR:
|
||||
case Operation::MOVEtoCCR:
|
||||
case Operation::MOVEtoCCR: // TODO: is this true?
|
||||
case Operation::CMPw: case Operation::CMPAw:
|
||||
case Operation::TSTw:
|
||||
case Operation::DBcc:
|
||||
@ -80,21 +86,24 @@ constexpr DataSize operand_size(Operation r_operation) {
|
||||
case Operation::MOVEPw:
|
||||
case Operation::ANDw: case Operation::EORw:
|
||||
case Operation::NOTw: case Operation::ORw:
|
||||
case Operation::DIVU: case Operation::DIVS:
|
||||
case Operation::MULU: case Operation::MULS:
|
||||
case Operation::DIVUw: case Operation::DIVSw:
|
||||
case Operation::MULUw: case Operation::MULSw:
|
||||
case Operation::EXTbtow:
|
||||
case Operation::LINKw:
|
||||
case Operation::CHK:
|
||||
case Operation::CHKw:
|
||||
return DataSize::Word;
|
||||
|
||||
case Operation::ADDl: case Operation::ADDAl:
|
||||
case Operation::ADDXl: case Operation::SUBl:
|
||||
case Operation::SUBAl: case Operation::SUBXl:
|
||||
case Operation::MOVEl: case Operation::MOVEAl:
|
||||
case Operation::MOVESl:
|
||||
case Operation::LEA: case Operation::PEA:
|
||||
case Operation::EXG: case Operation::SWAP:
|
||||
case Operation::MOVEtoUSP:
|
||||
case Operation::MOVEfromUSP:
|
||||
case Operation::MOVEtoC:
|
||||
case Operation::MOVEfromC:
|
||||
case Operation::CMPl: case Operation::CMPAl:
|
||||
case Operation::TSTl:
|
||||
case Operation::JMP: case Operation::JSR:
|
||||
@ -112,6 +121,10 @@ constexpr DataSize operand_size(Operation r_operation) {
|
||||
case Operation::EXTwtol:
|
||||
case Operation::UNLINK:
|
||||
return DataSize::LongWord;
|
||||
|
||||
default:
|
||||
// 68020 TODO.
|
||||
return DataSize::Byte;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -742,15 +742,15 @@ template <
|
||||
Multiplications.
|
||||
*/
|
||||
|
||||
case Operation::MULU: Primitive::multiply<true>(src.w, dest.l, status, flow_controller); break;
|
||||
case Operation::MULS: Primitive::multiply<false>(src.w, dest.l, status, flow_controller); break;
|
||||
case Operation::MULUw: Primitive::multiply<true>(src.w, dest.l, status, flow_controller); break;
|
||||
case Operation::MULSw: Primitive::multiply<false>(src.w, dest.l, status, flow_controller); break;
|
||||
|
||||
/*
|
||||
Divisions.
|
||||
*/
|
||||
|
||||
case Operation::DIVU: Primitive::divide<true, uint16_t, uint32_t>(src.w, dest.l, status, flow_controller); break;
|
||||
case Operation::DIVS: Primitive::divide<false, int16_t, int32_t>(src.w, dest.l, status, flow_controller); break;
|
||||
case Operation::DIVUw: Primitive::divide<true, uint16_t, uint32_t>(src.w, dest.l, status, flow_controller); break;
|
||||
case Operation::DIVSw: Primitive::divide<false, int16_t, int32_t>(src.w, dest.l, status, flow_controller); break;
|
||||
|
||||
// TRAP, which is a nicer form of ILLEGAL.
|
||||
case Operation::TRAP:
|
||||
@ -763,7 +763,7 @@ template <
|
||||
}
|
||||
} break;
|
||||
|
||||
case Operation::CHK: {
|
||||
case Operation::CHKw: {
|
||||
const bool is_under = s_extend16(dest.w) < 0;
|
||||
const bool is_over = s_extend16(dest.w) > s_extend16(src.w);
|
||||
|
||||
|
@ -45,6 +45,7 @@ std::string Preinstruction::operand_description(int index, int opcode) const {
|
||||
case AddressingMode::AbsoluteLong:
|
||||
return "(xxx).l";
|
||||
|
||||
case AddressingMode::ExtensionWord:
|
||||
case AddressingMode::ImmediateData:
|
||||
return "#";
|
||||
|
||||
@ -95,14 +96,21 @@ const char *_to_string(Operation operation, bool is_quick) {
|
||||
case Operation::MOVEAw: return "MOVEA.w";
|
||||
case Operation::MOVEAl: return "MOVEA.l";
|
||||
|
||||
case Operation::MOVESb: return "MOVES.b";
|
||||
case Operation::MOVESw: return "MOVES.w";
|
||||
case Operation::MOVESl: return "MOVES.l";
|
||||
|
||||
case Operation::LEA: return "LEA";
|
||||
case Operation::PEA: return "PEA";
|
||||
|
||||
case Operation::MOVEtoSR: return "MOVEtoSR";
|
||||
case Operation::MOVEfromSR: return "MOVEfromSR";
|
||||
case Operation::MOVEtoCCR: return "MOVEtoCCR";
|
||||
case Operation::MOVEfromCCR: return "MOVEfromCCR";
|
||||
case Operation::MOVEtoUSP: return "MOVEtoUSP";
|
||||
case Operation::MOVEfromUSP: return "MOVEfromUSP";
|
||||
case Operation::MOVEtoC: return "MOVEtoC";
|
||||
case Operation::MOVEfromC: return "MOVEfromC";
|
||||
|
||||
case Operation::ORItoSR: return "ORItoSR";
|
||||
case Operation::ORItoCCR: return "ORItoCCR";
|
||||
@ -130,8 +138,12 @@ const char *_to_string(Operation operation, bool is_quick) {
|
||||
case Operation::JMP: return "JMP";
|
||||
case Operation::JSR: return "JSR";
|
||||
case Operation::RTS: return "RTS";
|
||||
case Operation::RTD: return "RTD";
|
||||
case Operation::RTM: return "RTM";
|
||||
|
||||
case Operation::DBcc: return "DBcc";
|
||||
case Operation::Scc: return "Scc";
|
||||
case Operation::TRAPcc: return "TRAPcc";
|
||||
|
||||
case Operation::Bccb:
|
||||
case Operation::Bccl:
|
||||
@ -141,6 +153,13 @@ const char *_to_string(Operation operation, bool is_quick) {
|
||||
case Operation::BSRl:
|
||||
case Operation::BSRw: return "BSR";
|
||||
|
||||
case Operation::CASb: return "CAS.b";
|
||||
case Operation::CASw: return "CAS.w";
|
||||
case Operation::CASl: return "CAS.l";
|
||||
|
||||
case Operation::CAS2w: return "CAS2.w";
|
||||
case Operation::CAS2l: return "CAS2.l";
|
||||
|
||||
case Operation::CLRb: return "CLR.b";
|
||||
case Operation::CLRw: return "CLR.w";
|
||||
case Operation::CLRl: return "CLR.l";
|
||||
@ -217,17 +236,26 @@ const char *_to_string(Operation operation, bool is_quick) {
|
||||
case Operation::ORw: return "OR.w";
|
||||
case Operation::ORl: return "OR.l";
|
||||
|
||||
case Operation::MULU: return "MULU";
|
||||
case Operation::MULS: return "MULS";
|
||||
case Operation::DIVU: return "DIVU";
|
||||
case Operation::DIVS: return "DIVS";
|
||||
case Operation::MULUw: return "MULU";
|
||||
case Operation::MULSw: return "MULS";
|
||||
case Operation::MULSorMULUl: return "[MULS/MULU]{L}.l";
|
||||
|
||||
case Operation::DIVUw: return "DIVU";
|
||||
case Operation::DIVSw: return "DIVS";
|
||||
case Operation::DIVSorDIVUl: return "[DIVS/DIVU]{L}.l";
|
||||
|
||||
case Operation::RTE: return "RTE";
|
||||
case Operation::RTR: return "RTR";
|
||||
|
||||
case Operation::TRAP: return "TRAP";
|
||||
case Operation::TRAPV: return "TRAPV";
|
||||
case Operation::CHK: return "CHK";
|
||||
|
||||
case Operation::CHKw: return "CHK";
|
||||
case Operation::CHKl: return "CHK.l";
|
||||
|
||||
case Operation::CHKorCMP2b: return "[CHK/CMP]2.b";
|
||||
case Operation::CHKorCMP2w: return "[CHK/CMP]2.w";
|
||||
case Operation::CHKorCMP2l: return "[CHK/CMP]2.l";
|
||||
|
||||
case Operation::EXG: return "EXG";
|
||||
case Operation::SWAP: return "SWAP";
|
||||
@ -236,13 +264,29 @@ const char *_to_string(Operation operation, bool is_quick) {
|
||||
|
||||
case Operation::EXTbtow: return "EXT.w";
|
||||
case Operation::EXTwtol: return "EXT.l";
|
||||
case Operation::EXTbtol: return "EXTB.l";
|
||||
|
||||
case Operation::LINKw: return "LINK";
|
||||
case Operation::LINKl: return "LINK.l";
|
||||
case Operation::UNLINK: return "UNLINK";
|
||||
|
||||
case Operation::STOP: return "STOP";
|
||||
case Operation::RESET: return "RESET";
|
||||
|
||||
case Operation::BKPT: return "BKPT";
|
||||
|
||||
case Operation::BFCHG: return "BFCHG";
|
||||
case Operation::BFCLR: return "BFCLR";
|
||||
case Operation::BFEXTS: return "BFEXTS";
|
||||
case Operation::BFEXTU: return "BFEXTU";
|
||||
case Operation::BFFFO: return "BFFFO";
|
||||
case Operation::BFINS: return "BFINS";
|
||||
case Operation::BFSET: return "BFSET";
|
||||
case Operation::BFTST: return "BFTST";
|
||||
|
||||
case Operation::PACK: return "PACK";
|
||||
case Operation::UNPK: return "UNPK";
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
return "???";
|
||||
@ -268,6 +312,9 @@ std::string Preinstruction::to_string(int opcode) const {
|
||||
if(!operand1.empty()) result += std::string(" ") + operand1;
|
||||
if(!operand2.empty()) result += std::string(", ") + operand2;
|
||||
|
||||
const int extension_words = additional_extension_words();
|
||||
if(extension_words) result += std::string(" [+") + std::to_string(extension_words) + "]";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,10 @@ namespace M68k {
|
||||
enum class Operation: uint8_t {
|
||||
Undefined,
|
||||
|
||||
//
|
||||
// 68000 operations.
|
||||
//
|
||||
|
||||
NOP,
|
||||
|
||||
ABCD, SBCD, NBCD,
|
||||
@ -57,8 +61,8 @@ enum class Operation: uint8_t {
|
||||
DBcc,
|
||||
Scc,
|
||||
|
||||
Bccb, Bccw, Bccl,
|
||||
BSRb, BSRw, BSRl,
|
||||
Bccb, Bccw,
|
||||
BSRb, BSRw,
|
||||
|
||||
CLRb, CLRw, CLRl,
|
||||
NEGXb, NEGXw, NEGXl,
|
||||
@ -83,13 +87,13 @@ enum class Operation: uint8_t {
|
||||
NOTb, NOTw, NOTl,
|
||||
ORb, ORw, ORl,
|
||||
|
||||
MULU, MULS,
|
||||
DIVU, DIVS,
|
||||
MULUw, MULSw,
|
||||
DIVUw, DIVSw,
|
||||
|
||||
RTE, RTR,
|
||||
|
||||
TRAP, TRAPV,
|
||||
CHK,
|
||||
CHKw,
|
||||
|
||||
EXG, SWAP,
|
||||
|
||||
@ -101,7 +105,102 @@ enum class Operation: uint8_t {
|
||||
|
||||
STOP, RESET,
|
||||
|
||||
Max = RESET
|
||||
//
|
||||
// 68010 additions.
|
||||
//
|
||||
|
||||
MOVEfromCCR,
|
||||
MOVEtoC, MOVEfromC,
|
||||
MOVESb, MOVESw, MOVESl,
|
||||
BKPT, RTD,
|
||||
|
||||
//
|
||||
// 68020 additions.
|
||||
//
|
||||
|
||||
TRAPcc,
|
||||
|
||||
CALLM, RTM,
|
||||
|
||||
BFCHG, BFCLR,
|
||||
BFEXTS, BFEXTU,
|
||||
BFFFO, BFINS,
|
||||
BFSET, BFTST,
|
||||
|
||||
PACK, UNPK,
|
||||
|
||||
CASb, CASw, CASl,
|
||||
CAS2w, CAS2l,
|
||||
|
||||
// CHK2 and CMP2 are distinguished by their extension word;
|
||||
// since this code deals in Preinstructions, i.e. as much
|
||||
// as can be derived from the instruction word alone, in addition
|
||||
// to the full things, the following enums result.
|
||||
CHKorCMP2b, CHKorCMP2w, CHKorCMP2l,
|
||||
|
||||
// DIVS.l, DIVSL.l, DIVU.l and DIVUL.l are all distinguishable
|
||||
// only by the extension word.
|
||||
DIVSorDIVUl,
|
||||
|
||||
// MULS.l, MULSL.l, MULU.l and MULUL.l are all distinguishable
|
||||
// only by the extension word.
|
||||
MULSorMULUl,
|
||||
|
||||
Bccl, BSRl,
|
||||
LINKl, CHKl,
|
||||
|
||||
EXTbtol,
|
||||
|
||||
// Coprocessor instructions are omitted for now, until I can
|
||||
// determine by what mechanism the number of
|
||||
// "OPTIONAL COPROCESSOR-DEFINED EXTENSION WORDS" is determined.
|
||||
// cpBcc, cpDBcc, cpGEN,
|
||||
// cpScc, cpTRAPcc, cpRESTORE,
|
||||
// cpSAVE,
|
||||
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
Max68000 = RESET,
|
||||
Max68010 = RTD,
|
||||
Max68020 = EXTbtol,
|
||||
Max68030 = PTESTW,
|
||||
Max68040 = PTESTW,
|
||||
};
|
||||
|
||||
// Provide per-model max entries in Operation.
|
||||
template <Model> struct OperationMax {};
|
||||
template <> struct OperationMax<Model::M68000> {
|
||||
static constexpr Operation value = Operation::Max68000;
|
||||
};
|
||||
template <> struct OperationMax<Model::M68010> {
|
||||
static constexpr Operation value = Operation::Max68010;
|
||||
};
|
||||
template <> struct OperationMax<Model::M68020> {
|
||||
static constexpr Operation value = Operation::Max68020;
|
||||
};
|
||||
template <> struct OperationMax<Model::M68030> {
|
||||
static constexpr Operation value = Operation::Max68030;
|
||||
};
|
||||
template <> struct OperationMax<Model::M68040> {
|
||||
static constexpr Operation value = Operation::Max68040;
|
||||
};
|
||||
|
||||
const char *to_string(Operation op);
|
||||
@ -118,6 +217,7 @@ constexpr bool requires_supervisor(Operation op) {
|
||||
case Operation::EORItoSR: case Operation::RTE:
|
||||
case Operation::RESET: case Operation::STOP:
|
||||
case Operation::MOVEtoUSP: case Operation::MOVEfromUSP:
|
||||
case Operation::MOVEtoC: case Operation::MOVEfromC:
|
||||
case Operation::MOVEtoSR:
|
||||
return true;
|
||||
|
||||
@ -147,6 +247,7 @@ constexpr uint32_t quick(uint16_t instruction, Operation r_op = Operation::Undef
|
||||
case Operation::BSRb:
|
||||
case Operation::MOVEl: return uint32_t(int8_t(instruction));
|
||||
case Operation::TRAP: return uint32_t(instruction & 15);
|
||||
case Operation::BKPT: return uint32_t(instruction & 7);
|
||||
default: {
|
||||
uint32_t value = (instruction >> 9) & 7;
|
||||
value |= (value - 1)&8;
|
||||
@ -249,6 +350,10 @@ enum class AddressingMode: uint8_t {
|
||||
/// #
|
||||
ImmediateData = 0b01'100,
|
||||
|
||||
/// An additional word of data. Differs from ImmediateData by being
|
||||
/// a fixed size, rather than the @c operand_size of the operation.
|
||||
ExtensionWord = 0b01'111,
|
||||
|
||||
/// .q; value is embedded in the opcode.
|
||||
Quick = 0b01'110,
|
||||
};
|
||||
@ -308,14 +413,32 @@ class Preinstruction {
|
||||
return operands_[index] & 0xf;
|
||||
}
|
||||
|
||||
/// @returns @c true if this instruction requires supervisor privileges; @c false otherwise.
|
||||
bool requires_supervisor() const {
|
||||
return flags_ & 0x80;
|
||||
return flags_ & Flags::IsSupervisor;
|
||||
}
|
||||
/// @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 number of additional extension words required, beyond those encoded as operands.
|
||||
int additional_extension_words() const {
|
||||
return flags_ & Flags::RequiresFurtherExtension ? (flags_ & Flags::ConditionMask) >> Flags::ConditionShift : 0;
|
||||
}
|
||||
/// @returns The @c DataSize used for operands of this instruction, i.e. byte, word or longword.
|
||||
DataSize operand_size() const {
|
||||
return DataSize(flags_ & 0x03);
|
||||
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_ >> 2) & 0x0f);
|
||||
return Condition((flags_ & Flags::ConditionMask) >> Flags::ConditionShift);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -330,18 +453,33 @@ class Preinstruction {
|
||||
AddressingMode op1_mode, int op1_reg,
|
||||
AddressingMode op2_mode, int op2_reg,
|
||||
bool is_supervisor,
|
||||
int extension_words,
|
||||
DataSize size,
|
||||
Condition condition) : operation(operation)
|
||||
{
|
||||
operands_[0] = uint8_t((uint8_t(op1_mode) << 3) | op1_reg);
|
||||
operands_[1] = uint8_t((uint8_t(op2_mode) << 3) | op2_reg);
|
||||
flags_ = uint8_t(
|
||||
(is_supervisor ? 0x80 : 0x00) |
|
||||
(int(condition) << 2) |
|
||||
int(size)
|
||||
(is_supervisor ? Flags::IsSupervisor : 0x00) |
|
||||
(extension_words ? Flags::RequiresFurtherExtension : 0x00) |
|
||||
(int(condition) << Flags::ConditionShift) |
|
||||
(extension_words << Flags::ConditionShift) |
|
||||
(int(size) << Flags::SizeShift)
|
||||
);
|
||||
}
|
||||
|
||||
struct Flags {
|
||||
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 IsSupervisorShift = 7;
|
||||
static constexpr int RequiresFurtherExtensionShift = 6;
|
||||
static constexpr int ConditionShift = 2;
|
||||
static constexpr int SizeShift = 0;
|
||||
};
|
||||
|
||||
Preinstruction() {}
|
||||
|
||||
/// Produces a string description of this instruction; if @c opcode
|
||||
|
65538
OSBindings/Mac/Clock SignalTests/68000 Decoding/68010ops.json
Normal file
65538
OSBindings/Mac/Clock SignalTests/68000 Decoding/68010ops.json
Normal file
File diff suppressed because it is too large
Load Diff
65538
OSBindings/Mac/Clock SignalTests/68000 Decoding/68020ops.json
Normal file
65538
OSBindings/Mac/Clock SignalTests/68000 Decoding/68020ops.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -438,8 +438,8 @@ struct TestProcessor: public CPU::MC68000Mk2::BusHandler {
|
||||
|
||||
// For DIVU and DIVS, for now, test only the well-defined flags.
|
||||
if(
|
||||
instruction.operation != InstructionSet::M68k::Operation::DIVS &&
|
||||
instruction.operation != InstructionSet::M68k::Operation::DIVU
|
||||
instruction.operation != InstructionSet::M68k::Operation::DIVSw &&
|
||||
instruction.operation != InstructionSet::M68k::Operation::DIVUw
|
||||
) {
|
||||
[_failures addObject:name];
|
||||
} else {
|
||||
|
@ -15,20 +15,37 @@ using namespace InstructionSet::M68k;
|
||||
@interface M68000DecoderTests : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation M68000DecoderTests
|
||||
namespace {
|
||||
|
||||
- (void)testInstructionSpecs {
|
||||
template <Model model> void generate() {
|
||||
printf("{\n");
|
||||
Predecoder<model> decoder;
|
||||
for(int instr = 0; instr < 65536; instr++) {
|
||||
printf("\t\"%04x\": \"", instr);
|
||||
|
||||
const auto found = decoder.decode(uint16_t(instr));
|
||||
printf("%s\"", found.to_string(instr).c_str());
|
||||
|
||||
if(instr != 0xffff) printf(",");
|
||||
printf("\n");
|
||||
}
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
template <Model model> void test(NSString *filename, Class cls) {
|
||||
NSData *const testData =
|
||||
[NSData dataWithContentsOfURL:
|
||||
[[NSBundle bundleForClass:[self class]]
|
||||
URLForResource:@"68000ops"
|
||||
[[NSBundle bundleForClass:cls]
|
||||
URLForResource:filename
|
||||
withExtension:@"json"
|
||||
subdirectory:@"68000 Decoding"]];
|
||||
|
||||
NSDictionary<NSString *, NSString *> *const decodings = [NSJSONSerialization JSONObjectWithData:testData options:0 error:nil];
|
||||
XCTAssertNotNil(decodings);
|
||||
|
||||
Predecoder<Model::M68000> decoder;
|
||||
NSMutableArray<NSString *> *failures = [[NSMutableArray alloc] init];
|
||||
|
||||
Predecoder<model> decoder;
|
||||
for(int instr = 0; instr < 65536; instr++) {
|
||||
NSString *const instrName = [NSString stringWithFormat:@"%04x", instr];
|
||||
NSString *const expected = decodings[instrName];
|
||||
@ -36,9 +53,38 @@ using namespace InstructionSet::M68k;
|
||||
|
||||
const auto found = decoder.decode(uint16_t(instr));
|
||||
|
||||
// Test that all appropriate table entries are present for this operation.
|
||||
if(found.operation != Operation::Undefined) {
|
||||
operand_flags<model>(found.operation);
|
||||
operand_size(found.operation);
|
||||
}
|
||||
|
||||
NSString *const instruction = [NSString stringWithUTF8String:found.to_string(instr).c_str()];
|
||||
XCTAssertEqualObjects(instruction, expected, "%@ should decode as %@; got %@", instrName, expected, instruction);
|
||||
if(![instruction isEqualToString:expected]) {
|
||||
[failures addObject:[NSString stringWithFormat:@"%@ should decode as %@; got %@", instrName, expected, instruction]];
|
||||
}
|
||||
}
|
||||
|
||||
XCTAssertEqual([failures count], 0);
|
||||
if([failures count]) {
|
||||
NSLog(@"%@", failures);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@implementation M68000DecoderTests
|
||||
|
||||
- (void)test68000 {
|
||||
test<Model::M68000>(@"68000ops", [self class]);
|
||||
}
|
||||
|
||||
- (void)test68010 {
|
||||
test<Model::M68010>(@"68010ops", [self class]);
|
||||
}
|
||||
|
||||
- (void)test68020 {
|
||||
test<Model::M68020>(@"68020ops", [self class]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -540,17 +540,17 @@ void print_transactions(FILE *target, const std::vector<Transaction> &transactio
|
||||
InstructionSet::M68k::Operation::NOTw, // Old implementation omits an idle cycle before -(An)
|
||||
InstructionSet::M68k::Operation::TRAP, // Old implementation relocates the idle state near the end to the beginning.
|
||||
InstructionSet::M68k::Operation::TRAPV, // Old implementation relocates the idle state near the end to the beginning.
|
||||
InstructionSet::M68k::Operation::CHK, // Old implementation pauses four cycles too long.
|
||||
InstructionSet::M68k::Operation::CHKw, // Old implementation pauses four cycles too long.
|
||||
InstructionSet::M68k::Operation::TAS, // Old implementation just doesn't match published cycle counts.
|
||||
|
||||
//
|
||||
// Operations with timing discrepancies between the two 68000 implementations
|
||||
// that I think are _more_ accurate now, but possibly still need work:
|
||||
//
|
||||
InstructionSet::M68k::Operation::MULU,
|
||||
InstructionSet::M68k::Operation::MULS,
|
||||
InstructionSet::M68k::Operation::DIVU,
|
||||
InstructionSet::M68k::Operation::DIVS,
|
||||
InstructionSet::M68k::Operation::MULUw,
|
||||
InstructionSet::M68k::Operation::MULSw,
|
||||
InstructionSet::M68k::Operation::DIVUw,
|
||||
InstructionSet::M68k::Operation::DIVSw,
|
||||
};
|
||||
|
||||
int testsRun = 0;
|
||||
|
@ -826,7 +826,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
MoveToStateSpecific(TwoOp_Predec_bw);
|
||||
}
|
||||
|
||||
StdCASE(CHK, perform_state_ = CHK);
|
||||
StdCASE(CHKw, perform_state_ = CHK);
|
||||
|
||||
Duplicate(SUBb, ADDb) StdCASE(ADDb, perform_state_ = Perform_np)
|
||||
Duplicate(SUBw, ADDw) StdCASE(ADDw, perform_state_ = Perform_np)
|
||||
@ -1002,10 +1002,10 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
StdCASE(TSTw, perform_state_ = Perform_np);
|
||||
StdCASE(TSTl, perform_state_ = Perform_np);
|
||||
|
||||
StdCASE(DIVU, perform_state_ = DIVU_DIVS);
|
||||
StdCASE(DIVS, perform_state_ = DIVU_DIVS);
|
||||
StdCASE(MULU, perform_state_ = Perform_idle_dyamic_Dn);
|
||||
StdCASE(MULS, perform_state_ = Perform_idle_dyamic_Dn);
|
||||
StdCASE(DIVUw, perform_state_ = DIVU_DIVS);
|
||||
StdCASE(DIVSw, perform_state_ = DIVU_DIVS);
|
||||
StdCASE(MULUw, perform_state_ = Perform_idle_dyamic_Dn);
|
||||
StdCASE(MULSw, perform_state_ = Perform_idle_dyamic_Dn);
|
||||
|
||||
StdCASE(LEA, {
|
||||
post_ea_state_ = LEA;
|
||||
@ -1833,6 +1833,19 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
Prefetch(); // np
|
||||
MoveToNextOperand(FetchOperand_l);
|
||||
|
||||
//
|
||||
// ExtensionWord; always the same size.
|
||||
//
|
||||
BeginStateMode(FetchOperand_bw, ExtensionWord):
|
||||
operand_[next_operand_].w = prefetch_.w;
|
||||
Prefetch(); // np
|
||||
MoveToNextOperand(FetchOperand_bw);
|
||||
|
||||
BeginStateMode(FetchOperand_l, ExtensionWord):
|
||||
operand_[next_operand_].w = prefetch_.w;
|
||||
Prefetch(); // np
|
||||
MoveToNextOperand(FetchOperand_l);
|
||||
|
||||
#undef MoveToNextOperand
|
||||
|
||||
// MARK: - Store.
|
||||
@ -2033,7 +2046,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
//
|
||||
BeginState(CHK):
|
||||
Prefetch(); // np
|
||||
PerformSpecific(CHK);
|
||||
PerformSpecific(CHKw);
|
||||
|
||||
// Proper next state will have been set by the flow controller
|
||||
// call-in; just allow dispatch to whatever it was.
|
||||
|
Loading…
x
Reference in New Issue
Block a user