1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-09-27 18:55:48 +00:00

Merge pull request #1021 from TomHarte/68kDecoder

Establishes a formal 68k [pre-]decoder.
This commit is contained in:
Thomas Harte 2022-04-27 08:14:24 -04:00 committed by GitHub
commit 649fe7a1ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 67829 additions and 6 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,115 @@
//
// Decoder.hpp
// Clock Signal
//
// Created by Thomas Harte on 10/04/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#ifndef InstructionSets_M68k_Decoder_hpp
#define InstructionSets_M68k_Decoder_hpp
#include "Instruction.hpp"
#include "Model.hpp"
namespace InstructionSet {
namespace M68k {
/*!
A stateless decoder that can map from instruction words to preinstructions
(i.e. enough to know the operation and size, and either know the addressing mode
and registers or else know how many further extension words are needed).
WARNING: at present this handles the original 68000 instruction set only. It
requires a model only for the sake of not baking in assumptions about MOVE SR, etc,
and supporting extended addressing modes in some cases.
But it does not yet decode any operations which were not present on the 68000.
*/
template <Model model> class Predecoder {
public:
Preinstruction decode(uint16_t instruction);
private:
// Page by page decoders; each gets a bit ad hoc so
// it is neater to separate them.
Preinstruction decode0(uint16_t instruction);
Preinstruction decode1(uint16_t instruction);
Preinstruction decode2(uint16_t instruction);
Preinstruction decode3(uint16_t instruction);
Preinstruction decode4(uint16_t instruction);
Preinstruction decode5(uint16_t instruction);
Preinstruction decode6(uint16_t instruction);
Preinstruction decode7(uint16_t instruction);
Preinstruction decode8(uint16_t instruction);
Preinstruction decode9(uint16_t instruction);
Preinstruction decodeA(uint16_t instruction);
Preinstruction decodeB(uint16_t instruction);
Preinstruction decodeC(uint16_t instruction);
Preinstruction decodeD(uint16_t instruction);
Preinstruction decodeE(uint16_t instruction);
Preinstruction decodeF(uint16_t instruction);
using OpT = uint8_t;
// 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
);
template <uint8_t op> 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 {
MOVEMtoRl = uint8_t(Operation::Max) + 1, MOVEMtoRw,
MOVEMtoMl, MOVEMtoMw,
MOVEPtoRl, MOVEPtoRw,
MOVEPtoMl, MOVEPtoMw,
MOVEQ,
ADDQb, ADDQw, ADDQl,
ADDQAw, ADDQAl,
SUBQb, SUBQw, SUBQl,
SUBQAw, SUBQAl,
ADDIb, ADDIw, ADDIl,
ORIb, ORIw, ORIl,
SUBIb, SUBIw, SUBIl,
ANDIb, ANDIw, ANDIl,
EORIb, EORIw, EORIl,
CMPIb, CMPIw, CMPIl,
BTSTI, BCHGI, BCLRI, BSETI,
CMPMb, CMPMw, CMPMl,
MOVEq,
ADDtoMb, ADDtoMw, ADDtoMl,
ADDtoRb, ADDtoRw, ADDtoRl,
SUBtoMb, SUBtoMw, SUBtoMl,
SUBtoRb, SUBtoRw, SUBtoRl,
ANDtoMb, ANDtoMw, ANDtoMl,
ANDtoRb, ANDtoRw, ANDtoRl,
ORtoMb, ORtoMw, ORtoMl,
ORtoRb, ORtoRw, ORtoRl,
EXGRtoR, EXGAtoA, EXGRtoA,
};
static constexpr Operation operation(OpT op);
};
}
}
#endif /* InstructionSets_M68k_Decoder_hpp */

View File

