1 // license:BSD-3-Clause
2 // copyright-holders:Ryan Holtz, Vas Crabb
3 /*
4     SPARC disassembler
5 */
6 
7 #include "emu.h"
8 #include "sparcdasm.h"
9 #include "sparcdefs.h"
10 
11 #include <algorithm>
12 #include <cstdio>
13 
14 
15 namespace {
get_disp16(uint32_t op)16 	int32_t get_disp16(uint32_t op) { return DISP19; }
get_disp19(uint32_t op)17 	int32_t get_disp19(uint32_t op) { return DISP19; }
get_disp22(uint32_t op)18 	int32_t get_disp22(uint32_t op) { return DISP19; }
19 
bicc_comment(const sparc_disassembler::config * conf,bool use_cc,offs_t pc,uint32_t op)20 	const char *bicc_comment(const sparc_disassembler::config *conf, bool use_cc, offs_t pc, uint32_t op)
21 	{
22 		if (!conf || (conf->get_translated_pc() != pc)) return nullptr;
23 		auto const cc((use_cc && (BRCC & 0x2)) ? conf->get_xcc() : conf->get_icc());
24 		switch (COND)
25 		{
26 		case 0x0: return "will fall through";
27 		case 0x1: return (cc & 0x4) ? "will branch" : "will fall through";
28 		case 0x2: return ((cc & 0x04) | ((cc ^ (cc >> 2)) & 0x2)) ? "will branch" : "will fall through";
29 		case 0x3: return ((cc ^ (cc >> 2)) & 0x2) ? "will branch" : "will fall through";
30 		case 0x4: return (cc & 0x5) ? "will branch" : "will fall through";
31 		case 0x5: return (cc & 0x1) ? "will branch" : "will fall through";
32 		case 0x6: return (cc & 0x8) ? "will branch" : "will fall through";
33 		case 0x7: return (cc & 0x2) ? "will branch" : "will fall through";
34 		case 0x8: return "will branch";
35 		case 0x9: return (cc & 0x4) ? "will fall through" : "will branch";
36 		case 0xa: return ((cc & 0x04) | ((cc ^ (cc >> 2)) & 0x2)) ? "will fall through" : "will branch";
37 		case 0xb: return ((cc ^ (cc >> 2)) & 0x2) ? "will fall through" : "will branch";
38 		case 0xc: return (cc & 0x5) ? "will fall through" : "will branch";
39 		case 0xd: return (cc & 0x1) ? "will fall through" : "will branch";
40 		case 0xe: return (cc & 0x8) ? "will fall through" : "will branch";
41 		case 0xf: return (cc & 0x2) ? "will fall through" : "will branch";
42 		}
43 		return nullptr;
44 	}
bfcc_comment(const sparc_disassembler::config * conf,bool use_cc,offs_t pc,uint32_t op)45 	const char *bfcc_comment(const sparc_disassembler::config *conf, bool use_cc, offs_t pc, uint32_t op)
46 	{
47 		if (!conf || (conf->get_translated_pc() != pc)) return nullptr;
48 		auto const fcc(conf->get_fcc(use_cc ? BRCC : 0));
49 		switch (COND)
50 		{
51 		case 0x0: return "will fall through";
52 		case 0x1: return ((fcc == 1) || (fcc == 2) || (fcc == 3)) ? "will branch" : "will fall through";
53 		case 0x2: return ((fcc == 1) || (fcc == 2)) ? "will branch" : "will fall through";
54 		case 0x3: return ((fcc == 1) || (fcc == 3)) ? "will branch" : "will fall through";
55 		case 0x4: return (fcc == 1) ? "will branch" : "will fall through";
56 		case 0x5: return ((fcc == 2) || (fcc == 3)) ? "will branch" : "will fall through";
57 		case 0x6: return (fcc == 2) ? "will branch" : "will fall through";
58 		case 0x7: return (fcc == 3) ? "will branch" : "will fall through";
59 		case 0x8: return "will branch";
60 		case 0x9: return (fcc == 0) ? "will branch" : "will fall through";
61 		case 0xa: return ((fcc == 0) || (fcc == 3)) ? "will branch" : "will fall through";
62 		case 0xb: return ((fcc == 0) || (fcc == 2)) ? "will branch" : "will fall through";
63 		case 0xc: return ((fcc == 0) || (fcc == 2) || (fcc == 3)) ? "will branch" : "will fall through";
64 		case 0xd: return ((fcc == 0) || (fcc == 1)) ? "will branch" : "will fall through";
65 		case 0xe: return ((fcc == 0) || (fcc == 1) || (fcc == 3)) ? "will branch" : "will fall through";
66 		case 0xf: return ((fcc == 0) || (fcc == 1) || (fcc == 2)) ? "will branch" : "will fall through";
67 		}
68 		return nullptr;
69 	}
bpr_comment(const sparc_disassembler::config * conf,bool use_cc,offs_t pc,uint32_t op)70 	const char *bpr_comment(const sparc_disassembler::config *conf, bool use_cc, offs_t pc, uint32_t op)
71 	{
72 		if (!conf || (conf->get_translated_pc() != pc)) return nullptr;
73 		const int64_t reg(conf->get_reg_r(RS1));
74 		switch (COND)
75 		{
76 		case 1: return (reg == 0) ? "will branch" : "will fall through";
77 		case 2: return (reg <= 0) ? "will branch" : "will fall through";
78 		case 3: return (reg < 0) ? "will branch" : "will fall through";
79 		case 5: return (reg != 0) ? "will branch" : "will fall through";
80 		case 6: return (reg > 0) ? "will branch" : "will fall through";
81 		case 7: return (reg >= 0) ? "will branch" : "will fall through";
82 		}
83 		return nullptr;
84 	}
85 }
86 
87 const char * const sparc_disassembler::REG_NAMES[32] = {
88 	"%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7",
89 	"%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%o6", "%o7",
90 	"%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7",
91 	"%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%i6", "%i7"
92 };
93 
94 const sparc_disassembler::branch_desc sparc_disassembler::EMPTY_BRANCH_DESC = {
95 	nullptr, nullptr, 0, false, false,
96 	{ nullptr, nullptr, nullptr, nullptr },
97 	{
98 		nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
99 		nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
100 	}
101 };
102 
103 const sparc_disassembler::branch_desc sparc_disassembler::BPCC_DESC = {
104 	&get_disp19, &bicc_comment, 6, true, true,
105 	{ "%icc", nullptr, "%xcc", nullptr },
106 	{
107 		"bn",    "be",    "ble",   "bl",    "bleu",  "bcs",   "bneg",  "bvs",
108 		"ba",    "bne",   "bg",    "bge",   "bgu",   "bcc",   "bpos",  "bvc"
109 	}
110 };
111 
112 const sparc_disassembler::branch_desc sparc_disassembler::BICC_DESC = {
113 	&get_disp22, &bicc_comment, 6, false, false,
114 	{ nullptr, nullptr, nullptr, nullptr },
115 	{
116 		"bn",    "be",    "ble",   "bl",    "bleu",  "bcs",   "bneg",  "bvs",
117 		"ba",    "bne",   "bg",    "bge",   "bgu",   "bcc",   "bpos",  "bvc"
118 	}
119 };
120 
121 const sparc_disassembler::branch_desc sparc_disassembler::BPR_DESC = {
122 	&get_disp16, &bpr_comment, 5, true, false,
123 	{ nullptr, nullptr, nullptr, nullptr },
124 	{
125 		nullptr, "brz",   "brlez", "brlz",  nullptr, "brnz",  "brgz",  "brgez",
126 		nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
127 	}
128 };
129 
130 const sparc_disassembler::branch_desc sparc_disassembler::FBPFCC_DESC = {
131 	&get_disp19, &bfcc_comment, 6, true, true,
132 	{ "%fcc0", "%fcc1", "%fcc2", "%fcc3" },
133 	{
134 		"fbn",   "fbne",  "fblg",  "fbul",  "fbl",   "fbug",  "fbg",   "fbu",
135 		"fba",   "fbe",   "fbue",  "fbge",  "fbuge", "fble",  "fbule", "fbo"
136 	}
137 };
138 
139 const sparc_disassembler::branch_desc sparc_disassembler::FBFCC_DESC = {
140 	&get_disp22, &bfcc_comment, 6, false, false,
141 	{ nullptr, nullptr, nullptr, nullptr },
142 	{
143 		"fbn",   "fbne",  "fblg",  "fbul",  "fbl",   "fbug",  "fbg",   "fbu",
144 		"fba",   "fbe",   "fbue",  "fbge",  "fbuge", "fble",  "fbule", "fbo"
145 	}
146 };
147 
148 const sparc_disassembler::branch_desc sparc_disassembler::CBCCC_DESC = {
149 	&get_disp22, nullptr, 6, false, false,
150 	{ nullptr, nullptr, nullptr, nullptr },
151 	{
152 		"cbn",   "cb123", "cb12",  "cb13",  "cb1",   "cb23",  "cb2",   "cb3",
153 		"cba",   "cb0",   "cb03",  "cb02",  "cb023", "cb01",  "cb013", "cb012"
154 	}
155 };
156 
157 const sparc_disassembler::int_op_desc_map::value_type sparc_disassembler::V7_INT_OP_DESC[] = {
158 	{ 0x00, { false, "add",      nullptr } }, { 0x10, { false, "addcc",    nullptr } },
159 	{ 0x01, { true,  "and",      nullptr } }, { 0x11, { true,  "andcc",    "btst"  } },
160 	{ 0x02, { true,  "or",       nullptr } }, { 0x12, { true,  "orcc",     nullptr } },
161 	{ 0x03, { true,  "xor",      nullptr } }, { 0x13, { true,  "xorcc",    nullptr } },
162 	{ 0x04, { false, "sub",      nullptr } }, { 0x14, { false, "subcc",    "cmp"   } },
163 	{ 0x05, { true,  "andn",     nullptr } }, { 0x15, { true,  "andncc",   nullptr } },
164 	{ 0x06, { true,  "orn",      nullptr } }, { 0x16, { true,  "orncc",    nullptr } },
165 	{ 0x07, { true,  "xnor",     nullptr } }, { 0x17, { true,  "xnorcc",   nullptr } },
166 	{ 0x08, { false, "addx",     nullptr } }, { 0x18, { false, "addxcc",   nullptr } },
167 	{ 0x0c, { false, "subx",     nullptr } }, { 0x1c, { false, "subxcc",   nullptr } },
168 
169 	{ 0x20, { false, "taddcc",   nullptr } },
170 	{ 0x21, { false, "tsubcc",   nullptr } },
171 	{ 0x22, { false, "taddcctv", nullptr } },
172 	{ 0x23, { false, "tsubcctv", nullptr } },
173 	{ 0x24, { false, "mulscc",   nullptr } },
174 
175 	{ 0x3c, { false, "save"     } },
176 	{ 0x3d, { false, "restore"  } }
177 };
178 
179 const sparc_disassembler::int_op_desc_map::value_type sparc_disassembler::V8_INT_OP_DESC[] = {
180 	{ 0x0a, { false, "umul"     } }, { 0x1a, { false, "umulcc"   } },
181 	{ 0x0b, { false, "smul"     } }, { 0x1b, { false, "smulcc"   } },
182 	{ 0x0e, { false, "udiv"     } }, { 0x1e, { false, "udivcc"   } },
183 	{ 0x0f, { false, "sdiv"     } }, { 0x1f, { false, "sdivcc"   } }
184 };
185 
186 const sparc_disassembler::int_op_desc_map::value_type sparc_disassembler::V9_INT_OP_DESC[] = {
187 	{ 0x09, { false, "mulx"     } },
188 	{ 0x0d, { false, "udivx"    } },
189 
190 	{ 0x2d, { false, "sdivx"    } }
191 };
192 
193 const sparc_disassembler::state_reg_desc_map::value_type sparc_disassembler::V9_STATE_REG_DESC[] = {
194 	{  1, { true,  nullptr, nullptr } },
195 	{  2, { false, "%ccr",  "%ccr"  } },
196 	{  3, { false, "%asi",  "%asi"  } },
197 	{  4, { false, "%tick", nullptr } },
198 	{  5, { false, "%pc",   nullptr } },
199 	{  6, { false, "%fprs", "%fprs" } }
200 };
201 
202 const char * const sparc_disassembler::MOVCC_CC_NAMES[8] = {
203 	"%fcc0", "%fcc1", "%fcc2", "%fcc3", "%icc", nullptr, "%xcc", nullptr
204 };
205 
206 const char * const sparc_disassembler::MOVCC_COND_NAMES[32] = {
207 	"n",   "ne",  "lg",  "ul",  "l",   "ug",  "g",   "u",
208 	"a",   "e",   "ue",  "ge",  "uge", "le",  "ule", "o",
209 	"n",   "e",   "le",  "l",   "leu", "cs",  "neg", "vs",
210 	"a",   "ne",  "g",   "ge",  "gu",  "cc",  "pos", "vc"
211 };
212 
213 const char * const sparc_disassembler::MOVE_INT_COND_MNEMONICS[8] = {
214 	nullptr, "movrz", "movrlez", "movrlz", nullptr, "movrnz", "movrgz", "movrgez"
215 };
216 
217 const char * const sparc_disassembler::V9_PRIV_REG_NAMES[32] = {
218 	"%tpc",         "%tnpc",        "%tstate",      "%tt",          "%tick",        "%tba",         "%pstate",      "%tl",
219 	"%pil",         "%cwp",         "%cansave",     "%canrestore",  "%cleanwin",    "%otherwin",    "%wstate",      "%fq",
220 	nullptr,        nullptr,        nullptr,        nullptr,        nullptr,        nullptr,        nullptr,        nullptr,
221 	nullptr,        nullptr,        nullptr,        nullptr,        nullptr,        nullptr,        nullptr,        "%ver"
222 };
223 
224 const sparc_disassembler::fpop1_desc_map::value_type sparc_disassembler::V7_FPOP1_DESC[] = {
225 	{ 0x001, { false, false, false, false, "fmovs"  } },
226 	{ 0x005, { false, false, false, false, "fnegs"  } },
227 	{ 0x009, { false, false, false, false, "fabss"  } },
228 	{ 0x029, { false, false, false, false, "fsqrts" } },
229 	{ 0x02a, { false, false, true,  true,  "fsqrtd" } },
230 	{ 0x02b, { false, false, true,  true,  "fsqrtq" } },
231 	{ 0x041, { true,  false, false, false, "fadds"  } },
232 	{ 0x042, { true,  true,  true,  true,  "faddd"  } },
233 	{ 0x043, { true,  true,  true,  true,  "faddq"  } },
234 	{ 0x045, { true,  false, false, false, "fsubs"  } },
235 	{ 0x046, { true,  true,  true,  true,  "fsubd"  } },
236 	{ 0x047, { true,  true,  true,  true,  "fsubq"  } },
237 	{ 0x049, { true,  false, false, false, "fmuls"  } },
238 	{ 0x04a, { true,  true,  true,  true,  "fmuld"  } },
239 	{ 0x04b, { true,  true,  true,  true,  "fmulq"  } },
240 	{ 0x04d, { true,  false, false, false, "fdivs"  } },
241 	{ 0x04e, { true,  true,  true,  true,  "fdivd"  } },
242 	{ 0x04f, { true,  true,  true,  true,  "fdivq"  } },
243 	{ 0x069, { true,  false, false, true,  "fsmuld" } },
244 	{ 0x06e, { true,  true,  true,  true,  "fdmulq" } },
245 	{ 0x0c4, { false, false, false, false, "fitos"  } },
246 	{ 0x0c6, { false, false, true,  false, "fdtos"  } },
247 	{ 0x0c7, { false, false, true,  false, "fqtos"  } },
248 	{ 0x0c8, { false, false, false, true,  "fitod"  } },
249 	{ 0x0c9, { false, false, false, true,  "fstod"  } },
250 	{ 0x0cb, { false, false, true,  true,  "fqtod"  } },
251 	{ 0x0cc, { false, false, false, true,  "fitoq"  } },
252 	{ 0x0cd, { false, false, false, true,  "fstoq"  } },
253 	{ 0x0ce, { false, false, true,  true,  "fdtoq"  } },
254 	{ 0x0d1, { false, false, false, false, "fstoi"  } },
255 	{ 0x0d2, { false, false, true,  false, "fdtoi"  } },
256 	{ 0x0d3, { false, false, true,  false, "fqtoi"  } }
257 };
258 
259 const sparc_disassembler::fpop1_desc_map::value_type sparc_disassembler::V9_FPOP1_DESC[] = {
260 	{ 0x002, { false, false, true,  true,  "fmovd"  } },
261 	{ 0x003, { false, false, true,  true,  "fmovq"  } },
262 	{ 0x006, { false, false, true,  true,  "fnegd"  } },
263 	{ 0x007, { false, false, true,  true,  "fnegq"  } },
264 	{ 0x00a, { false, false, true,  true,  "fabsd"  } },
265 	{ 0x00b, { false, false, true,  true,  "fabsq"  } },
266 	{ 0x081, { false, false, false, true,  "fstox"  } },
267 	{ 0x082, { false, false, true,  true,  "fdtox"  } },
268 	{ 0x083, { false, false, true,  true,  "fqtox"  } },
269 	{ 0x084, { false, false, true,  false, "fxtos"  } },
270 	{ 0x088, { false, false, true,  true,  "fxtod"  } },
271 	{ 0x08c, { false, false, true,  true,  "fxtoq"  } }
272 };
273 
274 const sparc_disassembler::fpop2_desc_map::value_type sparc_disassembler::V7_FPOP2_DESC[] = {
275 	{ 0x051, { false, false, "fcmps"     } },
276 	{ 0x052, { false, true,  "fcmpd"     } },
277 	{ 0x053, { false, true,  "fcmpq"     } },
278 	{ 0x055, { false, false, "fcmpes"    } },
279 	{ 0x056, { false, true,  "fcmped"    } },
280 	{ 0x057, { false, true,  "fcmpeq"    } }
281 };
282 
283 const sparc_disassembler::fpop2_desc_map::value_type sparc_disassembler::V9_FPOP2_DESC[] = {
284 	{ 0x025, { true,  false, "fmovrse"   } },
285 	{ 0x026, { true,  true,  "fmovrde"   } },
286 	{ 0x027, { true,  true,  "fmovrqe"   } },
287 	{ 0x045, { true,  false, "fmovrslez" } },
288 	{ 0x046, { true,  true,  "fmovrdlez" } },
289 	{ 0x047, { true,  true,  "fmovrqlez" } },
290 	{ 0x065, { true,  false, "fmovrslz"  } },
291 	{ 0x066, { true,  true,  "fmovrdlz"  } },
292 	{ 0x067, { true,  true,  "fmovrqlz"  } },
293 	{ 0x0a5, { true,  false, "fmovrsne"  } },
294 	{ 0x0a6, { true,  true,  "fmovrdne"  } },
295 	{ 0x0a7, { true,  true,  "fmovrqne"  } },
296 	{ 0x0c5, { true,  false, "fmovrsgz"  } },
297 	{ 0x0c6, { true,  true,  "fmovrdgz"  } },
298 	{ 0x0c7, { true,  true,  "fmovrqgz"  } },
299 	{ 0x0e5, { true,  false, "fmovrsgez" } },
300 	{ 0x0e6, { true,  true,  "fmovrdgez" } },
301 	{ 0x0e7, { true,  true,  "fmovrqgez" } }
302 };
303 
304 const sparc_disassembler::ldst_desc_map::value_type sparc_disassembler::V7_LDST_DESC[] = {
305 	{ 0x00, { false, false, '\0', false, "ld",     nullptr } }, { 0x10, { false, true,  '\0', false, "lda",     nullptr } },
306 	{ 0x01, { false, false, '\0', false, "ldub",   nullptr } }, { 0x11, { false, true,  '\0', false, "lduba",   nullptr } },
307 	{ 0x02, { false, false, '\0', false, "lduh",   nullptr } }, { 0x12, { false, true,  '\0', false, "lduha",   nullptr } },
308 	{ 0x03, { false, false, '\0', false, "ldd",    nullptr } }, { 0x13, { false, true,  '\0', false, "ldda",    nullptr } },
309 	{ 0x04, { true,  false, '\0', false, "st",     "clr"   } }, { 0x14, { true,  true,  '\0', false, "sta",     nullptr } },
310 	{ 0x05, { true,  false, '\0', false, "stb",    "clrb"  } }, { 0x15, { true,  true,  '\0', false, "stba",    nullptr } },
311 	{ 0x06, { true,  false, '\0', false, "sth",    "clrh"  } }, { 0x16, { true,  true,  '\0', false, "stha",    nullptr } },
312 	{ 0x07, { true,  false, '\0', false, "std",    nullptr } }, { 0x17, { true,  true,  '\0', false, "stda",    nullptr } },
313 	{ 0x09, { false, false, '\0', false, "ldsb",   nullptr } }, { 0x19, { false, true,  '\0', false, "ldsba",   nullptr } },
314 	{ 0x0a, { false, false, '\0', false, "ldsh",   nullptr } }, { 0x1a, { false, true,  '\0', false, "ldsha",   nullptr } },
315 	{ 0x0d, { false, false, '\0', false, "ldstub", nullptr } }, { 0x1d, { false, true,  '\0', false, "ldstuba", nullptr } },
316 	{ 0x0f, { false, false, '\0', false, "swap",   nullptr } }, { 0x1f, { false, true,  '\0', false, "swapa",   nullptr } },
317 
318 	{ 0x20, { false, false, 'f',  false, "ld",     nullptr } }, { 0x30, { false, false, 'c',  false, "ld",      nullptr } },
319 	{ 0x23, { false, false, 'f',  true,  "ldd",    nullptr } }, { 0x33, { false, false, 'c',  false, "ldd",     nullptr } },
320 	{ 0x24, { true,  false, 'f',  false, "st",     nullptr } }, { 0x34, { true,  false, 'c',  false, "st",      nullptr } },
321 	{ 0x27, { true,  false, 'f',  true,  "std",    nullptr } }, { 0x37, { true,  false, 'c',  false, "std",     nullptr } }
322 };
323 
324 const sparc_disassembler::ldst_desc_map::value_type sparc_disassembler::V9_LDST_DESC[] = {
325 	{ 0x08, { false, false, '\0', false, "ldsw",   nullptr } }, { 0x18, { false, true,  '\0', false, "ldswa",   nullptr } },
326 	{ 0x0b, { false, false, '\0', false, "ldx",    nullptr } }, { 0x1b, { false, true,  '\0', false, "ldxa",    nullptr } },
327 	{ 0x0e, { true,  false, '\0', false, "stx",    "clrx"  } }, { 0x1e, { true,  true,  '\0', false, "stxa",    nullptr } },
328 
329 																{ 0x30, { false, true,  'f',  false, "lda",     nullptr } },
330 	{ 0x22, { false, false, 'f',  true,  "ldq",    nullptr } }, { 0x32, { false, true,  'f',  true,  "ldqa",    nullptr } },
331 																{ 0x33, { false, true,  'f',  true,  "ldda",    nullptr } },
332 																{ 0x34, { true,  true,  'f',  false, "sta",     nullptr } },
333 	{ 0x26, { true,  false, 'f',  true,  "stq",    nullptr } }, { 0x36, { true,  true,  'f',  true,  "stqa",    nullptr } },
334 																{ 0x37, { true,  true,  'f',  true,  "stda",    nullptr } }
335 };
336 
337 const sparc_disassembler::asi_desc_map::value_type sparc_disassembler::V9_ASI_DESC[] = {
338 	{ 0x10, { "#ASI_AIUP",   nullptr } },
339 	{ 0x11, { "#ASI_AIUS",   nullptr } },
340 	{ 0x18, { "#ASI_AIUP_L", nullptr } },
341 	{ 0x19, { "#ASI_AIUS_L", nullptr } },
342 	{ 0x80, { "#ASI_P",      nullptr } },
343 	{ 0x81, { "#ASI_S",      nullptr } },
344 	{ 0x82, { "#ASI_PNF",    nullptr } },
345 	{ 0x83, { "#ASI_SNF",    nullptr } },
346 	{ 0x88, { "#ASI_P_L",    nullptr } },
347 	{ 0x89, { "#ASI_S_L",    nullptr } },
348 	{ 0x8a, { "#ASI_PNF_L",  nullptr } },
349 	{ 0x8b, { "#ASI_SNF_L",  nullptr } }
350 };
351 
352 const sparc_disassembler::prftch_desc_map::value_type sparc_disassembler::V9_PRFTCH_DESC[] = {
353 	{ 0x00, { "#n_reads"   } },
354 	{ 0x01, { "#one_read"  } },
355 	{ 0x02, { "#n_writes"  } },
356 	{ 0x03, { "#one_write" } },
357 	{ 0x04, { "#page"      } }
358 };
359 
360 const sparc_disassembler::vis_op_desc_map::value_type sparc_disassembler::VIS1_OP_DESC[] = {
361 	{ 0x000, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "edge8"       } },
362 	{ 0x002, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "edge8l"      } },
363 	{ 0x004, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "edge16"      } },
364 	{ 0x006, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "edge16l"     } },
365 	{ 0x008, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "edge32"      } },
366 	{ 0x00a, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "edge32l"     } },
367 
368 	{ 0x010, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "array8"      } },
369 	{ 0x012, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "array16"     } },
370 	{ 0x014, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "array32"     } },
371 	{ 0x018, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  true,  "alignaddr"   } },
372 	{ 0x01a, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  true,  "alignaddrl"  } },
373 
374 	{ 0x020, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::R,  false, "fcmple16"    } },
375 	{ 0x022, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::R,  false, "fcmpne16"    } },
376 	{ 0x024, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::R,  false, "fcmple32"    } },
377 	{ 0x026, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::R,  false, "fcmpne32"    } },
378 	{ 0x028, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::R,  false, "fcmpgt16"    } },
379 	{ 0x02a, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::R,  false, "fcmpeq16"    } },
380 	{ 0x02c, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::R,  false, "fcmpgt32"    } },
381 	{ 0x02e, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::R,  false, "fcmpeq32"    } },
382 
383 	{ 0x031, { vis_op_desc::Fs, vis_op_desc::Fd, vis_op_desc::Fd, false, "fmul8x16"    } },
384 	{ 0x033, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fd, false, "fmul8x16au"  } },
385 	{ 0x035, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fd, false, "fmul8x16al"  } },
386 	{ 0x036, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fmul8sux16"  } },
387 	{ 0x037, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fmul8ulx16"  } },
388 	{ 0x038, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fd, false, "fmuld8sux16" } },
389 	{ 0x039, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fd, false, "fmuld8ulx16" } },
390 	{ 0x03a, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fpack32"     } },
391 	{ 0x03b, { vis_op_desc::X,  vis_op_desc::Fd, vis_op_desc::Fs, false, "fpack16"     } },
392 	{ 0x03d, { vis_op_desc::X,  vis_op_desc::Fd, vis_op_desc::Fs, false, "fpackfix"    } },
393 	{ 0x03e, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "pdist"       } },
394 
395 	{ 0x048, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "faligndata"  } },
396 	{ 0x04b, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fd, false, "fpmerge"     } },
397 	{ 0x04d, { vis_op_desc::X,  vis_op_desc::Fs, vis_op_desc::Fd, false, "fexpand"     } },
398 
399 	{ 0x050, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fpadd16"     } },
400 	{ 0x051, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fs, false, "fpadd16s"    } },
401 	{ 0x052, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fpadd32"     } },
402 	{ 0x053, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fs, false, "fpadd32s"    } },
403 	{ 0x054, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fpsub16"     } },
404 	{ 0x055, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fs, false, "fpsub16s"    } },
405 	{ 0x056, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fpsub32"     } },
406 	{ 0x057, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fs, false, "fpsub32s"    } },
407 
408 	{ 0x060, { vis_op_desc::X,  vis_op_desc::X,  vis_op_desc::Fd, false, "fzero"       } },
409 	{ 0x061, { vis_op_desc::X,  vis_op_desc::X,  vis_op_desc::Fs, false, "fzeros"      } },
410 	{ 0x062, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fnor"        } },
411 	{ 0x063, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fs, false, "fnors"       } },
412 	{ 0x064, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fandnot2"    } },
413 	{ 0x065, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fs, false, "fandnot2s"   } },
414 	{ 0x066, { vis_op_desc::X,  vis_op_desc::Fd, vis_op_desc::Fd, false, "fnot2"       } },
415 	{ 0x067, { vis_op_desc::X,  vis_op_desc::Fs, vis_op_desc::Fs, false, "fnot2s"      } },
416 	{ 0x068, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fandnot1"    } },
417 	{ 0x069, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fs, false, "fandnot1s"   } },
418 	{ 0x06a, { vis_op_desc::Fd, vis_op_desc::X,  vis_op_desc::Fd, false, "fnot1"       } },
419 	{ 0x06b, { vis_op_desc::Fs, vis_op_desc::X,  vis_op_desc::Fs, false, "fnot1s"      } },
420 	{ 0x06c, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fxor"        } },
421 	{ 0x06d, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fs, false, "fxors"       } },
422 	{ 0x06e, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fnand"       } },
423 	{ 0x06f, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fs, false, "fnands"      } },
424 
425 	{ 0x070, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fand"        } },
426 	{ 0x071, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fs, false, "fands"       } },
427 	{ 0x072, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fxnor"       } },
428 	{ 0x073, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fs, false, "fxnors"      } },
429 	{ 0x074, { vis_op_desc::Fd, vis_op_desc::X,  vis_op_desc::Fd, false, "fsrc1"       } },
430 	{ 0x075, { vis_op_desc::Fs, vis_op_desc::X,  vis_op_desc::Fs, false, "fsrc1s"      } },
431 	{ 0x076, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fornot2"     } },
432 	{ 0x077, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fs, false, "fornot2s"    } },
433 	{ 0x078, { vis_op_desc::X,  vis_op_desc::Fd, vis_op_desc::Fd, false, "fsrc2"       } },
434 	{ 0x079, { vis_op_desc::X,  vis_op_desc::Fs, vis_op_desc::Fs, false, "fsrc2s"      } },
435 	{ 0x07a, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fornot1"     } },
436 	{ 0x07b, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fs, false, "fornot1s"    } },
437 	{ 0x07c, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "for"         } },
438 	{ 0x07d, { vis_op_desc::Fs, vis_op_desc::Fs, vis_op_desc::Fs, false, "fors"        } },
439 	{ 0x07e, { vis_op_desc::X,  vis_op_desc::X,  vis_op_desc::Fd, false, "fone"        } },
440 	{ 0x07f, { vis_op_desc::X,  vis_op_desc::X,  vis_op_desc::Fs, false, "fones"       } },
441 
442 	{ 0x080, { vis_op_desc::X,  vis_op_desc::X,  vis_op_desc::X,  false, "shutdown"    } }
443 };
444 
445 const sparc_disassembler::state_reg_desc_map::value_type sparc_disassembler::VIS1_STATE_REG_DESC[] = {
446 	{ 19, { false,  "%gsr",  "%gsr"  } }
447 };
448 
449 const sparc_disassembler::asi_desc_map::value_type sparc_disassembler::VIS1_ASI_DESC[] = {
450 	{ 0x2c, { "#ASI_NUCLEUS_QUAD_LDD_L", nullptr } },
451 	{ 0x70, { "#ASI_BLK_AIUP",           nullptr } },
452 	{ 0x71, { "#ASI_BLK_AIUS",           nullptr } },
453 	{ 0x78, { "#ASI_BLK_AIUPL",          nullptr } },
454 	{ 0x79, { "#ASI_BLK_AIUSL",          nullptr } },
455 	{ 0xc0, { "#ASI_PST8_P",             nullptr } },
456 	{ 0xc1, { "#ASI_PST8_S",             nullptr } },
457 	{ 0xc2, { "#ASI_PST16_P",            nullptr } },
458 	{ 0xc3, { "#ASI_PST16_S",            nullptr } },
459 	{ 0xc4, { "#ASI_PST32_P",            nullptr } },
460 	{ 0xc5, { "#ASI_PST32_S",            nullptr } },
461 	{ 0xc8, { "#ASI_PST8_PL",            nullptr } },
462 	{ 0xc9, { "#ASI_PST8_SL",            nullptr } },
463 	{ 0xca, { "#ASI_PST16_PL",           nullptr } },
464 	{ 0xcb, { "#ASI_PST16_SL",           nullptr } },
465 	{ 0xcc, { "#ASI_PST32_PL",           nullptr } },
466 	{ 0xcd, { "#ASI_PST32_SL",           nullptr } },
467 	{ 0xd0, { "#ASI_FL8_P",              nullptr } },
468 	{ 0xd1, { "#ASI_FL8_S",              nullptr } },
469 	{ 0xd2, { "#ASI_FL16_P",             nullptr } },
470 	{ 0xd3, { "#ASI_FL16_S",             nullptr } },
471 	{ 0xd8, { "#ASI_FL8_PL",             nullptr } },
472 	{ 0xd9, { "#ASI_FL8_SL",             nullptr } },
473 	{ 0xda, { "#ASI_FL16_PL",            nullptr } },
474 	{ 0xdb, { "#ASI_FL16_SL",            nullptr } },
475 	{ 0xe0, { "#ASI_BLK_COMMIT_P",       nullptr } },
476 	{ 0xe1, { "#ASI_BLK_COMMIT_S",       nullptr } },
477 	{ 0xf0, { "#ASI_BLK_P",              nullptr } },
478 	{ 0xf1, { "#ASI_BLK_S",              nullptr } },
479 	{ 0xf8, { "#ASI_BLK_PL",             nullptr } },
480 	{ 0xf9, { "#ASI_BLK_SL",             nullptr } }
481 };
482 
483 const sparc_disassembler::vis_op_desc_map::value_type sparc_disassembler::VIS2_OP_DESC[] = {
484 	{ 0x001, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "edge8n"      } },
485 	{ 0x003, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "edge8ln"     } },
486 	{ 0x005, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "edge16n"     } },
487 	{ 0x007, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "edge16ln"    } },
488 	{ 0x009, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "edge32n"     } },
489 	{ 0x00b, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "edge32ln"    } },
490 
491 	{ 0x019, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  true,  "bmask"       } },
492 
493 	{ 0x04c, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "bshuffle"    } }
494 };
495 
496 const sparc_disassembler::asi_desc_map::value_type sparc_disassembler::VIS2P_ASI_DESC[] = {
497 	{ 0x22, { "#ASI_TWINX_AIUP",         nullptr } },
498 	{ 0x23, { "#ASI_TWINX_AIUS",         nullptr } },
499 	{ 0x26, { "#ASI_TWINX_REAL",         nullptr } },
500 	{ 0x27, { "#ASI_TWINX_N",            nullptr } },
501 	{ 0x2a, { "#ASI_TWINX_AIUP_L",       nullptr } },
502 	{ 0x2b, { "#ASI_TWINX_AIUS_L",       nullptr } },
503 	{ 0x2e, { "#ASI_TWINX_REAL_L",       nullptr } },
504 	{ 0x2f, { "#ASI_TWINX_NL",           nullptr } },
505 	{ 0xe2, { "#ASI_TWINX_P",            nullptr } },
506 	{ 0xe3, { "#ASI_TWINX_S",            nullptr } },
507 	{ 0xea, { "#ASI_TWINX_PL",           nullptr } },
508 	{ 0xeb, { "#ASI_TWINX_SL",           nullptr } }
509 };
510 
511 const sparc_disassembler::fpop1_desc_map::value_type sparc_disassembler::VIS3_FPOP1_DESC[] = {
512 	{ 0x051, { true,  false, false, false, "fnadds"  } },
513 	{ 0x052, { true,  true,  true,  true,  "fnaddd"  } },
514 	{ 0x059, { true,  false, false, false, "fnmuls"  } },
515 	{ 0x05a, { true,  true,  true,  true,  "fnmuld"  } },
516 
517 	{ 0x061, { true,  false, false, false, "fhadds"  } },
518 	{ 0x062, { true,  true,  true,  true,  "fhaddd"  } },
519 	{ 0x065, { true,  false, false, false, "fhsubs"  } },
520 	{ 0x066, { true,  true,  true,  true,  "fhsubd"  } },
521 
522 	{ 0x071, { true,  false, false, false, "fnhadds" } },
523 	{ 0x072, { true,  true,  true,  true,  "fnhaddd" } },
524 	{ 0x079, { true,  false, false, true,  "fnsmuld" } }
525 };
526 
527 const sparc_disassembler::vis_op_desc_map::value_type sparc_disassembler::VIS3_OP_DESC[] = {
528 	{ 0x011, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "addxc"          } },
529 	{ 0x013, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "addxccc"        } },
530 	{ 0x016, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "umulxhi"        } },
531 	{ 0x017, { vis_op_desc::X,  vis_op_desc::R,  vis_op_desc::R,  false, "lzcnt"          } },
532 	{ 0x01b, { vis_op_desc::X,  vis_op_desc::R,  vis_op_desc::X,  false, "cmask8"         } },
533 	{ 0x01d, { vis_op_desc::X,  vis_op_desc::R,  vis_op_desc::X,  false, "cmask16"        } },
534 	{ 0x01f, { vis_op_desc::X,  vis_op_desc::R,  vis_op_desc::X,  false, "cmask32"        } },
535 
536 	{ 0x021, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fsll16"         } },
537 	{ 0x023, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fsrl16"         } },
538 	{ 0x025, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fsll32"         } },
539 	{ 0x027, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fsrl32"         } },
540 	{ 0x029, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fslas16"        } },
541 	{ 0x02b, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fsra16"         } },
542 	{ 0x02d, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fslas32"        } },
543 	{ 0x02f, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fsra32"         } },
544 
545 	{ 0x03f, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::R,  false, "pdistn"         } },
546 
547 	{ 0x040, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fmean16"        } },
548 	{ 0x044, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fchksm16"       } },
549 
550 	{ 0x115, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "xmulx"          } },
551 	{ 0x116, { vis_op_desc::R,  vis_op_desc::R,  vis_op_desc::R,  false, "xmulxhi"        } }
552 };
553 
554 const sparc_disassembler::vis_op_desc_map::value_type sparc_disassembler::VIS3B_OP_DESC[] = {
555 	{ 0x042, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fpadd64"        } },
556 	{ 0x046, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::Fd, false, "fpsub64"        } },
557 
558 	{ 0x110, { vis_op_desc::X,  vis_op_desc::Fd, vis_op_desc::R,  false, "movdtox"        } },
559 	{ 0x111, { vis_op_desc::X,  vis_op_desc::Fs, vis_op_desc::R,  false, "movstouw"       } },
560 	{ 0x113, { vis_op_desc::X,  vis_op_desc::Fs, vis_op_desc::R,  false, "movstosw"       } },
561 	{ 0x118, { vis_op_desc::X,  vis_op_desc::R,  vis_op_desc::Fd, false, "movxtod"        } },
562 	{ 0x119, { vis_op_desc::X,  vis_op_desc::R,  vis_op_desc::Fs, false, "movwtos"        } },
563 
564 	{ 0x120, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::R,  false, "fpcmpule8"      } },
565 	{ 0x122, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::R,  false, "fpcmpune8"      } },
566 	{ 0x128, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::R,  false, "fpcmpugt8"      } },
567 	{ 0x12a, { vis_op_desc::Fd, vis_op_desc::Fd, vis_op_desc::R,  false, "fpcmpueq8"      } }
568 };
569 
570 
freg(uint32_t val,bool shift) const571 inline uint32_t sparc_disassembler::freg(uint32_t val, bool shift) const
572 {
573 	return (shift && (m_version >= 9)) ? ((val & 0x1e) | ((val << 5) & 0x20)) : val;
574 }
575 
add_int_op_desc(const T & desc)576 template <typename T> inline void sparc_disassembler::add_int_op_desc(const T &desc)
577 {
578 	for (const auto &it : desc)
579 		m_int_op_desc.insert(it);
580 }
581 
add_fpop1_desc(const T & desc)582 template <typename T> inline void sparc_disassembler::add_fpop1_desc(const T &desc)
583 {
584 	for (const auto &it : desc)
585 		m_fpop1_desc.insert(it);
586 }
587 
add_fpop2_desc(const T & desc)588 template <typename T> inline void sparc_disassembler::add_fpop2_desc(const T &desc)
589 {
590 	for (const auto &it : desc)
591 		m_fpop2_desc.insert(it);
592 }
593 
add_ldst_desc(const T & desc)594 template <typename T> inline void sparc_disassembler::add_ldst_desc(const T &desc)
595 {
596 	for (const auto &it : desc)
597 		m_ldst_desc.insert(it);
598 }
599 
add_vis_op_desc(const T & desc)600 template <typename T> inline void sparc_disassembler::add_vis_op_desc(const T &desc)
601 {
602 	for (const auto &it : desc)
603 		m_vis_op_desc.insert(it);
604 }
605 
pad_op_field(std::ostream & stream,std::streampos start_position) const606 inline void sparc_disassembler::pad_op_field(std::ostream &stream, std::streampos start_position) const
607 {
608 	const std::streamoff difference(stream.tellp() - start_position);
609 	for (std::streamoff i = difference; i < m_op_field_width; i++)
610 		stream << ' ';
611 }
612 
sparc_disassembler(const config * conf,unsigned version)613 sparc_disassembler::sparc_disassembler(const config *conf, unsigned version)
614 	: sparc_disassembler(conf, version, vis_none)
615 {
616 }
617 
sparc_disassembler(const config * conf,unsigned version,vis_level vis)618 sparc_disassembler::sparc_disassembler(const config *conf, unsigned version, vis_level vis)
619 	: m_version(version)
620 	, m_vis_level(vis)
621 	, m_op_field_width(9)
622 	, m_branch_desc{
623 		EMPTY_BRANCH_DESC,
624 		(version >= 9) ? BPCC_DESC : EMPTY_BRANCH_DESC,     // branch on integer condition codes with prediction, SPARCv9
625 		BICC_DESC,                                          // branch on integer condition codes
626 		(version >= 9) ? BPR_DESC : EMPTY_BRANCH_DESC,      // branch on integer register with prediction, SPARCv9
627 		EMPTY_BRANCH_DESC,
628 		(version >= 9) ? FBPFCC_DESC : EMPTY_BRANCH_DESC,   // branch on floating-point condition codes with prediction, SPARCv9
629 		FBFCC_DESC,                                         // branch on floating-point condition codes
630 		(version == 8) ? CBCCC_DESC : EMPTY_BRANCH_DESC     // branch on coprocessor condition codes, SPARCv8
631 	}
632 	, m_int_op_desc(std::begin(V7_INT_OP_DESC), std::end(V7_INT_OP_DESC))
633 	, m_state_reg_desc()
634 	, m_fpop1_desc(std::begin(V7_FPOP1_DESC), std::end(V7_FPOP1_DESC))
635 	, m_fpop2_desc(std::begin(V7_FPOP2_DESC), std::end(V7_FPOP2_DESC))
636 	, m_ldst_desc(std::begin(V7_LDST_DESC), std::end(V7_LDST_DESC))
637 	, m_asi_desc()
638 	, m_prftch_desc()
639 	, m_vis_op_desc()
640 {
641 	if (m_version >= 8)
642 	{
643 		add_int_op_desc(V8_INT_OP_DESC);
644 	}
645 
646 	if (m_version >= 9)
647 	{
648 		m_op_field_width = 11;
649 
650 		m_int_op_desc.find(0x08)->second.mnemonic = "addc";
651 		m_int_op_desc.find(0x0c)->second.mnemonic = "subc";
652 		m_int_op_desc.find(0x18)->second.mnemonic = "addccc";
653 		m_int_op_desc.find(0x1c)->second.mnemonic = "subccc";
654 		add_int_op_desc(V9_INT_OP_DESC);
655 
656 		add_state_reg_desc(V9_STATE_REG_DESC),
657 		add_fpop1_desc(V9_FPOP1_DESC);
658 		add_fpop2_desc(V9_FPOP2_DESC);
659 
660 		m_ldst_desc.find(0x00)->second.mnemonic = "lduw";
661 		m_ldst_desc.find(0x04)->second.mnemonic = "stw";
662 		m_ldst_desc.find(0x10)->second.mnemonic = "lduwa";
663 		m_ldst_desc.find(0x14)->second.mnemonic = "stwa";
664 		m_ldst_desc.erase(0x30); // LDC
665 		m_ldst_desc.erase(0x33); // LDDC
666 		m_ldst_desc.erase(0x34); // STC
667 		m_ldst_desc.erase(0x37); // STDC
668 		add_ldst_desc(V9_LDST_DESC);
669 
670 		add_asi_desc(V9_ASI_DESC);
671 
672 		add_prftch_desc(V9_PRFTCH_DESC);
673 	}
674 
675 	switch (m_vis_level)
676 	{
677 	case vis_3b:
678 		add_vis_op_desc(VIS3B_OP_DESC);
679 	case vis_3:
680 		add_fpop1_desc(VIS3_FPOP1_DESC);
681 		add_vis_op_desc(VIS3_OP_DESC);
682 	case vis_2p:
683 		add_asi_desc(VIS2P_ASI_DESC);
684 	case vis_2:
685 		add_vis_op_desc(VIS2_OP_DESC);
686 	case vis_1:
687 		m_op_field_width = std::max(m_op_field_width, 12);
688 		add_vis_op_desc(VIS1_OP_DESC);
689 		add_state_reg_desc(VIS1_STATE_REG_DESC);
690 		add_asi_desc(VIS1_ASI_DESC);
691 		if (m_vis_level >= vis_3)
692 		{
693 			m_vis_op_desc.find(0x020)->second.mnemonic = "fpcmple16";
694 			m_vis_op_desc.find(0x022)->second.mnemonic = "fpcmpne16";
695 			m_vis_op_desc.find(0x024)->second.mnemonic = "fpcmple32";
696 			m_vis_op_desc.find(0x026)->second.mnemonic = "fpcmpne32";
697 			m_vis_op_desc.find(0x028)->second.mnemonic = "fpcmpgt16";
698 			m_vis_op_desc.find(0x02a)->second.mnemonic = "fpcmpeq16";
699 			m_vis_op_desc.find(0x02c)->second.mnemonic = "fpcmpgt32";
700 			m_vis_op_desc.find(0x02e)->second.mnemonic = "fpcmpeq32";
701 
702 			m_vis_op_desc.find(0x060)->second.mnemonic = "fzerod";
703 			m_vis_op_desc.find(0x062)->second.mnemonic = "fnord";
704 			m_vis_op_desc.find(0x064)->second.mnemonic = "fandnot2d";
705 			m_vis_op_desc.find(0x066)->second.mnemonic = "fnot2d";
706 			m_vis_op_desc.find(0x068)->second.mnemonic = "fandnot1d";
707 			m_vis_op_desc.find(0x06a)->second.mnemonic = "fnot1d";
708 			m_vis_op_desc.find(0x06c)->second.mnemonic = "fxord";
709 			m_vis_op_desc.find(0x06e)->second.mnemonic = "fnandd";
710 
711 			m_vis_op_desc.find(0x070)->second.mnemonic = "fandd";
712 			m_vis_op_desc.find(0x072)->second.mnemonic = "fxnord";
713 			m_vis_op_desc.find(0x074)->second.mnemonic = "fsrc1d";
714 			m_vis_op_desc.find(0x076)->second.mnemonic = "fornot2d";
715 			m_vis_op_desc.find(0x078)->second.mnemonic = "fsrc2d";
716 			m_vis_op_desc.find(0x07a)->second.mnemonic = "fornot1d";
717 			m_vis_op_desc.find(0x07c)->second.mnemonic = "ford";
718 			m_vis_op_desc.find(0x07e)->second.mnemonic = "foned";
719 		}
720 	case vis_none:
721 		break;
722 	}
723 }
724 
opcode_alignment() const725 u32 sparc_disassembler::opcode_alignment() const
726 {
727 	return 4;
728 }
729 
disassemble(std::ostream & stream,offs_t pc,const data_buffer & opcodes,const data_buffer & params)730 offs_t sparc_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
731 {
732 	return dasm(stream, pc, opcodes.r32(pc));
733 }
734 
dasm(std::ostream & stream,offs_t pc,uint32_t op) const735 offs_t sparc_disassembler::dasm(std::ostream &stream, offs_t pc, uint32_t op) const
736 {
737 	switch (OP)
738 	{
739 	case 0: // Branches & SETHI
740 		switch (OP2)
741 		{
742 		case 0:
743 			util::stream_format(stream, "%-*s0x%06x", m_op_field_width, (m_version >= 9) ? "illtrap" : "unimp", CONST22);
744 			break;
745 		case 4:
746 			if (IMM22 == 0 && RD == 0)
747 				util::stream_format(stream, "nop");
748 			else
749 				util::stream_format(stream, "%-*s%%hi(0x%08x),%s", m_op_field_width, "sethi", IMM22, REG_NAMES[RD]);
750 			break;
751 		default:
752 			return dasm_branch(stream, pc, op);
753 		}
754 		return 4 | SUPPORTED;
755 	case 1:
756 		util::stream_format(stream, "%-*s%%pc%c0x%08x ! 0x%08x", m_op_field_width, "call", (DISP30 < 0) ? '-' : '+', std::abs(DISP30), pc + DISP30);
757 		return 4 | SUPPORTED;
758 	case 2:
759 		switch (OP3)
760 		{
761 		case 0x00:
762 			if (USEIMM && (RS1 == RD))
763 			{
764 				if (SIMM13 == 1)    util::stream_format(stream, "%-*s%s", m_op_field_width, "inc", REG_NAMES[RD]);
765 				else                util::stream_format(stream, "%-*s%d,%s", m_op_field_width, "inc", SIMM13, REG_NAMES[RD]);
766 				return 4 | SUPPORTED;
767 			}
768 			break;
769 		case 0x02:
770 			if (RS1 == 0)
771 			{
772 				if (USEIMM)         util::stream_format(stream, "%-*s%d,%s", m_op_field_width, "mov", SIMM13, REG_NAMES[RD]);
773 				else if (RS2 == 0)  util::stream_format(stream, "%-*s%s", m_op_field_width, "clr", REG_NAMES[RD]);
774 				else                util::stream_format(stream, "%-*s%s,%s", m_op_field_width, "mov", REG_NAMES[RS2], REG_NAMES[RD]);
775 				return 4 | SUPPORTED;
776 			}
777 			else if (RS1 == RD)
778 			{
779 				if (USEIMM) util::stream_format(stream, "%-*s0x%08x,%s", m_op_field_width, "bset", SIMM13, REG_NAMES[RD]);
780 				else        util::stream_format(stream, "%-*s%s,%s", m_op_field_width, "bset", REG_NAMES[RS2], REG_NAMES[RD]);
781 				return 4 | SUPPORTED;
782 			}
783 			break;
784 		case 0x03:
785 			if (RS1 == RD)
786 			{
787 				if (USEIMM) util::stream_format(stream, "%-*s0x%08x,%s", m_op_field_width, "btog", SIMM13, REG_NAMES[RD]);
788 				else        util::stream_format(stream, "%-*s%s,%s", m_op_field_width, "btog", REG_NAMES[RS2], REG_NAMES[RD]);
789 				return 4 | SUPPORTED;
790 			}
791 			break;
792 		case 0x04:
793 			if (USEIMM)
794 			{
795 				if (RS1 == RD)
796 				{
797 					if (SIMM13 == 1)    util::stream_format(stream, "%-*s%s", m_op_field_width, "dec", REG_NAMES[RD]);
798 					else                util::stream_format(stream, "%-*s%d,%s", m_op_field_width, "dec", SIMM13, REG_NAMES[RD]);
799 					return 4 | SUPPORTED;
800 				}
801 			}
802 			else
803 			{
804 				if (RS1 == 0)
805 				{
806 					if (RS2 == RD)  util::stream_format(stream, "%-*s%s", m_op_field_width, "neg", REG_NAMES[RD]);
807 					else            util::stream_format(stream, "%-*s%s,%s", m_op_field_width, "neg", REG_NAMES[RS2], REG_NAMES[RD]);
808 					return 4 | SUPPORTED;
809 				}
810 			}
811 			break;
812 		case 0x05:
813 			if (RS1 == RD)
814 			{
815 				if (USEIMM) util::stream_format(stream, "%-*s0x%08x,%s", m_op_field_width, "bclr", SIMM13, REG_NAMES[RD]);
816 				else        util::stream_format(stream, "%-*s%s,%s", m_op_field_width, "bclr", REG_NAMES[RS2], REG_NAMES[RD]);
817 				return 4 | SUPPORTED;
818 			}
819 			break;
820 		case 0x07:
821 			if ((USEIMM && (SIMM13 == 0)) || (!USEIMM && (RS2 == 0)))
822 			{
823 				if (RS1 == RD)  util::stream_format(stream, "%-*s%s", m_op_field_width, "not", REG_NAMES[RD]);
824 				else            util::stream_format(stream, "%-*s%s,%s", m_op_field_width, "not", REG_NAMES[RS1], REG_NAMES[RD]);
825 				return 4 | SUPPORTED;
826 			}
827 			break;
828 		case 0x10:
829 			if (USEIMM && (RS1 == RD))
830 			{
831 				if (SIMM13 == 1)    util::stream_format(stream, "%-*s%s", m_op_field_width, "inccc", REG_NAMES[RD]);
832 				else                util::stream_format(stream, "%-*s%d,%s", m_op_field_width, "inccc", SIMM13, REG_NAMES[RD]);
833 				return 4 | SUPPORTED;
834 			}
835 			break;
836 		case 0x12:
837 			if (!USEIMM && (RS1 == 0) && (RD == 0))
838 			{
839 				util::stream_format(stream, "%-*s%s", m_op_field_width, "tst", REG_NAMES[RS2]);
840 				return 4 | SUPPORTED;
841 			}
842 			break;
843 		case 0x14:
844 			if (USEIMM && (RS1 == RD) && (RD != 0))
845 			{
846 				if (SIMM13 == 1)    util::stream_format(stream, "%-*s%s", m_op_field_width, "deccc", REG_NAMES[RD]);
847 				else                util::stream_format(stream, "%-*s%d,%s", m_op_field_width, "deccc", SIMM13, REG_NAMES[RD]);
848 				return 4 | SUPPORTED;
849 			}
850 			break;
851 		case 0x25:
852 			return dasm_shift(stream, pc, op, "sll", "sllx", nullptr);
853 		case 0x26:
854 			return dasm_shift(stream, pc, op, "srl", "srlx", "clruw");
855 		case 0x27:
856 			return dasm_shift(stream, pc, op, "sra", "srax", "signx");
857 		case 0x28:
858 			return dasm_read_state_reg(stream, pc, op);
859 		case 0x29:
860 			if (m_version <= 8)
861 			{
862 				util::stream_format(stream, "%-*s%%psr,%s", m_op_field_width, "rd", REG_NAMES[RD]);
863 				return 4 | SUPPORTED;
864 			}
865 			break;
866 		case 0x2a:
867 			if (m_version >= 9)
868 			{
869 				if (V9_PRIV_REG_NAMES[RS1])
870 				{
871 					util::stream_format(stream, "%-*s%s,%s", m_op_field_width, "rdpr", V9_PRIV_REG_NAMES[RS1], REG_NAMES[RD]);
872 					return 4 | SUPPORTED;
873 				}
874 			}
875 			else
876 			{
877 				util::stream_format(stream, "%-*s%%wim,%s", m_op_field_width, "rd", REG_NAMES[RD]);
878 				return 4 | SUPPORTED;
879 			}
880 			break;
881 		case 0x2b:
882 			if (m_version >= 9)
883 			{
884 				if (!USEIMM)
885 				{
886 					util::stream_format(stream, "flushw");
887 					return 4 | SUPPORTED;
888 				}
889 			}
890 			else
891 			{
892 				util::stream_format(stream, "%-*s%%tbr,%s", m_op_field_width, "rd", REG_NAMES[RD]);
893 				return 4 | SUPPORTED;
894 			}
895 			break;
896 		case 0x2c:
897 			return dasm_move_cond(stream, pc, op);
898 		case 0x2e:
899 			if ((m_version >= 9) && (RS1 == 0))
900 			{
901 				if (USEIMM) util::stream_format(stream, "%-*s%d,%s", m_op_field_width, "popc", SIMM13, REG_NAMES[RD]);
902 				else        util::stream_format(stream, "%-*s%s,%s", m_op_field_width, "popc", REG_NAMES[RS2], REG_NAMES[RD]);
903 				return 4 | SUPPORTED;
904 			}
905 			break;
906 		case 0x2f:
907 			return dasm_move_reg_cond(stream, pc, op);
908 		case 0x30:
909 			return dasm_write_state_reg(stream, pc, op);
910 		case 0x31:
911 			if (m_version >= 9)
912 			{
913 				switch (RD)
914 				{
915 				case 0:
916 					util::stream_format(stream, "saved");
917 					return 4 | SUPPORTED;
918 				case 1:
919 					util::stream_format(stream, "restored");
920 					return 4 | SUPPORTED;
921 				}
922 			}
923 			else
924 			{
925 				if (RS1 == 0)
926 				{
927 					if (USEIMM) util::stream_format(stream, "%-*s0x%08x,%%psr", m_op_field_width, "mov", SIMM13);
928 					else        util::stream_format(stream, "%-*s%s,%%psr", m_op_field_width, "mov", REG_NAMES[RS2]);
929 				}
930 				else
931 				{
932 					if (USEIMM) util::stream_format(stream, "%-*s%s,0x%08x,%%psr", m_op_field_width, "wr", REG_NAMES[RS1], SIMM13);
933 					else        util::stream_format(stream, "%-*s%s,%s,%%psr", m_op_field_width, "wr", REG_NAMES[RS1], REG_NAMES[RS2]);
934 				}
935 				return 4 | SUPPORTED;
936 			}
937 			break;
938 		case 0x32:
939 			if (m_version >= 9)
940 			{
941 				if (V9_PRIV_REG_NAMES[RD])
942 				{
943 					// FIXME: this disassembles wrpr to %fq and %ver which are actually illegal
944 					if (!USEIMM)        util::stream_format(stream, "%-*s%s,%s,%s", m_op_field_width, "wrpr", REG_NAMES[RS1], REG_NAMES[RS2], V9_PRIV_REG_NAMES[RD]);
945 					else if (RS1 == 0)  util::stream_format(stream, "%-*s0x%08x,%s", m_op_field_width, "wrpr", SIMM13, V9_PRIV_REG_NAMES[RD]);
946 					else                util::stream_format(stream, "%-*s%s,0x%08x,%s", m_op_field_width, "wrpr", REG_NAMES[RS1], SIMM13, V9_PRIV_REG_NAMES[RD]);
947 					return 4 | SUPPORTED;
948 				}
949 			}
950 			else
951 			{
952 				if (RS1 == 0)
953 				{
954 					if (USEIMM) util::stream_format(stream, "%-*s0x%08x,%%wim", m_op_field_width, "mov", SIMM13);
955 					else        util::stream_format(stream, "%-*s%s,%%wim", m_op_field_width, "mov", REG_NAMES[RS2]);
956 				}
957 				else
958 				{
959 					if (USEIMM) util::stream_format(stream, "%-*s%s,0x%08x,%%wim", m_op_field_width, "wr", REG_NAMES[RS1], SIMM13);
960 					else        util::stream_format(stream, "%-*s%s,%s,%%wim", m_op_field_width, "wr", REG_NAMES[RS1], REG_NAMES[RS2]);
961 				}
962 				return 4 | SUPPORTED;
963 			}
964 			break;
965 		case 0x33:
966 			if (m_version <= 8)
967 			{
968 				if (RS1 == 0)
969 				{
970 					if (USEIMM) util::stream_format(stream, "%-*s0x%08x,%%tbr", m_op_field_width, "mov", SIMM13);
971 					else        util::stream_format(stream, "%-*s%s,%%tbr", m_op_field_width, "mov", REG_NAMES[RS2]);
972 				}
973 				else
974 				{
975 					if (USEIMM) util::stream_format(stream, "%-*s%s,0x%08x,%%tbr", m_op_field_width, "wr", REG_NAMES[RS1], SIMM13);
976 					else        util::stream_format(stream, "%-*s%s,%s,%%tbr", m_op_field_width, "wr", REG_NAMES[RS1], REG_NAMES[RS2]);
977 				}
978 				return 4 | SUPPORTED;
979 			}
980 			break;
981 		case 0x34:
982 			return dasm_fpop1(stream, pc, op);
983 		case 0x35:
984 			return dasm_fpop2(stream, pc, op);
985 		case 0x36:
986 			return dasm_impdep1(stream, pc, op);
987 		case 0x37:
988 			// TODO: hooks for IMPDEP2/CPop2
989 			break;
990 		case 0x38:
991 			return dasm_jmpl(stream, pc, op);
992 		case 0x39:
993 			return dasm_return(stream, pc, op);
994 		case 0x3a:
995 			return dasm_tcc(stream, pc, op);
996 		case 0x3b:
997 			if (m_version >= 8)
998 			{
999 				util::stream_format(stream, "%-*s", m_op_field_width, "flush");
1000 				dasm_address(stream, op);
1001 				return 4 | SUPPORTED;
1002 			}
1003 			break;
1004 		case 0x3c:
1005 			if (!USEIMM && (RS1 == RS2) && (RS2 == RD) && (RD == 0))
1006 			{
1007 				util::stream_format(stream, "save");
1008 				return 4 | SUPPORTED;
1009 			}
1010 			break;
1011 		case 0x3d:
1012 			if (!USEIMM && (RS1 == RS2) && (RS2 == RD) && (RD == 0))
1013 			{
1014 				util::stream_format(stream, "restore");
1015 				return 4 | SUPPORTED;
1016 			}
1017 			break;
1018 		case 0x3e:
1019 			if ((m_version >= 9) & ((op & 0x7ffff) == 0))
1020 			{
1021 				switch (RD)
1022 				{
1023 				case 0: util::stream_format(stream, "done"); return 4 | SUPPORTED;
1024 				case 1: util::stream_format(stream, "retry"); return 4 | SUPPORTED;
1025 				}
1026 			}
1027 			break;
1028 		}
1029 		{
1030 			const auto it(m_int_op_desc.find(OP3));
1031 			if (it != m_int_op_desc.end())
1032 			{
1033 				if (it->second.g0_synth && (RD == 0))
1034 				{
1035 					if (!USEIMM)
1036 						util::stream_format(stream, "%-*s%s,%s", m_op_field_width, it->second.g0_synth, REG_NAMES[RS1], REG_NAMES[RS2]);
1037 					else if (it->second.hex_imm)
1038 						util::stream_format(stream, "%-*s%s,0x%08x", m_op_field_width, it->second.g0_synth, REG_NAMES[RS1], SIMM13);
1039 					else
1040 						util::stream_format(stream, "%-*s%s,%d", m_op_field_width, it->second.g0_synth, REG_NAMES[RS1], SIMM13);
1041 				}
1042 				else
1043 				{
1044 					if (!USEIMM)
1045 						util::stream_format(stream, "%-*s%s,%s,%s", m_op_field_width, it->second.mnemonic, REG_NAMES[RS1], REG_NAMES[RS2], REG_NAMES[RD]);
1046 					else if (it->second.hex_imm)
1047 						util::stream_format(stream, "%-*s%s,0x%08x,%s", m_op_field_width, it->second.mnemonic, REG_NAMES[RS1], SIMM13, REG_NAMES[RD]);
1048 					else
1049 						util::stream_format(stream, "%-*s%s,%d,%s", m_op_field_width, it->second.mnemonic, REG_NAMES[RS1], SIMM13, REG_NAMES[RD]);
1050 				}
1051 				return 4 | SUPPORTED;
1052 			}
1053 		}
1054 		break;
1055 	case 3:
1056 		return dasm_ldst(stream, pc, op);
1057 	}
1058 	return dasm_invalid(stream, pc, op);
1059 }
1060 
1061 
dasm_invalid(std::ostream & stream,offs_t pc,uint32_t op) const1062 offs_t sparc_disassembler::dasm_invalid(std::ostream &stream, offs_t pc, uint32_t op) const
1063 {
1064 	util::stream_format(stream, "%-*s0x%08x ! ", m_op_field_width, ".word", op);
1065 	if (OP == 0)
1066 	{
1067 		util::stream_format(stream, "op=%x op2=%01x a=%01x cond=%01x", OP, OP2, ANNUL, COND);
1068 	}
1069 	else if ((OP == 2) && ((OP3 == 0x34) || (OP3 == 0x35)))
1070 	{
1071 		util::stream_format(stream, "FPop%d opf=%03x rd=%d rs1=%d rs2=%d", 1 + (OP3 & 1), OPF, RD, RS1, RS2);
1072 	}
1073 	else if ((OP == 2) && ((OP3 == 0x36) || (OP3 == 0x37)))
1074 	{
1075 		if (m_version >= 9)
1076 			util::stream_format(stream, "IMPDEP%d impl-dep=%02x impl-dep=%05x", 1 + (OP3 & 1), RD, op & 0x7ffff);
1077 		else
1078 			util::stream_format(stream, "CPop%d opf=%03x rd=%d rs1=%d rs2=%d", 1 + (OP3 & 1), OPC, RD, RS1, RS2);
1079 	}
1080 	else
1081 	{
1082 		util::stream_format(stream, "op=%x op3=%02x i=%01x rd=%d", OP, OP3, USEIMM, RD);
1083 	}
1084 	return 4 | SUPPORTED;
1085 }
1086 
1087 
dasm_branch(std::ostream & stream,offs_t pc,uint32_t op) const1088 offs_t sparc_disassembler::dasm_branch(std::ostream &stream, offs_t pc, uint32_t op) const
1089 {
1090 	const std::streampos start_position(stream.tellp());
1091 	const branch_desc &desc(m_branch_desc[OP2]);
1092 	const char * const mnemonic(desc.mnemonic[COND]);
1093 	if (!mnemonic || (desc.use_cc && !desc.reg_cc[BRCC])) return dasm_invalid(stream, pc, op);
1094 
1095 	util::stream_format(stream, "%s%s%s", mnemonic, ANNUL ? ",a" : "", (desc.use_pred && !PRED) ? ",pn" : "");
1096 	pad_op_field(stream, start_position);
1097 	if (desc.use_cc) util::stream_format(stream, "%s,", desc.reg_cc[BRCC]);
1098 	if (OP2 == 3) util::stream_format(stream, "%s,", REG_NAMES[RS1]);
1099 	const int32_t disp(desc.get_disp(op));
1100 	util::stream_format(stream, "%%pc%c0x%0*x ! 0x%08x", (disp < 0) ? '-' : '+', desc.disp_width, std::abs(disp), pc + disp);
1101 	//const char * const comment(desc.get_comment ? desc.get_comment(m_config, desc.use_cc, pc, op) : nullptr);
1102 	//if (comment) util::stream_format(stream, " - %s", comment);
1103 
1104 	return 4 | SUPPORTED;
1105 }
1106 
1107 
dasm_shift(std::ostream & stream,offs_t pc,uint32_t op,const char * mnemonic,const char * mnemonicx,const char * mnemonicx0) const1108 offs_t sparc_disassembler::dasm_shift(std::ostream &stream, offs_t pc, uint32_t op, const char *mnemonic, const char *mnemonicx, const char *mnemonicx0) const
1109 {
1110 	if ((m_version >= 9) && USEEXT)
1111 	{
1112 		if (USEIMM)
1113 			util::stream_format(stream, "%-*s%s,%d,%s", m_op_field_width, mnemonicx, REG_NAMES[RS1], SHCNT64, REG_NAMES[RD]);
1114 		else if (!mnemonicx0 || (RS2 != 0))
1115 			util::stream_format(stream, "%-*s%s,%s,%s", m_op_field_width, mnemonicx, REG_NAMES[RS1], REG_NAMES[RS2], REG_NAMES[RD]);
1116 		else if (RS1 == RD)
1117 			util::stream_format(stream, "%-*s%s", m_op_field_width, mnemonicx0, REG_NAMES[RD]);
1118 		else
1119 			util::stream_format(stream, "%-*s%s,%s", m_op_field_width, mnemonicx0, REG_NAMES[RS1], REG_NAMES[RD]);
1120 	}
1121 	else if (USEIMM)
1122 	{
1123 		util::stream_format(stream, "%-*s%s,%d,%s", m_op_field_width, mnemonic, REG_NAMES[RS1], SHCNT32, REG_NAMES[RD]);
1124 	}
1125 	else
1126 	{
1127 		util::stream_format(stream, "%-*s%s,%s,%s", m_op_field_width, mnemonic, REG_NAMES[RS1], REG_NAMES[RS2], REG_NAMES[RD]);
1128 	}
1129 	return 4 | SUPPORTED;
1130 }
1131 
1132 
dasm_read_state_reg(std::ostream & stream,offs_t pc,uint32_t op) const1133 offs_t sparc_disassembler::dasm_read_state_reg(std::ostream &stream, offs_t pc, uint32_t op) const
1134 {
1135 	if (RS1 == 0)
1136 	{
1137 		util::stream_format(stream, "%-*s%%y,%s", m_op_field_width, "rd", REG_NAMES[RD]);
1138 		return 4 | SUPPORTED;
1139 	}
1140 	else if ((m_version == 8) || ((m_version >= 9) && !USEIMM))
1141 	{
1142 		if (!USEIMM && (RS1 == 15) && (RD == 0))
1143 		{
1144 			util::stream_format(stream, "stbar");
1145 			return 4 | SUPPORTED;
1146 		}
1147 		else
1148 		{
1149 			const auto it(m_state_reg_desc.find(RS1));
1150 			if ((it == m_state_reg_desc.end()) || !it->second.reserved)
1151 			{
1152 				if ((it != m_state_reg_desc.end()) && it->second.read_name)
1153 					util::stream_format(stream, "%-*s%s,%s", m_op_field_width, "rd", it->second.read_name, REG_NAMES[RD]);
1154 				else
1155 					util::stream_format(stream, "%-*s%%asr%d,%s ! %s", m_op_field_width, "rd", RS1, REG_NAMES[RD], (RS1 < 16) ? "reserved" : "implementation-dependent");
1156 				return 4 | SUPPORTED;
1157 			}
1158 		}
1159 	}
1160 	else if ((m_version >= 9) && USEIMM && (RS1 == 15) && (RD == 0))
1161 	{
1162 		util::stream_format(stream, "%-*s", m_op_field_width, "membar");
1163 		uint32_t mask(MMASK | (CMASK << 4));
1164 		if (mask == 0) util::stream_format(stream, "0");
1165 		if (mask & 1) util::stream_format(stream, "#LoadLoad%s", (mask >> 1) ? "|" : "");
1166 		mask >>= 1;
1167 		if (mask & 1) util::stream_format(stream, "#StoreLoad%s", (mask >> 1) ? "|" : "");
1168 		mask >>= 1;
1169 		if (mask & 1) util::stream_format(stream, "#LoadStore%s", (mask >> 1) ? "|" : "");
1170 		mask >>= 1;
1171 		if (mask & 1) util::stream_format(stream, "#StoreStore%s", (mask >> 1) ? "|" : "");
1172 		mask >>= 1;
1173 		if (mask & 1) util::stream_format(stream, "#Lookaside%s", (mask >> 1) ? "|" : "");
1174 		mask >>= 1;
1175 		if (mask & 1) util::stream_format(stream, "#MemIssue%s", (mask >> 1) ? "|" : "");
1176 		mask >>= 1;
1177 		if (mask & 1) util::stream_format(stream, "#Sync");
1178 		return 4 | SUPPORTED;
1179 	}
1180 	return dasm_invalid(stream, pc, op);
1181 }
1182 
1183 
dasm_write_state_reg(std::ostream & stream,offs_t pc,uint32_t op) const1184 offs_t sparc_disassembler::dasm_write_state_reg(std::ostream &stream, offs_t pc, uint32_t op) const
1185 {
1186 	if (RD == 0)
1187 	{
1188 		if (RS1 == 0)
1189 		{
1190 			if (USEIMM) util::stream_format(stream, "%-*s%d,%%y", m_op_field_width, "mov", SIMM13);
1191 			else        util::stream_format(stream, "%-*s%s,%%y", m_op_field_width, "mov", REG_NAMES[RS2]);
1192 		}
1193 		else
1194 		{
1195 			if (USEIMM) util::stream_format(stream, "%-*s%s,%08x,%%y", m_op_field_width, "wr", REG_NAMES[RS1], SIMM13);
1196 			else        util::stream_format(stream, "%-*s%s,%s,%%y", m_op_field_width, "wr", REG_NAMES[RS1], REG_NAMES[RS2]);
1197 		}
1198 		return 4 | SUPPORTED;
1199 	}
1200 	else if (m_version >= 8)
1201 	{
1202 		if ((m_version >= 9) && USEIMM && (RS1 == 0) && (RD == 15))
1203 		{
1204 			util::stream_format(stream, "%-*s%d", m_op_field_width, "sir", SIMM13);
1205 			return 4 | SUPPORTED;
1206 		}
1207 		else
1208 		{
1209 			const auto it(m_state_reg_desc.find(RD));
1210 			if ((it == m_state_reg_desc.end()) || !it->second.reserved)
1211 			{
1212 				if ((it != m_state_reg_desc.end()) && it->second.write_name)
1213 				{
1214 					if (RS1 == 0)
1215 					{
1216 						if (USEIMM) util::stream_format(stream, "%-*s%d,%s", m_op_field_width, "mov", SIMM13, it->second.write_name);
1217 						else        util::stream_format(stream, "%-*s%s,%s", m_op_field_width, "mov", REG_NAMES[RS2], it->second.write_name);
1218 					}
1219 					else
1220 					{
1221 						if (USEIMM) util::stream_format(stream, "%-*s%s,%08x,%s", m_op_field_width, "wr", REG_NAMES[RS1], SIMM13, it->second.write_name);
1222 						else        util::stream_format(stream, "%-*s%s,%s,%s", m_op_field_width, "wr", REG_NAMES[RS1], REG_NAMES[RS2], it->second.write_name);
1223 					}
1224 				}
1225 				else
1226 				{
1227 					const char * const comment((RD < 16) ? "reserved" : "implementation-dependent");
1228 					if (RS1 == 0)
1229 					{
1230 						if (USEIMM) util::stream_format(stream, "%-*s%d,%%asr%d ! %s", m_op_field_width, "mov", SIMM13, RD, comment);
1231 						else        util::stream_format(stream, "%-*s%s,%%asr%d ! %s", m_op_field_width, "mov", REG_NAMES[RS2], RD, comment);
1232 					}
1233 					else
1234 					{
1235 						if (USEIMM) util::stream_format(stream, "%-*s%s,%08x,%%asr%d ! %s", m_op_field_width, "wr", REG_NAMES[RS1], SIMM13, RD, comment);
1236 						else        util::stream_format(stream, "%-*s%s,%s,%%asr%d ! %s", m_op_field_width, "wr", REG_NAMES[RS1], REG_NAMES[RS2], RD, comment);
1237 					}
1238 				}
1239 				return 4 | SUPPORTED;
1240 			}
1241 		}
1242 	}
1243 	return dasm_invalid(stream, pc, op);
1244 }
1245 
1246 
dasm_move_cond(std::ostream & stream,offs_t pc,uint32_t op) const1247 offs_t sparc_disassembler::dasm_move_cond(std::ostream &stream, offs_t pc, uint32_t op) const
1248 {
1249 	if ((m_version < 9) || !MOVCC_CC_NAMES[MOVCC]) return dasm_invalid(stream, pc, op);
1250 
1251 	const std::streampos start_position(stream.tellp());
1252 	util::stream_format(stream, "mov%s", MOVCC_COND_NAMES[MOVCOND | ((MOVCC << 2) & 16)]);
1253 	pad_op_field(stream, start_position);
1254 	if (USEIMM)
1255 		util::stream_format(stream, "%s,%d,%s", MOVCC_CC_NAMES[MOVCC], SIMM11, REG_NAMES[RD]);
1256 	else
1257 		util::stream_format(stream, "%s,%s,%s", MOVCC_CC_NAMES[MOVCC], REG_NAMES[RS2], REG_NAMES[RD]);
1258 
1259 	return 4 | SUPPORTED;
1260 }
1261 
dasm_move_reg_cond(std::ostream & stream,offs_t pc,uint32_t op) const1262 offs_t sparc_disassembler::dasm_move_reg_cond(std::ostream &stream, offs_t pc, uint32_t op) const
1263 {
1264 	if ((m_version < 9) || !MOVE_INT_COND_MNEMONICS[RCOND]) return dasm_invalid(stream, pc, op);
1265 
1266 	if (USEIMM)
1267 		util::stream_format(stream, "%-*s%s,%d,%s", m_op_field_width, MOVE_INT_COND_MNEMONICS[RCOND], REG_NAMES[RS1], SIMM10, REG_NAMES[RD]);
1268 	else
1269 		util::stream_format(stream, "%-*s%s,%s,%s", m_op_field_width, MOVE_INT_COND_MNEMONICS[RCOND], REG_NAMES[RS1], REG_NAMES[RS2], REG_NAMES[RD]);
1270 
1271 	return 4 | SUPPORTED;
1272 }
1273 
1274 
dasm_fpop1(std::ostream & stream,offs_t pc,uint32_t op) const1275 offs_t sparc_disassembler::dasm_fpop1(std::ostream &stream, offs_t pc, uint32_t op) const
1276 {
1277 	const auto it(m_fpop1_desc.find(OPF));
1278 	if (it == m_fpop1_desc.end()) return dasm_invalid(stream, pc, op);
1279 
1280 	if (it->second.three_op)
1281 		util::stream_format(stream, "%-*s%%f%d,%%f%d,%%f%d", m_op_field_width, it->second.mnemonic, freg(RS1, it->second.rs1_shift), freg(RS2, it->second.rs2_shift), freg(RD, it->second.rd_shift));
1282 	else
1283 		util::stream_format(stream, "%-*s%%f%d,%%f%d", m_op_field_width, it->second.mnemonic, freg(RS2, it->second.rs2_shift), freg(RD, it->second.rd_shift));
1284 	return 4 | SUPPORTED;
1285 }
1286 
1287 
dasm_fpop2(std::ostream & stream,offs_t pc,uint32_t op) const1288 offs_t sparc_disassembler::dasm_fpop2(std::ostream &stream, offs_t pc, uint32_t op) const
1289 {
1290 	// Move Floating-Point Register on Condition
1291 	if ((m_version >= 9) && (((op >> 18) & 1) == 0) && MOVCC_CC_NAMES[OPFCC])
1292 	{
1293 		const char *mnemonic;
1294 		bool shift;
1295 		switch (OPFLOW)
1296 		{
1297 		case 1:  mnemonic = "fmovs"; shift = false; break;
1298 		case 2:  mnemonic = "fmovd"; shift = true; break;
1299 		case 3:  mnemonic = "fmovq"; shift = true; break;
1300 		default: mnemonic = nullptr; shift = false; break;
1301 		}
1302 		if (mnemonic)
1303 		{
1304 			const std::streampos start_position(stream.tellp());
1305 			util::stream_format(stream, "%s%s", mnemonic, MOVCC_COND_NAMES[MOVCOND | ((OPFCC << 2) & 16)]);
1306 			pad_op_field(stream, start_position);
1307 			util::stream_format(stream, "%s,%%f%d,%%f%d", MOVCC_CC_NAMES[OPFCC], freg(RS2, shift), freg(RD, shift));
1308 			return 4 | SUPPORTED;
1309 		}
1310 	}
1311 
1312 	const auto it(m_fpop2_desc.find(OPF));
1313 	if (it != m_fpop2_desc.end())
1314 	{
1315 		if (m_version >= 9)
1316 		{
1317 			if (it->second.int_rs1)
1318 			{
1319 				util::stream_format(stream, "%-*s%s,%%f%d,%%f%d", m_op_field_width, it->second.mnemonic, REG_NAMES[RS1], freg(RS2, it->second.shift), freg(RD, it->second.shift));
1320 				return 4 | SUPPORTED;
1321 			}
1322 			else if (RD < 4)
1323 			{
1324 				util::stream_format(stream, "%-*s%%fcc%d,%%f%d,%%f%d", m_op_field_width, it->second.mnemonic, RD, freg(RS1, it->second.shift), freg(RS2, it->second.shift));
1325 				return 4 | SUPPORTED;
1326 			}
1327 		}
1328 		else if (!it->second.int_rs1)
1329 		{
1330 			util::stream_format(stream, "%-*s%%f%d,%%f%d", m_op_field_width, it->second.mnemonic, freg(RS1, it->second.shift), freg(RS2, it->second.shift));
1331 			return 4 | SUPPORTED;
1332 		}
1333 	}
1334 
1335 	return dasm_invalid(stream, pc, op);
1336 }
1337 
1338 
dasm_impdep1(std::ostream & stream,offs_t pc,uint32_t op) const1339 offs_t sparc_disassembler::dasm_impdep1(std::ostream &stream, offs_t pc, uint32_t op) const
1340 {
1341 	const auto it(m_vis_op_desc.find(OPF));
1342 	if (it != m_vis_op_desc.end())
1343 	{
1344 		util::stream_format(stream, "%-*s", m_op_field_width, it->second.mnemonic);
1345 		bool args(false);
1346 		if (it->second.collapse && !RS1)
1347 		{
1348 			dasm_vis_arg(stream, args, it->second.rs2, RS2);
1349 		}
1350 		else if (it->second.collapse && !RS2)
1351 		{
1352 			dasm_vis_arg(stream, args, it->second.rs1, RS1);
1353 		}
1354 		else
1355 		{
1356 			dasm_vis_arg(stream, args, it->second.rs1, RS1);
1357 			dasm_vis_arg(stream, args, it->second.rs2, RS2);
1358 		}
1359 		dasm_vis_arg(stream, args, it->second.rd, RD);
1360 		return 4 | SUPPORTED;
1361 	}
1362 
1363 	switch (OPF)
1364 	{
1365 	case 0x081:
1366 		if (m_vis_level >= vis_2)
1367 		{
1368 			util::stream_format(stream, "%-*s0x%x", m_op_field_width, "siam", IAMODE);
1369 			return 4 | SUPPORTED;
1370 		}
1371 		break;
1372 	case 0x151:
1373 	case 0x152:
1374 		if (m_vis_level >= vis_3)
1375 		{
1376 			const bool shift(OPF == 0x152);
1377 			util::stream_format(stream, "%-*s%%fcc%d,%%f%d,%%f%d", m_op_field_width, (shift) ? "flcmpd" : "flcmps", RD & 3, freg(RS1, shift), freg(RS2, shift));
1378 			return 4 | SUPPORTED;
1379 		}
1380 		break;
1381 	}
1382 
1383 	// TODO: driver hook for other kinds of coprocessor?
1384 
1385 	return dasm_invalid(stream, pc, op);
1386 }
1387 
1388 
dasm_jmpl(std::ostream & stream,offs_t pc,uint32_t op) const1389 offs_t sparc_disassembler::dasm_jmpl(std::ostream &stream, offs_t pc, uint32_t op) const
1390 {
1391 	if (USEIMM && (RD == 0) && ((RS1 == 15) || (RS1 == 31)) && (SIMM13 == 8))
1392 	{
1393 		util::stream_format(stream, (RS1 == 31) ? "ret" : "retl");
1394 	}
1395 	else
1396 	{
1397 		util::stream_format(stream, "%-*s", m_op_field_width, (RD == 0) ? "jmp" : (RD == 15) ? "call" : "jmpl");
1398 		dasm_address(stream, op);
1399 		if ((RD != 0) && (RD != 15))
1400 			util::stream_format(stream, ",%s", REG_NAMES[RD]);
1401 	}
1402 	return 4 | SUPPORTED;
1403 }
1404 
1405 
dasm_return(std::ostream & stream,offs_t pc,uint32_t op) const1406 offs_t sparc_disassembler::dasm_return(std::ostream &stream, offs_t pc, uint32_t op) const
1407 {
1408 	util::stream_format(stream, "%-*s", m_op_field_width, (m_version >= 9) ? "return" : "rett");
1409 	dasm_address(stream, op);
1410 	return 4 | SUPPORTED;
1411 }
1412 
1413 
dasm_tcc(std::ostream & stream,offs_t pc,uint32_t op) const1414 offs_t sparc_disassembler::dasm_tcc(std::ostream &stream, offs_t pc, uint32_t op) const
1415 {
1416 	static const char *const tcc_names[16] = {
1417 		"tn",   "te",   "tle",  "tl",   "tleu", "tcs",  "tneg", "tvs",
1418 		"ta",   "tne",  "tg",   "tge",  "tgu",  "tcc",  "tpos", "tvc"
1419 	};
1420 	static const char *const cc_names[4] = { "%icc", nullptr, "%xcc", nullptr };
1421 	const char *const mnemonic(tcc_names[COND]);
1422 	if (m_version >= 9)
1423 	{
1424 		const char *const cc(cc_names[TCCCC]);
1425 		if (!cc) return dasm_invalid(stream, pc, op);
1426 		util::stream_format(stream, "%-*s%s,", m_op_field_width, mnemonic, cc);
1427 	}
1428 	else
1429 	{
1430 		util::stream_format(stream, "%-*s", m_op_field_width, mnemonic);
1431 	}
1432 	if (USEIMM)
1433 	{
1434 		if (RS1 == 0)       util::stream_format(stream, "%d", IMM7);
1435 		else                util::stream_format(stream, "%s,%d", REG_NAMES[RS1], IMM7);
1436 	}
1437 	else
1438 	{
1439 		if (RS1 == 0)       util::stream_format(stream, "%s", REG_NAMES[RS2]);
1440 		else if (RS2 == 0)  util::stream_format(stream, "%s", REG_NAMES[RS1]);
1441 		else                util::stream_format(stream, "%s,%s", REG_NAMES[RS1], REG_NAMES[RS2]);
1442 	}
1443 	return 4 | SUPPORTED;
1444 }
1445 
1446 
dasm_ldst(std::ostream & stream,offs_t pc,uint32_t op) const1447 offs_t sparc_disassembler::dasm_ldst(std::ostream &stream, offs_t pc, uint32_t op) const
1448 {
1449 	if (m_version >= 9)
1450 	{
1451 		switch (OP3)
1452 		{
1453 		case 0x21: // Load floating-point state register
1454 			if ((RD == 0) || (RD == 1))
1455 			{
1456 				util::stream_format(stream, "%-*s[", m_op_field_width, (RD == 1) ? "ldx" : "ld");
1457 				dasm_address(stream, op);
1458 				util::stream_format(stream, "],%%fsr");
1459 				return 4 | SUPPORTED;
1460 			}
1461 			else if ((RD == 3) && (m_vis_level >= vis_3b))
1462 			{
1463 				util::stream_format(stream, "%-*s[", m_op_field_width, "ldx");
1464 				dasm_address(stream, op);
1465 				util::stream_format(stream, "],%%efsr");
1466 			}
1467 			break;
1468 		case 0x25: // Store floating-point state register
1469 			if ((RD == 0) || (RD == 1))
1470 			{
1471 				util::stream_format(stream, "%-*s%%fsr,[", m_op_field_width, (RD == 1) ? "stx" : "st");
1472 				dasm_address(stream, op);
1473 				stream << ']';
1474 				return 4 | SUPPORTED;
1475 			}
1476 			break;
1477 		case 0x3c: // Compare and swap word in alternate space
1478 		case 0x3e: // Compare and swap doubleword in alternate space
1479 			{
1480 				bool print_asi(true);
1481 				const char *mnemonic((OP3 == 0x3e) ? "casxa" : "casa");
1482 				if (!USEIMM)
1483 				{
1484 					if (ASI == 0x80)
1485 					{
1486 						print_asi = false;
1487 						mnemonic = (OP3 == 0x3e) ? "casx" : "cas";
1488 					}
1489 					else if (ASI == 0x88)
1490 					{
1491 						print_asi = false;
1492 						mnemonic = (OP3 == 0x3e) ? "casxl" : "casl";
1493 					}
1494 				}
1495 				util::stream_format(stream, "%-*s[%s]", m_op_field_width, mnemonic, REG_NAMES[RS1]);
1496 				if (print_asi) dasm_asi(stream, op);
1497 				util::stream_format(stream, ",%s,%s", REG_NAMES[RS2], REG_NAMES[RD]);
1498 				if (print_asi) dasm_asi_comment(stream, op);
1499 			}
1500 			return 4 | SUPPORTED;
1501 		case 0x2d: // Prefetch data
1502 		case 0x3d: // Prefetch data from alternate space
1503 			{
1504 				util::stream_format(stream, "%-*s[", m_op_field_width, (OP3 == 0x3d) ? "prefetcha" : "prefetch");
1505 				dasm_address(stream, op);
1506 				stream << ']';
1507 				if (OP3 == 0x3d) dasm_asi(stream, op);
1508 				const auto it(m_prftch_desc.find(RD));
1509 				if (it != m_prftch_desc.end())  util::stream_format(stream, ",%s", it->second.name);
1510 				else                            util::stream_format(stream, ",0x%02x", RD);
1511 				if (OP3 == 0x3d) dasm_asi_comment(stream, op);
1512 			}
1513 			return 4 | SUPPORTED;
1514 		}
1515 	}
1516 	else
1517 	{
1518 		switch (OP3)
1519 		{
1520 		case 0x21: // Load Floating-point State Register
1521 		case 0x31: // Load Coprocessor State Register
1522 			util::stream_format(stream, "%-*s[", m_op_field_width, "ld");
1523 			dasm_address(stream, op);
1524 			util::stream_format(stream, "],%%%csr", (OP3 == 0x31) ? 'c' : 'f');
1525 			return 4 | SUPPORTED;
1526 		case 0x25: // Store Floating-point State Register
1527 		case 0x35: // Store Coprocessor State Register
1528 			util::stream_format(stream, "%-*s%%%csr,[", m_op_field_width, "st", (OP3 == 0x35) ? 'c' : 'f');
1529 			dasm_address(stream, op);
1530 			stream << ']';
1531 			return 4 | SUPPORTED;
1532 		case 0x26: // Store Floating-point deferred-trap Queue
1533 		case 0x36: // Store Coprocessor deferred-trap Queue
1534 			util::stream_format(stream, "%-*s%%%cq,[", m_op_field_width, "std", (OP3 == 0x36) ? 'c' : 'f');
1535 			dasm_address(stream, op);
1536 			stream << ']';
1537 			return 4 | SUPPORTED;
1538 		}
1539 	}
1540 
1541 	const auto it(m_ldst_desc.find(OP3));
1542 	if (it == m_ldst_desc.end())
1543 		return dasm_invalid(stream, pc, op);
1544 
1545 	if (it->second.alternate && USEIMM && (m_version < 9))
1546 		return dasm_invalid(stream, pc, op);
1547 
1548 	if (it->second.g0_synth && (RD == 0))
1549 	{
1550 		util::stream_format(stream, "%-*s[", m_op_field_width, it->second.g0_synth);
1551 		dasm_address(stream, op);
1552 		stream << ']';
1553 		if (it->second.alternate)
1554 		{
1555 			dasm_asi(stream, op);
1556 			dasm_asi_comment(stream, op);
1557 		}
1558 	}
1559 	else
1560 	{
1561 		util::stream_format(stream, "%-*s", m_op_field_width, it->second.mnemonic);
1562 		if (it->second.rd_first)
1563 		{
1564 			if (it->second.rd_alt_reg)  util::stream_format(stream, "%%%c%d,", it->second.rd_alt_reg, freg(RD, it->second.rd_shift));
1565 			else                        util::stream_format(stream, "%s,", REG_NAMES[RD]);
1566 		}
1567 		stream << '[';
1568 		dasm_address(stream, op);
1569 		stream << ']';
1570 		if (it->second.alternate) dasm_asi(stream, op);
1571 		if (!it->second.rd_first)
1572 		{
1573 			if (it->second.rd_alt_reg)  util::stream_format(stream, ",%%%c%d", it->second.rd_alt_reg, freg(RD, it->second.rd_shift));
1574 			else                        util::stream_format(stream, ",%s", REG_NAMES[RD]);
1575 		}
1576 		if (it->second.alternate) dasm_asi_comment(stream, op);
1577 	}
1578 	return 4 | SUPPORTED;
1579 }
1580 
1581 
dasm_address(std::ostream & stream,uint32_t op) const1582 void sparc_disassembler::dasm_address(std::ostream &stream, uint32_t op) const
1583 {
1584 	if (USEIMM)
1585 	{
1586 		if (RS1 == 0)       util::stream_format(stream, "0x%08x", SIMM13);
1587 		else                util::stream_format(stream, "%s%c0x%04x", REG_NAMES[RS1], (SIMM13 < 0) ? '-' : '+', std::abs(SIMM13));
1588 	}
1589 	else
1590 	{
1591 		if (RS1 == 0)       util::stream_format(stream, "%s", REG_NAMES[RS2]);
1592 		else if (RS2 == 0)  util::stream_format(stream, "%s", REG_NAMES[RS1]);
1593 		else                util::stream_format(stream, "%s+%s", REG_NAMES[RS1], REG_NAMES[RS2]);
1594 	}
1595 }
1596 
1597 
dasm_asi(std::ostream & stream,uint32_t op) const1598 void sparc_disassembler::dasm_asi(std::ostream &stream, uint32_t op) const
1599 {
1600 	if (USEIMM)
1601 	{
1602 		util::stream_format(stream, "%%asi");
1603 	}
1604 	else
1605 	{
1606 		const auto it(m_asi_desc.find(ASI));
1607 		if ((it != m_asi_desc.end()) && it->second.name)
1608 			util::stream_format(stream, "%s", it->second.name);
1609 		else
1610 			util::stream_format(stream, "0x%02x", ASI);
1611 	}
1612 }
1613 
1614 
dasm_asi_comment(std::ostream & stream,uint32_t op) const1615 void sparc_disassembler::dasm_asi_comment(std::ostream &stream, uint32_t op) const
1616 {
1617 	if (!USEIMM)
1618 	{
1619 		const auto it(m_asi_desc.find(ASI));
1620 		if ((it != m_asi_desc.end()) && it->second.desc)
1621 			util::stream_format(stream, " ! %s", it->second.desc);
1622 	}
1623 }
1624 
1625 
dasm_vis_arg(std::ostream & stream,bool & args,vis_op_desc::arg fmt,uint32_t reg) const1626 void sparc_disassembler::dasm_vis_arg(std::ostream &stream, bool &args, vis_op_desc::arg fmt, uint32_t reg) const
1627 {
1628 	switch (fmt)
1629 	{
1630 	case vis_op_desc::X:
1631 		break;
1632 	case vis_op_desc::R:
1633 		util::stream_format(stream, args ? ",%s" : "%s", REG_NAMES[reg]);
1634 		args = true;
1635 		break;
1636 	case vis_op_desc::Fs:
1637 	case vis_op_desc::Fd:
1638 		util::stream_format(stream, args ? ",%%f%d" : "%%f%d", freg(reg, (fmt == vis_op_desc::Fd)));
1639 		args = true;
1640 		break;
1641 	};
1642 }
1643