1//===-- M68kInstPrinter.cpp - Convert M68k MCInst to asm --------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file contains definitions for an M68k MCInst printer.
11///
12//===----------------------------------------------------------------------===//
13
14// TODO Conform with all supported Motorola ASM syntax
15// Motorola's assembly has several syntax variants, especially on
16// addressing modes.
17// For example, you can write pc indirect w/ displacement as
18// `x(%pc)`, where `x` is the displacement imm, or `(x,%pc)`.
19// Currently we're picking the variant that is different from
20// GCC, albeit being recognizable by GNU AS.
21// Not sure what is the impact now (e.g. some syntax might
22// not be recognized by some old consoles' toolchains, in which
23// case we can not use our integrated assembler), but either way,
24// it will be great to support all of the variants in the future.
25
26#include "M68kInstPrinter.h"
27#include "M68kBaseInfo.h"
28
29#include "llvm/ADT/StringExtras.h"
30#include "llvm/MC/MCExpr.h"
31#include "llvm/MC/MCInst.h"
32#include "llvm/MC/MCInstrInfo.h"
33#include "llvm/MC/MCSymbol.h"
34#include "llvm/Support/ErrorHandling.h"
35#include "llvm/Support/raw_ostream.h"
36
37using namespace llvm;
38
39#define DEBUG_TYPE "asm-printer"
40
41#define PRINT_ALIAS_INSTR
42#include "M68kGenAsmWriter.inc"
43
44void M68kInstPrinter::printRegName(raw_ostream &OS, MCRegister Reg) const {
45  OS << "%" << getRegisterName(Reg);
46}
47
48void M68kInstPrinter::printInst(const MCInst *MI, uint64_t Address,
49                                StringRef Annot, const MCSubtargetInfo &STI,
50                                raw_ostream &O) {
51  if (!printAliasInstr(MI, Address, O))
52    printInstruction(MI, Address, O);
53
54  printAnnotation(O, Annot);
55}
56
57void M68kInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
58                                   raw_ostream &O) {
59  const MCOperand &MO = MI->getOperand(OpNo);
60  if (MO.isReg()) {
61    printRegName(O, MO.getReg());
62    return;
63  }
64
65  if (MO.isImm()) {
66    printImmediate(MI, OpNo, O);
67    return;
68  }
69
70  assert(MO.isExpr() && "Unknown operand kind in printOperand");
71  MO.getExpr()->print(O, &MAI);
72}
73
74void M68kInstPrinter::printImmediate(const MCInst *MI, unsigned opNum,
75                                     raw_ostream &O) {
76  const MCOperand &MO = MI->getOperand(opNum);
77  if (MO.isImm())
78    O << '#' << MO.getImm();
79  else if (MO.isExpr()) {
80    O << '#';
81    MO.getExpr()->print(O, &MAI);
82  } else
83    llvm_unreachable("Unknown immediate kind");
84}
85
86void M68kInstPrinter::printMoveMask(const MCInst *MI, unsigned opNum,
87                                    raw_ostream &O) {
88  unsigned Mask = MI->getOperand(opNum).getImm();
89  assert((Mask & 0xFFFF) == Mask && "Mask is always 16 bits");
90
91  // A move mask is splitted into two parts:
92  // bits 0 ~ 7  correspond to D0 ~ D7 regs
93  // bits 8 ~ 15 correspond to A0 ~ A7 regs
94  //
95  // In the assembly syntax, we want to use a dash to replace
96  // a continuous range of registers. For example, if the bit
97  // mask is 0b101110, we want to print "D1-D3,D5" instead of
98  // "D1,D2,D3,D4,D5".
99  //
100  // However, we don't want a dash to cross between data registers
101  // and address registers (i.e. there shouldn't be a dash crossing
102  // bit 7 and 8) since that is not really intuitive. So we simply
103  // print the data register part (bit 0~7) and address register part
104  // separately.
105  uint8_t HalfMask;
106  unsigned Reg;
107  for (int s = 0; s < 16; s += 8) {
108    HalfMask = (Mask >> s) & 0xFF;
109    // Print separation comma only if
110    // both data & register parts have bit(s) set
111    if (s != 0 && (Mask & 0xFF) && HalfMask)
112      O << '/';
113
114    for (int i = 0; HalfMask; ++i) {
115      if ((HalfMask >> i) & 0b1) {
116        HalfMask ^= 0b1 << i;
117        Reg = M68kII::getMaskedSpillRegister(i + s);
118        printRegName(O, Reg);
119
120        int j = i;
121        while ((HalfMask >> (j + 1)) & 0b1)
122          HalfMask ^= 0b1 << ++j;
123
124        if (j != i) {
125          O << '-';
126          Reg = M68kII::getMaskedSpillRegister(j + s);
127          printRegName(O, Reg);
128        }
129
130        i = j;
131
132        if (HalfMask)
133          O << '/';
134      }
135    }
136  }
137}
138
139void M68kInstPrinter::printDisp(const MCInst *MI, unsigned opNum,
140                                raw_ostream &O) {
141  const MCOperand &Op = MI->getOperand(opNum);
142  if (Op.isImm()) {
143    O << Op.getImm();
144    return;
145  }
146  assert(Op.isExpr() && "Unknown operand kind in printOperand");
147  Op.getExpr()->print(O, &MAI);
148}
149
150// NOTE forcing (W,L) size available since M68020 only
151void M68kInstPrinter::printAbsMem(const MCInst *MI, unsigned opNum,
152                                  raw_ostream &O) {
153  const MCOperand &MO = MI->getOperand(opNum);
154
155  if (MO.isExpr()) {
156    MO.getExpr()->print(O, &MAI);
157    return;
158  }
159
160  assert(MO.isImm() && "absolute memory addressing needs an immediate");
161  O << format("$%0" PRIx64, (uint64_t)MO.getImm());
162}
163