//===-- 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 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() ->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 X(getTheW65816Target()); }