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 ¶ms)
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