1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-25 16:31:42 +00:00
CLK/InstructionSets/M68k/Instruction.hpp

499 lines
14 KiB
C++
Raw Normal View History

2022-04-11 19:00:55 +00:00
//
// Instruction.hpp
// Clock Signal
//
// Created by Thomas Harte on 10/04/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#pragma once
2022-04-11 19:00:55 +00:00
#include "Model.hpp"
2022-04-11 19:00:55 +00:00
#include <cassert>
#include <cstdint>
#include <string>
2023-05-10 21:02:18 +00:00
namespace InstructionSet::M68k {
2022-04-11 19:00:55 +00:00
enum class Operation: uint8_t {
Undefined,
//
// 68000 operations.
//
2022-04-12 11:49:08 +00:00
NOP,
2022-04-11 19:00:55 +00:00
ABCD, SBCD, NBCD,
ADDb, ADDw, ADDl,
ADDAw, ADDAl,
ADDXb, ADDXw, ADDXl,
SUBb, SUBw, SUBl,
SUBAw, SUBAl,
SUBXb, SUBXw, SUBXl,
MOVEb, MOVEw, MOVEl,
2022-04-11 19:00:55 +00:00
MOVEAw, MOVEAl,
LEA, PEA,
2022-04-11 19:00:55 +00:00
MOVEtoSR, MOVEfromSR,
MOVEtoCCR,
2022-04-12 11:49:08 +00:00
MOVEtoUSP, MOVEfromUSP,
2022-04-11 19:00:55 +00:00
ORItoSR, ORItoCCR,
ANDItoSR, ANDItoCCR,
EORItoSR, EORItoCCR,
2022-04-15 19:33:54 +00:00
BTST, BCLR,
BCHG, BSET,
2022-04-11 19:00:55 +00:00
CMPb, CMPw, CMPl,
2022-04-12 11:49:08 +00:00
CMPAw, CMPAl,
2022-04-11 19:00:55 +00:00
TSTb, TSTw, TSTl,
JMP,
JSR, RTS,
2022-04-11 19:00:55 +00:00
DBcc,
Scc,
Bccb, Bccw,
BSRb, BSRw,
2022-04-11 19:00:55 +00:00
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,
2022-05-06 13:45:06 +00:00
MOVEMtoRl, MOVEMtoRw,
MOVEMtoMl, MOVEMtoMw,
MOVEPl, MOVEPw,
2022-04-11 19:00:55 +00:00
ANDb, ANDw, ANDl,
EORb, EORw, EORl,
NOTb, NOTw, NOTl,
2022-04-11 19:00:55 +00:00
ORb, ORw, ORl,
MULUw, MULSw,
DIVUw, DIVSw,
2022-04-11 19:00:55 +00:00
2022-04-12 11:49:08 +00:00
RTE, RTR,
2022-04-11 19:00:55 +00:00
TRAP, TRAPV,
CHKw,
2022-04-11 19:00:55 +00:00
EXG, SWAP,
TAS,
EXTbtow, EXTwtol,
LINKw, UNLINK,
2022-04-11 19:00:55 +00:00
2022-04-12 11:49:08 +00:00
STOP, RESET,
//
// 68010 additions.
//
MOVEfromCCR,
2022-10-26 16:50:15 +00:00
MOVEtoC, MOVEfromC,
2022-10-26 17:34:01 +00:00
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,
2022-10-30 01:32:48 +00:00
// 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,
2022-10-30 15:32:36 +00:00
// 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,
2022-04-11 19:00:55 +00:00
};
// 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);
2022-06-01 12:20:06 +00:00
template <Model model>
constexpr bool requires_supervisor(Operation op) {
switch(op) {
2022-04-26 00:05:45 +00:00
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:
2022-10-26 16:50:15 +00:00
case Operation::MOVEtoC: case Operation::MOVEfromC:
case Operation::MOVEtoSR:
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.
2022-05-05 22:51:29 +00:00
///
/// For any operations that don't fit the neat model of reading one or two operands,
/// then writing zero or one, the size determines the data size of the operands only,
/// not any other accesses.
template <Operation t_operation = Operation::Undefined>
constexpr DataSize operand_size(Operation operation = Operation::Undefined);
template <Operation t_op = Operation::Undefined>
constexpr uint32_t quick(uint16_t instruction, Operation r_op = Operation::Undefined) {
switch((t_op != Operation::Undefined) ? t_op : r_op) {
2022-04-21 20:05:00 +00:00
case Operation::Bccb:
2022-04-21 20:13:06 +00:00
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);
2022-04-21 20:05:00 +00:00
default: {
uint32_t value = (instruction >> 9) & 7;
2022-04-21 20:05:00 +00:00
value |= (value - 1)&8;
return value;
}
}
}
static constexpr uint8_t FetchOp1 = (1 << 0);
static constexpr uint8_t FetchOp2 = (1 << 1);
static constexpr uint8_t StoreOp1 = (1 << 2);
static constexpr uint8_t StoreOp2 = (1 << 3);
/*!
Provides a bitfield with a value in the range 015 indicating which of the provided operation's
operands are accessed via standard fetch and store cycles; the bitfield is composted of
[Fetch/Store]Op[1/2] as defined above.
Unusual bus sequences, such as TAS or MOVEM, are not described here.
*/
2022-05-09 13:24:35 +00:00
template <Model model, Operation t_operation = Operation::Undefined>
2022-05-19 19:49:42 +00:00
constexpr uint8_t operand_flags(Operation r_operation = Operation::Undefined);
2022-04-21 20:05:00 +00:00
2022-05-09 13:24:35 +00:00
/// Lists the various condition codes used by the 680x0.
enum class Condition {
True = 0x00, False = 0x01,
High = 0x02, LowOrSame = 0x03,
CarryClear = 0x04, CarrySet = 0x05,
NotEqual = 0x06, Equal = 0x07,
OverflowClear = 0x08, OverflowSet = 0x09,
Positive = 0x0a, Negative = 0x0b,
GreaterThanOrEqual = 0x0c, LessThan = 0x0d,
GreaterThan = 0x0e, LessThanOrEqual = 0x0f,
};
2022-04-11 19:00:55 +00:00
/// 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
2023-05-16 20:40:09 +00:00
/// exception is AddressRegisterDirect, which exists on a 68000 but isn't specifiable by a
2022-04-11 19:00:55 +00:00
/// 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,
2022-04-11 19:00:55 +00:00
/// Dn
DataRegisterDirect = 0b00'000,
/// An
AddressRegisterDirect = 0b00'001,
2022-04-11 19:00:55 +00:00
/// (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) [68020+]
2022-04-11 19:00:55 +00:00
AddressRegisterIndirectWithIndexBaseDisplacement = 0b10'000,
/// ([bd, An, Xn], od) [68020+]
2022-04-11 19:00:55 +00:00
MemoryIndirectPostindexed = 0b10'001,
/// ([bd, An], Xn, od) [68020+]
2022-04-11 19:00:55 +00:00
MemoryIndirectPreindexed = 0b10'010,
/// (d16, PC)
ProgramCounterIndirectWithDisplacement = 0b01'010,
/// (d8, PC, Xn)
ProgramCounterIndirectWithIndex8bitDisplacement = 0b01'011,
/// (bd, PC, Xn) [68020+]
2022-04-11 19:00:55 +00:00
ProgramCounterIndirectWithIndexBaseDisplacement = 0b10'011,
/// ([bd, PC, Xn], od) [68020+]
2022-04-11 19:00:55 +00:00
ProgramCounterMemoryIndirectPostindexed = 0b10'100,
/// ([bc, PC], Xn, od) [68020+]
2022-04-11 19:00:55 +00:00
ProgramCounterMemoryIndirectPreindexed = 0b10'101,
/// (xxx).W
AbsoluteShort = 0b01'000,
/// (xxx).L
AbsoluteLong = 0b01'001,
/// #
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,
2022-04-11 19:00:55 +00:00
};
/// Guaranteed to be 1+[largest value used by AddressingMode].
static constexpr int AddressingModeCount = 0b10'110;
2022-04-11 19:00:55 +00:00
/*!
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.
//
// The versions templated on index do a range check;
// if using the runtime versions then results for indices
// other than 0 and 1 are undefined.
AddressingMode mode(int index) const {
return AddressingMode(operands_[index] >> 3);
}
2022-04-19 18:35:40 +00:00
template <int index> AddressingMode mode() const {
if constexpr (index > 1) {
return AddressingMode::None;
}
return mode(index);
}
int reg(int index) const {
return operands_[index] & 7;
2022-04-11 19:00:55 +00:00
}
2022-04-19 18:35:40 +00:00
template <int index> int reg() const {
if constexpr (index > 1) {
return 0;
}
return reg(index);
2022-04-11 19:00:55 +00:00
}
/// @returns 07 to indicate data registers 0 to 7, or 815 to indicate address registers 0 to 7 respectively.
/// Provides undefined results if the addressing mode is not either @c DataRegisterDirect or
/// @c AddressRegisterDirect.
int lreg(int index) const {
return operands_[index] & 0xf;
}
/// @returns @c true if this instruction requires supervisor privileges; @c false otherwise.
bool requires_supervisor() const {
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_ & 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_ & Flags::ConditionMask) >> Flags::ConditionShift);
}
2022-04-11 19:00:55 +00:00
private:
uint8_t operands_[2] = { uint8_t(AddressingMode::None), uint8_t(AddressingMode::None)};
uint8_t flags_ = 0;
2022-04-11 19:00:55 +00:00
std::string operand_description(int index, int opcode) const;
2022-04-11 19:00:55 +00:00
public:
Preinstruction(
Operation operation,
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 ? Flags::IsSupervisor : 0x00) |
(extension_words ? Flags::RequiresFurtherExtension : 0x00) |
(int(condition) << Flags::ConditionShift) |
(extension_words << Flags::ConditionShift) |
(int(size) << Flags::SizeShift)
);
2022-04-11 19:00:55 +00:00
}
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;
};
2024-02-17 02:47:23 +00:00
Preinstruction() = default;
/// Produces a string description of this instruction; if @c opcode
/// is supplied then any quick fields in this instruction will be decoded;
/// otherwise they'll be printed as just 'Q'.
std::string to_string(int opcode = -1) const;
/// Produces a slightly-more-idiomatic version of the operation name than
/// a direct to_string(instruction.operation) would, given that this decoder
/// sometimes aliases operations, disambiguating based on addressing mode
/// (e.g. MOVEQ is MOVE.l with the Q addressing mode).
const char *operation_string() const;
2022-04-11 19:00:55 +00:00
};
}
2022-05-09 13:24:35 +00:00
#include "Implementation/InstructionOperandSize.hpp"
#include "Implementation/InstructionOperandFlags.hpp"