1
0
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:
Thomas Harte 2022-04-11 15:00:55 -04:00
parent 945e935312
commit 8e3cccf4d6
4 changed files with 441 additions and 0 deletions

View 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();
}

View 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 */

View 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 */

View File

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