1//===-- SparcInstPrinter.cpp - Convert Sparc 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 Sparc 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_SPARC
18
19#ifdef _MSC_VER
20#define _CRT_SECURE_NO_WARNINGS
21#endif
22
23#if defined (WIN32) || defined (WIN64) || defined (_WIN32) || defined (_WIN64)
24#pragma warning(disable:28719)		// disable MSVC's warning on strncpy()
25#endif
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include "SparcInstPrinter.h"
32#include "../../MCInst.h"
33#include "../../utils.h"
34#include "../../SStream.h"
35#include "../../MCRegisterInfo.h"
36#include "../../MathExtras.h"
37#include "SparcMapping.h"
38
39#include "Sparc.h"
40
41static char *getRegisterName(unsigned RegNo);
42static void printInstruction(MCInst *MI, SStream *O, MCRegisterInfo *MRI);
43static void printMemOperand(MCInst *MI, int opNum, SStream *O, const char *Modifier);
44static void printOperand(MCInst *MI, int opNum, SStream *O);
45
46static void Sparc_add_hint(MCInst *MI, unsigned int hint)
47{
48	if (MI->csh->detail) {
49		MI->flat_insn->detail->sparc.hint = hint;
50	}
51}
52
53static void Sparc_add_reg(MCInst *MI, unsigned int reg)
54{
55	if (MI->csh->detail) {
56		MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].type = SPARC_OP_REG;
57		MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].reg = reg;
58		MI->flat_insn->detail->sparc.op_count++;
59	}
60}
61
62static void set_mem_access(MCInst *MI, bool status)
63{
64	if (MI->csh->detail != CS_OPT_ON)
65		return;
66
67	MI->csh->doing_mem = status;
68
69	if (status) {
70		MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].type = SPARC_OP_MEM;
71		MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].mem.base = SPARC_REG_INVALID;
72		MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].mem.disp = 0;
73	} else {
74		// done, create the next operand slot
75		MI->flat_insn->detail->sparc.op_count++;
76	}
77}
78
79void Sparc_post_printer(csh ud, cs_insn *insn, char *insn_asm, MCInst *mci)
80{
81	if (((cs_struct *)ud)->detail != CS_OPT_ON)
82		return;
83
84	// fix up some instructions
85	if (insn->id == SPARC_INS_CASX) {
86		// first op is actually a memop, not regop
87		insn->detail->sparc.operands[0].type = SPARC_OP_MEM;
88		insn->detail->sparc.operands[0].mem.base = (uint8_t)insn->detail->sparc.operands[0].reg;
89		insn->detail->sparc.operands[0].mem.disp = 0;
90	}
91}
92
93static void printRegName(SStream *OS, unsigned RegNo)
94{
95	SStream_concat0(OS, "%");
96	SStream_concat0(OS, getRegisterName(RegNo));
97}
98
99#define GET_INSTRINFO_ENUM
100#include "SparcGenInstrInfo.inc"
101
102#define GET_REGINFO_ENUM
103#include "SparcGenRegisterInfo.inc"
104
105static bool printSparcAliasInstr(MCInst *MI, SStream *O)
106{
107	switch (MCInst_getOpcode(MI)) {
108		default: return false;
109		case SP_JMPLrr:
110		case SP_JMPLri:
111				 if (MCInst_getNumOperands(MI) != 3)
112					 return false;
113				 if (!MCOperand_isReg(MCInst_getOperand(MI, 0)))
114					 return false;
115
116				 switch (MCOperand_getReg(MCInst_getOperand(MI, 0))) {
117					 default: return false;
118					 case SP_G0: // jmp $addr | ret | retl
119							  if (MCOperand_isImm(MCInst_getOperand(MI, 2)) &&
120									MCOperand_getImm(MCInst_getOperand(MI, 2)) == 8) {
121								  switch(MCOperand_getReg(MCInst_getOperand(MI, 1))) {
122									  default: break;
123									  case SP_I7: SStream_concat0(O, "ret"); MCInst_setOpcodePub(MI, SPARC_INS_RET); return true;
124									  case SP_O7: SStream_concat0(O, "retl"); MCInst_setOpcodePub(MI, SPARC_INS_RETL); return true;
125								  }
126							  }
127
128							  SStream_concat0(O, "jmp\t");
129							  MCInst_setOpcodePub(MI, SPARC_INS_JMP);
130							  printMemOperand(MI, 1, O, NULL);
131							  return true;
132					 case SP_O7: // call $addr
133							  SStream_concat0(O, "call ");
134							  MCInst_setOpcodePub(MI, SPARC_INS_CALL);
135							  printMemOperand(MI, 1, O, NULL);
136							  return true;
137				 }
138		case SP_V9FCMPS:
139		case SP_V9FCMPD:
140		case SP_V9FCMPQ:
141		case SP_V9FCMPES:
142		case SP_V9FCMPED:
143		case SP_V9FCMPEQ:
144				 if (MI->csh->mode & CS_MODE_V9 || (MCInst_getNumOperands(MI) != 3) ||
145						 (!MCOperand_isReg(MCInst_getOperand(MI, 0))) ||
146						 (MCOperand_getReg(MCInst_getOperand(MI, 0)) != SP_FCC0))
147						 return false;
148				 // if V8, skip printing %fcc0.
149				 switch(MCInst_getOpcode(MI)) {
150					 default:
151					 case SP_V9FCMPS:  SStream_concat0(O, "fcmps\t"); MCInst_setOpcodePub(MI, SPARC_INS_FCMPS); break;
152					 case SP_V9FCMPD:  SStream_concat0(O, "fcmpd\t"); MCInst_setOpcodePub(MI, SPARC_INS_FCMPD); break;
153					 case SP_V9FCMPQ:  SStream_concat0(O, "fcmpq\t"); MCInst_setOpcodePub(MI, SPARC_INS_FCMPQ); break;
154					 case SP_V9FCMPES: SStream_concat0(O, "fcmpes\t"); MCInst_setOpcodePub(MI, SPARC_INS_FCMPES); break;
155					 case SP_V9FCMPED: SStream_concat0(O, "fcmped\t"); MCInst_setOpcodePub(MI, SPARC_INS_FCMPED); break;
156					 case SP_V9FCMPEQ: SStream_concat0(O, "fcmpeq\t"); MCInst_setOpcodePub(MI, SPARC_INS_FCMPEQ); break;
157				 }
158				 printOperand(MI, 1, O);
159				 SStream_concat0(O, ", ");
160				 printOperand(MI, 2, O);
161				 return true;
162	}
163}
164
165static void printOperand(MCInst *MI, int opNum, SStream *O)
166{
167	int Imm;
168	unsigned reg;
169	MCOperand *MO = MCInst_getOperand(MI, opNum);
170
171	if (MCOperand_isReg(MO)) {
172		reg = MCOperand_getReg(MO);
173		printRegName(O, reg);
174		reg = Sparc_map_register(reg);
175
176		if (MI->csh->detail) {
177			if (MI->csh->doing_mem) {
178				if (MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].mem.base)
179					MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].mem.index = (uint8_t)reg;
180				else
181					MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].mem.base = (uint8_t)reg;
182			} else {
183				MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].type = SPARC_OP_REG;
184				MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].reg = reg;
185				MI->flat_insn->detail->sparc.op_count++;
186			}
187		}
188
189		return;
190	}
191
192	if (MCOperand_isImm(MO)) {
193		Imm = (int)MCOperand_getImm(MO);
194
195		// Conditional branches displacements needs to be signextended to be
196		// able to jump backwards.
197		//
198		// Displacements are measured as the number of instructions forward or
199		// backward, so they need to be multiplied by 4
200		switch (MI->Opcode) {
201			case SP_CALL:
202				Imm = SignExtend32(Imm, 30);
203				Imm += (uint32_t)MI->address;
204				break;
205
206			// Branch on integer condition with prediction (BPcc)
207			// Branch on floating point condition with prediction (FBPfcc)
208			case SP_BPICC:
209			case SP_BPICCA:
210			case SP_BPICCANT:
211			case SP_BPICCNT:
212			case SP_BPXCC:
213			case SP_BPXCCA:
214			case SP_BPXCCANT:
215			case SP_BPXCCNT:
216			case SP_BPFCC:
217			case SP_BPFCCA:
218			case SP_BPFCCANT:
219			case SP_BPFCCNT:
220				Imm = SignExtend32(Imm, 19);
221				Imm = (uint32_t)MI->address + Imm * 4;
222				break;
223
224			// Branch on integer condition (Bicc)
225			// Branch on floating point condition (FBfcc)
226			case SP_BA:
227			case SP_BCOND:
228			case SP_BCONDA:
229			case SP_FBCOND:
230			case SP_FBCONDA:
231				Imm = SignExtend32(Imm, 22);
232				Imm = (uint32_t)MI->address + Imm * 4;
233				break;
234
235			// Branch on integer register with prediction (BPr)
236			case SP_BPGEZapn:
237			case SP_BPGEZapt:
238			case SP_BPGEZnapn:
239			case SP_BPGEZnapt:
240			case SP_BPGZapn:
241			case SP_BPGZapt:
242			case SP_BPGZnapn:
243			case SP_BPGZnapt:
244			case SP_BPLEZapn:
245			case SP_BPLEZapt:
246			case SP_BPLEZnapn:
247			case SP_BPLEZnapt:
248			case SP_BPLZapn:
249			case SP_BPLZapt:
250			case SP_BPLZnapn:
251			case SP_BPLZnapt:
252			case SP_BPNZapn:
253			case SP_BPNZapt:
254			case SP_BPNZnapn:
255			case SP_BPNZnapt:
256			case SP_BPZapn:
257			case SP_BPZapt:
258			case SP_BPZnapn:
259			case SP_BPZnapt:
260				Imm = SignExtend32(Imm, 16);
261				Imm = (uint32_t)MI->address + Imm * 4;
262				break;
263		}
264
265		if (Imm >= 0) {
266			if (Imm > HEX_THRESHOLD)
267				SStream_concat(O, "0x%x", Imm);
268			else
269				SStream_concat(O, "%u", Imm);
270		} else {
271			if (Imm < -HEX_THRESHOLD)
272				SStream_concat(O, "-0x%x", -Imm);
273			else
274				SStream_concat(O, "-%u", -Imm);
275		}
276
277		if (MI->csh->detail) {
278			if (MI->csh->doing_mem) {
279				MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].mem.disp = Imm;
280			} else {
281				MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].type = SPARC_OP_IMM;
282				MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].imm = Imm;
283				MI->flat_insn->detail->sparc.op_count++;
284			}
285		}
286	}
287
288	return;
289}
290
291static void printMemOperand(MCInst *MI, int opNum, SStream *O, const char *Modifier)
292{
293	MCOperand *MO;
294
295	set_mem_access(MI, true);
296	printOperand(MI, opNum, O);
297
298	// If this is an ADD operand, emit it like normal operands.
299	if (Modifier && !strcmp(Modifier, "arith")) {
300		SStream_concat0(O, ", ");
301		printOperand(MI, opNum + 1, O);
302		set_mem_access(MI, false);
303		return;
304	}
305
306	MO = MCInst_getOperand(MI, opNum + 1);
307
308	if (MCOperand_isReg(MO) && (MCOperand_getReg(MO) == SP_G0)) {
309		set_mem_access(MI, false);
310		return;   // don't print "+%g0"
311	}
312
313	if (MCOperand_isImm(MO) && (MCOperand_getImm(MO) == 0)) {
314		set_mem_access(MI, false);
315		return;   // don't print "+0"
316	}
317
318	SStream_concat0(O, "+");	// qq
319
320	printOperand(MI, opNum + 1, O);
321	set_mem_access(MI, false);
322}
323
324static void printCCOperand(MCInst *MI, int opNum, SStream *O)
325{
326	int CC = (int)MCOperand_getImm(MCInst_getOperand(MI, opNum)) + 256;
327
328	switch (MCInst_getOpcode(MI)) {
329		default: break;
330		case SP_FBCOND:
331		case SP_FBCONDA:
332		case SP_BPFCC:
333		case SP_BPFCCA:
334		case SP_BPFCCNT:
335		case SP_BPFCCANT:
336		case SP_MOVFCCrr:  case SP_V9MOVFCCrr:
337		case SP_MOVFCCri:  case SP_V9MOVFCCri:
338		case SP_FMOVS_FCC: case SP_V9FMOVS_FCC:
339		case SP_FMOVD_FCC: case SP_V9FMOVD_FCC:
340		case SP_FMOVQ_FCC: case SP_V9FMOVQ_FCC:
341				 // Make sure CC is a fp conditional flag.
342				 CC = (CC < 16+256) ? (CC + 16) : CC;
343				 break;
344	}
345
346	SStream_concat0(O, SPARCCondCodeToString((sparc_cc)CC));
347
348	if (MI->csh->detail)
349		MI->flat_insn->detail->sparc.cc = (sparc_cc)CC;
350}
351
352
353static bool printGetPCX(MCInst *MI, unsigned opNum, SStream *O)
354{
355	return true;
356}
357
358
359#define PRINT_ALIAS_INSTR
360#include "SparcGenAsmWriter.inc"
361
362void Sparc_printInst(MCInst *MI, SStream *O, void *Info)
363{
364	char *mnem, *p;
365	char instr[64];	// Sparc has no instruction this long
366
367	mnem = printAliasInstr(MI, O, Info);
368	if (mnem) {
369		// fixup instruction id due to the change in alias instruction
370		strncpy(instr, mnem, strlen(mnem));
371		instr[strlen(mnem)] = '\0';
372		// does this contains hint with a coma?
373		p = strchr(instr, ',');
374		if (p)
375			*p = '\0';	// now instr only has instruction mnemonic
376		MCInst_setOpcodePub(MI, Sparc_map_insn(instr));
377		switch(MCInst_getOpcode(MI)) {
378			case SP_BCOND:
379			case SP_BCONDA:
380			case SP_BPICCANT:
381			case SP_BPICCNT:
382			case SP_BPXCCANT:
383			case SP_BPXCCNT:
384			case SP_TXCCri:
385			case SP_TXCCrr:
386				if (MI->csh->detail) {
387					// skip 'b', 't'
388					MI->flat_insn->detail->sparc.cc = Sparc_map_ICC(instr + 1);
389					MI->flat_insn->detail->sparc.hint = Sparc_map_hint(mnem);
390				}
391				break;
392			case SP_BPFCCANT:
393			case SP_BPFCCNT:
394				if (MI->csh->detail) {
395					// skip 'fb'
396					MI->flat_insn->detail->sparc.cc = Sparc_map_FCC(instr + 2);
397					MI->flat_insn->detail->sparc.hint = Sparc_map_hint(mnem);
398				}
399				break;
400			case SP_FMOVD_ICC:
401			case SP_FMOVD_XCC:
402			case SP_FMOVQ_ICC:
403			case SP_FMOVQ_XCC:
404			case SP_FMOVS_ICC:
405			case SP_FMOVS_XCC:
406				if (MI->csh->detail) {
407					// skip 'fmovd', 'fmovq', 'fmovs'
408					MI->flat_insn->detail->sparc.cc = Sparc_map_ICC(instr + 5);
409					MI->flat_insn->detail->sparc.hint = Sparc_map_hint(mnem);
410				}
411				break;
412			case SP_MOVICCri:
413			case SP_MOVICCrr:
414			case SP_MOVXCCri:
415			case SP_MOVXCCrr:
416				if (MI->csh->detail) {
417					// skip 'mov'
418					MI->flat_insn->detail->sparc.cc = Sparc_map_ICC(instr + 3);
419					MI->flat_insn->detail->sparc.hint = Sparc_map_hint(mnem);
420				}
421				break;
422			case SP_V9FMOVD_FCC:
423			case SP_V9FMOVQ_FCC:
424			case SP_V9FMOVS_FCC:
425				if (MI->csh->detail) {
426					// skip 'fmovd', 'fmovq', 'fmovs'
427					MI->flat_insn->detail->sparc.cc = Sparc_map_FCC(instr + 5);
428					MI->flat_insn->detail->sparc.hint = Sparc_map_hint(mnem);
429				}
430				break;
431			case SP_V9MOVFCCri:
432			case SP_V9MOVFCCrr:
433				if (MI->csh->detail) {
434					// skip 'mov'
435					MI->flat_insn->detail->sparc.cc = Sparc_map_FCC(instr + 3);
436					MI->flat_insn->detail->sparc.hint = Sparc_map_hint(mnem);
437				}
438				break;
439			default:
440				break;
441		}
442		cs_mem_free(mnem);
443	} else {
444		if (!printSparcAliasInstr(MI, O))
445			printInstruction(MI, O, NULL);
446	}
447}
448
449void Sparc_addReg(MCInst *MI, int reg)
450{
451	if (MI->csh->detail) {
452		MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].type = SPARC_OP_REG;
453		MI->flat_insn->detail->sparc.operands[MI->flat_insn->detail->sparc.op_count].reg = reg;
454		MI->flat_insn->detail->sparc.op_count++;
455	}
456}
457
458#endif
459