1//===-- MipsInstPrinter.cpp - Convert Mips MCInst to assembly syntax ------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This class prints an Mips MCInst to a .s file.
11//
12//===----------------------------------------------------------------------===//
13
14/* Capstone Disassembly Engine */
15/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2014 */
16
17#ifdef CAPSTONE_HAS_MIPS
18
19#include <platform.h>
20#include <stdlib.h>
21#include <stdio.h>	// debug
22#include <string.h>
23
24#include "MipsInstPrinter.h"
25#include "../../MCInst.h"
26#include "../../utils.h"
27#include "../../SStream.h"
28#include "../../MCRegisterInfo.h"
29#include "MipsMapping.h"
30
31#include "MipsInstPrinter.h"
32
33static void printUnsignedImm(MCInst *MI, int opNum, SStream *O);
34static char *printAliasInstr(MCInst *MI, SStream *O, void *info);
35static char *printAlias(MCInst *MI, SStream *OS);
36
37// These enumeration declarations were originally in MipsInstrInfo.h but
38// had to be moved here to avoid circular dependencies between
39// LLVMMipsCodeGen and LLVMMipsAsmPrinter.
40
41// Mips Condition Codes
42typedef enum Mips_CondCode {
43	// To be used with float branch True
44	Mips_FCOND_F,
45	Mips_FCOND_UN,
46	Mips_FCOND_OEQ,
47	Mips_FCOND_UEQ,
48	Mips_FCOND_OLT,
49	Mips_FCOND_ULT,
50	Mips_FCOND_OLE,
51	Mips_FCOND_ULE,
52	Mips_FCOND_SF,
53	Mips_FCOND_NGLE,
54	Mips_FCOND_SEQ,
55	Mips_FCOND_NGL,
56	Mips_FCOND_LT,
57	Mips_FCOND_NGE,
58	Mips_FCOND_LE,
59	Mips_FCOND_NGT,
60
61	// To be used with float branch False
62	// This conditions have the same mnemonic as the
63	// above ones, but are used with a branch False;
64	Mips_FCOND_T,
65	Mips_FCOND_OR,
66	Mips_FCOND_UNE,
67	Mips_FCOND_ONE,
68	Mips_FCOND_UGE,
69	Mips_FCOND_OGE,
70	Mips_FCOND_UGT,
71	Mips_FCOND_OGT,
72	Mips_FCOND_ST,
73	Mips_FCOND_GLE,
74	Mips_FCOND_SNE,
75	Mips_FCOND_GL,
76	Mips_FCOND_NLT,
77	Mips_FCOND_GE,
78	Mips_FCOND_NLE,
79	Mips_FCOND_GT
80} Mips_CondCode;
81
82#define GET_INSTRINFO_ENUM
83#include "MipsGenInstrInfo.inc"
84
85static char *getRegisterName(unsigned RegNo);
86static void printInstruction(MCInst *MI, SStream *O, MCRegisterInfo *MRI);
87
88static void set_mem_access(MCInst *MI, bool status)
89{
90	MI->csh->doing_mem = status;
91
92	if (MI->csh->detail != CS_OPT_ON)
93		return;
94
95	if (status) {
96		MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_MEM;
97		MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.base = MIPS_REG_INVALID;
98		MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.disp = 0;
99	} else {
100		// done, create the next operand slot
101		MI->flat_insn->detail->mips.op_count++;
102	}
103}
104
105static bool isReg(MCInst *MI, unsigned OpNo, unsigned R)
106{
107	return (MCOperand_isReg(MCInst_getOperand(MI, OpNo)) &&
108			MCOperand_getReg(MCInst_getOperand(MI, OpNo)) == R);
109}
110
111static char* MipsFCCToString(Mips_CondCode CC)
112{
113	switch (CC) {
114		default: return 0;	// never reach
115		case Mips_FCOND_F:
116		case Mips_FCOND_T:   return "f";
117		case Mips_FCOND_UN:
118		case Mips_FCOND_OR:  return "un";
119		case Mips_FCOND_OEQ:
120		case Mips_FCOND_UNE: return "eq";
121		case Mips_FCOND_UEQ:
122		case Mips_FCOND_ONE: return "ueq";
123		case Mips_FCOND_OLT:
124		case Mips_FCOND_UGE: return "olt";
125		case Mips_FCOND_ULT:
126		case Mips_FCOND_OGE: return "ult";
127		case Mips_FCOND_OLE:
128		case Mips_FCOND_UGT: return "ole";
129		case Mips_FCOND_ULE:
130		case Mips_FCOND_OGT: return "ule";
131		case Mips_FCOND_SF:
132		case Mips_FCOND_ST:  return "sf";
133		case Mips_FCOND_NGLE:
134		case Mips_FCOND_GLE: return "ngle";
135		case Mips_FCOND_SEQ:
136		case Mips_FCOND_SNE: return "seq";
137		case Mips_FCOND_NGL:
138		case Mips_FCOND_GL:  return "ngl";
139		case Mips_FCOND_LT:
140		case Mips_FCOND_NLT: return "lt";
141		case Mips_FCOND_NGE:
142		case Mips_FCOND_GE:  return "nge";
143		case Mips_FCOND_LE:
144		case Mips_FCOND_NLE: return "le";
145		case Mips_FCOND_NGT:
146		case Mips_FCOND_GT:  return "ngt";
147	}
148}
149
150static void printRegName(SStream *OS, unsigned RegNo)
151{
152	SStream_concat(OS, "$%s", getRegisterName(RegNo));
153}
154
155void Mips_printInst(MCInst *MI, SStream *O, void *info)
156{
157	char *mnem;
158
159	switch (MCInst_getOpcode(MI)) {
160		default: break;
161		case Mips_Save16:
162		case Mips_SaveX16:
163		case Mips_Restore16:
164		case Mips_RestoreX16:
165			return;
166	}
167
168	// Try to print any aliases first.
169	mnem = printAliasInstr(MI, O, info);
170	if (!mnem) {
171		mnem = printAlias(MI, O);
172		if (!mnem) {
173			printInstruction(MI, O, NULL);
174		}
175	}
176
177	if (mnem) {
178		// fixup instruction id due to the change in alias instruction
179		MCInst_setOpcodePub(MI, Mips_map_insn(mnem));
180		cs_mem_free(mnem);
181	}
182}
183
184static void printOperand(MCInst *MI, unsigned OpNo, SStream *O)
185{
186	MCOperand *Op;
187
188	if (OpNo >= MI->size)
189		return;
190
191	Op = MCInst_getOperand(MI, OpNo);
192	if (MCOperand_isReg(Op)) {
193		unsigned int reg = MCOperand_getReg(Op);
194		printRegName(O, reg);
195		reg = Mips_map_register(reg);
196		if (MI->csh->detail) {
197			if (MI->csh->doing_mem) {
198				MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.base = reg;
199			} else {
200				MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_REG;
201				MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].reg = reg;
202				MI->flat_insn->detail->mips.op_count++;
203			}
204		}
205	} else if (MCOperand_isImm(Op)) {
206		int64_t imm = MCOperand_getImm(Op);
207		if (MI->csh->doing_mem) {
208			if (imm) {	// only print Imm offset if it is not 0
209				if (imm >= 0) {
210					if (imm > HEX_THRESHOLD)
211						SStream_concat(O, "0x%"PRIx64, imm);
212					else
213						SStream_concat(O, "%"PRIu64, imm);
214				} else {
215					if (imm < -HEX_THRESHOLD)
216						SStream_concat(O, "-0x%"PRIx64, -imm);
217					else
218						SStream_concat(O, "-%"PRIu64, -imm);
219				}
220			}
221			if (MI->csh->detail)
222				MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.disp = imm;
223		} else {
224			if (imm >= 0) {
225				if (imm > HEX_THRESHOLD)
226					SStream_concat(O, "0x%"PRIx64, imm);
227				else
228					SStream_concat(O, "%"PRIu64, imm);
229			} else {
230				if (imm < -HEX_THRESHOLD)
231					SStream_concat(O, "-0x%"PRIx64, -imm);
232				else
233					SStream_concat(O, "-%"PRIu64, -imm);
234			}
235
236			if (MI->csh->detail) {
237				MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM;
238				MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = imm;
239				MI->flat_insn->detail->mips.op_count++;
240			}
241		}
242	}
243}
244
245static void printUnsignedImm(MCInst *MI, int opNum, SStream *O)
246{
247	MCOperand *MO = MCInst_getOperand(MI, opNum);
248	if (MCOperand_isImm(MO)) {
249		int64_t imm = MCOperand_getImm(MO);
250		if (imm >= 0) {
251			if (imm > HEX_THRESHOLD)
252				SStream_concat(O, "0x%x", (unsigned short int)imm);
253			else
254				SStream_concat(O, "%u", (unsigned short int)imm);
255		} else {
256			if (imm < -HEX_THRESHOLD)
257				SStream_concat(O, "-0x%x", (short int)-imm);
258			else
259				SStream_concat(O, "-%u", (short int)-imm);
260		}
261		if (MI->csh->detail) {
262			MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM;
263			MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = (unsigned short int)imm;
264			MI->flat_insn->detail->mips.op_count++;
265		}
266	} else
267		printOperand(MI, opNum, O);
268}
269
270static void printUnsignedImm8(MCInst *MI, int opNum, SStream *O)
271{
272	MCOperand *MO = MCInst_getOperand(MI, opNum);
273	if (MCOperand_isImm(MO)) {
274		uint8_t imm = (uint8_t)MCOperand_getImm(MO);
275		if (imm > HEX_THRESHOLD)
276			SStream_concat(O, "0x%x", imm);
277		else
278			SStream_concat(O, "%u", imm);
279		if (MI->csh->detail) {
280			MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM;
281			MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = imm;
282			MI->flat_insn->detail->mips.op_count++;
283		}
284	} else
285		printOperand(MI, opNum, O);
286}
287
288static void printMemOperand(MCInst *MI, int opNum, SStream *O)
289{
290	// Load/Store memory operands -- imm($reg)
291	// If PIC target the target is loaded as the
292	// pattern lw $25,%call16($28)
293	set_mem_access(MI, true);
294	printOperand(MI, opNum + 1, O);
295	SStream_concat0(O, "(");
296	printOperand(MI, opNum, O);
297	SStream_concat0(O, ")");
298	set_mem_access(MI, false);
299}
300
301// TODO???
302static void printMemOperandEA(MCInst *MI, int opNum, SStream *O)
303{
304	// when using stack locations for not load/store instructions
305	// print the same way as all normal 3 operand instructions.
306	printOperand(MI, opNum, O);
307	SStream_concat0(O, ", ");
308	printOperand(MI, opNum + 1, O);
309	return;
310}
311
312static void printFCCOperand(MCInst *MI, int opNum, SStream *O)
313{
314	MCOperand *MO = MCInst_getOperand(MI, opNum);
315	SStream_concat0(O, MipsFCCToString((Mips_CondCode)MCOperand_getImm(MO)));
316}
317
318static char *printAlias1(char *Str, MCInst *MI, unsigned OpNo, SStream *OS)
319{
320	SStream_concat(OS, "%s\t", Str);
321	printOperand(MI, OpNo, OS);
322	return cs_strdup(Str);
323}
324
325static char *printAlias2(char *Str, MCInst *MI,
326		unsigned OpNo0, unsigned OpNo1, SStream *OS)
327{
328	char *tmp;
329
330	tmp = printAlias1(Str, MI, OpNo0, OS);
331	SStream_concat0(OS, ", ");
332	printOperand(MI, OpNo1, OS);
333
334	return tmp;
335}
336
337#define GET_REGINFO_ENUM
338#include "MipsGenRegisterInfo.inc"
339
340static char *printAlias(MCInst *MI, SStream *OS)
341{
342	switch (MCInst_getOpcode(MI)) {
343		case Mips_BEQ:
344			// beq $zero, $zero, $L2 => b $L2
345			// beq $r0, $zero, $L2 => beqz $r0, $L2
346			if (isReg(MI, 0, Mips_ZERO) && isReg(MI, 1, Mips_ZERO))
347				return printAlias1("b", MI, 2, OS);
348			if (isReg(MI, 1, Mips_ZERO))
349				return printAlias2("beqz", MI, 0, 2, OS);
350			return NULL;
351		case Mips_BEQL:
352			// beql $r0, $zero, $L2 => beqzl $r0, $L2
353			if (isReg(MI, 0, Mips_ZERO) && isReg(MI, 1, Mips_ZERO))
354				return printAlias2("beqzl", MI, 0, 2, OS);
355			return NULL;
356		case Mips_BEQ64:
357			// beq $r0, $zero, $L2 => beqz $r0, $L2
358			if (isReg(MI, 1, Mips_ZERO_64))
359				return printAlias2("beqz", MI, 0, 2, OS);
360			return NULL;
361		case Mips_BNE:
362			// bne $r0, $zero, $L2 => bnez $r0, $L2
363			if (isReg(MI, 1, Mips_ZERO))
364				return printAlias2("bnez", MI, 0, 2, OS);
365			return NULL;
366		case Mips_BNEL:
367			// bnel $r0, $zero, $L2 => bnezl $r0, $L2
368			if (isReg(MI, 1, Mips_ZERO))
369				return printAlias2("bnezl", MI, 0, 2, OS);
370			return NULL;
371		case Mips_BNE64:
372			// bne $r0, $zero, $L2 => bnez $r0, $L2
373			if (isReg(MI, 1, Mips_ZERO_64))
374				return printAlias2("bnez", MI, 0, 2, OS);
375			return NULL;
376		case Mips_BGEZAL:
377			// bgezal $zero, $L1 => bal $L1
378			if (isReg(MI, 0, Mips_ZERO))
379				return printAlias1("bal", MI, 1, OS);
380			return NULL;
381		case Mips_BC1T:
382			// bc1t $fcc0, $L1 => bc1t $L1
383			if (isReg(MI, 0, Mips_FCC0))
384				return printAlias1("bc1t", MI, 1, OS);
385			return NULL;
386		case Mips_BC1F:
387			// bc1f $fcc0, $L1 => bc1f $L1
388			if (isReg(MI, 0, Mips_FCC0))
389				return printAlias1("bc1f", MI, 1, OS);
390			return NULL;
391		case Mips_JALR:
392			// jalr $ra, $r1 => jalr $r1
393			if (isReg(MI, 0, Mips_RA))
394				return printAlias1("jalr", MI, 1, OS);
395			return NULL;
396		case Mips_JALR64:
397			// jalr $ra, $r1 => jalr $r1
398			if (isReg(MI, 0, Mips_RA_64))
399				return printAlias1("jalr", MI, 1, OS);
400			return NULL;
401		case Mips_NOR:
402		case Mips_NOR_MM:
403			// nor $r0, $r1, $zero => not $r0, $r1
404			if (isReg(MI, 2, Mips_ZERO))
405				return printAlias2("not", MI, 0, 1, OS);
406			return NULL;
407		case Mips_NOR64:
408			// nor $r0, $r1, $zero => not $r0, $r1
409			if (isReg(MI, 2, Mips_ZERO_64))
410				return printAlias2("not", MI, 0, 1, OS);
411			return NULL;
412		case Mips_OR:
413			// or $r0, $r1, $zero => move $r0, $r1
414			if (isReg(MI, 2, Mips_ZERO))
415				return printAlias2("move", MI, 0, 1, OS);
416			return NULL;
417		default: return NULL;
418	}
419}
420
421#define PRINT_ALIAS_INSTR
422#include "MipsGenAsmWriter.inc"
423
424#endif
425