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

Merge pull request #1099 from TomHarte/68020

Expand 68k decoder to the 68020.
This commit is contained in:
Thomas Harte 2022-11-14 16:08:52 -05:00 committed by GitHub
commit c8a82933bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 131923 additions and 103 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;
@ -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; 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,
Condition((instruction >> 8) & 0xf));
//
// MARK: CHK
// MARK: CHKw
//
// Implicitly: destination is a register;
// b0b2 and b3b5: 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
//
// b0b2: register Ry (data or address, address if exchange is address <-> data);
// b9b11: 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
// b9b11: destination data register;
// b0b2 and b3b5: 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
// b0b2: a register to shift (the source here, for consistency with the memory operations);
// b8: 0 => b9b11 are a direct count of bits to shift; 1 => b9b11 identify a register containing the shift count;
// b9b11: 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
//
// b0b2 and b3b5: 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; 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::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
//
// b0b2 and b3b5: 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
//
// b0b2 and b3b5: 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
//
// b0b2: 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
//
// b9b11: Rx (destination)
// b0b2: 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
//
// b0b2 and b3b5: 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
//
// b0b2 and b3b5: 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
//
// b0b2: '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;
// b0b2 and b3b5: 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
//
// b0b2: 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.

View File

@ -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,

View File

@ -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;
//

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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 {

View File

@ -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

View File

@ -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;

View File

@ -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.