//===-- 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/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbol.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;

  // Reset per-function state (defensive — SkipNextSepImm should
  // already be cleared by the next emitInstruction, but guarantee
  // it's not leaked across functions if a function ends mid-elision).
  void emitFunctionBodyEnd() override {
    SkipNextSepImm = -1;
    SkipNextStaAbs = false;
    SkipNextPush16 = false;
    SkipNextSta8Wrap = false;
  }
  // Reset on MBB entry too — labels emit before the MIs of a new MBB,
  // and a stale flag from a previous MBB's last LDAi8imm could
  // accidentally swallow the new MBB's first SEP.
  void emitBasicBlockStart(const MachineBasicBlock &MBB) override {
    SkipNextSepImm = -1;
    SkipNextStaAbs = false;
    SkipNextPush16 = false;
    SkipNextSta8Wrap = false;
    AsmPrinter::emitBasicBlockStart(MBB);
  }

  // When >=0, the next SEP #imm instruction with this immediate is
  // dropped (consumed) and this field reset to -1.  Used by the
  // LDAi8imm expansion to elide a redundant REP/SEP pair when the
  // following MI is the byte-store wrap's opening SEP — the LDAi8imm
  // already left M=8, so the wrap's SEP would be a no-op.
  int SkipNextSepImm = -1;

  // When true, the next STAabs is consumed (already replaced with STZ
  // by the LDAi16imm-0 peephole).
  bool SkipNextStaAbs = false;

  // When true, the next PUSH16 is consumed (already replaced with PEA
  // by the LDAi16imm + PUSH16 peephole).
  bool SkipNextPush16 = false;

  // When true, the next STA8abs / STA8long should skip emitting its
  // opening SEP (we already entered M=8 via the preceding LDAi8imm
  // collapse) and skip its closing REP (the LDAi8imm consumer will
  // restore M=16 itself).  Avoids 4 B / 6 cyc per byte-store-of-imm.
  bool SkipNextSta8Wrap = false;

  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());

  // Drop a SEP that the previous LDAi8imm expansion marked redundant.
  // The LDAi8imm peephole leaves M=8 set when its successor (or the
  // next non-mode-neutral MI) is a SEP #$20 — that SEP would re-set
  // the same flag, so we elide it.  Mode-neutral MIs (X-flag-only
  // index ops, branches, transfers that don't touch A) pass through
  // freely without invalidating the skip.
  if (SkipNextSepImm >= 0 && !MI->isDebugInstr()) {
    if (MI->getOpcode() == W65816::SEP &&
        MI->getNumOperands() >= 1 && MI->getOperand(0).isImm() &&
        MI->getOperand(0).getImm() == SkipNextSepImm) {
      SkipNextSepImm = -1;
      return;  // consume the SEP, don't emit
    }
    // Check if MI is mode-neutral; if so, pass through and KEEP the skip.
    bool isMNeutral = false;
    if (MI->isBranch() || MI->isReturn()) isMNeutral = true;
    else switch (MI->getOpcode()) {
    case W65816::LDX_Imm16: case W65816::LDX_DP: case W65816::LDX_Abs:
    case W65816::LDX_DPY:   case W65816::LDX_AbsY:
    case W65816::LDY_Imm16: case W65816::LDY_DP: case W65816::LDY_Abs:
    case W65816::LDY_DPX:   case W65816::LDY_AbsX:
    case W65816::STX_DP:    case W65816::STX_Abs: case W65816::STX_DPY:
    case W65816::STY_DP:    case W65816::STY_Abs: case W65816::STY_DPX:
    case W65816::INX:       case W65816::DEX:
    case W65816::INY:       case W65816::DEY:
    case W65816::CPX_Imm16: case W65816::CPX_DP: case W65816::CPX_Abs:
    case W65816::CPY_Imm16: case W65816::CPY_DP: case W65816::CPY_Abs:
    case W65816::PHX:       case W65816::PHY:
    case W65816::PLX:       case W65816::PLY:
    case W65816::NOP:
      isMNeutral = true; break;
    default: break;
    }
    // Anything else invalidates the elision (might re-clear M, push/pop
    // 8-bit P that observes mode, call out, etc.).
    if (!isMNeutral) SkipNextSepImm = -1;
  }

  // Drop the STAabs that the LDAi16imm-0 peephole replaced with STZ.
  if (SkipNextStaAbs && !MI->isDebugInstr()) {
    if (MI->getOpcode() == W65816::STAabs) {
      SkipNextStaAbs = false;
      return;  // consume, already emitted as STZ
    }
    // Anything other than the expected STAabs cancels the elision.
    SkipNextStaAbs = false;
  }

  // Drop the PUSH16 that the LDAi16imm + PUSH16 peephole replaced
  // with PEA.
  if (SkipNextPush16 && !MI->isDebugInstr()) {
    if (MI->getOpcode() == W65816::PUSH16) {
      SkipNextPush16 = false;
      return;  // consume, already emitted as PEA
    }
    SkipNextPush16 = false;
  }

  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::INC_HI_IF_CARRY_StackRel: {
    // Conditional-increment of an i32's hi half on stack at (off, S).
    // Z is presumed set/clear from the preceding LDA-INA-STA on the
    // lo half: Z=1 means lo wrapped 0xFFFF→0, so hi must be incremented;
    // Z=0 means no overflow, skip.  STA preserves N/Z so the gap
    // between the lo's INA and our BNE is OK.
    //
    // Emits:
    //   bne <skip>
    //   lda $off, s
    //   inc a
    //   sta $off, s
    //   <skip>:
    int64_t Off = MI->getOperand(0).getImm();
    MCSymbol *SkipSym = OutContext.createTempSymbol();
    {
      MCInst BneI;
      BneI.setOpcode(W65816::BNE);
      BneI.addOperand(MCOperand::createExpr(
          MCSymbolRefExpr::create(SkipSym, OutContext)));
      EmitToStreamer(*OutStreamer, BneI);
    }
    {
      MCInst Lda;
      Lda.setOpcode(W65816::LDA_StackRel);
      Lda.addOperand(MCOperand::createImm(Off));
      EmitToStreamer(*OutStreamer, Lda);
    }
    {
      MCInst Ina;
      Ina.setOpcode(W65816::INA);
      EmitToStreamer(*OutStreamer, Ina);
    }
    {
      MCInst Sta;
      Sta.setOpcode(W65816::STA_StackRel);
      Sta.addOperand(MCOperand::createImm(Off));
      EmitToStreamer(*OutStreamer, Sta);
    }
    OutStreamer->emitLabel(SkipSym);
    return;
  }
  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_bank: {
    // Bank-byte materialization for the HIGH half of a `&symbol` 32-bit
    // pointer.  At runtime crt0 sets $00:00BE = PBR (placement bank)
    // and $00:00BF = 0 (pad), so `lda $BE` in 16-bit M reads the 16-bit
    // value $00PBR — exactly the bank|pad word we want.
    //
    // Why not a fixup_bank16 reloc that the OMF Loader patches?  Tried
    // first; the Loader's cRELOC `BitShift=16` mechanism doesn't track
    // the placed bank (segPlacedBase appears 16-bit-only in the formula
    // it actually uses).  `lda $BE` is also 2 bytes vs 3 for `lda #imm`
    // so this is a size win too.  The symbol operand on the pseudo is
    // ignored — only the SDNode's value-type matters, which is i16
    // bank|pad.
    MCInst Lda;
    Lda.setOpcode(W65816::LDA_DP);
    Lda.addOperand(MCOperand::createImm(0xBE));
    EmitToStreamer(*OutStreamer, Lda);
    return;
  }
  case W65816::LDAi16imm: {
    // Peek at the next non-debug MI for two peephole patterns:
    //   (1) LDAi16imm 0 + STAabs $g  -> STZ_Abs $g (saves 3B)
    //   (2) LDAi16imm $imm + PUSH16  -> PEA #$imm  (saves 1B)
    // Both replace the LDA+next pair with a single 3- or 4-byte op
    // that achieves the same memory effect.
    auto It = std::next(MI->getIterator());
    while (It != MI->getParent()->end() && It->isDebugInstr()) ++It;

    bool IsZero = MI->getOperand(1).isImm() &&
                  MI->getOperand(1).getImm() == 0;
    // STAabs operand layout: (value:Acc16, addr).  Operand 0 is the A
    // register; only fire when this STA kills A — otherwise dropping
    // the LDA leaves a later A-consumer without its value.  E.g. SDAG
    // CSE'd `g16 = 0; g32 = 0;` shares one LDAi16imm 0 across multiple
    // STAabs; only the LAST one kills A, so the peephole would have
    // miscompiled the earlier two by deleting the LDA but not the
    // remaining STAs.
    if (IsZero && It != MI->getParent()->end() &&
        It->getOpcode() == W65816::STAabs &&
        It->getOperand(0).isReg() && It->getOperand(0).isKill()) {
      // Only collapse to STZ when the address fits in 16 bits.  STZ
      // has no long-form (24-bit) variant — STZ_Abs is DBR-relative
      // 3-byte and would drop a non-zero bank byte.  When the user
      // writes `*(uint16 *)0xE19E00UL = 0` we MUST keep the
      // LDA #0 + STA_Long pair so the bank-explicit form survives.
      bool AddrFitsIn16 = !It->getOperand(1).isImm() ||
                          (It->getOperand(1).getImm() & 0xFF0000) == 0;
      if (AddrFitsIn16) {
        MCInst Stz;
        Stz.setOpcode(W65816::STZ_Abs);
        Stz.addOperand(lowerOperand(It->getOperand(1), MCInstLowering));
        EmitToStreamer(*OutStreamer, Stz);
        SkipNextStaAbs = true;
        return;
      }
    }

    // PEA peephole: LDAi16imm + PUSH16 -> PEA.  Safe iff A is dead
    // after the PUSH16 — the next instruction must redefine A WITHOUT
    // reading A first.  JSL has implicit-def $a (caller-save) AND
    // implicit-use $a when A is an arg (the W65816 calling convention).
    // The peephole must NOT fire when the next op USES A: dropping
    // the LDA would leave A undefined going into the callee.  Same
    // shape as the PEI peephole below.
    if (It != MI->getParent()->end() && It->getOpcode() == W65816::PUSH16) {
      auto It2 = std::next(It);
      while (It2 != MI->getParent()->end() && It2->isDebugInstr()) ++It2;
      bool ADead = false;
      if (It2 != MI->getParent()->end()) {
        const TargetRegisterInfo *TRI =
            MI->getParent()->getParent()->getSubtarget().getRegisterInfo();
        if (It2->modifiesRegister(W65816::A, TRI) &&
            !It2->readsRegister(W65816::A, TRI))
          ADead = true;
      } else {
        // PUSH16 is the last instruction in the BB.  A is dead at
        // BB exit iff it's not live-out.  Check the BB's live-out
        // set via successors; if no successor lists A as live-in,
        // it's safe.  Conservative: treat as not-dead (skip peephole).
        // This case is uncommon — the PUSH chain almost always feeds
        // a JSL within the same BB.
      }
      if (ADead) {
        MCInst Pea;
        Pea.setOpcode(W65816::PEA);
        Pea.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
        EmitToStreamer(*OutStreamer, Pea);
        SkipNextPush16 = true;
        return;
      }
    }

    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.
    //
    // Lookahead optimization: if the next MI is `SEP #$20` (the
    // STBptr inserter's byte-store wrap opener, hoisted above this
    // LDAi8imm by W65816SepRepCleanup), the closing REP here would
    // immediately be cancelled by that SEP.  Skip both — the byte
    // STA runs in our LDAi8imm-set M=8 mode.  Saves 4B / 6cyc per
    // hit.  We mark the next-SEP-to-skip via a per-AsmPrinter flag
    // so the SEP visit drops it.
    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);
    bool SkipRep = false;
    // Walk past mode-neutral MIs (X-flag-only ops, branches, transfers
    // that don't touch A) to find the next SEP/REP — same idea as the
    // pre-emit REP/SEP scheduler, but applied to LDAi8imm's closing
    // REP.  If a SEP #$20 sits there, we can elide the REP+SEP pair.
    auto isMNeutralMI = [](const MachineInstr &MI) -> bool {
      if (MI.isDebugInstr()) return true;
      if (MI.isBranch() || MI.isReturn()) return true;
      unsigned O = MI.getOpcode();
      switch (O) {
      case W65816::LDX_Imm16: case W65816::LDX_DP: case W65816::LDX_Abs:
      case W65816::LDX_DPY:   case W65816::LDX_AbsY:
      case W65816::LDY_Imm16: case W65816::LDY_DP: case W65816::LDY_Abs:
      case W65816::LDY_DPX:   case W65816::LDY_AbsX:
      case W65816::STX_DP:    case W65816::STX_Abs: case W65816::STX_DPY:
      case W65816::STY_DP:    case W65816::STY_Abs: case W65816::STY_DPX:
      case W65816::INX:       case W65816::DEX:
      case W65816::INY:       case W65816::DEY:
      case W65816::CPX_Imm16: case W65816::CPX_DP: case W65816::CPX_Abs:
      case W65816::CPY_Imm16: case W65816::CPY_DP: case W65816::CPY_Abs:
      case W65816::PHX:       case W65816::PHY:
      case W65816::PLX:       case W65816::PLY:
      case W65816::CLC:       case W65816::SEC:
      case W65816::PHP:       case W65816::PLP:
      case W65816::NOP:
        return true;
      default:
        return false;
      }
    };
    auto It = std::next(MI->getIterator());
    while (It != MI->getParent()->end() && isMNeutralMI(*It)) ++It;
    if (It != MI->getParent()->end() &&
        It->getOpcode() == W65816::SEP &&
        It->getNumOperands() >= 1 && It->getOperand(0).isImm() &&
        It->getOperand(0).getImm() == 0x20) {
      SkipRep = true;
      SkipNextSepImm = 0x20;
    }
    // STA8abs / STA8long don't expose their SEP at MIR — the wrap is
    // emitted at MC layer.  Detect them here so we can elide the
    // closing REP and the store's opening SEP+REP wrap entirely:
    // the 4 emitted bytes (REP/SEP between LDAi8imm and STA, plus
    // REP after STA) all collapse, leaving SEP/LDA/STA/REP as the
    // tight sequence the user wrote.
    else if (It != MI->getParent()->end() &&
             (It->getOpcode() == W65816::STA8abs ||
              It->getOpcode() == W65816::STA8long)) {
      SkipRep = true;
      SkipNextSta8Wrap = true;
    }
    if (!SkipRep) {
      MCInst Rep; Rep.setOpcode(W65816::REP);
      Rep.addOperand(MCOperand::createImm(0x20));
      EmitToStreamer(*OutStreamer, Rep);
    }
    return;
  }
  case W65816::LDAabs: {
    // Same bank-byte detection as STAabs above.  `*(uint16 *)0xE19E00UL`
    // needs LDA_Long; `*(uint16 *)0x5000` stays DBR-relative.
    const MachineOperand &AddrOp = MI->getOperand(1);
    bool UseLong = false;
    if (AddrOp.isImm() && (AddrOp.getImm() & 0xFF0000) != 0)
      UseLong = true;
    MCInst Lda;
    Lda.setOpcode(UseLong ? W65816::LDA_Long : W65816::LDA_Abs);
    Lda.addOperand(lowerOperand(AddrOp, 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.
    //
    // If the address is an immediate with a non-zero bank byte (e.g.
    // `*(uint16 *)0xE19E00UL = v` for an SHR palette write), emit
    // STA_Long (4-byte, bank-explicit) instead of STA_Abs (3-byte,
    // DBR-relative).  Without this the bank byte gets dropped and
    // the store lands in whatever bank DBR happens to be — wrong for
    // any IIgs hardware region outside bank 0.
    const MachineOperand &AddrOp = MI->getOperand(1);
    bool UseLong = false;
    if (AddrOp.isImm() && (AddrOp.getImm() & 0xFF0000) != 0)
      UseLong = true;
    MCInst Sta;
    Sta.setOpcode(UseLong ? W65816::STA_Long : W65816::STA_Abs);
    Sta.addOperand(lowerOperand(AddrOp, MCInstLowering));
    EmitToStreamer(*OutStreamer, Sta);
    return;
  }
  case W65816::LDA_DP: {
    // PEI peephole: LDA_DP $dp + PUSH16 -> PEI $dp.  PEI pushes the
    // 16-bit value at direct page $dp directly onto the stack without
    // touching A, saving 1 byte (PEI=2B vs LDA_DP+PHA=3B).  Safe iff
    // A is dead after the PUSH16, same as the LDAi16imm+PUSH16
    // peephole.  Common case: i64-libcall-return forwarding —
    // copyPhysReg(A=DPF0) emits LDA $F0; the next op is PUSH16 to
    // forward the i64 high-half into a downstream call's args; the
    // chained call's first op then redefines A.
    auto It = std::next(MI->getIterator());
    while (It != MI->getParent()->end() && It->isDebugInstr()) ++It;
    if (It != MI->getParent()->end() &&
        It->getOpcode() == W65816::PUSH16) {
      auto It2 = std::next(It);
      while (It2 != MI->getParent()->end() && It2->isDebugInstr()) ++It2;
      bool ADead = false;
      if (It2 != MI->getParent()->end()) {
        const TargetRegisterInfo *TRI =
            MI->getParent()->getParent()->getSubtarget().getRegisterInfo();
        // PEI doesn't load A, so the LDA's value-set is needed if
        // the next instruction READS A.  JSL has implicit-def $a
        // (caller-save) AND implicit-use $a (when A is an arg) —
        // modifiesRegister returns true for both, but readsRegister
        // is what tells us if A's value is consumed.  Drop the LDA
        // ONLY when the next op modifies A WITHOUT reading it.
        if (It2->modifiesRegister(W65816::A, TRI) &&
            !It2->readsRegister(W65816::A, TRI))
          ADead = true;
      }
      if (ADead) {
        MCInst Pei;
        Pei.setOpcode(W65816::PEI_DP);
        Pei.addOperand(lowerOperand(MI->getOperand(0), MCInstLowering));
        EmitToStreamer(*OutStreamer, Pei);
        SkipNextPush16 = true;
        return;
      }
    }
    // Fall through to default emit (no peephole opportunity).
    break;
  }
  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:
  case W65816::LDA8long: {
    // i8 absolute load — same M=8 wrap as LDA_Abs; LDA8long uses
    // LDA_Long (0xAF, bank-explicit) for const-int MMIO addresses.
    bool IsLong = MI->getOpcode() == W65816::LDA8long;
    MCInst Sep; Sep.setOpcode(W65816::SEP);
    Sep.addOperand(MCOperand::createImm(0x20));
    EmitToStreamer(*OutStreamer, Sep);
    MCInst Lda;
    Lda.setOpcode(IsLong ? W65816::LDA_Long : W65816::LDA_Abs);
    MCOperand Addr = lowerOperand(MI->getOperand(1), MCInstLowering);
    if (IsLong && Addr.isImm()) {
      // 16-bit pointer sign-extended into i32 imm — mask back to 16 bits
      // so the encoded bank byte is 0.  See STA8long for the rationale.
      Addr = MCOperand::createImm(Addr.getImm() & 0xFFFFu);
    }
    Lda.addOperand(Addr);
    EmitToStreamer(*OutStreamer, Lda);
    MCInst Rep; Rep.setOpcode(W65816::REP);
    Rep.addOperand(MCOperand::createImm(0x20));
    EmitToStreamer(*OutStreamer, Rep);
    return;
  }
  case W65816::LDA8absX: {
    // i8 indexed-global load: SEP #0x20 ; LDA <addr>, X ; REP #0x20
    // X holds the index (set up by CopyToReg before this MI).
    MCInst Sep; Sep.setOpcode(W65816::SEP);
    Sep.addOperand(MCOperand::createImm(0x20));
    EmitToStreamer(*OutStreamer, Sep);
    MCInst Lda;
    Lda.setOpcode(W65816::LDA_AbsX);
    Lda.addOperand(lowerOperand(MI->getOperand(0), MCInstLowering));
    EmitToStreamer(*OutStreamer, Lda);
    MCInst Rep; Rep.setOpcode(W65816::REP);
    Rep.addOperand(MCOperand::createImm(0x20));
    EmitToStreamer(*OutStreamer, Rep);
    return;
  }
  case W65816::STA8absX: {
    // i8 indexed-global store: SEP #0x20 ; STA <addr>, X ; REP #0x20
    // A holds the value, X holds the index.
    MCInst Sep; Sep.setOpcode(W65816::SEP);
    Sep.addOperand(MCOperand::createImm(0x20));
    EmitToStreamer(*OutStreamer, Sep);
    MCInst Sta;
    Sta.setOpcode(W65816::STA_AbsX);
    Sta.addOperand(lowerOperand(MI->getOperand(0), MCInstLowering));
    EmitToStreamer(*OutStreamer, Sta);
    MCInst Rep; Rep.setOpcode(W65816::REP);
    Rep.addOperand(MCOperand::createImm(0x20));
    EmitToStreamer(*OutStreamer, Rep);
    return;
  }
  case W65816::STA8abs:
  case W65816::STA8long: {
    // STA_Abs / STA_Long are 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.  STA8long differs
    // from STA8abs only in the underlying opcode (0x8F vs 0x8D): long
    // is bank-explicit, abs is DBR-relative.  Long is required for
    // const-int MMIO addresses since the data bank is non-zero under
    // GS/OS Loader.
    bool IsLong = MI->getOpcode() == W65816::STA8long;
    bool UsesAcc8 = MI->getMF()
                        ->getInfo<W65816MachineFunctionInfo>()
                        ->getUsesAcc8();
    // SkipOpenSep: LDAi8imm collapse already left M=8, so skip our
    // opening SEP — but we still own the closing REP since LDAi8imm
    // dropped its.
    bool SkipOpenSep = SkipNextSta8Wrap;
    SkipNextSta8Wrap = false;
    if (!UsesAcc8 && !SkipOpenSep) {
      MCInst Sep; Sep.setOpcode(W65816::SEP);
      Sep.addOperand(MCOperand::createImm(0x20));
      EmitToStreamer(*OutStreamer, Sep);
    }
    MCInst Sta;
    Sta.setOpcode(IsLong ? W65816::STA_Long : W65816::STA_Abs);
    MCOperand Addr = lowerOperand(MI->getOperand(1), MCInstLowering);
    // STA_Long takes a 24-bit absolute address.  When the input is a
    // const-int cast through a 16-bit pointer, TableGen sign-extends
    // the 16-bit value into the i32 imm operand: 0xC035 (i16) becomes
    // 0xFFFFC035 (i64).  Mask to 16 bits to recover the original
    // pointer; the resulting encoding has bank=0 explicit.  Users who
    // need a banked address should construct a far pointer rather than
    // casting an int.
    if (IsLong && Addr.isImm()) {
      Addr = MCOperand::createImm(Addr.getImm() & 0xFFFFu);
    }
    Sta.addOperand(Addr);
    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:
  case W65816::JSLpseudo32: {
    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());
}
