Commit | Line | Data |
---|---|---|
3d3d428f | 1 | /* Disassembler code for CR16. |
b3adc24a | 2 | Copyright (C) 2007-2020 Free Software Foundation, Inc. |
3d3d428f NC |
3 | Contributed by M R Swami Reddy (MR.Swami.Reddy@nsc.com). |
4 | ||
5 | This file is part of GAS, GDB and the GNU binutils. | |
6 | ||
9b201bb5 NC |
7 | This program is free software; you can redistribute it and/or modify it |
8 | under the terms of the GNU General Public License as published by the | |
9 | Free Software Foundation; either version 3, or (at your option) | |
3d3d428f NC |
10 | any later version. |
11 | ||
12 | This program is distributed in the hope that it will be useful, but WITHOUT | |
13 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 | more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program; if not, write to the Free Software Foundation, | |
19 | Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ | |
20 | ||
3d3d428f | 21 | #include "sysdep.h" |
88c1242d | 22 | #include "disassemble.h" |
3d3d428f NC |
23 | #include "opcode/cr16.h" |
24 | #include "libiberty.h" | |
25 | ||
26 | /* String to print when opcode was not matched. */ | |
27 | #define ILLEGAL "illegal" | |
28 | /* Escape to 16-bit immediate. */ | |
29 | #define ESCAPE_16_BIT 0xB | |
30 | ||
31 | /* Extract 'n_bits' from 'a' starting from offset 'offs'. */ | |
2781f857 | 32 | #define EXTRACT(a, offs, n_bits) \ |
0ef562a4 | 33 | (((a) >> (offs)) & ((1ul << ((n_bits) - 1) << 1) - 1)) |
3d3d428f | 34 | |
0ef562a4 AM |
35 | /* Set Bit Mask - a mask to set all bits in a 32-bit word starting |
36 | from offset 'offs'. */ | |
37 | #define SBM(offs) ((1ul << 31 << 1) - (1ul << (offs))) | |
3d3d428f | 38 | |
3d3d428f NC |
39 | /* Structure to map valid 'cinv' instruction options. */ |
40 | ||
41 | typedef struct | |
42 | { | |
43 | /* Cinv printed string. */ | |
44 | char *istr; | |
45 | /* Value corresponding to the string. */ | |
46 | char *ostr; | |
47 | } | |
48 | cinv_entry; | |
49 | ||
50 | /* CR16 'cinv' options mapping. */ | |
e5d70d6b | 51 | static const cinv_entry cr16_cinvs[] = |
3d3d428f NC |
52 | { |
53 | {"cinv[i]", "cinv [i]"}, | |
54 | {"cinv[i,u]", "cinv [i,u]"}, | |
55 | {"cinv[d]", "cinv [d]"}, | |
56 | {"cinv[d,u]", "cinv [d,u]"}, | |
57 | {"cinv[d,i]", "cinv [d,i]"}, | |
58 | {"cinv[d,i,u]", "cinv [d,i,u]"} | |
59 | }; | |
60 | ||
61 | /* Number of valid 'cinv' instruction options. */ | |
62 | static int NUMCINVS = ARRAY_SIZE (cr16_cinvs); | |
63 | ||
64 | /* Enum to distinguish different registers argument types. */ | |
65 | typedef enum REG_ARG_TYPE | |
66 | { | |
67 | /* General purpose register (r<N>). */ | |
68 | REG_ARG = 0, | |
69 | /*Processor register */ | |
70 | P_ARG, | |
71 | } | |
72 | REG_ARG_TYPE; | |
73 | ||
74 | /* Current opcode table entry we're disassembling. */ | |
e5d70d6b | 75 | static const inst *instruction; |
3d3d428f | 76 | /* Current instruction we're disassembling. */ |
e5d70d6b | 77 | static ins cr16_currInsn; |
3d3d428f | 78 | /* The current instruction is read into 3 consecutive words. */ |
e5d70d6b | 79 | static wordU cr16_words[3]; |
3d3d428f | 80 | /* Contains all words in appropriate order. */ |
e5d70d6b | 81 | static ULONGLONG cr16_allWords; |
3d3d428f | 82 | /* Holds the current processed argument number. */ |
e5d70d6b | 83 | static int processing_argument_number; |
3d3d428f | 84 | /* Nonzero means a IMM4 instruction. */ |
e5d70d6b | 85 | static int imm4flag; |
3d3d428f NC |
86 | /* Nonzero means the instruction's original size is |
87 | incremented (escape sequence is used). */ | |
e5d70d6b | 88 | static int size_changed; |
3d3d428f NC |
89 | |
90 | ||
91 | /* Print the constant expression length. */ | |
92 | ||
93 | static char * | |
94 | print_exp_len (int size) | |
95 | { | |
96 | switch (size) | |
97 | { | |
98 | case 4: | |
99 | case 5: | |
100 | case 6: | |
101 | case 8: | |
102 | case 14: | |
103 | case 16: | |
104 | return ":s"; | |
105 | case 20: | |
106 | case 24: | |
107 | case 32: | |
108 | return ":m"; | |
109 | case 48: | |
110 | return ":l"; | |
111 | default: | |
112 | return ""; | |
113 | } | |
114 | } | |
115 | ||
116 | ||
117 | /* Retrieve the number of operands for the current assembled instruction. */ | |
118 | ||
119 | static int | |
120 | get_number_of_operands (void) | |
121 | { | |
122 | int i; | |
123 | ||
124 | for (i = 0; instruction->operands[i].op_type && i < MAX_OPERANDS; i++) | |
125 | ; | |
126 | ||
127 | return i; | |
128 | } | |
129 | ||
130 | /* Return the bit size for a given operand. */ | |
131 | ||
132 | static int | |
133 | getbits (operand_type op) | |
134 | { | |
135 | if (op < MAX_OPRD) | |
136 | return cr16_optab[op].bit_size; | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
141 | /* Return the argument type of a given operand. */ | |
142 | ||
143 | static argtype | |
144 | getargtype (operand_type op) | |
145 | { | |
146 | if (op < MAX_OPRD) | |
147 | return cr16_optab[op].arg_type; | |
148 | ||
149 | return nullargs; | |
150 | } | |
151 | ||
152 | /* Given a 'CC' instruction constant operand, return its corresponding | |
153 | string. This routine is used when disassembling the 'CC' instruction. */ | |
154 | ||
155 | static char * | |
91d6fa6a | 156 | getccstring (unsigned cc_insn) |
3d3d428f | 157 | { |
91d6fa6a | 158 | return (char *) cr16_b_cond_tab[cc_insn]; |
3d3d428f NC |
159 | } |
160 | ||
161 | ||
162 | /* Given a 'cinv' instruction constant operand, return its corresponding | |
163 | string. This routine is used when disassembling the 'cinv' instruction. */ | |
164 | ||
165 | static char * | |
ddb341a7 | 166 | getcinvstring (const char *str) |
3d3d428f NC |
167 | { |
168 | const cinv_entry *cinv; | |
169 | ||
170 | for (cinv = cr16_cinvs; cinv < (cr16_cinvs + NUMCINVS); cinv++) | |
171 | if (strcmp (cinv->istr, str) == 0) | |
172 | return cinv->ostr; | |
173 | ||
174 | return ILLEGAL; | |
175 | } | |
176 | ||
177 | /* Given the trap index in dispatch table, return its name. | |
178 | This routine is used when disassembling the 'excp' instruction. */ | |
179 | ||
180 | static char * | |
91d6fa6a | 181 | gettrapstring (unsigned int trap_index) |
3d3d428f NC |
182 | { |
183 | const trap_entry *trap; | |
184 | ||
185 | for (trap = cr16_traps; trap < cr16_traps + NUMTRAPS; trap++) | |
91d6fa6a | 186 | if (trap->entry == trap_index) |
3d3d428f NC |
187 | return trap->name; |
188 | ||
189 | return ILLEGAL; | |
190 | } | |
191 | ||
192 | /* Given a register enum value, retrieve its name. */ | |
193 | ||
194 | static char * | |
195 | getregname (reg r) | |
196 | { | |
91d6fa6a | 197 | const reg_entry * regentry = cr16_regtab + r; |
3d3d428f | 198 | |
91d6fa6a | 199 | if (regentry->type != CR16_R_REGTYPE) |
3d3d428f NC |
200 | return ILLEGAL; |
201 | ||
91d6fa6a | 202 | return regentry->name; |
3d3d428f NC |
203 | } |
204 | ||
205 | /* Given a register pair enum value, retrieve its name. */ | |
206 | ||
207 | static char * | |
208 | getregpname (reg r) | |
209 | { | |
91d6fa6a | 210 | const reg_entry * regentry = cr16_regptab + r; |
3d3d428f | 211 | |
91d6fa6a | 212 | if (regentry->type != CR16_RP_REGTYPE) |
3d3d428f NC |
213 | return ILLEGAL; |
214 | ||
91d6fa6a | 215 | return regentry->name; |
3d3d428f NC |
216 | } |
217 | ||
218 | /* Given a index register pair enum value, retrieve its name. */ | |
219 | ||
220 | static char * | |
221 | getidxregpname (reg r) | |
222 | { | |
91d6fa6a | 223 | const reg_entry * regentry; |
3d3d428f NC |
224 | |
225 | switch (r) | |
226 | { | |
227 | case 0: r = 0; break; | |
228 | case 1: r = 2; break; | |
229 | case 2: r = 4; break; | |
230 | case 3: r = 6; break; | |
231 | case 4: r = 8; break; | |
232 | case 5: r = 10; break; | |
233 | case 6: r = 3; break; | |
234 | case 7: r = 5; break; | |
235 | default: | |
236 | break; | |
237 | } | |
238 | ||
91d6fa6a | 239 | regentry = cr16_regptab + r; |
3d3d428f | 240 | |
91d6fa6a | 241 | if (regentry->type != CR16_RP_REGTYPE) |
3d3d428f NC |
242 | return ILLEGAL; |
243 | ||
91d6fa6a | 244 | return regentry->name; |
3d3d428f NC |
245 | } |
246 | ||
247 | /* Getting a processor register name. */ | |
248 | ||
249 | static char * | |
91d6fa6a | 250 | getprocregname (int reg_index) |
3d3d428f NC |
251 | { |
252 | const reg_entry *r; | |
253 | ||
254 | for (r = cr16_pregtab; r < cr16_pregtab + NUMPREGS; r++) | |
91d6fa6a | 255 | if (r->image == reg_index) |
3d3d428f NC |
256 | return r->name; |
257 | ||
258 | return "ILLEGAL REGISTER"; | |
259 | } | |
260 | ||
261 | /* Getting a processor register name - 32 bit size. */ | |
262 | ||
263 | static char * | |
91d6fa6a | 264 | getprocpregname (int reg_index) |
3d3d428f NC |
265 | { |
266 | const reg_entry *r; | |
267 | ||
268 | for (r = cr16_pregptab; r < cr16_pregptab + NUMPREGPS; r++) | |
91d6fa6a | 269 | if (r->image == reg_index) |
3d3d428f NC |
270 | return r->name; |
271 | ||
272 | return "ILLEGAL REGISTER"; | |
273 | } | |
274 | ||
bab4becb | 275 | /* START and END are relating 'cr16_allWords' struct, which is 48 bits size. |
3d3d428f NC |
276 | |
277 | START|--------|END | |
278 | +---------+---------+---------+---------+ | |
279 | | | V | A | L | | |
280 | +---------+---------+---------+---------+ | |
281 | 0 16 32 48 | |
282 | words [0] [1] [2] */ | |
283 | ||
2781f857 | 284 | static inline dwordU |
3d3d428f NC |
285 | makelongparameter (ULONGLONG val, int start, int end) |
286 | { | |
2781f857 | 287 | return EXTRACT (val, 48 - end, end - start); |
3d3d428f NC |
288 | } |
289 | ||
290 | /* Build a mask of the instruction's 'constant' opcode, | |
291 | based on the instruction's printing flags. */ | |
292 | ||
293 | static unsigned long | |
294 | build_mask (void) | |
295 | { | |
296 | unsigned long mask = SBM (instruction->match_bits); | |
3ce6fddb NC |
297 | |
298 | /* Adjust mask for bcond with 32-bit size instruction. */ | |
299 | if ((IS_INSN_MNEMONIC("b") && instruction->size == 2)) | |
300 | mask = 0xff0f0000; | |
301 | ||
3d3d428f NC |
302 | return mask; |
303 | } | |
304 | ||
305 | /* Search for a matching opcode. Return 1 for success, 0 for failure. */ | |
306 | ||
bab4becb | 307 | int |
73335eae | 308 | cr16_match_opcode (void) |
3d3d428f NC |
309 | { |
310 | unsigned long mask; | |
f1a133ff | 311 | /* The instruction 'constant' opcode doesn't exceed 32 bits. */ |
5f57d4ec | 312 | unsigned long doubleWord = cr16_words[1] + ((unsigned) cr16_words[0] << 16); |
3d3d428f NC |
313 | |
314 | /* Start searching from end of instruction table. */ | |
315 | instruction = &cr16_instruction[NUMOPCODES - 2]; | |
316 | ||
317 | /* Loop over instruction table until a full match is found. */ | |
318 | while (instruction >= cr16_instruction) | |
319 | { | |
320 | mask = build_mask (); | |
59b098c9 | 321 | |
3d3d428f | 322 | if ((doubleWord & mask) == BIN (instruction->match, |
2781f857 AM |
323 | instruction->match_bits)) |
324 | return 1; | |
3d3d428f | 325 | else |
2781f857 | 326 | instruction--; |
3d3d428f NC |
327 | } |
328 | return 0; | |
329 | } | |
330 | ||
331 | /* Set the proper parameter value for different type of arguments. */ | |
332 | ||
333 | static void | |
334 | make_argument (argument * a, int start_bits) | |
335 | { | |
336 | int inst_bit_size; | |
2781f857 | 337 | dwordU p; |
3d3d428f NC |
338 | |
339 | if ((instruction->size == 3) && a->size >= 16) | |
340 | inst_bit_size = 48; | |
341 | else | |
342 | inst_bit_size = 32; | |
343 | ||
344 | switch (a->type) | |
345 | { | |
346 | case arg_r: | |
43e65147 | 347 | p = makelongparameter (cr16_allWords, |
bab4becb NC |
348 | inst_bit_size - (start_bits + a->size), |
349 | inst_bit_size - start_bits); | |
2781f857 | 350 | a->r = p; |
3d3d428f NC |
351 | break; |
352 | ||
353 | case arg_rp: | |
bab4becb NC |
354 | p = makelongparameter (cr16_allWords, |
355 | inst_bit_size - (start_bits + a->size), | |
356 | inst_bit_size - start_bits); | |
2781f857 | 357 | a->rp = p; |
3d3d428f NC |
358 | break; |
359 | ||
360 | case arg_pr: | |
bab4becb NC |
361 | p = makelongparameter (cr16_allWords, |
362 | inst_bit_size - (start_bits + a->size), | |
363 | inst_bit_size - start_bits); | |
2781f857 | 364 | a->pr = p; |
3d3d428f NC |
365 | break; |
366 | ||
367 | case arg_prp: | |
bab4becb NC |
368 | p = makelongparameter (cr16_allWords, |
369 | inst_bit_size - (start_bits + a->size), | |
370 | inst_bit_size - start_bits); | |
2781f857 | 371 | a->prp = p; |
3d3d428f NC |
372 | break; |
373 | ||
374 | case arg_ic: | |
43e65147 | 375 | p = makelongparameter (cr16_allWords, |
bab4becb NC |
376 | inst_bit_size - (start_bits + a->size), |
377 | inst_bit_size - start_bits); | |
2781f857 | 378 | a->constant = p; |
3d3d428f NC |
379 | break; |
380 | ||
381 | case arg_cc: | |
bab4becb NC |
382 | p = makelongparameter (cr16_allWords, |
383 | inst_bit_size - (start_bits + a->size), | |
384 | inst_bit_size - start_bits); | |
2781f857 | 385 | a->cc = p; |
3d3d428f NC |
386 | break; |
387 | ||
388 | case arg_idxr: | |
2781f857 | 389 | if (IS_INSN_TYPE (CSTBIT_INS) && instruction->mnemonic[4] == 'b') |
bab4becb | 390 | p = makelongparameter (cr16_allWords, 8, 9); |
3d3d428f | 391 | else |
bab4becb | 392 | p = makelongparameter (cr16_allWords, 9, 10); |
2781f857 | 393 | a->i_r = p; |
bab4becb NC |
394 | p = makelongparameter (cr16_allWords, |
395 | inst_bit_size - a->size, inst_bit_size); | |
2781f857 | 396 | a->constant = p; |
3d3d428f NC |
397 | break; |
398 | ||
399 | case arg_idxrp: | |
bab4becb | 400 | p = makelongparameter (cr16_allWords, start_bits + 12, start_bits + 13); |
2781f857 | 401 | a->i_r = p; |
bab4becb | 402 | p = makelongparameter (cr16_allWords, start_bits + 13, start_bits + 16); |
2781f857 | 403 | a->rp = p; |
3d3d428f NC |
404 | if (inst_bit_size > 32) |
405 | { | |
bab4becb | 406 | p = makelongparameter (cr16_allWords, inst_bit_size - start_bits - 12, |
3d3d428f | 407 | inst_bit_size); |
2781f857 | 408 | a->constant = (p & 0xffff) | (p >> 8 & 0xf0000); |
3d3d428f NC |
409 | } |
410 | else if (instruction->size == 2) | |
411 | { | |
bab4becb NC |
412 | p = makelongparameter (cr16_allWords, inst_bit_size - 22, |
413 | inst_bit_size); | |
2781f857 AM |
414 | a->constant = ((p & 0xf) | (((p >> 20) & 0x3) << 4) |
415 | | ((p >> 14 & 0x3) << 6) | (((p >>7) & 0x1f) << 7)); | |
3d3d428f NC |
416 | } |
417 | else if (instruction->size == 1 && a->size == 0) | |
418 | a->constant = 0; | |
419 | ||
420 | break; | |
421 | ||
422 | case arg_rbase: | |
bab4becb | 423 | p = makelongparameter (cr16_allWords, inst_bit_size, inst_bit_size); |
2781f857 | 424 | a->constant = p; |
bab4becb | 425 | p = makelongparameter (cr16_allWords, inst_bit_size - (start_bits + 4), |
2781f857 AM |
426 | inst_bit_size - start_bits); |
427 | a->r = p; | |
3d3d428f NC |
428 | break; |
429 | ||
430 | case arg_cr: | |
bab4becb | 431 | p = makelongparameter (cr16_allWords, start_bits + 12, start_bits + 16); |
2781f857 AM |
432 | a->r = p; |
433 | p = makelongparameter (cr16_allWords, inst_bit_size - 28, inst_bit_size); | |
434 | a->constant = ((p >> 8) & 0xf0000) | (p & 0xffff); | |
3d3d428f NC |
435 | break; |
436 | ||
437 | case arg_crp: | |
438 | if (instruction->size == 1) | |
bab4becb | 439 | p = makelongparameter (cr16_allWords, 12, 16); |
3d3d428f | 440 | else |
bab4becb | 441 | p = makelongparameter (cr16_allWords, start_bits + 12, start_bits + 16); |
2781f857 | 442 | a->rp = p; |
3d3d428f NC |
443 | |
444 | if (inst_bit_size > 32) | |
445 | { | |
bab4becb | 446 | p = makelongparameter (cr16_allWords, inst_bit_size - start_bits - 12, |
3d3d428f | 447 | inst_bit_size); |
2781f857 | 448 | a->constant = ((p & 0xffff) | (p >> 8 & 0xf0000)); |
3d3d428f NC |
449 | } |
450 | else if (instruction->size == 2) | |
451 | { | |
43e65147 | 452 | p = makelongparameter (cr16_allWords, inst_bit_size - 16, |
bab4becb | 453 | inst_bit_size); |
2781f857 | 454 | a->constant = p; |
3d3d428f NC |
455 | } |
456 | else if (instruction->size == 1 && a->size != 0) | |
457 | { | |
bab4becb | 458 | p = makelongparameter (cr16_allWords, 4, 8); |
3d3d428f NC |
459 | if (IS_INSN_MNEMONIC ("loadw") |
460 | || IS_INSN_MNEMONIC ("loadd") | |
461 | || IS_INSN_MNEMONIC ("storw") | |
462 | || IS_INSN_MNEMONIC ("stord")) | |
2781f857 | 463 | a->constant = p * 2; |
3d3d428f | 464 | else |
2781f857 | 465 | a->constant = p; |
3d3d428f NC |
466 | } |
467 | else /* below case for 0x0(reg pair) */ | |
468 | a->constant = 0; | |
469 | ||
470 | break; | |
471 | ||
472 | case arg_c: | |
473 | ||
474 | if ((IS_INSN_TYPE (BRANCH_INS)) | |
475 | || (IS_INSN_MNEMONIC ("bal")) | |
476 | || (IS_INSN_TYPE (CSTBIT_INS)) | |
477 | || (IS_INSN_TYPE (LD_STOR_INS))) | |
478 | { | |
479 | switch (a->size) | |
480 | { | |
481 | case 8 : | |
bab4becb | 482 | p = makelongparameter (cr16_allWords, 0, start_bits); |
2781f857 | 483 | a->constant = ((p & 0xf00) >> 4) | (p & 0xf); |
3d3d428f NC |
484 | break; |
485 | ||
486 | case 24: | |
487 | if (instruction->size == 3) | |
488 | { | |
bab4becb | 489 | p = makelongparameter (cr16_allWords, 16, inst_bit_size); |
2781f857 AM |
490 | a->constant = ((((p >> 16) & 0xf) << 20) |
491 | | (((p >> 24) & 0xf) << 16) | |
492 | | (p & 0xffff)); | |
3d3d428f NC |
493 | } |
494 | else if (instruction->size == 2) | |
495 | { | |
bab4becb | 496 | p = makelongparameter (cr16_allWords, 8, inst_bit_size); |
2781f857 | 497 | a->constant = p; |
3d3d428f NC |
498 | } |
499 | break; | |
500 | ||
501 | default: | |
bab4becb NC |
502 | p = makelongparameter (cr16_allWords, |
503 | inst_bit_size - (start_bits + a->size), | |
504 | inst_bit_size - start_bits); | |
2781f857 | 505 | a->constant = p; |
3d3d428f NC |
506 | break; |
507 | } | |
508 | } | |
509 | else | |
510 | { | |
bab4becb NC |
511 | p = makelongparameter (cr16_allWords, |
512 | inst_bit_size - (start_bits + a->size), | |
3d3d428f | 513 | inst_bit_size - start_bits); |
2781f857 | 514 | a->constant = p; |
3d3d428f NC |
515 | } |
516 | break; | |
517 | ||
518 | default: | |
519 | break; | |
520 | } | |
521 | } | |
522 | ||
523 | /* Print a single argument. */ | |
524 | ||
525 | static void | |
526 | print_arg (argument *a, bfd_vma memaddr, struct disassemble_info *info) | |
527 | { | |
528 | LONGLONG longdisp, mask; | |
529 | int sign_flag = 0; | |
530 | int relative = 0; | |
531 | bfd_vma number; | |
532 | PTR stream = info->stream; | |
533 | fprintf_ftype func = info->fprintf_func; | |
534 | ||
535 | switch (a->type) | |
536 | { | |
537 | case arg_r: | |
538 | func (stream, "%s", getregname (a->r)); | |
539 | break; | |
540 | ||
541 | case arg_rp: | |
542 | func (stream, "%s", getregpname (a->rp)); | |
543 | break; | |
544 | ||
545 | case arg_pr: | |
546 | func (stream, "%s", getprocregname (a->pr)); | |
547 | break; | |
548 | ||
549 | case arg_prp: | |
550 | func (stream, "%s", getprocpregname (a->prp)); | |
551 | break; | |
552 | ||
553 | case arg_cc: | |
554 | func (stream, "%s", getccstring (a->cc)); | |
555 | func (stream, "%s", "\t"); | |
556 | break; | |
557 | ||
558 | case arg_ic: | |
559 | if (IS_INSN_MNEMONIC ("excp")) | |
560 | { | |
561 | func (stream, "%s", gettrapstring (a->constant)); | |
562 | break; | |
563 | } | |
564 | else if ((IS_INSN_TYPE (ARITH_INS) || IS_INSN_TYPE (ARITH_BYTE_INS)) | |
565 | && ((instruction->size == 1) && (a->constant == 9))) | |
566 | func (stream, "$%d", -1); | |
567 | else if (INST_HAS_REG_LIST) | |
568 | func (stream, "$0x%lx", a->constant +1); | |
569 | else if (IS_INSN_TYPE (SHIFT_INS)) | |
570 | { | |
571 | longdisp = a->constant; | |
572 | mask = ((LONGLONG)1 << a->size) - 1; | |
573 | if (longdisp & ((LONGLONG)1 << (a->size -1))) | |
574 | { | |
575 | sign_flag = 1; | |
576 | longdisp = ~(longdisp) + 1; | |
577 | } | |
578 | a->constant = (unsigned long int) (longdisp & mask); | |
579 | func (stream, "$%d", ((int)(sign_flag ? -a->constant : | |
580 | a->constant))); | |
581 | } | |
582 | else | |
583 | func (stream, "$0x%lx", a->constant); | |
584 | switch (a->size) | |
585 | { | |
586 | case 4 : case 5 : case 6 : case 8 : | |
587 | func (stream, "%s", ":s"); break; | |
588 | case 16 : case 20 : func (stream, "%s", ":m"); break; | |
589 | case 24 : case 32 : func (stream, "%s", ":l"); break; | |
590 | default: break; | |
591 | } | |
592 | break; | |
593 | ||
594 | case arg_idxr: | |
595 | if (a->i_r == 0) func (stream, "[r12]"); | |
596 | if (a->i_r == 1) func (stream, "[r13]"); | |
597 | func (stream, "0x%lx", a->constant); | |
598 | func (stream, "%s", print_exp_len (instruction->size * 16)); | |
599 | break; | |
600 | ||
601 | case arg_idxrp: | |
602 | if (a->i_r == 0) func (stream, "[r12]"); | |
603 | if (a->i_r == 1) func (stream, "[r13]"); | |
604 | func (stream, "0x%lx", a->constant); | |
605 | func (stream, "%s", print_exp_len (instruction->size * 16)); | |
606 | func (stream, "%s", getidxregpname (a->rp)); | |
607 | break; | |
608 | ||
609 | case arg_rbase: | |
610 | func (stream, "(%s)", getregname (a->r)); | |
611 | break; | |
612 | ||
613 | case arg_cr: | |
614 | func (stream, "0x%lx", a->constant); | |
615 | func (stream, "%s", print_exp_len (instruction->size * 16)); | |
616 | func (stream, "(%s)", getregname (a->r)); | |
617 | break; | |
618 | ||
619 | case arg_crp: | |
620 | func (stream, "0x%lx", a->constant); | |
621 | func (stream, "%s", print_exp_len (instruction->size * 16)); | |
622 | func (stream, "%s", getregpname (a->rp)); | |
623 | break; | |
624 | ||
625 | case arg_c: | |
626 | /*Removed the *2 part as because implicit zeros are no more required. | |
627 | Have to fix this as this needs a bit of extension in terms of branch | |
628 | instructions. */ | |
629 | if (IS_INSN_TYPE (BRANCH_INS) || IS_INSN_MNEMONIC ("bal")) | |
630 | { | |
631 | relative = 1; | |
632 | longdisp = a->constant; | |
633 | /* REVISIT: To sync with WinIDEA and CR16 4.1tools, the below | |
634 | line commented */ | |
635 | /* longdisp <<= 1; */ | |
636 | mask = ((LONGLONG)1 << a->size) - 1; | |
637 | switch (a->size) | |
638 | { | |
639 | case 8 : | |
640 | { | |
641 | longdisp <<= 1; | |
642 | if (longdisp & ((LONGLONG)1 << a->size)) | |
643 | { | |
644 | sign_flag = 1; | |
645 | longdisp = ~(longdisp) + 1; | |
646 | } | |
647 | break; | |
648 | } | |
649 | case 16 : | |
650 | case 24 : | |
651 | { | |
652 | if (longdisp & 1) | |
653 | { | |
654 | sign_flag = 1; | |
655 | longdisp = ~(longdisp) + 1; | |
656 | } | |
657 | break; | |
658 | } | |
659 | default: | |
660 | func (stream, "Wrong offset used in branch/bal instruction"); | |
661 | break; | |
662 | } | |
663 | a->constant = (unsigned long int) (longdisp & mask); | |
664 | } | |
665 | /* For branch Neq instruction it is 2*offset + 2. */ | |
666 | else if (IS_INSN_TYPE (BRANCH_NEQ_INS)) | |
667 | a->constant = 2 * a->constant + 2; | |
668 | ||
669 | if ((!IS_INSN_TYPE (CSTBIT_INS)) && (!IS_INSN_TYPE (LD_STOR_INS))) | |
670 | (sign_flag) ? func (stream, "%s", "*-"): func (stream, "%s","*+"); | |
671 | ||
522fe561 | 672 | /* PR 10173: Avoid printing the 0x prefix twice. */ |
e20cc039 | 673 | if (info->symtab_size > 0) |
522fe561 | 674 | func (stream, "%s", "0x"); |
3d3d428f NC |
675 | number = ((relative ? memaddr : 0) + |
676 | (sign_flag ? ((- a->constant) & 0xffffffe) : a->constant)); | |
677 | ||
678 | (*info->print_address_func) ((number & ((1 << 24) - 1)), info); | |
679 | ||
680 | func (stream, "%s", print_exp_len (instruction->size * 16)); | |
681 | break; | |
682 | ||
683 | default: | |
684 | break; | |
685 | } | |
686 | } | |
687 | ||
688 | /* Print all the arguments of CURRINSN instruction. */ | |
689 | ||
690 | static void | |
91d6fa6a | 691 | print_arguments (ins *currentInsn, bfd_vma memaddr, struct disassemble_info *info) |
3d3d428f NC |
692 | { |
693 | int i; | |
694 | ||
695 | /* For "pop/push/popret RA instruction only. */ | |
696 | if ((IS_INSN_MNEMONIC ("pop") | |
697 | || (IS_INSN_MNEMONIC ("popret") | |
698 | || (IS_INSN_MNEMONIC ("push")))) | |
91d6fa6a | 699 | && currentInsn->nargs == 1) |
3d3d428f NC |
700 | { |
701 | info->fprintf_func (info->stream, "RA"); | |
702 | return; | |
703 | } | |
704 | ||
91d6fa6a | 705 | for (i = 0; i < currentInsn->nargs; i++) |
3d3d428f NC |
706 | { |
707 | processing_argument_number = i; | |
708 | ||
709 | /* For "bal (ra), disp17" instruction only. */ | |
710 | if ((IS_INSN_MNEMONIC ("bal")) && (i == 0) && instruction->size == 2) | |
2781f857 AM |
711 | { |
712 | info->fprintf_func (info->stream, "(ra),"); | |
713 | continue; | |
714 | } | |
3d3d428f NC |
715 | |
716 | if ((INST_HAS_REG_LIST) && (i == 2)) | |
2781f857 | 717 | info->fprintf_func (info->stream, "RA"); |
3d3d428f | 718 | else |
2781f857 | 719 | print_arg (¤tInsn->arg[i], memaddr, info); |
3d3d428f | 720 | |
91d6fa6a | 721 | if ((i != currentInsn->nargs - 1) && (!IS_INSN_MNEMONIC ("b"))) |
2781f857 | 722 | info->fprintf_func (info->stream, ","); |
3d3d428f NC |
723 | } |
724 | } | |
725 | ||
726 | /* Build the instruction's arguments. */ | |
727 | ||
bab4becb | 728 | void |
73335eae | 729 | cr16_make_instruction (void) |
3d3d428f NC |
730 | { |
731 | int i; | |
732 | unsigned int shift; | |
733 | ||
bab4becb | 734 | for (i = 0; i < cr16_currInsn.nargs; i++) |
3d3d428f NC |
735 | { |
736 | argument a; | |
737 | ||
738 | memset (&a, 0, sizeof (a)); | |
739 | a.type = getargtype (instruction->operands[i].op_type); | |
740 | a.size = getbits (instruction->operands[i].op_type); | |
741 | shift = instruction->operands[i].shift; | |
742 | ||
743 | make_argument (&a, shift); | |
bab4becb | 744 | cr16_currInsn.arg[i] = a; |
3d3d428f NC |
745 | } |
746 | ||
747 | /* Calculate instruction size (in bytes). */ | |
bab4becb | 748 | cr16_currInsn.size = instruction->size + (size_changed ? 1 : 0); |
3d3d428f | 749 | /* Now in bits. */ |
bab4becb | 750 | cr16_currInsn.size *= 2; |
3d3d428f NC |
751 | } |
752 | ||
753 | /* Retrieve a single word from a given memory address. */ | |
754 | ||
755 | static wordU | |
756 | get_word_at_PC (bfd_vma memaddr, struct disassemble_info *info) | |
757 | { | |
758 | bfd_byte buffer[4]; | |
759 | int status; | |
760 | wordU insn = 0; | |
761 | ||
762 | status = info->read_memory_func (memaddr, buffer, 2, info); | |
763 | ||
764 | if (status == 0) | |
765 | insn = (wordU) bfd_getl16 (buffer); | |
766 | ||
767 | return insn; | |
768 | } | |
769 | ||
770 | /* Retrieve multiple words (3) from a given memory address. */ | |
771 | ||
772 | static void | |
773 | get_words_at_PC (bfd_vma memaddr, struct disassemble_info *info) | |
774 | { | |
775 | int i; | |
776 | bfd_vma mem; | |
777 | ||
778 | for (i = 0, mem = memaddr; i < 3; i++, mem += 2) | |
bab4becb | 779 | cr16_words[i] = get_word_at_PC (mem, info); |
3d3d428f | 780 | |
43e65147 | 781 | cr16_allWords = ((ULONGLONG) cr16_words[0] << 32) |
bab4becb | 782 | + ((unsigned long) cr16_words[1] << 16) + cr16_words[2]; |
3d3d428f NC |
783 | } |
784 | ||
785 | /* Prints the instruction by calling print_arguments after proper matching. */ | |
786 | ||
787 | int | |
788 | print_insn_cr16 (bfd_vma memaddr, struct disassemble_info *info) | |
789 | { | |
790 | int is_decoded; /* Nonzero means instruction has a match. */ | |
791 | ||
792 | /* Initialize global variables. */ | |
793 | imm4flag = 0; | |
794 | size_changed = 0; | |
795 | ||
796 | /* Retrieve the encoding from current memory location. */ | |
797 | get_words_at_PC (memaddr, info); | |
798 | /* Find a matching opcode in table. */ | |
73335eae | 799 | is_decoded = cr16_match_opcode (); |
3d3d428f | 800 | /* If found, print the instruction's mnemonic and arguments. */ |
616ec358 | 801 | if (is_decoded > 0 && (cr16_words[0] != 0 || cr16_words[1] != 0)) |
3d3d428f NC |
802 | { |
803 | if (strneq (instruction->mnemonic, "cinv", 4)) | |
2781f857 AM |
804 | info->fprintf_func (info->stream,"%s", |
805 | getcinvstring (instruction->mnemonic)); | |
3d3d428f | 806 | else |
2781f857 | 807 | info->fprintf_func (info->stream, "%s", instruction->mnemonic); |
3d3d428f | 808 | |
bab4becb | 809 | if (((cr16_currInsn.nargs = get_number_of_operands ()) != 0) |
3d3d428f | 810 | && ! (IS_INSN_MNEMONIC ("b"))) |
2781f857 | 811 | info->fprintf_func (info->stream, "\t"); |
73335eae | 812 | cr16_make_instruction (); |
3d3d428f | 813 | /* For push/pop/pushrtn with RA instructions. */ |
bab4becb | 814 | if ((INST_HAS_REG_LIST) && ((cr16_words[0] >> 7) & 0x1)) |
2781f857 | 815 | cr16_currInsn.nargs +=1; |
bab4becb NC |
816 | print_arguments (&cr16_currInsn, memaddr, info); |
817 | return cr16_currInsn.size; | |
3d3d428f NC |
818 | } |
819 | ||
820 | /* No match found. */ | |
821 | info->fprintf_func (info->stream,"%s ",ILLEGAL); | |
822 | return 2; | |
823 | } |