mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 23:52:26 +00:00
Begins a formalised 68k decoder.
This commit is contained in:
parent
945e935312
commit
8e3cccf4d6
166
InstructionSets/68k/Decoder.cpp
Normal file
166
InstructionSets/68k/Decoder.cpp
Normal file
@ -0,0 +1,166 @@
|
||||
//
|
||||
// Decoder.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/04/2022.
|
||||
// Copyright © 2022 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Decoder.hpp"
|
||||
|
||||
using namespace InstructionSet::M68k;
|
||||
|
||||
namespace {
|
||||
|
||||
/// @returns The @c AddressingMode given the specified mode and reg, subject to potential
|
||||
/// aliasing on the '020+ as described above the @c AddressingMode enum.
|
||||
constexpr AddressingMode combined_mode(int mode, int reg) {
|
||||
return (mode != 7) ? AddressingMode(mode) : AddressingMode(0b01'000 | reg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Instruction decoders.
|
||||
|
||||
template <Operation operation> Preinstruction Predecoder::decode(uint16_t instruction) {
|
||||
// Fields used pervasively below.
|
||||
//
|
||||
// Underlying assumption: the compiler will discard whatever of these
|
||||
// isn't actually used.
|
||||
const auto ea_register = instruction & 7;
|
||||
const auto ea_mode = (instruction >> 3) & 7;
|
||||
const auto ea_combined_mode = combined_mode(ea_mode, ea_register);
|
||||
|
||||
const auto opmode = (instruction >> 6) & 7;
|
||||
const auto data_register = (instruction >> 9) & 7;
|
||||
|
||||
switch(operation) {
|
||||
|
||||
//
|
||||
// MARK: ABCD, SBCD.
|
||||
//
|
||||
case Operation::ABCD: case Operation::SBCD: {
|
||||
const auto addressing_mode = (instruction & 8) ?
|
||||
AddressingMode::AddressRegisterIndirectWithPredecrement : AddressingMode::DataRegisterDirect;
|
||||
|
||||
return Preinstruction(operation,
|
||||
addressing_mode, ea_register,
|
||||
addressing_mode, data_register);
|
||||
}
|
||||
|
||||
//
|
||||
// MARK: AND, OR, EOR.
|
||||
//
|
||||
case Operation::ANDb: case Operation::ANDw: case Operation::ANDl:
|
||||
case Operation::ORb: case Operation::ORw: case Operation::ORl:
|
||||
case Operation::EORb: case Operation::EORw: case Operation::EORl: {
|
||||
// Opmode 7 is illegal.
|
||||
if(opmode == 7) {
|
||||
return Preinstruction();
|
||||
}
|
||||
|
||||
constexpr bool is_eor = operation == Operation::EORb || operation == Operation::EORw || operation == Operation::EORl;
|
||||
|
||||
if(opmode & 4) {
|
||||
// Dn Λ < ea > → < ea >
|
||||
|
||||
// The operations other than EOR do not permit <ea>
|
||||
// to be a data register; targetting a data register
|
||||
// should be achieved with the alternative opmode.
|
||||
if constexpr (!is_eor) {
|
||||
if(ea_combined_mode == AddressingMode::DataRegisterDirect) {
|
||||
return Preinstruction();
|
||||
}
|
||||
}
|
||||
|
||||
return Preinstruction(operation,
|
||||
AddressingMode::DataRegisterDirect, data_register,
|
||||
ea_combined_mode, ea_register);
|
||||
} else {
|
||||
// < ea > Λ Dn → Dn
|
||||
|
||||
// EOR doesn't permit → Dn.
|
||||
if constexpr (is_eor) {
|
||||
return Preinstruction();
|
||||
}
|
||||
|
||||
return Preinstruction(operation,
|
||||
ea_combined_mode, ea_register,
|
||||
AddressingMode::DataRegisterDirect, data_register);
|
||||
}
|
||||
|
||||
return Preinstruction();
|
||||
}
|
||||
|
||||
//
|
||||
// MARK: EXG.
|
||||
//
|
||||
case Operation::EXG:
|
||||
switch((instruction >> 3)&31) {
|
||||
default: return Preinstruction();
|
||||
|
||||
case 0x08: return Preinstruction(operation,
|
||||
AddressingMode::DataRegisterDirect, ea_register,
|
||||
AddressingMode::DataRegisterDirect, data_register);
|
||||
|
||||
case 0x09: return Preinstruction(operation,
|
||||
AddressingMode::AddressRegisterDirect, ea_register,
|
||||
AddressingMode::AddressRegisterDirect, data_register);
|
||||
|
||||
case 0x11: return Preinstruction(operation,
|
||||
AddressingMode::AddressRegisterDirect, ea_register,
|
||||
AddressingMode::DataRegisterDirect, data_register);
|
||||
}
|
||||
|
||||
//
|
||||
// MARK: MULU, MULS.
|
||||
//
|
||||
case Operation::MULU: case Operation::MULS:
|
||||
return Preinstruction(operation,
|
||||
ea_combined_mode, ea_register,
|
||||
AddressingMode::DataRegisterDirect, data_register);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Page decoders.
|
||||
|
||||
Preinstruction Predecoder::decodeC(uint16_t instruction) {
|
||||
// 4-3 (p107)
|
||||
if((instruction & 0x1f0) == 0x100) return decode<Operation::ABCD>(instruction);
|
||||
|
||||
// 4-15 (p119)
|
||||
switch(instruction & 0x0c0) {
|
||||
case 0x00: return decode<Operation::ANDb>(instruction);
|
||||
case 0x40: return decode<Operation::ANDw>(instruction);
|
||||
case 0x80: return decode<Operation::ANDl>(instruction);
|
||||
default: break;
|
||||
}
|
||||
|
||||
switch(instruction & 0x1c0) {
|
||||
case 0x0c0: return decode<Operation::MULU>(instruction); // 4-139 (p243)
|
||||
case 0x1c0: return decode<Operation::MULS>(instruction); // 4-136 (p240)
|
||||
default: break;
|
||||
}
|
||||
|
||||
// 4-105 (p209)
|
||||
switch(instruction & 0x1f8) {
|
||||
case 0x140:
|
||||
case 0x148:
|
||||
case 0x188: return decode<Operation::EXG>(instruction);
|
||||
}
|
||||
return Preinstruction();
|
||||
}
|
||||
|
||||
// MARK: - Main decoder.
|
||||
|
||||
Preinstruction Predecoder::decode(uint16_t instruction) {
|
||||
// Divide first based on line.
|
||||
switch(instruction & 0xf000) {
|
||||
case 0xc000: return decodeC(instruction);
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
return Preinstruction();
|
||||
}
|
38
InstructionSets/68k/Decoder.hpp
Normal file
38
InstructionSets/68k/Decoder.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
//
|
||||
// Decoder.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/04/2022.
|
||||
// Copyright © 2022 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Decoder_hpp
|
||||
#define Decoder_hpp
|
||||
|
||||
#include "Instruction.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).
|
||||
*/
|
||||
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 decodeC(uint16_t instruction);
|
||||
|
||||
// Specific instruction decoders.
|
||||
template <Operation operation> Preinstruction decode(uint16_t instruction);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Decoder_hpp */
|
219
InstructionSets/68k/Instruction.hpp
Normal file
219
InstructionSets/68k/Instruction.hpp
Normal file
@ -0,0 +1,219 @@
|
||||
//
|
||||
// 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>
|
||||
|
||||
namespace InstructionSet {
|
||||
namespace M68k {
|
||||
|
||||
enum class Operation: uint8_t {
|
||||
Undefined,
|
||||
|
||||
ABCD, SBCD, NBCD,
|
||||
|
||||
ADDb, ADDw, ADDl,
|
||||
ADDQb, ADDQw, ADDQl,
|
||||
ADDAw, ADDAl,
|
||||
ADDQAw, ADDQAl,
|
||||
ADDXb, ADDXw, ADDXl,
|
||||
|
||||
SUBb, SUBw, SUBl,
|
||||
SUBQb, SUBQw, SUBQl,
|
||||
SUBAw, SUBAl,
|
||||
SUBQAw, SUBQAl,
|
||||
SUBXb, SUBXw, SUBXl,
|
||||
|
||||
MOVEb, MOVEw, MOVEl, MOVEq,
|
||||
MOVEAw, MOVEAl,
|
||||
PEA,
|
||||
|
||||
MOVEtoSR, MOVEfromSR,
|
||||
MOVEtoCCR,
|
||||
|
||||
ORItoSR, ORItoCCR,
|
||||
ANDItoSR, ANDItoCCR,
|
||||
EORItoSR, EORItoCCR,
|
||||
|
||||
BTSTb, BTSTl,
|
||||
BCLRl, BCLRb,
|
||||
CMPb, CMPw, CMPl,
|
||||
CMPAw,
|
||||
TSTb, TSTw, TSTl,
|
||||
|
||||
JMP, RTS,
|
||||
BRA, Bcc,
|
||||
DBcc,
|
||||
Scc,
|
||||
|
||||
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,
|
||||
|
||||
MOVEMtoRl, MOVEMtoRw,
|
||||
MOVEMtoMl, MOVEMtoMw,
|
||||
|
||||
MOVEPtoRl, MOVEPtoRw,
|
||||
MOVEPtoMl, MOVEPtoMw,
|
||||
|
||||
ANDb, ANDw, ANDl,
|
||||
EORb, EORw, EORl,
|
||||
NOTb, NOTw, NOTl,
|
||||
ORb, ORw, ORl,
|
||||
|
||||
MULU, MULS,
|
||||
DIVU, DIVS,
|
||||
|
||||
RTE_RTR,
|
||||
|
||||
TRAP, TRAPV,
|
||||
CHK,
|
||||
|
||||
EXG, SWAP,
|
||||
|
||||
BCHGl, BCHGb,
|
||||
BSETl, BSETb,
|
||||
|
||||
TAS,
|
||||
|
||||
EXTbtow, EXTwtol,
|
||||
|
||||
LINK, UNLINK,
|
||||
|
||||
STOP,
|
||||
};
|
||||
|
||||
/// 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 = 0b11'111,
|
||||
|
||||
/// Dn
|
||||
DataRegisterDirect = 0b00'000,
|
||||
|
||||
/// An
|
||||
AddressRegisterDirect = 0b11'000,
|
||||
/// (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,
|
||||
};
|
||||
|
||||
/*!
|
||||
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;
|
||||
|
||||
// First operand.
|
||||
AddressingMode source_mode() {
|
||||
return AddressingMode(source_ & 0x1f);
|
||||
}
|
||||
int source() {
|
||||
return source_ >> 5;
|
||||
}
|
||||
|
||||
// Second operand.
|
||||
AddressingMode destination_mode() {
|
||||
return AddressingMode(destination_ & 0x1f);
|
||||
}
|
||||
int destination() {
|
||||
return destination_ >> 5;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t source_ = 0;
|
||||
uint8_t destination_ = 0;
|
||||
|
||||
public:
|
||||
Preinstruction(
|
||||
Operation operation,
|
||||
AddressingMode source_mode,
|
||||
int source,
|
||||
AddressingMode destination_mode,
|
||||
int destination) : operation(operation) {
|
||||
source_ = uint8_t(source_mode) | uint8_t(source << 5);
|
||||
destination_ = uint8_t(destination_mode) | uint8_t(destination << 5);
|
||||
}
|
||||
|
||||
Preinstruction() {}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* InstructionSets_68k_Instruction_hpp */
|
@ -902,6 +902,8 @@
|
||||
4BBB70A8202014E2002FE009 /* MultiProducer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A6202014E2002FE009 /* MultiProducer.cpp */; };
|
||||
4BBB70A9202014E2002FE009 /* MultiProducer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A6202014E2002FE009 /* MultiProducer.cpp */; };
|
||||
4BBC951E1F368D83008F4C34 /* i8272.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBC951C1F368D83008F4C34 /* i8272.cpp */; };
|
||||
4BBD689928037E53004790C1 /* Decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBD689728037E53004790C1 /* Decoder.cpp */; };
|
||||
4BBD689A28037E53004790C1 /* Decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBD689728037E53004790C1 /* Decoder.cpp */; };
|
||||
4BBF49AF1ED2880200AB3669 /* FUSETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF49AE1ED2880200AB3669 /* FUSETests.swift */; };
|
||||
4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBFBB6A1EE8401E00C01E7A /* ZX8081.cpp */; };
|
||||
4BBFE83D21015D9C00BF1C40 /* CSJoystickManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBFE83C21015D9C00BF1C40 /* CSJoystickManager.m */; };
|
||||
@ -1946,6 +1948,9 @@
|
||||
4BBB70A7202014E2002FE009 /* MultiProducer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MultiProducer.hpp; sourceTree = "<group>"; };
|
||||
4BBC951C1F368D83008F4C34 /* i8272.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = i8272.cpp; sourceTree = "<group>"; };
|
||||
4BBC951D1F368D83008F4C34 /* i8272.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = i8272.hpp; sourceTree = "<group>"; };
|
||||
4BBD689328037B0E004790C1 /* Instruction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Instruction.hpp; sourceTree = "<group>"; };
|
||||
4BBD689728037E53004790C1 /* Decoder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Decoder.cpp; sourceTree = "<group>"; };
|
||||
4BBD689828037E53004790C1 /* Decoder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Decoder.hpp; sourceTree = "<group>"; };
|
||||
4BBF49AE1ED2880200AB3669 /* FUSETests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FUSETests.swift; sourceTree = "<group>"; };
|
||||
4BBF990E1C8FBA6F0075DAFB /* Flywheel.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Flywheel.hpp; sourceTree = "<group>"; };
|
||||
4BBFBB6A1EE8401E00C01E7A /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZX8081.cpp; path = Parsers/ZX8081.cpp; sourceTree = "<group>"; };
|
||||
@ -4282,6 +4287,16 @@
|
||||
path = 8272;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BBD689228037B0E004790C1 /* 68k */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BBD689328037B0E004790C1 /* Instruction.hpp */,
|
||||
4BBD689728037E53004790C1 /* Decoder.cpp */,
|
||||
4BBD689828037E53004790C1 /* Decoder.hpp */,
|
||||
);
|
||||
path = 68k;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BBF49B41ED2881600AB3669 /* FUSE */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -4718,6 +4733,7 @@
|
||||
4BEDA45425B5ECAB000C2DBD /* CachingExecutor.hpp */,
|
||||
4BE8EB5425C0E9D40040BC40 /* Disassembler.hpp */,
|
||||
4BEDA3B625B25563000C2DBD /* README.md */,
|
||||
4BBD689228037B0E004790C1 /* 68k */,
|
||||
4BEDA40925B2844B000C2DBD /* M50740 */,
|
||||
4BEDA3B325B25563000C2DBD /* PowerPC */,
|
||||
4BEDA3B725B25563000C2DBD /* x86 */,
|
||||
@ -5431,6 +5447,7 @@
|
||||
4BFEA2F02682A7B900EBF94C /* Dave.cpp in Sources */,
|
||||
4BDACBED22FFA5D20045EF7E /* ncr5380.cpp in Sources */,
|
||||
4BC131772346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */,
|
||||
4BBD689A28037E53004790C1 /* Decoder.cpp in Sources */,
|
||||
4B055ACF1FAE9B030060FFFF /* SoundGenerator.cpp in Sources */,
|
||||
4B4DEC08252BFA56004583AC /* 65816Base.cpp in Sources */,
|
||||
4B894519201967B4007DE474 /* ConfidenceCounter.cpp in Sources */,
|
||||
@ -5684,6 +5701,7 @@
|
||||
4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */,
|
||||
4B9EC0EA26B384080060A31F /* Keyboard.cpp in Sources */,
|
||||
4B7913CC1DFCD80E00175A82 /* Video.cpp in Sources */,
|
||||
4BBD689928037E53004790C1 /* Decoder.cpp in Sources */,
|
||||
4BC57CD92436A62900FBC404 /* State.cpp in Sources */,
|
||||
4BDA00E622E699B000AC3CD0 /* CSMachine.mm in Sources */,
|
||||
4B4518831F75E91A00926311 /* PCMTrack.cpp in Sources */,
|
||||
|
Loading…
Reference in New Issue
Block a user