@ -0,0 +1,392 @@
//
// Instruction.hpp
// Clock Signal
//
// Created by Thomas Harte on 10/04/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#ifndef InstructionSets_68k_Instruction_hpp
#define InstructionSets_68k_Instruction_hpp
#include <cstdint>
#include "Model.hpp"
namespace InstructionSet {
namespace M68k {
enum class Operation: uint8_t {
Undefined,
NOP,
ABCD, SBCD, NBCD,
ADDb, ADDw, ADDl,
ADDAw, ADDAl,
ADDXb, ADDXw, ADDXl,
SUBb, SUBw, SUBl,
SUBAw, SUBAl,
SUBXb, SUBXw, SUBXl,
MOVEb, MOVEw, MOVEl,
MOVEAw, MOVEAl,
LEA, PEA,
MOVEtoSR, MOVEfromSR,
MOVEtoCCR,
MOVEtoUSP, MOVEfromUSP,
ORItoSR, ORItoCCR,
ANDItoSR, ANDItoCCR,
EORItoSR, EORItoCCR,
BTST, BCLR,
BCHG, BSET,
CMPb, CMPw, CMPl,
CMPAw, CMPAl,
TSTb, TSTw, TSTl,
JMP,
JSR, RTS,
DBcc,
Scc,
Bccb, Bccw, Bccl,
BSRb, BSRw, BSRl,
CLRb, CLRw, CLRl,
NEGXb, NEGXw, NEGXl,
NEGb, NEGw, NEGl,
ASLb, ASLw, ASLl, ASLm,
ASRb, ASRw, ASRl, ASRm,
LSLb, LSLw, LSLl, LSLm,
LSRb, LSRw, LSRl, LSRm,
ROLb, ROLw, ROLl, ROLm,
RORb, RORw, RORl, RORm,
ROXLb, ROXLw, ROXLl, ROXLm,
ROXRb, ROXRw, ROXRl, ROXRm,
MOVEMl, MOVEMw,
MOVEPl, MOVEPw,
ANDb, ANDw, ANDl,
EORb, EORw, EORl,
NOTb, NOTw, NOTl,
ORb, ORw, ORl,
MULU, MULS,
DIVU, DIVS,
RTE, RTR,
TRAP, TRAPV,
CHK,
EXG, SWAP,
TAS,
EXTbtow, EXTwtol,
LINKw, UNLINK,
STOP, RESET,
Max = RESET
};
template <Model model>
constexpr bool requires_supervisor(Operation op) {
switch(op) {
case Operation::MOVEfromSR:
if constexpr (model == Model::M68000) {
return false;
}
[[fallthrough]];
case Operation::ORItoSR: case Operation::ANDItoSR:
case Operation::EORItoSR: case Operation::RTE:
case Operation::RESET: case Operation::STOP:
case Operation::MOVEtoUSP: case Operation::MOVEfromUSP:
return true;
default:
return false;
}
}
enum class DataSize {
Byte = 0,
Word = 1,
LongWord = 2,
};
/// Classifies operations by the size of their memory accesses, if any.
constexpr DataSize size(Operation operation) {
switch(operation) {
// These are given a value arbitrarily, to
// complete the switch statement.
case Operation::Undefined:
case Operation::NOP:
case Operation::STOP:
case Operation::RESET:
case Operation::RTE: case Operation::RTR:
case Operation::TRAP:
case Operation::TRAPV:
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::ORItoCCR:
case Operation::ANDItoCCR:
case Operation::EORItoCCR:
case Operation::BTST: case Operation::BCLR:
case Operation::BCHG: case Operation::BSET:
case Operation::CMPb: case Operation::TSTb:
case Operation::Bccb: case Operation::BSRb:
case Operation::CLRb:
case Operation::NEGXb: case Operation::NEGb:
case Operation::ASLb: case Operation::ASRb:
case Operation::LSLb: case Operation::LSRb:
case Operation::ROLb: case Operation::RORb:
case Operation::ROXLb: case Operation::ROXRb:
case Operation::ANDb: case Operation::EORb:
case Operation::NOTb: case Operation::ORb:
case Operation::CHK:
case Operation::TAS:
return DataSize::Byte;
case Operation::ADDw: case Operation::ADDAw:
case Operation::ADDXw: case Operation::SUBw:
case Operation::SUBAw: case Operation::SUBXw:
case Operation::MOVEw: case Operation::MOVEAw:
case Operation::ORItoSR:
case Operation::ANDItoSR:
case Operation::EORItoSR:
case Operation::MOVEtoSR:
case Operation::MOVEfromSR:
case Operation::MOVEtoCCR:
case Operation::CMPw: case Operation::CMPAw:
case Operation::TSTw:
case Operation::DBcc: case Operation::Scc:
case Operation::Bccw: case Operation::BSRw:
case Operation::CLRw:
case Operation::NEGXw: case Operation::NEGw:
case Operation::ASLw: case Operation::ASLm:
case Operation::ASRw: case Operation::ASRm:
case Operation::LSLw: case Operation::LSLm:
case Operation::LSRw: case Operation::LSRm:
case Operation::ROLw: case Operation::ROLm:
case Operation::RORw: case Operation::RORm:
case Operation::ROXLw: case Operation::ROXLm:
case Operation::ROXRw: case Operation::ROXRm:
case Operation::MOVEMw:
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::EXTbtow:
case Operation::LINKw:
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::LEA: case Operation::PEA:
case Operation::EXG: case Operation::SWAP:
case Operation::MOVEtoUSP:
case Operation::MOVEfromUSP:
case Operation::CMPl: case Operation::CMPAl:
case Operation::TSTl:
case Operation::JMP: case Operation::JSR:
case Operation::RTS:
case Operation::Bccl: case Operation::BSRl:
case Operation::CLRl:
case Operation::NEGXl: case Operation::NEGl:
case Operation::ASLl: case Operation::ASRl:
case Operation::LSLl: case Operation::LSRl:
case Operation::ROLl: case Operation::RORl:
case Operation::ROXLl: case Operation::ROXRl:
case Operation::MOVEMl:
case Operation::MOVEPl:
case Operation::ANDl: case Operation::EORl:
case Operation::NOTl: case Operation::ORl:
case Operation::EXTwtol:
case Operation::UNLINK:
return DataSize::LongWord;
}
}
template <Operation op>
constexpr uint32_t quick(uint16_t instruction) {
switch(op) {
case Operation::Bccb:
case Operation::BSRb:
case Operation::MOVEl: return uint32_t(int8_t(instruction));
case Operation::TRAP: return uint32_t(instruction & 15);
default: {
uint32_t value = (instruction >> 9) & 7;
value |= (value - 1)&8;
return value;
}
}
}
constexpr uint32_t quick(Operation op, uint16_t instruction) {
switch(op) {
case Operation::MOVEl: return quick<Operation::MOVEl>(instruction);
case Operation::Bccb: return quick<Operation::Bccb>(instruction);
case Operation::BSRb: return quick<Operation::BSRb>(instruction);
case Operation::TRAP: return quick<Operation::TRAP>(instruction);
default:
// ADDw is arbitrary; anything other than those listed above will do.
return quick<Operation::ADDw>(instruction);
}
}
/// Indicates the addressing mode applicable to an operand.
///
/// Implementation notes:
///
/// Those entries starting 0b00 or 0b01 are mapped as per the 68000's native encoding;
/// those starting 0b00 are those which are indicated directly by a mode field and those starting
/// 0b01 are those which are indicated by a register field given a mode of 0b111. The only minor
/// exception is AddressRegisterDirect, which exists on a 68000 but isn't specifiable by a
/// mode and register, it's contextual based on the instruction.
///
/// Those modes starting in 0b10 are the various extended addressing modes introduced as
/// of the 68020, which can be detected only after interpreting an extension word. At the
/// Preinstruction stage:
///
/// * AddressRegisterIndirectWithIndexBaseDisplacement, MemoryIndirectPostindexed
/// and MemoryIndirectPreindexed will have been partially decoded as
/// AddressRegisterIndirectWithIndex8bitDisplacement; and
/// * ProgramCounterIndirectWithIndexBaseDisplacement,
/// ProgramCounterMemoryIndirectPostindexed and
/// ProgramCounterMemoryIndirectPreindexed will have been partially decoded
/// as ProgramCounterIndirectWithIndex8bitDisplacement.
enum class AddressingMode: uint8_t {
/// No adddressing mode; this operand doesn't exist.
None = 0b01'101,
/// Dn
DataRegisterDirect = 0b00'000,
/// An
AddressRegisterDirect = 0b00'001,
/// (An)
AddressRegisterIndirect = 0b00'010,
/// (An)+
AddressRegisterIndirectWithPostincrement = 0b00'011,
/// -(An)
AddressRegisterIndirectWithPredecrement = 0b00'100,
/// (d16, An)
AddressRegisterIndirectWithDisplacement = 0b00'101,
/// (d8, An, Xn)
AddressRegisterIndirectWithIndex8bitDisplacement = 0b00'110,
/// (bd, An, Xn)
AddressRegisterIndirectWithIndexBaseDisplacement = 0b10'000,
/// ([bd, An, Xn], od)
MemoryIndirectPostindexed = 0b10'001,
/// ([bd, An], Xn, od)
MemoryIndirectPreindexed = 0b10'010,
/// (d16, PC)
ProgramCounterIndirectWithDisplacement = 0b01'010,
/// (d8, PC, Xn)
ProgramCounterIndirectWithIndex8bitDisplacement = 0b01'011,
/// (bd, PC, Xn)
ProgramCounterIndirectWithIndexBaseDisplacement = 0b10'011,
/// ([bd, PC, Xn], od)
ProgramCounterMemoryIndirectPostindexed = 0b10'100,
/// ([bc, PC], Xn, od)
ProgramCounterMemoryIndirectPreindexed = 0b10'101,
/// (xxx).W
AbsoluteShort = 0b01'000,
/// (xxx).L
AbsoluteLong = 0b01'001,
/// #
ImmediateData = 0b01'100,
/// .q; value is embedded in the opcode.
Quick = 0b01'110,
};
/*!
A preinstruction is as much of an instruction as can be decoded with
only the first instruction word i.e. an operation, and:
* on the 68000 and 68010, the complete addressing modes;
* on subsequent, a decent proportion of the addressing mode. See
the notes on @c AddressingMode for potential aliasing.
*/
class Preinstruction {
public:
Operation operation = Operation::Undefined;
// Instructions come with 0, 1 or 2 operands;
// the getters below act to provide a list of operands
// that is terminated by an AddressingMode::None.
//
// For two-operand instructions, argument 0 is a source
// and argument 1 is a destination.
//
// For one-operand instructions, only argument 0 will
// be provided, and will be a source and/or destination as
// per the semantics of the operation.
template <int index> AddressingMode mode() const {
if constexpr (index > 1) {
return AddressingMode::None;
}
return AddressingMode(operands_[index] & 0x1f);
}
template <int index> int reg() const {
if constexpr (index > 1) {
return 0;
}
return operands_[index] >> 5;
}
bool requires_supervisor() {
return flags_ & 0x80;
}
DataSize size() {
return DataSize(flags_ & 0x7f);
}
private:
uint8_t operands_[2] = { uint8_t(AddressingMode::None), uint8_t(AddressingMode::None)};
uint8_t flags_ = 0;
public:
Preinstruction(
Operation operation,
AddressingMode op1_mode, int op1_reg,
AddressingMode op2_mode, int op2_reg,
bool is_supervisor,
DataSize size) : operation(operation)
{
operands_[0] = uint8_t(op1_mode) | uint8_t(op1_reg << 5);
operands_[1] = uint8_t(op2_mode) | uint8_t(op2_reg << 5);
flags_ = (is_supervisor ? 0x80 : 0x00) | uint8_t(size);
}
Preinstruction() {}
};
}
}
#endif /* InstructionSets_68k_Instruction_hpp */

