65816-llvm-mos/src/llvm/lib/Target/W65816/W65816AsmPrinter.cpp
2026-04-30 18:49:00 -05:00

637 lines
22 KiB
C++

//===-- W65816AsmPrinter.cpp - W65816 LLVM assembly writer ----------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Skeleton assembly printer. The MCInst lowering path is wired up but no
// target-specific operand formatting is implemented yet.
//
//===----------------------------------------------------------------------===//
#include "MCTargetDesc/W65816InstPrinter.h"
#include "W65816MachineFunctionInfo.h"
#include "W65816MCInstLower.h"
#include "W65816TargetMachine.h"
#include "TargetInfo/W65816TargetInfo.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/Compiler.h"
using namespace llvm;
#define DEBUG_TYPE "asm-printer"
namespace {
class W65816AsmPrinter : public AsmPrinter {
public:
W65816AsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
: AsmPrinter(TM, std::move(Streamer), ID) {}
StringRef getPassName() const override { return "W65816 Assembly Printer"; }
void emitInstruction(const MachineInstr *MI) override;
static char ID;
};
} // end anonymous namespace
// Convert a single MachineOperand to an MCOperand using the standard
// rules (register / immediate / global / external / etc.). Used by
// pseudo expansion to lift symbolic operands; mirrors the per-operand
// path in W65816MCInstLower::Lower.
static MCOperand lowerOperand(const MachineOperand &MO,
W65816MCInstLower &Lower) {
switch (MO.getType()) {
case MachineOperand::MO_Register:
return MCOperand::createReg(MO.getReg());
case MachineOperand::MO_Immediate:
return MCOperand::createImm(MO.getImm());
case MachineOperand::MO_GlobalAddress:
return Lower.LowerSymbolOperand(MO, Lower.GetGlobalAddressSymbol(MO));
case MachineOperand::MO_ExternalSymbol:
return Lower.LowerSymbolOperand(MO, Lower.GetExternalSymbolSymbol(MO));
case MachineOperand::MO_BlockAddress:
return Lower.LowerSymbolOperand(MO, Lower.GetBlockAddressSymbol(MO));
case MachineOperand::MO_JumpTableIndex:
return Lower.LowerSymbolOperand(MO, Lower.GetJumpTableSymbol(MO));
case MachineOperand::MO_ConstantPoolIndex:
return Lower.LowerSymbolOperand(MO, Lower.GetConstantPoolIndexSymbol(MO));
default:
llvm_unreachable("W65816: unsupported operand type in pseudo lower");
}
}
void W65816AsmPrinter::emitInstruction(const MachineInstr *MI) {
W65816_MC::verifyInstructionPredicates(MI->getOpcode(),
getSubtargetInfo().getFeatureBits());
W65816MCInstLower MCInstLowering(OutContext, *this);
// Expand codegen pseudos into their MC-layer realisations. Keep this
// switch in sync with the pseudo defs at the bottom of
// W65816InstrInfo.td. All single-output pseudos here have operand
// layout (outs $dst, ins $value); we drop $dst (it lives implicitly in
// A) and lift $value into the MC instruction's single source operand.
switch (MI->getOpcode()) {
default:
break;
case W65816::ADJCALLSTACKDOWN: {
// DOWN is a no-op in our scheme — the PUSH16 sequence in LowerCall
// already shifted SP incrementally as args were pushed. Nothing
// to emit; PEI may or may not have processed it, either is fine.
return;
}
case W65816::ADJCALLSTACKUP: {
// PEI's eliminateCallFramePseudoInstr handles UP whenever the
// function has any frame work (StackSize > 0 or any FI use).
// Frame-less functions — e.g. `unsigned short sqr(unsigned short
// x) { return x*x; }` lowers to PUSH16 + jsl __mulhi3 + RTL with
// no locals — get skipped by PEI's call-frame phase, leaving
// ADJCALLSTACKUP as a pseudo all the way to here. Previously we
// silently dropped it, which left SP off by N bytes after the
// call and corrupted the caller's stack frame (caught by sqr(x)
// segfaulting MAME). Emit the SP fixup ourselves: PLY*N/2 for
// small even N, otherwise the TAY/TSC-ADC/TYA bracket.
int N = MI->getOperand(0).getImm();
if (N == 0) return;
// A holds the callee's return value; preserve it. Walk forward
// looking for X/Y uses (i64-return halves) — same logic as
// eliminateCallFramePseudoInstr.
bool YLive = false;
for (auto J = std::next(MI->getIterator()); J != MI->getParent()->end();
++J) {
if (J->isCall()) break;
bool yDef = false;
for (const MachineOperand &MO : J->operands()) {
if (!MO.isReg()) continue;
if (MO.getReg() == W65816::Y) {
if (MO.isUse()) { YLive = true; break; }
if (MO.isDef()) yDef = true;
}
}
if (YLive || yDef) break;
}
if (YLive) {
// Route through DP $E0 to preserve both A and Y.
MCInst Sta; Sta.setOpcode(W65816::STA_DP);
Sta.addOperand(MCOperand::createImm(0xE0));
EmitToStreamer(*OutStreamer, Sta);
MCInst Tsc; Tsc.setOpcode(W65816::TSC); EmitToStreamer(*OutStreamer, Tsc);
MCInst Clc; Clc.setOpcode(W65816::CLC); EmitToStreamer(*OutStreamer, Clc);
MCInst Adc; Adc.setOpcode(W65816::ADC_Imm16);
Adc.addOperand(MCOperand::createImm(N));
EmitToStreamer(*OutStreamer, Adc);
MCInst Tcs; Tcs.setOpcode(W65816::TCS); EmitToStreamer(*OutStreamer, Tcs);
MCInst Lda; Lda.setOpcode(W65816::LDA_DP);
Lda.addOperand(MCOperand::createImm(0xE0));
EmitToStreamer(*OutStreamer, Lda);
} else if (N <= 14 && (N % 2) == 0) {
for (int i = 0; i < N / 2; ++i) {
MCInst Ply; Ply.setOpcode(W65816::PLY);
EmitToStreamer(*OutStreamer, Ply);
}
} else {
MCInst Tay; Tay.setOpcode(W65816::TAY); EmitToStreamer(*OutStreamer, Tay);
MCInst Tsc; Tsc.setOpcode(W65816::TSC); EmitToStreamer(*OutStreamer, Tsc);
MCInst Clc; Clc.setOpcode(W65816::CLC); EmitToStreamer(*OutStreamer, Clc);
MCInst Adc; Adc.setOpcode(W65816::ADC_Imm16);
Adc.addOperand(MCOperand::createImm(N));
EmitToStreamer(*OutStreamer, Adc);
MCInst Tcs; Tcs.setOpcode(W65816::TCS); EmitToStreamer(*OutStreamer, Tcs);
MCInst Tya; Tya.setOpcode(W65816::TYA); EmitToStreamer(*OutStreamer, Tya);
}
return;
}
case W65816::LDXi16imm: {
MCInst Ldx;
Ldx.setOpcode(W65816::LDX_Imm16);
Ldx.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
EmitToStreamer(*OutStreamer, Ldx);
return;
}
case W65816::LDAi16imm: {
MCInst Lda;
Lda.setOpcode(W65816::LDA_Imm16);
Lda.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
EmitToStreamer(*OutStreamer, Lda);
return;
}
case W65816::LDAi8imm: {
// i8 immediate — requires M=1 so the CPU reads only 1 immediate
// byte. The function runs in M=0 (prologue convention), so wrap
// with SEP/REP. Adjacent i8 ops collapse via W65816SepRepCleanup.
MCInst Sep; Sep.setOpcode(W65816::SEP);
Sep.addOperand(MCOperand::createImm(0x20));
EmitToStreamer(*OutStreamer, Sep);
MCInst Lda;
Lda.setOpcode(W65816::LDA_Imm8);
int64_t Val = MI->getOperand(1).getImm() & 0xFF;
Lda.addOperand(MCOperand::createImm(Val));
EmitToStreamer(*OutStreamer, Lda);
MCInst Rep; Rep.setOpcode(W65816::REP);
Rep.addOperand(MCOperand::createImm(0x20));
EmitToStreamer(*OutStreamer, Rep);
return;
}
case W65816::LDAabs: {
MCInst Lda;
Lda.setOpcode(W65816::LDA_Abs);
Lda.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
EmitToStreamer(*OutStreamer, Lda);
return;
}
case W65816::STAabs: {
// STAabs is (outs), (ins Acc16:$src, addr:$addr). The MC STA_Abs
// takes only $addr; $src lives in the implicit A.
MCInst Sta;
Sta.setOpcode(W65816::STA_Abs);
Sta.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
EmitToStreamer(*OutStreamer, Sta);
return;
}
case W65816::ADCi16imm:
case W65816::SBCi16imm: {
bool IsSub = MI->getOpcode() == W65816::SBCi16imm;
MCInst Carry;
Carry.setOpcode(IsSub ? W65816::SEC : W65816::CLC);
EmitToStreamer(*OutStreamer, Carry);
MCInst Op;
Op.setOpcode(IsSub ? W65816::SBC_Imm16 : W65816::ADC_Imm16);
Op.addOperand(lowerOperand(MI->getOperand(2), MCInstLowering));
EmitToStreamer(*OutStreamer, Op);
return;
}
case W65816::ADCEi16imm:
case W65816::SBCEi16imm: {
// Chained ADC/SBC: no CLC/SEC prefix — the carry/borrow from the
// previous addc/adde/subc/sube is already in P. See ADCi16imm
// comment in W65816InstrInfo.td.
bool IsSub = MI->getOpcode() == W65816::SBCEi16imm;
MCInst Op;
Op.setOpcode(IsSub ? W65816::SBC_Imm16 : W65816::ADC_Imm16);
Op.addOperand(lowerOperand(MI->getOperand(2), MCInstLowering));
EmitToStreamer(*OutStreamer, Op);
return;
}
case W65816::ADCi8imm:
case W65816::SBCi8imm: {
bool IsSub = MI->getOpcode() == W65816::SBCi8imm;
// SEP/REP wrap (see LDAi8imm comment).
MCInst Sep; Sep.setOpcode(W65816::SEP);
Sep.addOperand(MCOperand::createImm(0x20));
EmitToStreamer(*OutStreamer, Sep);
MCInst Carry;
Carry.setOpcode(IsSub ? W65816::SEC : W65816::CLC);
EmitToStreamer(*OutStreamer, Carry);
MCInst Op;
Op.setOpcode(IsSub ? W65816::SBC_Imm8 : W65816::ADC_Imm8);
int64_t Val = MI->getOperand(2).getImm() & 0xFF;
Op.addOperand(MCOperand::createImm(Val));
EmitToStreamer(*OutStreamer, Op);
MCInst Rep; Rep.setOpcode(W65816::REP);
Rep.addOperand(MCOperand::createImm(0x20));
EmitToStreamer(*OutStreamer, Rep);
return;
}
case W65816::ANDi8imm:
case W65816::ORAi8imm:
case W65816::EORi8imm: {
MCInst Op;
unsigned mc = 0;
switch (MI->getOpcode()) {
case W65816::ANDi8imm: mc = W65816::AND_Imm8; break;
case W65816::ORAi8imm: mc = W65816::ORA_Imm8; break;
case W65816::EORi8imm: mc = W65816::EOR_Imm8; break;
}
Op.setOpcode(mc);
// Mask to 8 bits so the printer doesn't show the sign-extended
// i8 value as a wider hex literal (e.g. -16 → 0xFFF0); the
// encoder only takes the low byte anyway.
int64_t Val = MI->getOperand(2).getImm() & 0xFF;
Op.addOperand(MCOperand::createImm(Val));
// SEP/REP wrap (see LDAi8imm comment).
MCInst Sep; Sep.setOpcode(W65816::SEP);
Sep.addOperand(MCOperand::createImm(0x20));
EmitToStreamer(*OutStreamer, Sep);
EmitToStreamer(*OutStreamer, Op);
MCInst Rep; Rep.setOpcode(W65816::REP);
Rep.addOperand(MCOperand::createImm(0x20));
EmitToStreamer(*OutStreamer, Rep);
return;
}
case W65816::LDA8abs: {
// i8 absolute load — same byte sequence as LDA_Abs in M=0, but
// semantically loads 1 byte not 2. Need M=1 wrap so we don't
// also pull in the byte at addr+1 (often another global, which is
// harmless to read but corrupts A_hi for any consumer that cares).
MCInst Sep; Sep.setOpcode(W65816::SEP);
Sep.addOperand(MCOperand::createImm(0x20));
EmitToStreamer(*OutStreamer, Sep);
MCInst Lda;
Lda.setOpcode(W65816::LDA_Abs);
Lda.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
EmitToStreamer(*OutStreamer, Lda);
MCInst Rep; Rep.setOpcode(W65816::REP);
Rep.addOperand(MCOperand::createImm(0x20));
EmitToStreamer(*OutStreamer, Rep);
return;
}
case W65816::STA8abs: {
// STA_Abs is 16-bit when M=0, 8-bit when M=1. Pure-i8 functions
// run with M=1 and a bare STA is correct. M=0 functions need an
// SEP/REP wrap so the STA stores only one byte — without it, the
// store clobbers the byte at addr+1 (potentially another global).
bool UsesAcc8 = MI->getMF()
->getInfo<W65816MachineFunctionInfo>()
->getUsesAcc8();
if (!UsesAcc8) {
MCInst Sep; Sep.setOpcode(W65816::SEP);
Sep.addOperand(MCOperand::createImm(0x20));
EmitToStreamer(*OutStreamer, Sep);
}
MCInst Sta;
Sta.setOpcode(W65816::STA_Abs);
Sta.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
EmitToStreamer(*OutStreamer, Sta);
if (!UsesAcc8) {
MCInst Rep; Rep.setOpcode(W65816::REP);
Rep.addOperand(MCOperand::createImm(0x20));
EmitToStreamer(*OutStreamer, Rep);
}
return;
}
case W65816::ADCabs:
case W65816::SBCabs: {
bool IsSub = MI->getOpcode() == W65816::SBCabs;
MCInst Carry;
Carry.setOpcode(IsSub ? W65816::SEC : W65816::CLC);
EmitToStreamer(*OutStreamer, Carry);
MCInst Op;
Op.setOpcode(IsSub ? W65816::SBC_Abs : W65816::ADC_Abs);
Op.addOperand(lowerOperand(MI->getOperand(2), MCInstLowering));
EmitToStreamer(*OutStreamer, Op);
return;
}
case W65816::ADCEabs:
case W65816::SBCEabs: {
// Chained variant — no CLC/SEC prefix.
bool IsSub = MI->getOpcode() == W65816::SBCEabs;
MCInst Op;
Op.setOpcode(IsSub ? W65816::SBC_Abs : W65816::ADC_Abs);
Op.addOperand(lowerOperand(MI->getOperand(2), MCInstLowering));
EmitToStreamer(*OutStreamer, Op);
return;
}
case W65816::CMPi16imm: {
// CMPi16imm has (outs), (ins Acc16:$lhs, i16imm:$rhs); MC needs only
// the immediate.
MCInst Cmp;
Cmp.setOpcode(W65816::CMP_Imm16);
Cmp.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
EmitToStreamer(*OutStreamer, Cmp);
return;
}
case W65816::CMPi8imm: {
// i8 immediate compare — needs M=1 so the CPU only reads 1 byte
// for the immediate. See LDAi8imm comment for the wrap rationale.
MCInst Sep; Sep.setOpcode(W65816::SEP);
Sep.addOperand(MCOperand::createImm(0x20));
EmitToStreamer(*OutStreamer, Sep);
MCInst Cmp;
Cmp.setOpcode(W65816::CMP_Imm8);
int64_t Val = MI->getOperand(1).getImm() & 0xFF;
Cmp.addOperand(MCOperand::createImm(Val));
EmitToStreamer(*OutStreamer, Cmp);
MCInst Rep; Rep.setOpcode(W65816::REP);
Rep.addOperand(MCOperand::createImm(0x20));
EmitToStreamer(*OutStreamer, Rep);
return;
}
case W65816::CMPabs: {
MCInst Cmp;
Cmp.setOpcode(W65816::CMP_Abs);
Cmp.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
EmitToStreamer(*OutStreamer, Cmp);
return;
}
// Bitwise immediate / memory pseudos: simple opcode swap, no carry
// prefix. Operand 0 = $dst (output, tied), 1 = $src (use), 2 = imm/addr.
case W65816::ANDi16imm:
case W65816::ORAi16imm:
case W65816::EORi16imm: {
MCInst Op;
unsigned mc = 0;
switch (MI->getOpcode()) {
case W65816::ANDi16imm: mc = W65816::AND_Imm16; break;
case W65816::ORAi16imm: mc = W65816::ORA_Imm16; break;
case W65816::EORi16imm: mc = W65816::EOR_Imm16; break;
}
Op.setOpcode(mc);
Op.addOperand(lowerOperand(MI->getOperand(2), MCInstLowering));
EmitToStreamer(*OutStreamer, Op);
return;
}
case W65816::ANDabs:
case W65816::ORAabs:
case W65816::EORabs: {
MCInst Op;
unsigned mc = 0;
switch (MI->getOpcode()) {
case W65816::ANDabs: mc = W65816::AND_Abs; break;
case W65816::ORAabs: mc = W65816::ORA_Abs; break;
case W65816::EORabs: mc = W65816::EOR_Abs; break;
}
Op.setOpcode(mc);
Op.addOperand(lowerOperand(MI->getOperand(2), MCInstLowering));
EmitToStreamer(*OutStreamer, Op);
return;
}
case W65816::JSLpseudo: {
MCInst Jsl;
Jsl.setOpcode(W65816::JSL_Long);
Jsl.addOperand(lowerOperand(MI->getOperand(0), MCInstLowering));
EmitToStreamer(*OutStreamer, Jsl);
return;
}
case W65816::PUSH16: {
MCInst Pha;
Pha.setOpcode(W65816::PHA);
EmitToStreamer(*OutStreamer, Pha);
return;
}
case W65816::ALLOCAfi: {
// VLA / dynamic_stackalloc: A holds size on entry; on exit A holds
// pointer to the allocated region.
// TSC ; A = SP
// SEC ; clear borrow
// SBC size (in $E0) ; A = SP - size
// TCS ; SP = A
// INC A ; A = SP + 1, the lowest byte of the region
// Size is in A on entry — but we need A=SP after TSC, so first
// stash the size to DP scratch.
MCInst Sta1; Sta1.setOpcode(W65816::STA_DP);
Sta1.addOperand(MCOperand::createImm(0xE0));
EmitToStreamer(*OutStreamer, Sta1);
MCInst Tsc; Tsc.setOpcode(W65816::TSC); EmitToStreamer(*OutStreamer, Tsc);
MCInst Sec; Sec.setOpcode(W65816::SEC); EmitToStreamer(*OutStreamer, Sec);
MCInst Sbc; Sbc.setOpcode(W65816::SBC_DP);
Sbc.addOperand(MCOperand::createImm(0xE0));
EmitToStreamer(*OutStreamer, Sbc);
MCInst Tcs; Tcs.setOpcode(W65816::TCS); EmitToStreamer(*OutStreamer, Tcs);
MCInst Ina; Ina.setOpcode(W65816::INA); EmitToStreamer(*OutStreamer, Ina);
return;
}
case W65816::PUSH16X: {
MCInst Phx;
Phx.setOpcode(W65816::PHX);
EmitToStreamer(*OutStreamer, Phx);
return;
}
case W65816::ASLA16: {
MCInst Asl;
Asl.setOpcode(W65816::ASL_A);
EmitToStreamer(*OutStreamer, Asl);
return;
}
case W65816::LSRA16:
case W65816::LSRA8: {
MCInst Lsr;
Lsr.setOpcode(W65816::LSR_A);
EmitToStreamer(*OutStreamer, Lsr);
return;
}
case W65816::ASLA8: {
MCInst Asl;
Asl.setOpcode(W65816::ASL_A);
EmitToStreamer(*OutStreamer, Asl);
return;
}
case W65816::ASRA16: {
// PHA ; ASL A (sets carry from sign bit) ; PLA ; ROR A
MCInst pha; pha.setOpcode(W65816::PHA); EmitToStreamer(*OutStreamer, pha);
MCInst asl; asl.setOpcode(W65816::ASL_A); EmitToStreamer(*OutStreamer, asl);
MCInst pla; pla.setOpcode(W65816::PLA); EmitToStreamer(*OutStreamer, pla);
MCInst ror; ror.setOpcode(W65816::ROR_A); EmitToStreamer(*OutStreamer, ror);
return;
}
case W65816::XBA16: {
MCInst Xba;
Xba.setOpcode(W65816::XBA);
EmitToStreamer(*OutStreamer, Xba);
return;
}
case W65816::INA_PSEUDO: {
MCInst In;
In.setOpcode(W65816::INA);
EmitToStreamer(*OutStreamer, In);
return;
}
case W65816::DEA_PSEUDO:
case W65816::DEA_PSEUDO8: {
MCInst De;
De.setOpcode(W65816::DEA);
EmitToStreamer(*OutStreamer, De);
return;
}
case W65816::INA_PSEUDO8: {
MCInst In;
In.setOpcode(W65816::INA);
EmitToStreamer(*OutStreamer, In);
return;
}
case W65816::NEGA16: {
// EOR #$FFFF; INC A.
MCInst Eor;
Eor.setOpcode(W65816::EOR_Imm16);
Eor.addOperand(MCOperand::createImm(0xFFFF));
EmitToStreamer(*OutStreamer, Eor);
MCInst Inc;
Inc.setOpcode(W65816::INA);
EmitToStreamer(*OutStreamer, Inc);
return;
}
case W65816::NEGA8: {
// EOR #$FF; INC A — same idea as NEGA16 but in 8-bit M.
// The function context is already 8-bit M when an i8-only path
// is selected, so no SEP/REP wrap is needed here.
MCInst Eor;
Eor.setOpcode(W65816::EOR_Imm8);
Eor.addOperand(MCOperand::createImm(0xFF));
EmitToStreamer(*OutStreamer, Eor);
MCInst Inc;
Inc.setOpcode(W65816::INA);
EmitToStreamer(*OutStreamer, Inc);
return;
}
case W65816::NEGC16: {
// (subc 0, x) — lo half of multi-precision negate.
// EOR #$FFFF; CLC; ADC #1. C-out = 1 iff result = 0 (i.e. x was 0),
// matching SBC's "no borrow" convention.
MCInst Eor;
Eor.setOpcode(W65816::EOR_Imm16);
Eor.addOperand(MCOperand::createImm(0xFFFF));
EmitToStreamer(*OutStreamer, Eor);
MCInst Clc;
Clc.setOpcode(W65816::CLC);
EmitToStreamer(*OutStreamer, Clc);
MCInst Adc;
Adc.setOpcode(W65816::ADC_Imm16);
Adc.addOperand(MCOperand::createImm(1));
EmitToStreamer(*OutStreamer, Adc);
return;
}
case W65816::SRL15A: {
// ASL A; LDA #0; ROL A — extract bit 15 to bit 0.
MCInst Asl;
Asl.setOpcode(W65816::ASL_A);
EmitToStreamer(*OutStreamer, Asl);
MCInst Lda;
Lda.setOpcode(W65816::LDA_Imm16);
Lda.addOperand(MCOperand::createImm(0));
EmitToStreamer(*OutStreamer, Lda);
MCInst Rol;
Rol.setOpcode(W65816::ROL_A);
EmitToStreamer(*OutStreamer, Rol);
return;
}
case W65816::SHL15A: {
// LSR A; LDA #0; ROR A — move bit 0 to bit 15.
MCInst Lsr;
Lsr.setOpcode(W65816::LSR_A);
EmitToStreamer(*OutStreamer, Lsr);
MCInst Lda;
Lda.setOpcode(W65816::LDA_Imm16);
Lda.addOperand(MCOperand::createImm(0));
EmitToStreamer(*OutStreamer, Lda);
MCInst Ror;
Ror.setOpcode(W65816::ROR_A);
EmitToStreamer(*OutStreamer, Ror);
return;
}
case W65816::SRL8A: {
// XBA; AND #$00FF — high byte to low byte, zero high.
MCInst Xba;
Xba.setOpcode(W65816::XBA);
EmitToStreamer(*OutStreamer, Xba);
MCInst And;
And.setOpcode(W65816::AND_Imm16);
And.addOperand(MCOperand::createImm(0x00FF));
EmitToStreamer(*OutStreamer, And);
return;
}
case W65816::SHL8A: {
// XBA; AND #$FF00 — low byte to high byte, zero low.
MCInst Xba;
Xba.setOpcode(W65816::XBA);
EmitToStreamer(*OutStreamer, Xba);
MCInst And;
And.setOpcode(W65816::AND_Imm16);
And.addOperand(MCOperand::createImm(0xFF00));
EmitToStreamer(*OutStreamer, And);
return;
}
case W65816::SRA15A: {
// ASL A; LDA #0; ADC #-1; EOR #-1 — sign-fill from bit 15.
// ASL: C = bit 15 of input (the sign).
// LDA #0: A = 0, C unchanged.
// ADC #-1: A = 0 + (-1) + C = -1 + C. If C=1 (neg): A = 0; if
// C=0 (pos): A = -1. Inverted from what we want.
// EOR #-1: flip bits — A = -1 (neg) or 0 (pos), correct.
MCInst Asl;
Asl.setOpcode(W65816::ASL_A);
EmitToStreamer(*OutStreamer, Asl);
MCInst Lda;
Lda.setOpcode(W65816::LDA_Imm16);
Lda.addOperand(MCOperand::createImm(0));
EmitToStreamer(*OutStreamer, Lda);
MCInst Adc;
Adc.setOpcode(W65816::ADC_Imm16);
Adc.addOperand(MCOperand::createImm(0xFFFF));
EmitToStreamer(*OutStreamer, Adc);
MCInst Eor;
Eor.setOpcode(W65816::EOR_Imm16);
Eor.addOperand(MCOperand::createImm(0xFFFF));
EmitToStreamer(*OutStreamer, Eor);
return;
}
case W65816::NEGE16: {
// (sube 0, x) — hi half of multi-precision negate.
// EOR #$FFFF; ADC #0. Carry-in from the previous subc/sube is
// already in P; ADC #0 propagates it as ~x + C, which matches
// 0 - x - !C in two's complement.
MCInst Eor;
Eor.setOpcode(W65816::EOR_Imm16);
Eor.addOperand(MCOperand::createImm(0xFFFF));
EmitToStreamer(*OutStreamer, Eor);
MCInst Adc;
Adc.setOpcode(W65816::ADC_Imm16);
Adc.addOperand(MCOperand::createImm(0));
EmitToStreamer(*OutStreamer, Adc);
return;
}
}
MCInst TmpInst;
MCInstLowering.Lower(MI, TmpInst);
EmitToStreamer(*OutStreamer, TmpInst);
}
char W65816AsmPrinter::ID = 0;
INITIALIZE_PASS(W65816AsmPrinter, "w65816-asm-printer",
"W65816 Assembly Printer", false, false)
extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
LLVMInitializeW65816AsmPrinter() {
RegisterAsmPrinter<W65816AsmPrinter> X(getTheW65816Target());
}