65816-llvm-mos/src/llvm/lib/Target/W65816/Disassembler/W65816Disassembler.cpp
Scott Duensing 873eab4922 Checkpoint.
2026-04-25 17:07:28 -05:00

190 lines
7.1 KiB
C++

//===-- W65816Disassembler.cpp - Disassembler for W65816 -----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements the W65816Disassembler class, which converts a byte
// stream into a stream of MCInsts. W65816 instructions are 1-4 bytes long:
// a single-byte opcode followed by 0-3 bytes of little-endian operand data.
//
// For every opcode that has both an _Imm8 and _Imm16 variant (LDA, LDX, LDY,
// ADC, SBC, CMP, AND, ORA, EOR, BIT, CPX, CPY), the bytes on the wire are
// identical for both forms up to the opcode; the real operand width depends
// on the M/X processor-mode bits. The TableGen descriptions put the _Imm8
// forms into the "W65816MHigh" / "W65816XHigh" decoder namespaces, so the
// default "W65816" table contains only the 3-byte _Imm16 forms. This
// scaffold disassembler reads exclusively from the default table, so those
// mnemonics always decode as 3-byte (16-bit) immediates. A future mode-
// tracking pass will consult the MHigh / XHigh tables when MReg / XReg are
// set by preceding REP/SEP instructions.
//
//===----------------------------------------------------------------------===//
#include "MCTargetDesc/W65816MCTargetDesc.h"
#include "TargetInfo/W65816TargetInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDecoder.h"
#include "llvm/MC/MCDecoderOps.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/Compiler.h"
using namespace llvm;
using namespace llvm::MCD;
#define DEBUG_TYPE "w65816-disassembler"
using DecodeStatus = MCDisassembler::DecodeStatus;
namespace {
class W65816Disassembler : public MCDisassembler {
public:
W65816Disassembler(const MCSubtargetInfo &STI, MCContext &Ctx)
: MCDisassembler(STI, Ctx) {}
DecodeStatus getInstruction(MCInst &MI, uint64_t &Size,
ArrayRef<uint8_t> Bytes, uint64_t Address,
raw_ostream &CStream) const override;
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Operand decoder callbacks. The encodings are little-endian immediates
// immediately following the opcode; each callback just creates an MCOperand
// whose integer value is the raw field bits. The printer does the pretty-
// printing (hex, '$' prefix, etc.).
//===----------------------------------------------------------------------===//
static DecodeStatus decodeImm8(MCInst &Inst, uint64_t Imm, uint64_t Address,
const MCDisassembler *Decoder) {
Inst.addOperand(MCOperand::createImm(Imm & 0xFF));
return MCDisassembler::Success;
}
static DecodeStatus decodeImm16(MCInst &Inst, uint64_t Imm, uint64_t Address,
const MCDisassembler *Decoder) {
Inst.addOperand(MCOperand::createImm(Imm & 0xFFFF));
return MCDisassembler::Success;
}
static DecodeStatus decodeAddr8(MCInst &Inst, uint64_t Imm, uint64_t Address,
const MCDisassembler *Decoder) {
Inst.addOperand(MCOperand::createImm(Imm & 0xFF));
return MCDisassembler::Success;
}
static DecodeStatus decodeAddr16(MCInst &Inst, uint64_t Imm, uint64_t Address,
const MCDisassembler *Decoder) {
Inst.addOperand(MCOperand::createImm(Imm & 0xFFFF));
return MCDisassembler::Success;
}
static DecodeStatus decodeAddr24(MCInst &Inst, uint64_t Imm, uint64_t Address,
const MCDisassembler *Decoder) {
Inst.addOperand(MCOperand::createImm(Imm & 0xFFFFFF));
return MCDisassembler::Success;
}
static DecodeStatus decodePCRel8(MCInst &Inst, uint64_t Imm, uint64_t Address,
const MCDisassembler *Decoder) {
// Sign-extend the 8-bit displacement so the printer shows the correct
// signed offset. Address-symbolization happens later if requested.
int8_t Disp = static_cast<int8_t>(Imm & 0xFF);
Inst.addOperand(MCOperand::createImm(Disp));
return MCDisassembler::Success;
}
static DecodeStatus decodePCRel16(MCInst &Inst, uint64_t Imm, uint64_t Address,
const MCDisassembler *Decoder) {
int16_t Disp = static_cast<int16_t>(Imm & 0xFFFF);
Inst.addOperand(MCOperand::createImm(Disp));
return MCDisassembler::Success;
}
// The generated decoder tables reference the decode callbacks above by name,
// so this include must come AFTER their declarations.
#include "W65816GenDisassemblerTables.inc"
//===----------------------------------------------------------------------===//
// Size-dispatch: try each 1/2/3/4-byte table in turn from shortest to longest,
// accepting the first successful decode. The tablegen tables are keyed on
// the full little-endian byte pattern, so a single-byte implied-operand
// opcode like NOP (0xEA) matches in DecoderTableW658168 and shorter-circuits
// any accidental collision with a longer encoding that happens to begin with
// the same byte. Every W65816 opcode byte uniquely determines the
// instruction (modulo the M/X-mode ambiguity documented above), so the
// first successful decode is always the correct one.
//===----------------------------------------------------------------------===//
DecodeStatus W65816Disassembler::getInstruction(MCInst &MI, uint64_t &Size,
ArrayRef<uint8_t> Bytes,
uint64_t Address,
raw_ostream &CStream) const {
struct TableEntry {
const uint8_t *Table;
unsigned Bytes;
};
static const TableEntry Tables[] = {
{ DecoderTableW658168, 1 },
{ DecoderTableW6581616, 2 },
{ DecoderTableW6581624, 3 },
{ DecoderTableW6581632, 4 },
};
for (const TableEntry &T : Tables) {
if (Bytes.size() < T.Bytes) {
continue;
}
uint64_t Insn = 0;
for (unsigned I = 0; I < T.Bytes; ++I) {
Insn |= static_cast<uint64_t>(Bytes[I]) << (8 * I);
}
MCInst Candidate;
DecodeStatus Result = decodeInstruction(T.Table, Candidate, Insn, Address,
this, STI);
if (Result != MCDisassembler::Fail) {
MI = Candidate;
Size = T.Bytes;
return Result;
}
}
Size = 1;
return MCDisassembler::Fail;
}
//===----------------------------------------------------------------------===//
// Registration.
//===----------------------------------------------------------------------===//
static MCDisassembler *createW65816Disassembler(const Target &T,
const MCSubtargetInfo &STI,
MCContext &Ctx) {
return new W65816Disassembler(STI, Ctx);
}
extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
LLVMInitializeW65816Disassembler() {
TargetRegistry::RegisterMCDisassembler(getTheW65816Target(),
createW65816Disassembler);
}