View File

@ -0,0 +1,26 @@
//
// Model.hpp
// Clock Signal
//
// Created by Thomas Harte on 15/04/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#ifndef InstructionSets_M68k_Model_hpp
#define InstructionSets_M68k_Model_hpp
namespace InstructionSet {
namespace M68k {
enum class Model {
M68000,
M68010,
M68020,
M68030,
M68040,
};
}
}
#endif /* InstructionSets_M68k_Model_hpp */

View File

@ -309,6 +309,8 @@
4B74CF822312FA9C00500CE8 /* HFV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF802312FA9C00500CE8 /* HFV.cpp */; };
4B74CF85231370BC00500CE8 /* MacintoshVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF83231370BC00500CE8 /* MacintoshVolume.cpp */; };
4B74CF86231370BC00500CE8 /* MacintoshVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF83231370BC00500CE8 /* MacintoshVolume.cpp */; };
4B75F979280D7C5100121055 /* 68000DecoderTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B75F978280D7C5100121055 /* 68000DecoderTests.mm */; };
4B75F97B280D7C7700121055 /* 68000 Decoding in Resources */ = {isa = PBXBuildFile; fileRef = 4B75F97A280D7C7700121055 /* 68000 Decoding */; };
4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */; };
4B778EF023A5D68C0000D260 /* 68000Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFF1D3822337B0300838EA1 /* 68000Storage.cpp */; };
4B778EF123A5D6B50000D260 /* 9918.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E04F91FC9FA3100F43484 /* 9918.cpp */; };
@ -422,6 +424,9 @@
4B778F6223A5F35F0000D260 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894500201967B4007DE474 /* File.cpp */; };
4B778F6323A5F3630000D260 /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894501201967B4007DE474 /* Tape.cpp */; };
4B7913CC1DFCD80E00175A82 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7913CA1DFCD80E00175A82 /* Video.cpp */; };
4B7962A02819681F008130F9 /* Decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B79629F2819681F008130F9 /* Decoder.cpp */; };
4B7962A12819681F008130F9 /* Decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B79629F2819681F008130F9 /* Decoder.cpp */; };
4B7962A22819681F008130F9 /* Decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B79629F2819681F008130F9 /* Decoder.cpp */; };
4B79A5011FC913C900EEDAD5 /* MSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B79A4FF1FC913C900EEDAD5 /* MSX.cpp */; };
4B79E4441E3AF38600141F11 /* cassette.png in Resources */ = {isa = PBXBuildFile; fileRef = 4B79E4411E3AF38600141F11 /* cassette.png */; };
4B79E4451E3AF38600141F11 /* floppy35.png in Resources */ = {isa = PBXBuildFile; fileRef = 4B79E4421E3AF38600141F11 /* floppy35.png */; };
@ -1411,10 +1416,16 @@
4B74CF802312FA9C00500CE8 /* HFV.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HFV.cpp; sourceTree = "<group>"; };
4B74CF83231370BC00500CE8 /* MacintoshVolume.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MacintoshVolume.cpp; path = Encodings/MacintoshVolume.cpp; sourceTree = "<group>"; };
4B74CF84231370BC00500CE8 /* MacintoshVolume.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = MacintoshVolume.hpp; path = Encodings/MacintoshVolume.hpp; sourceTree = "<group>"; };
4B75F978280D7C5100121055 /* 68000DecoderTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000DecoderTests.mm; sourceTree = "<group>"; };
4B75F97A280D7C7700121055 /* 68000 Decoding */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "68000 Decoding"; sourceTree = "<group>"; };
4B77069C1EC904570053B588 /* Z80.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Z80.hpp; sourceTree = "<group>"; };
4B770A961FE9EE770026DC70 /* CompoundSource.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CompoundSource.hpp; sourceTree = "<group>"; };
4B7913CA1DFCD80E00175A82 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Video.cpp; sourceTree = "<group>"; };
4B7913CB1DFCD80E00175A82 /* Video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Video.hpp; sourceTree = "<group>"; };
4B79629C2819681F008130F9 /* Instruction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Instruction.hpp; sourceTree = "<group>"; };
4B79629D2819681F008130F9 /* Model.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Model.hpp; sourceTree = "<group>"; };
4B79629E2819681F008130F9 /* Decoder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Decoder.hpp; sourceTree = "<group>"; };
4B79629F2819681F008130F9 /* Decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Decoder.cpp; sourceTree = "<group>"; };
4B79A4FE1FC9082300EEDAD5 /* TypedDynamicMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TypedDynamicMachine.hpp; sourceTree = "<group>"; };
4B79A4FF1FC913C900EEDAD5 /* MSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MSX.cpp; sourceTree = "<group>"; };
4B79A5001FC913C900EEDAD5 /* MSX.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MSX.hpp; sourceTree = "<group>"; };
@ -2392,22 +2403,23 @@
4B1414631B588A1100E04248 /* Test Binaries */ = {
isa = PBXGroup;
children = (
4B018B88211930DE002A3937 /* 65C02_extended_opcodes_test.bin */,
4BE211DD253E4E4800435408 /* 65C02_no_Rockwell_test.bin */,
4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */,
4B680CE323A555CA00451D43 /* 68000 Comparative Tests */,
4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */,
4B75F97A280D7C7700121055 /* 68000 Decoding */,
4B683B002727BE6F0043E541 /* Amiga Blitter Tests */,
4B9252CD1E74D28200B76AF1 /* Atari ROMs */,
4B44EBF81DC9898E00A7820C /* BCDTEST_beeb */,
4BB0CAB127E51D2A00672A88 /* dingusdev PowerPC tests */,
4B98A1CD1FFADEC400ADF63B /* MSX ROMs */,
4B018B88211930DE002A3937 /* 65C02_extended_opcodes_test.bin */,
4BE211DD253E4E4800435408 /* 65C02_no_Rockwell_test.bin */,
4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */,
4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */,
4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */,
4B8DF5212550D91400F3433C /* emudev.de krom traces */,
4B2530F2244E6773007980BF /* FM Synthesis */,
4BBF49B41ED2881600AB3669 /* FUSE */,
4B4F475B2533EA64004245B8 /* jeek816 */,
4B8DF5392550D91400F3433C /* krom 65816 */,
4B98A1CD1FFADEC400ADF63B /* MSX ROMs */,
4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */,
4B670A822401CB8400D4E002 /* Patrik Rak Z80 Tests */,
4B9F11C72272375400701480 /* QL Startup */,
4B85322B227793CA00F26553 /* TOS Startup */,
@ -3142,6 +3154,17 @@
path = Z80;
sourceTree = "<group>";
};
4B79629B2819681F008130F9 /* M68k */ = {
isa = PBXGroup;
children = (
4B79629C2819681F008130F9 /* Instruction.hpp */,
4B79629D2819681F008130F9 /* Model.hpp */,
4B79629E2819681F008130F9 /* Decoder.hpp */,
4B79629F2819681F008130F9 /* Decoder.cpp */,
);
path = M68k;
sourceTree = "<group>";
};
4B79A4FC1FC8FF9800EEDAD5 /* MSX */ = {
isa = PBXGroup;
children = (
@ -4133,6 +4156,7 @@
4B90467322C6FADD000E2074 /* 68000BitwiseTests.mm */,
4B680CE123A5553100451D43 /* 68000ComparativeTests.mm */,
4B9D0C4C22C7DA1A00DE1AD3 /* 68000ControlFlowTests.mm */,
4B75F978280D7C5100121055 /* 68000DecoderTests.mm */,
4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */,
4B9D0C4E22C7E0CF00DE1AD3 /* 68000RollShiftTests.mm */,
4BD388872239E198002D14B5 /* 68000Tests.mm */,
@ -4718,6 +4742,7 @@
4BEDA45425B5ECAB000C2DBD /* CachingExecutor.hpp */,
4BE8EB5425C0E9D40040BC40 /* Disassembler.hpp */,
4BEDA3B625B25563000C2DBD /* README.md */,
4B79629B2819681F008130F9 /* M68k */,
4BEDA40925B2844B000C2DBD /* M50740 */,
4BEDA3B325B25563000C2DBD /* PowerPC */,
4BEDA3B725B25563000C2DBD /* x86 */,
@ -5195,6 +5220,7 @@
4BB2996E1B587D8400A49093 /* laxa in Resources */,
4BB2990A1B587D8400A49093 /* aslzx in Resources */,
4BB299281B587D8400A49093 /* cia2pb6 in Resources */,
4B75F97B280D7C7700121055 /* 68000 Decoding in Resources */,
4BB2991E1B587D8400A49093 /* branchwrap in Resources */,
4BB299121B587D8400A49093 /* axsa in Resources */,
4BB299561B587D8400A49093 /* eorz in Resources */,
@ -5431,6 +5457,7 @@
4BFEA2F02682A7B900EBF94C /* Dave.cpp in Sources */,
4BDACBED22FFA5D20045EF7E /* ncr5380.cpp in Sources */,
4BC131772346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */,
4B7962A22819681F008130F9 /* Decoder.cpp in Sources */,
4B055ACF1FAE9B030060FFFF /* SoundGenerator.cpp in Sources */,
4B4DEC08252BFA56004583AC /* 65816Base.cpp in Sources */,
4B894519201967B4007DE474 /* ConfidenceCounter.cpp in Sources */,
@ -5684,6 +5711,7 @@
4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */,
4B9EC0EA26B384080060A31F /* Keyboard.cpp in Sources */,
4B7913CC1DFCD80E00175A82 /* Video.cpp in Sources */,
4B7962A02819681F008130F9 /* Decoder.cpp in Sources */,
4BC57CD92436A62900FBC404 /* State.cpp in Sources */,
4BDA00E622E699B000AC3CD0 /* CSMachine.mm in Sources */,
4B4518831F75E91A00926311 /* PCMTrack.cpp in Sources */,
@ -5862,6 +5890,7 @@
4B778F3723A5F11C0000D260 /* Parser.cpp in Sources */,
4B778F4523A5F1CD0000D260 /* SegmentParser.cpp in Sources */,
4B90467422C6FADD000E2074 /* 68000BitwiseTests.mm in Sources */,
4B7962A12819681F008130F9 /* Decoder.cpp in Sources */,
4B778F2A23A5EF0F0000D260 /* BitReverse.cpp in Sources */,
4B778F1D23A5ED470000D260 /* DiskController.cpp in Sources */,
4B778F0023A5EB990000D260 /* G64.cpp in Sources */,
@ -5891,6 +5920,7 @@
4B778EFC23A5EB8B0000D260 /* AcornADF.cpp in Sources */,
4B778F2023A5EDCE0000D260 /* HFV.cpp in Sources */,
4B778F3323A5F0FB0000D260 /* MassStorageDevice.cpp in Sources */,
4B75F979280D7C5100121055 /* 68000DecoderTests.mm in Sources */,
4B778F2C23A5EF0F0000D260 /* ZX8081.cpp in Sources */,
4B778F3023A5F0C50000D260 /* Macintosh.cpp in Sources */,
4B3BA0D11D318B44005DD7A7 /* TestMachine6502.mm in Sources */,

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
# 68000 Opcode list
This is **work in progress** and acts primarily as a *regression test*.
The objective is to get it to perfect, but it is unlikely currently to be there.

View File

@ -0,0 +1,290 @@
//
// m68kDecoderTests.m
// Clock Signal
//
// Created by Thomas Harte on 18/04/2022.
// Copyright 2022 Thomas Harte. All rights reserved.
//
#import <XCTest/XCTest.h>
#include "../../../InstructionSets/M68k/Decoder.hpp"
using namespace InstructionSet::M68k;
@interface M68000DecoderTests : XCTestCase
@end
namespace {
template <int index> NSString *operand(Preinstruction instruction, uint16_t opcode) {
switch(instruction.mode<index>()) {
default: return [NSString stringWithFormat:@"[Mode %d?]", int(instruction.mode<index>())];
case AddressingMode::None:
return @"";
case AddressingMode::DataRegisterDirect:
return [NSString stringWithFormat:@"D%d", instruction.reg<index>()];
case AddressingMode::AddressRegisterDirect:
return [NSString stringWithFormat:@"A%d", instruction.reg<index>()];
case AddressingMode::AddressRegisterIndirect:
return [NSString stringWithFormat:@"(A%d)", instruction.reg<index>()];
case AddressingMode::AddressRegisterIndirectWithPostincrement:
return [NSString stringWithFormat:@"(A%d)+", instruction.reg<index>()];
case AddressingMode::AddressRegisterIndirectWithPredecrement:
return [NSString stringWithFormat:@"-(A%d)", instruction.reg<index>()];
case AddressingMode::AddressRegisterIndirectWithDisplacement:
return [NSString stringWithFormat:@"(d16, A%d)", instruction.reg<index>()];
case AddressingMode::AddressRegisterIndirectWithIndex8bitDisplacement:
return [NSString stringWithFormat:@"(d8, A%d, Xn)", instruction.reg<index>()];
case AddressingMode::ProgramCounterIndirectWithDisplacement:
return @"(d16, PC)";
case AddressingMode::ProgramCounterIndirectWithIndex8bitDisplacement:
return @"(d8, PC, Xn)";
case AddressingMode::AbsoluteShort:
return @"(xxx).w";
case AddressingMode::AbsoluteLong:
return @"(xxx).l";
case AddressingMode::ImmediateData:
return @"#";
case AddressingMode::Quick:
return [NSString stringWithFormat:@"%d", quick(instruction.operation, opcode)];
}
}
}
@implementation M68000DecoderTests
- (void)testInstructionSpecs {
NSData *const testData =
[NSData dataWithContentsOfURL:
[[NSBundle bundleForClass:[self class]]
URLForResource:@"68000ops"
withExtension:@"json"
subdirectory:@"68000 Decoding"]];
NSDictionary<NSString *, NSString *> *const decodings = [NSJSONSerialization JSONObjectWithData:testData options:0 error:nil];
XCTAssertNotNil(decodings);
Predecoder<Model::M68000> decoder;
for(int instr = 0; instr < 65536; instr++) {
NSString *const instrName = [NSString stringWithFormat:@"%04x", instr];
NSString *const expected = decodings[instrName];
XCTAssertNotNil(expected);
const auto found = decoder.decode(uint16_t(instr));
NSString *instruction;
switch(found.operation) {
case Operation::Undefined: instruction = @"None"; break;
case Operation::NOP: instruction = @"NOP"; break;
case Operation::ABCD: instruction = @"ABCD"; break;
case Operation::SBCD: instruction = @"SBCD"; break;
case Operation::NBCD: instruction = @"NBCD"; break;
case Operation::ADDb: instruction = @"ADD.b"; break;
case Operation::ADDw: instruction = @"ADD.w"; break;
case Operation::ADDl: instruction = @"ADD.l"; break;
case Operation::ADDAw: instruction = @"ADDA.w"; break;
case Operation::ADDAl: instruction = @"ADDA.l"; break;
case Operation::ADDXb: instruction = @"ADDX.b"; break;
case Operation::ADDXw: instruction = @"ADDX.w"; break;
case Operation::ADDXl: instruction = @"ADDX.l"; break;
case Operation::SUBb: instruction = @"SUB.b"; break;
case Operation::SUBw: instruction = @"SUB.w"; break;
case Operation::SUBl: instruction = @"SUB.l"; break;
case Operation::SUBAw: instruction = @"SUBA.w"; break;
case Operation::SUBAl: instruction = @"SUBA.l"; break;
case Operation::SUBXb: instruction = @"SUBX.b"; break;
case Operation::SUBXw: instruction = @"SUBX.w"; break;
case Operation::SUBXl: instruction = @"SUBX.l"; break;
case Operation::MOVEb: instruction = @"MOVE.b"; break;
case Operation::MOVEw: instruction = @"MOVE.w"; break;
case Operation::MOVEl:
if(found.mode<0>() == AddressingMode::Quick) {
instruction = @"MOVE.q";
} else {
instruction = @"MOVE.l";
}
break;
case Operation::MOVEAw: instruction = @"MOVEA.w"; break;
case Operation::MOVEAl: instruction = @"MOVEA.l"; break;
case Operation::LEA: instruction = @"LEA"; break;
case Operation::PEA: instruction = @"PEA"; break;
case Operation::MOVEtoSR: instruction = @"MOVEtoSR"; break;
case Operation::MOVEfromSR: instruction = @"MOVEfromSR"; break;
case Operation::MOVEtoCCR: instruction = @"MOVEtoCCR"; break;
case Operation::MOVEtoUSP: instruction = @"MOVEtoUSP"; break;
case Operation::MOVEfromUSP: instruction = @"MOVEfromUSP"; break;
case Operation::ORItoSR: instruction = @"ORItoSR"; break;
case Operation::ORItoCCR: instruction = @"ORItoCCR"; break;
case Operation::ANDItoSR: instruction = @"ANDItoSR"; break;
case Operation::ANDItoCCR: instruction = @"ANDItoCCR"; break;
case Operation::EORItoSR: instruction = @"EORItoSR"; break;
case Operation::EORItoCCR: instruction = @"EORItoCCR"; break;
case Operation::BTST: instruction = @"BTST"; break;
case Operation::BCLR: instruction = @"BCLR"; break;
case Operation::BCHG: instruction = @"BCHG"; break;
case Operation::BSET: instruction = @"BSET"; break;
case Operation::CMPb: instruction = @"CMP.b"; break;
case Operation::CMPw: instruction = @"CMP.w"; break;
case Operation::CMPl: instruction = @"CMP.l"; break;
case Operation::CMPAw: instruction = @"CMPA.w"; break;
case Operation::CMPAl: instruction = @"CMPA.l"; break;
case Operation::TSTb: instruction = @"TST.b"; break;
case Operation::TSTw: instruction = @"TST.w"; break;
case Operation::TSTl: instruction = @"TST.l"; break;
case Operation::JMP: instruction = @"JMP"; break;
case Operation::JSR: instruction = @"JSR"; break;
case Operation::RTS: instruction = @"RTS"; break;
case Operation::DBcc: instruction = @"DBcc"; break;
case Operation::Scc: instruction = @"Scc"; break;
case Operation::Bccb:
case Operation::Bccl:
case Operation::Bccw: instruction = @"Bcc"; break;
case Operation::BSRb:
case Operation::BSRl:
case Operation::BSRw: instruction = @"BSR"; break;
case Operation::CLRb: instruction = @"CLR.b"; break;
case Operation::CLRw: instruction = @"CLR.w"; break;
case Operation::CLRl: instruction = @"CLR.l"; break;
case Operation::NEGXb: instruction = @"NEGX.b"; break;
case Operation::NEGXw: instruction = @"NEGX.w"; break;
case Operation::NEGXl: instruction = @"NEGX.l"; break;
case Operation::NEGb: instruction = @"NEG.b"; break;
case Operation::NEGw: instruction = @"NEG.w"; break;
case Operation::NEGl: instruction = @"NEG.l"; break;
case Operation::ASLb: instruction = @"ASL.b"; break;
case Operation::ASLw: instruction = @"ASL.w"; break;
case Operation::ASLl: instruction = @"ASL.l"; break;
case Operation::ASLm: instruction = @"ASL.w"; break;
case Operation::ASRb: instruction = @"ASR.b"; break;
case Operation::ASRw: instruction = @"ASR.w"; break;
case Operation::ASRl: instruction = @"ASR.l"; break;
case Operation::ASRm: instruction = @"ASR.w"; break;
case Operation::LSLb: instruction = @"LSL.b"; break;
case Operation::LSLw: instruction = @"LSL.w"; break;
case Operation::LSLl: instruction = @"LSL.l"; break;
case Operation::LSLm: instruction = @"LSL.w"; break;
case Operation::LSRb: instruction = @"LSR.b"; break;
case Operation::LSRw: instruction = @"LSR.w"; break;
case Operation::LSRl: instruction = @"LSR.l"; break;
case Operation::LSRm: instruction = @"LSR.w"; break;
case Operation::ROLb: instruction = @"ROL.b"; break;
case Operation::ROLw: instruction = @"ROL.w"; break;
case Operation::ROLl: instruction = @"ROL.l"; break;
case Operation::ROLm: instruction = @"ROL.w"; break;
case Operation::RORb: instruction = @"ROR.b"; break;
case Operation::RORw: instruction = @"ROR.w"; break;
case Operation::RORl: instruction = @"ROR.l"; break;
case Operation::RORm: instruction = @"ROR.w"; break;
case Operation::ROXLb: instruction = @"ROXL.b"; break;
case Operation::ROXLw: instruction = @"ROXL.w"; break;
case Operation::ROXLl: instruction = @"ROXL.l"; break;
case Operation::ROXLm: instruction = @"ROXL.w"; break;
case Operation::ROXRb: instruction = @"ROXR.b"; break;
case Operation::ROXRw: instruction = @"ROXR.w"; break;
case Operation::ROXRl: instruction = @"ROXR.l"; break;
case Operation::ROXRm: instruction = @"ROXR.w"; break;
case Operation::MOVEMl: instruction = @"MOVEM.l"; break;
case Operation::MOVEMw: instruction = @"MOVEM.w"; break;
case Operation::MOVEPl: instruction = @"MOVEP.l"; break;
case Operation::MOVEPw: instruction = @"MOVEP.w"; break;
case Operation::ANDb: instruction = @"AND.b"; break;
case Operation::ANDw: instruction = @"AND.w"; break;
case Operation::ANDl: instruction = @"AND.l"; break;
case Operation::EORb: instruction = @"EOR.b"; break;
case Operation::EORw: instruction = @"EOR.w"; break;
case Operation::EORl: instruction = @"EOR.l"; break;
case Operation::NOTb: instruction = @"NOT.b"; break;
case Operation::NOTw: instruction = @"NOT.w"; break;
case Operation::NOTl: instruction = @"NOT.l"; break;
case Operation::ORb: instruction = @"OR.b"; break;
case Operation::ORw: instruction = @"OR.w"; break;
case Operation::ORl: instruction = @"OR.l"; break;
case Operation::MULU: instruction = @"MULU"; break;
case Operation::MULS: instruction = @"MULS"; break;
case Operation::DIVU: instruction = @"DIVU"; break;
case Operation::DIVS: instruction = @"DIVS"; break;
case Operation::RTE: instruction = @"RTE"; break;
case Operation::RTR: instruction = @"RTR"; break;
case Operation::TRAP: instruction = @"TRAP"; break;
case Operation::TRAPV: instruction = @"TRAPV"; break;
case Operation::CHK: instruction = @"CHK"; break;
case Operation::EXG: instruction = @"EXG"; break;
case Operation::SWAP: instruction = @"SWAP"; break;
case Operation::TAS: instruction = @"TAS"; break;
case Operation::EXTbtow: instruction = @"EXT.w"; break;
case Operation::EXTwtol: instruction = @"EXT.l"; break;
case Operation::LINKw: instruction = @"LINK"; break;
case Operation::UNLINK: instruction = @"UNLINK"; break;
case Operation::STOP: instruction = @"STOP"; break;
case Operation::RESET: instruction = @"RESET"; break;
// For now, skip any unmapped operations.
default:
XCTAssert(false, @"Operation %d unhandled by test case", int(found.operation));
continue;
}
NSString *const operand1 = operand<0>(found, uint16_t(instr));
NSString *const operand2 = operand<1>(found, uint16_t(instr));
if(operand1.length) instruction = [instruction stringByAppendingFormat:@" %@", operand1];
if(operand2.length) instruction = [instruction stringByAppendingFormat:@", %@", operand2];
XCTAssertFalse(found.mode<0>() == AddressingMode::None && found.mode<1>() != AddressingMode::None, @"Decoding of %@ provided a second operand but not a first", instrName);
XCTAssertEqualObjects(instruction, expected, "%@ should decode as %@; got %@", instrName, expected, instruction);
}
}
@end

View File

@ -78,6 +78,7 @@ SOURCES += \
$$SRC/Inputs/*.cpp \
\
$$SRC/InstructionSets/M50740/*.cpp \
$$SRC/InstructionSets/M68k/*.cpp \
$$SRC/InstructionSets/PowerPC/*.cpp \
$$SRC/InstructionSets/x86/*.cpp \
\
@ -211,6 +212,7 @@ HEADERS += \
$$SRC/Inputs/QuadratureMouse/*.hpp \
\
$$SRC/InstructionSets/M50740/*.hpp \
$$SRC/InstructionSets/M68k/*.hpp \
$$SRC/InstructionSets/PowerPC/*.hpp \
$$SRC/InstructionSets/x86/*.hpp \
\

View File

@ -65,6 +65,7 @@ SOURCES += glob.glob('../../Configurable/*.cpp')
SOURCES += glob.glob('../../Inputs/*.cpp')
SOURCES += glob.glob('../../InstructionSets/M50740/*.cpp')
SOURCES += glob.glob('../../InstructionSets/M68k/*.cpp')
SOURCES += glob.glob('../../InstructionSets/PowerPC/*.cpp')
SOURCES += glob.glob('../../InstructionSets/x86/*.cpp')