190 lines
7.1 KiB
C++
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);
|
|
}
|