| //===-- M68kInstPrinter.cpp - Convert M68k MCInst to asm ----*- C++ -*-===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// This file contains definitions for an M68k MCInst printer. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| // TODO Conform with all supported Motorola ASM syntax |
| // Motorola's assembly has several syntax variants, especially on |
| // addressing modes. |
| // For example, you can write pc indirect w/ displacement as |
| // `x(%pc)`, where `x` is the displacement imm, or `(x,%pc)`. |
| // Currently we're picking the variant that is different from |
| // GCC, albeit being recognizable by GNU AS. |
| // Not sure what is the impact now (e.g. some syntax might |
| // not be recognized by some old consoles' toolchains, in which |
| // case we can not use our integrated assembler), but either way, |
| // it will be great to support all of the variants in the future. |
| |
| #include "M68kInstPrinter.h" |
| #include "M68kBaseInfo.h" |
| |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/MC/MCExpr.h" |
| #include "llvm/MC/MCInst.h" |
| #include "llvm/MC/MCInstrInfo.h" |
| #include "llvm/MC/MCSymbol.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "asm-printer" |
| |
| #define PRINT_ALIAS_INSTR |
| #include "M68kGenAsmWriter.inc" |
| |
| void M68kInstPrinter::printRegName(raw_ostream &OS, unsigned RegNo) const { |
| OS << "%" << getRegisterName(RegNo); |
| } |
| |
| void M68kInstPrinter::printInst(const MCInst *MI, uint64_t Address, |
| StringRef Annot, const MCSubtargetInfo &STI, |
| raw_ostream &O) { |
| if (!printAliasInstr(MI, Address, O)) |
| printInstruction(MI, Address, O); |
| |
| printAnnotation(O, Annot); |
| } |
| |
| void M68kInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, |
| raw_ostream &O) { |
| const MCOperand &MO = MI->getOperand(OpNo); |
| if (MO.isReg()) { |
| printRegName(O, MO.getReg()); |
| return; |
| } |
| |
| if (MO.isImm()) { |
| printImmediate(MI, OpNo, O); |
| return; |
| } |
| |
| assert(MO.isExpr() && "Unknown operand kind in printOperand"); |
| MO.getExpr()->print(O, &MAI); |
| } |
| |
| void M68kInstPrinter::printImmediate(const MCInst *MI, unsigned opNum, |
| raw_ostream &O) { |
| const MCOperand &MO = MI->getOperand(opNum); |
| if (MO.isImm()) |
| O << '#' << MO.getImm(); |
| else if (MO.isExpr()) { |
| O << '#'; |
| MO.getExpr()->print(O, &MAI); |
| } else |
| llvm_unreachable("Unknown immediate kind"); |
| } |
| |
| void M68kInstPrinter::printMoveMask(const MCInst *MI, unsigned opNum, |
| raw_ostream &O) { |
| unsigned Mask = MI->getOperand(opNum).getImm(); |
| assert((Mask & 0xFFFF) == Mask && "Mask is always 16 bits"); |
| |
| // A move mask is splitted into two parts: |
| // bits 0 ~ 7 correspond to D0 ~ D7 regs |
| // bits 8 ~ 15 correspond to A0 ~ A7 regs |
| // |
| // In the assembly syntax, we want to use a dash to replace |
| // a continuous range of registers. For example, if the bit |
| // mask is 0b101110, we want to print "D1-D3,D5" instead of |
| // "D1,D2,D3,D4,D5". |
| // |
| // However, we don't want a dash to cross between data registers |
| // and address registers (i.e. there shouldn't be a dash crossing |
| // bit 7 and 8) since that is not really intuitive. So we simply |
| // print the data register part (bit 0~7) and address register part |
| // separately. |
| uint8_t HalfMask; |
| unsigned Reg; |
| for (int s = 0; s < 16; s += 8) { |
| HalfMask = (Mask >> s) & 0xFF; |
| // Print separation comma only if |
| // both data & register parts have bit(s) set |
| if (s != 0 && (Mask & 0xFF) && HalfMask) |
| O << '/'; |
| |
| for (int i = 0; HalfMask; ++i) { |
| if ((HalfMask >> i) & 0b1) { |
| HalfMask ^= 0b1 << i; |
| Reg = M68kII::getMaskedSpillRegister(i + s); |
| printRegName(O, Reg); |
| |
| int j = i; |
| while ((HalfMask >> (j + 1)) & 0b1) |
| HalfMask ^= 0b1 << ++j; |
| |
| if (j != i) { |
| O << '-'; |
| Reg = M68kII::getMaskedSpillRegister(j + s); |
| printRegName(O, Reg); |
| } |
| |
| i = j; |
| |
| if (HalfMask) |
| O << '/'; |
| } |
| } |
| } |
| } |
| |
| void M68kInstPrinter::printDisp(const MCInst *MI, unsigned opNum, |
| raw_ostream &O) { |
| const MCOperand &Op = MI->getOperand(opNum); |
| if (Op.isImm()) { |
| O << Op.getImm(); |
| return; |
| } |
| assert(Op.isExpr() && "Unknown operand kind in printOperand"); |
| Op.getExpr()->print(O, &MAI); |
| } |
| |
| void M68kInstPrinter::printARIMem(const MCInst *MI, unsigned opNum, |
| raw_ostream &O) { |
| O << '('; |
| printOperand(MI, opNum, O); |
| O << ')'; |
| } |
| |
| void M68kInstPrinter::printARIPIMem(const MCInst *MI, unsigned opNum, |
| raw_ostream &O) { |
| O << "("; |
| printOperand(MI, opNum, O); |
| O << ")+"; |
| } |
| |
| void M68kInstPrinter::printARIPDMem(const MCInst *MI, unsigned opNum, |
| raw_ostream &O) { |
| O << "-("; |
| printOperand(MI, opNum, O); |
| O << ")"; |
| } |
| |
| void M68kInstPrinter::printARIDMem(const MCInst *MI, unsigned opNum, |
| raw_ostream &O) { |
| O << '('; |
| printDisp(MI, opNum + M68k::MemDisp, O); |
| O << ','; |
| printOperand(MI, opNum + M68k::MemBase, O); |
| O << ')'; |
| } |
| |
| void M68kInstPrinter::printARIIMem(const MCInst *MI, unsigned opNum, |
| raw_ostream &O) { |
| O << '('; |
| printDisp(MI, opNum + M68k::MemDisp, O); |
| O << ','; |
| printOperand(MI, opNum + M68k::MemBase, O); |
| O << ','; |
| printOperand(MI, opNum + M68k::MemIndex, O); |
| O << ')'; |
| } |
| |
| // NOTE forcing (W,L) size available since M68020 only |
| void M68kInstPrinter::printAbsMem(const MCInst *MI, unsigned opNum, |
| raw_ostream &O) { |
| const MCOperand &MO = MI->getOperand(opNum); |
| |
| if (MO.isExpr()) { |
| MO.getExpr()->print(O, &MAI); |
| return; |
| } |
| |
| assert(MO.isImm() && "absolute memory addressing needs an immediate"); |
| O << format("$%0" PRIx64, (uint64_t)MO.getImm()); |
| } |
| |
| void M68kInstPrinter::printPCDMem(const MCInst *MI, uint64_t Address, |
| unsigned opNum, raw_ostream &O) { |
| O << '('; |
| printDisp(MI, opNum + M68k::PCRelDisp, O); |
| O << ",%pc)"; |
| } |
| |
| void M68kInstPrinter::printPCIMem(const MCInst *MI, uint64_t Address, |
| unsigned opNum, raw_ostream &O) { |
| O << '('; |
| printDisp(MI, opNum + M68k::PCRelDisp, O); |
| O << ",%pc,"; |
| printOperand(MI, opNum + M68k::PCRelIndex, O); |
| O << ')'; |
| } |