|  | /* Disassembler code for CR16. | 
|  | Copyright (C) 2007-2025 Free Software Foundation, Inc. | 
|  | Contributed by M R Swami Reddy (MR.Swami.Reddy@nsc.com). | 
|  |  | 
|  | This file is part of GAS, GDB and the GNU binutils. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify it | 
|  | under the terms of the GNU General Public License as published by the | 
|  | Free Software Foundation; either version 3, or (at your option) | 
|  | any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, but WITHOUT | 
|  | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|  | FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
|  | more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program; if not, write to the Free Software Foundation, | 
|  | Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */ | 
|  |  | 
|  | #include "sysdep.h" | 
|  | #include "disassemble.h" | 
|  | #include "opcode/cr16.h" | 
|  | #include "libiberty.h" | 
|  |  | 
|  | /* String to print when opcode was not matched.  */ | 
|  | #define ILLEGAL  "illegal" | 
|  | /* Escape to 16-bit immediate.  */ | 
|  | #define ESCAPE_16_BIT  0xB | 
|  |  | 
|  | /* Extract 'n_bits' from 'a' starting from offset 'offs'.  */ | 
|  | #define EXTRACT(a, offs, n_bits) \ | 
|  | (((a) >> (offs)) & ((1ul << ((n_bits) - 1) << 1) - 1)) | 
|  |  | 
|  | /* Set Bit Mask - a mask to set all bits in a 32-bit word starting | 
|  | from offset 'offs'.  */ | 
|  | #define SBM(offs)  ((1ul << 31 << 1) - (1ul << (offs))) | 
|  |  | 
|  | /* Structure to map valid 'cinv' instruction options.  */ | 
|  |  | 
|  | typedef struct | 
|  | { | 
|  | /* Cinv printed string.  */ | 
|  | char *istr; | 
|  | /* Value corresponding to the string.  */ | 
|  | char *ostr; | 
|  | } | 
|  | cinv_entry; | 
|  |  | 
|  | /* CR16 'cinv' options mapping.  */ | 
|  | static const cinv_entry cr16_cinvs[] = | 
|  | { | 
|  | {"cinv[i]",     "cinv    [i]"}, | 
|  | {"cinv[i,u]",   "cinv    [i,u]"}, | 
|  | {"cinv[d]",     "cinv    [d]"}, | 
|  | {"cinv[d,u]",   "cinv    [d,u]"}, | 
|  | {"cinv[d,i]",   "cinv    [d,i]"}, | 
|  | {"cinv[d,i,u]", "cinv    [d,i,u]"} | 
|  | }; | 
|  |  | 
|  | /* Number of valid 'cinv' instruction options.  */ | 
|  | static int NUMCINVS = ARRAY_SIZE (cr16_cinvs); | 
|  |  | 
|  | /* Enum to distinguish different registers argument types.  */ | 
|  | typedef enum REG_ARG_TYPE | 
|  | { | 
|  | /* General purpose register (r<N>).  */ | 
|  | REG_ARG = 0, | 
|  | /*Processor register   */ | 
|  | P_ARG, | 
|  | } | 
|  | REG_ARG_TYPE; | 
|  |  | 
|  | /* Current opcode table entry we're disassembling.  */ | 
|  | static const inst *instruction; | 
|  | /* Current instruction we're disassembling.  */ | 
|  | static ins cr16_currInsn; | 
|  | /* The current instruction is read into 3 consecutive words.  */ | 
|  | static wordU cr16_words[3]; | 
|  | /* Contains all words in appropriate order.  */ | 
|  | static ULONGLONG cr16_allWords; | 
|  | /* Holds the current processed argument number.  */ | 
|  | static int processing_argument_number; | 
|  | /* Nonzero means a IMM4 instruction.  */ | 
|  | static int imm4flag; | 
|  | /* Nonzero means the instruction's original size is | 
|  | incremented (escape sequence is used).  */ | 
|  | static int size_changed; | 
|  |  | 
|  |  | 
|  | /* Print the constant expression length.  */ | 
|  |  | 
|  | static char * | 
|  | print_exp_len (int size) | 
|  | { | 
|  | switch (size) | 
|  | { | 
|  | case 4: | 
|  | case 5: | 
|  | case 6: | 
|  | case 8: | 
|  | case 14: | 
|  | case 16: | 
|  | return ":s"; | 
|  | case 20: | 
|  | case 24: | 
|  | case 32: | 
|  | return ":m"; | 
|  | case 48: | 
|  | return ":l"; | 
|  | default: | 
|  | return ""; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Retrieve the number of operands for the current assembled instruction.  */ | 
|  |  | 
|  | static int | 
|  | get_number_of_operands (void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; instruction->operands[i].op_type && i < MAX_OPERANDS; i++) | 
|  | ; | 
|  |  | 
|  | return i; | 
|  | } | 
|  |  | 
|  | /* Return the bit size for a given operand.  */ | 
|  |  | 
|  | static int | 
|  | getbits (operand_type op) | 
|  | { | 
|  | if (op < MAX_OPRD) | 
|  | return cr16_optab[op].bit_size; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Return the argument type of a given operand.  */ | 
|  |  | 
|  | static argtype | 
|  | getargtype (operand_type op) | 
|  | { | 
|  | if (op < MAX_OPRD) | 
|  | return cr16_optab[op].arg_type; | 
|  |  | 
|  | return nullargs; | 
|  | } | 
|  |  | 
|  | /* Given a 'CC' instruction constant operand, return its corresponding | 
|  | string. This routine is used when disassembling the 'CC' instruction.  */ | 
|  |  | 
|  | static char * | 
|  | getccstring (unsigned cc_insn) | 
|  | { | 
|  | return (char *) cr16_b_cond_tab[cc_insn]; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Given a 'cinv' instruction constant operand, return its corresponding | 
|  | string. This routine is used when disassembling the 'cinv' instruction. */ | 
|  |  | 
|  | static char * | 
|  | getcinvstring (const char *str) | 
|  | { | 
|  | const cinv_entry *cinv; | 
|  |  | 
|  | for (cinv = cr16_cinvs; cinv < (cr16_cinvs + NUMCINVS); cinv++) | 
|  | if (strcmp (cinv->istr, str) == 0) | 
|  | return cinv->ostr; | 
|  |  | 
|  | return ILLEGAL; | 
|  | } | 
|  |  | 
|  | /* Given the trap index in dispatch table, return its name. | 
|  | This routine is used when disassembling the 'excp' instruction.  */ | 
|  |  | 
|  | static char * | 
|  | gettrapstring (unsigned int trap_index) | 
|  | { | 
|  | const trap_entry *trap; | 
|  |  | 
|  | for (trap = cr16_traps; trap < cr16_traps + NUMTRAPS; trap++) | 
|  | if (trap->entry == trap_index) | 
|  | return trap->name; | 
|  |  | 
|  | return ILLEGAL; | 
|  | } | 
|  |  | 
|  | /* Given a register enum value, retrieve its name.  */ | 
|  |  | 
|  | static char * | 
|  | getregname (reg r) | 
|  | { | 
|  | const reg_entry * regentry = cr16_regtab + r; | 
|  |  | 
|  | if (regentry->type != CR16_R_REGTYPE) | 
|  | return ILLEGAL; | 
|  |  | 
|  | return regentry->name; | 
|  | } | 
|  |  | 
|  | /* Given a register pair enum value, retrieve its name.  */ | 
|  |  | 
|  | static char * | 
|  | getregpname (reg r) | 
|  | { | 
|  | const reg_entry * regentry = cr16_regptab + r; | 
|  |  | 
|  | if (regentry->type != CR16_RP_REGTYPE) | 
|  | return ILLEGAL; | 
|  |  | 
|  | return regentry->name; | 
|  | } | 
|  |  | 
|  | /* Given a index register pair enum value, retrieve its name.  */ | 
|  |  | 
|  | static char * | 
|  | getidxregpname (reg r) | 
|  | { | 
|  | const reg_entry * regentry; | 
|  |  | 
|  | switch (r) | 
|  | { | 
|  | case 0: r = 0; break; | 
|  | case 1: r = 2; break; | 
|  | case 2: r = 4; break; | 
|  | case 3: r = 6; break; | 
|  | case 4: r = 8; break; | 
|  | case 5: r = 10; break; | 
|  | case 6: r = 3; break; | 
|  | case 7: r = 5; break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | regentry = cr16_regptab + r; | 
|  |  | 
|  | if (regentry->type != CR16_RP_REGTYPE) | 
|  | return ILLEGAL; | 
|  |  | 
|  | return regentry->name; | 
|  | } | 
|  |  | 
|  | /* Getting a processor register name.  */ | 
|  |  | 
|  | static char * | 
|  | getprocregname (int reg_index) | 
|  | { | 
|  | const reg_entry *r; | 
|  |  | 
|  | for (r = cr16_pregtab; r < cr16_pregtab + NUMPREGS; r++) | 
|  | if (r->image == reg_index) | 
|  | return r->name; | 
|  |  | 
|  | return "ILLEGAL REGISTER"; | 
|  | } | 
|  |  | 
|  | /* Getting a processor register name - 32 bit size.  */ | 
|  |  | 
|  | static char * | 
|  | getprocpregname (int reg_index) | 
|  | { | 
|  | const reg_entry *r; | 
|  |  | 
|  | for (r = cr16_pregptab; r < cr16_pregptab + NUMPREGPS; r++) | 
|  | if (r->image == reg_index) | 
|  | return r->name; | 
|  |  | 
|  | return "ILLEGAL REGISTER"; | 
|  | } | 
|  |  | 
|  | /* START and END are relating 'cr16_allWords' struct, which is 48 bits size. | 
|  |  | 
|  | START|--------|END | 
|  | +---------+---------+---------+---------+ | 
|  | |         |   V    |     A    |   L     | | 
|  | +---------+---------+---------+---------+ | 
|  | 0         16        32        48 | 
|  | words                  [0]       [1]       [2]      */ | 
|  |  | 
|  | static inline dwordU | 
|  | makelongparameter (ULONGLONG val, int start, int end) | 
|  | { | 
|  | return EXTRACT (val, 48 - end, end - start); | 
|  | } | 
|  |  | 
|  | /* Build a mask of the instruction's 'constant' opcode, | 
|  | based on the instruction's printing flags.  */ | 
|  |  | 
|  | static unsigned long | 
|  | build_mask (void) | 
|  | { | 
|  | unsigned long mask = SBM (instruction->match_bits); | 
|  |  | 
|  | /* Adjust mask for bcond with 32-bit size instruction.  */ | 
|  | if ((IS_INSN_MNEMONIC("b") && instruction->size == 2)) | 
|  | mask = 0xff0f0000; | 
|  |  | 
|  | return mask; | 
|  | } | 
|  |  | 
|  | /* Search for a matching opcode. Return 1 for success, 0 for failure.  */ | 
|  |  | 
|  | int | 
|  | cr16_match_opcode (void) | 
|  | { | 
|  | unsigned long mask; | 
|  | /* The instruction 'constant' opcode doesn't exceed 32 bits.  */ | 
|  | unsigned long doubleWord = cr16_words[1] + ((unsigned) cr16_words[0] << 16); | 
|  |  | 
|  | /* Start searching from end of instruction table.  */ | 
|  | instruction = &cr16_instruction[NUMOPCODES - 2]; | 
|  |  | 
|  | /* Loop over instruction table until a full match is found.  */ | 
|  | while (instruction >= cr16_instruction) | 
|  | { | 
|  | mask = build_mask (); | 
|  |  | 
|  | if ((doubleWord & mask) == BIN (instruction->match, | 
|  | instruction->match_bits)) | 
|  | return 1; | 
|  | else | 
|  | instruction--; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Set the proper parameter value for different type of arguments.  */ | 
|  |  | 
|  | static void | 
|  | make_argument (argument * a, int start_bits) | 
|  | { | 
|  | int inst_bit_size; | 
|  | dwordU p; | 
|  |  | 
|  | if ((instruction->size == 3) && a->size >= 16) | 
|  | inst_bit_size = 48; | 
|  | else | 
|  | inst_bit_size = 32; | 
|  |  | 
|  | switch (a->type) | 
|  | { | 
|  | case arg_r: | 
|  | p = makelongparameter (cr16_allWords, | 
|  | inst_bit_size - (start_bits + a->size), | 
|  | inst_bit_size - start_bits); | 
|  | a->r = p; | 
|  | break; | 
|  |  | 
|  | case arg_rp: | 
|  | p = makelongparameter (cr16_allWords, | 
|  | inst_bit_size - (start_bits + a->size), | 
|  | inst_bit_size - start_bits); | 
|  | a->rp = p; | 
|  | break; | 
|  |  | 
|  | case arg_pr: | 
|  | p = makelongparameter (cr16_allWords, | 
|  | inst_bit_size - (start_bits + a->size), | 
|  | inst_bit_size - start_bits); | 
|  | a->pr = p; | 
|  | break; | 
|  |  | 
|  | case arg_prp: | 
|  | p = makelongparameter (cr16_allWords, | 
|  | inst_bit_size - (start_bits + a->size), | 
|  | inst_bit_size - start_bits); | 
|  | a->prp = p; | 
|  | break; | 
|  |  | 
|  | case arg_ic: | 
|  | p = makelongparameter (cr16_allWords, | 
|  | inst_bit_size - (start_bits + a->size), | 
|  | inst_bit_size - start_bits); | 
|  | a->constant = p; | 
|  | break; | 
|  |  | 
|  | case arg_cc: | 
|  | p = makelongparameter (cr16_allWords, | 
|  | inst_bit_size - (start_bits + a->size), | 
|  | inst_bit_size - start_bits); | 
|  | a->cc = p; | 
|  | break; | 
|  |  | 
|  | case arg_idxr: | 
|  | if (IS_INSN_TYPE (CSTBIT_INS) && instruction->mnemonic[4] == 'b') | 
|  | p = makelongparameter (cr16_allWords, 8, 9); | 
|  | else | 
|  | p = makelongparameter (cr16_allWords, 9, 10); | 
|  | a->i_r = p; | 
|  | p = makelongparameter (cr16_allWords, | 
|  | inst_bit_size - a->size, inst_bit_size); | 
|  | a->constant = p; | 
|  | break; | 
|  |  | 
|  | case arg_idxrp: | 
|  | p = makelongparameter (cr16_allWords, start_bits + 12, start_bits + 13); | 
|  | a->i_r = p; | 
|  | p = makelongparameter (cr16_allWords, start_bits + 13, start_bits + 16); | 
|  | a->rp = p; | 
|  | if (inst_bit_size > 32) | 
|  | { | 
|  | p = makelongparameter (cr16_allWords, inst_bit_size - start_bits - 12, | 
|  | inst_bit_size); | 
|  | a->constant = (p & 0xffff) | (p >> 8 & 0xf0000); | 
|  | } | 
|  | else if (instruction->size == 2) | 
|  | { | 
|  | p = makelongparameter (cr16_allWords, inst_bit_size - 22, | 
|  | inst_bit_size); | 
|  | a->constant = ((p & 0xf) | (((p >> 20) & 0x3) << 4) | 
|  | | ((p >> 14 & 0x3) << 6) | (((p >>7) & 0x1f) << 7)); | 
|  | } | 
|  | else if (instruction->size == 1 && a->size == 0) | 
|  | a->constant = 0; | 
|  |  | 
|  | break; | 
|  |  | 
|  | case arg_rbase: | 
|  | p = makelongparameter (cr16_allWords, inst_bit_size, inst_bit_size); | 
|  | a->constant = p; | 
|  | p = makelongparameter (cr16_allWords, inst_bit_size - (start_bits + 4), | 
|  | inst_bit_size - start_bits); | 
|  | a->r = p; | 
|  | break; | 
|  |  | 
|  | case arg_cr: | 
|  | p = makelongparameter (cr16_allWords, start_bits + 12, start_bits + 16); | 
|  | a->r = p; | 
|  | p = makelongparameter (cr16_allWords, inst_bit_size - 28, inst_bit_size); | 
|  | a->constant = ((p >> 8) & 0xf0000) | (p & 0xffff); | 
|  | break; | 
|  |  | 
|  | case arg_crp: | 
|  | if (instruction->size == 1) | 
|  | p = makelongparameter (cr16_allWords, 12, 16); | 
|  | else | 
|  | p = makelongparameter (cr16_allWords, start_bits + 12, start_bits + 16); | 
|  | a->rp = p; | 
|  |  | 
|  | if (inst_bit_size > 32) | 
|  | { | 
|  | p = makelongparameter (cr16_allWords, inst_bit_size - start_bits - 12, | 
|  | inst_bit_size); | 
|  | a->constant = ((p & 0xffff) | (p >> 8 & 0xf0000)); | 
|  | } | 
|  | else if (instruction->size == 2) | 
|  | { | 
|  | p = makelongparameter (cr16_allWords, inst_bit_size - 16, | 
|  | inst_bit_size); | 
|  | a->constant = p; | 
|  | } | 
|  | else if (instruction->size == 1 && a->size != 0) | 
|  | { | 
|  | p = makelongparameter (cr16_allWords, 4, 8); | 
|  | if (IS_INSN_MNEMONIC ("loadw") | 
|  | || IS_INSN_MNEMONIC ("loadd") | 
|  | || IS_INSN_MNEMONIC ("storw") | 
|  | || IS_INSN_MNEMONIC ("stord")) | 
|  | a->constant = p * 2; | 
|  | else | 
|  | a->constant = p; | 
|  | } | 
|  | else /* below case for 0x0(reg pair) */ | 
|  | a->constant = 0; | 
|  |  | 
|  | break; | 
|  |  | 
|  | case arg_c: | 
|  |  | 
|  | if ((IS_INSN_TYPE (BRANCH_INS)) | 
|  | || (IS_INSN_MNEMONIC ("bal")) | 
|  | || (IS_INSN_TYPE (CSTBIT_INS)) | 
|  | || (IS_INSN_TYPE (LD_STOR_INS))) | 
|  | { | 
|  | switch (a->size) | 
|  | { | 
|  | case 8 : | 
|  | p = makelongparameter (cr16_allWords, 0, start_bits); | 
|  | a->constant = ((p & 0xf00) >> 4) | (p & 0xf); | 
|  | break; | 
|  |  | 
|  | case 24: | 
|  | if (instruction->size == 3) | 
|  | { | 
|  | p = makelongparameter (cr16_allWords, 16, inst_bit_size); | 
|  | a->constant = ((((p >> 16) & 0xf) << 20) | 
|  | | (((p >> 24) & 0xf) << 16) | 
|  | | (p & 0xffff)); | 
|  | } | 
|  | else if (instruction->size == 2) | 
|  | { | 
|  | p = makelongparameter (cr16_allWords, 8, inst_bit_size); | 
|  | a->constant = p; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | p = makelongparameter (cr16_allWords, | 
|  | inst_bit_size - (start_bits + a->size), | 
|  | inst_bit_size - start_bits); | 
|  | a->constant = p; | 
|  | break; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | p = makelongparameter (cr16_allWords, | 
|  | inst_bit_size - (start_bits + a->size), | 
|  | inst_bit_size - start_bits); | 
|  | a->constant = p; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /*  Print a single argument.  */ | 
|  |  | 
|  | static void | 
|  | print_arg (argument *a, bfd_vma memaddr, struct disassemble_info *info) | 
|  | { | 
|  | LONGLONG longdisp, mask; | 
|  | int sign_flag = 0; | 
|  | int relative = 0; | 
|  | bfd_vma number; | 
|  | void *stream = info->stream; | 
|  | fprintf_ftype func = info->fprintf_func; | 
|  |  | 
|  | switch (a->type) | 
|  | { | 
|  | case arg_r: | 
|  | func (stream, "%s", getregname (a->r)); | 
|  | break; | 
|  |  | 
|  | case arg_rp: | 
|  | func (stream, "%s", getregpname (a->rp)); | 
|  | break; | 
|  |  | 
|  | case arg_pr: | 
|  | func (stream, "%s", getprocregname (a->pr)); | 
|  | break; | 
|  |  | 
|  | case arg_prp: | 
|  | func (stream, "%s", getprocpregname (a->prp)); | 
|  | break; | 
|  |  | 
|  | case arg_cc: | 
|  | func (stream, "%s", getccstring (a->cc)); | 
|  | func (stream, "%s", "\t"); | 
|  | break; | 
|  |  | 
|  | case arg_ic: | 
|  | if (IS_INSN_MNEMONIC ("excp")) | 
|  | { | 
|  | func (stream, "%s", gettrapstring (a->constant)); | 
|  | break; | 
|  | } | 
|  | else if ((IS_INSN_TYPE (ARITH_INS) || IS_INSN_TYPE (ARITH_BYTE_INS)) | 
|  | && ((instruction->size == 1) && (a->constant == 9))) | 
|  | func (stream, "$%d", -1); | 
|  | else if (INST_HAS_REG_LIST) | 
|  | func (stream, "$0x%lx", a->constant +1); | 
|  | else if (IS_INSN_TYPE (SHIFT_INS)) | 
|  | { | 
|  | longdisp = a->constant; | 
|  | mask = ((LONGLONG)1 << a->size) - 1; | 
|  | if (longdisp & ((LONGLONG)1 << (a->size -1))) | 
|  | { | 
|  | sign_flag = 1; | 
|  | longdisp = ~(longdisp) + 1; | 
|  | } | 
|  | a->constant = (unsigned long int) (longdisp & mask); | 
|  | func (stream, "$%d", ((int)(sign_flag ? -a->constant : | 
|  | a->constant))); | 
|  | } | 
|  | else | 
|  | func (stream, "$0x%lx", a->constant); | 
|  | switch (a->size) | 
|  | { | 
|  | case 4  : case 5  : case 6  : case 8  : | 
|  | func (stream, "%s", ":s"); break; | 
|  | case 16 : case 20 : func (stream, "%s", ":m"); break; | 
|  | case 24 : case 32 : func (stream, "%s", ":l"); break; | 
|  | default: break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case arg_idxr: | 
|  | if (a->i_r == 0) func (stream, "[r12]"); | 
|  | if (a->i_r == 1) func (stream, "[r13]"); | 
|  | func (stream, "0x%lx", a->constant); | 
|  | func (stream, "%s", print_exp_len (instruction->size * 16)); | 
|  | break; | 
|  |  | 
|  | case arg_idxrp: | 
|  | if (a->i_r == 0) func (stream, "[r12]"); | 
|  | if (a->i_r == 1) func (stream, "[r13]"); | 
|  | func (stream, "0x%lx", a->constant); | 
|  | func (stream, "%s", print_exp_len (instruction->size * 16)); | 
|  | func (stream, "%s", getidxregpname (a->rp)); | 
|  | break; | 
|  |  | 
|  | case arg_rbase: | 
|  | func (stream, "(%s)", getregname (a->r)); | 
|  | break; | 
|  |  | 
|  | case arg_cr: | 
|  | func (stream, "0x%lx", a->constant); | 
|  | func (stream, "%s", print_exp_len (instruction->size * 16)); | 
|  | func (stream, "(%s)", getregname (a->r)); | 
|  | break; | 
|  |  | 
|  | case arg_crp: | 
|  | func (stream, "0x%lx", a->constant); | 
|  | func (stream, "%s", print_exp_len (instruction->size * 16)); | 
|  | func (stream, "%s", getregpname (a->rp)); | 
|  | break; | 
|  |  | 
|  | case arg_c: | 
|  | /*Removed the *2 part as because implicit zeros are no more required. | 
|  | Have to fix this as this needs a bit of extension in terms of branch | 
|  | instructions. */ | 
|  | if (IS_INSN_TYPE (BRANCH_INS) || IS_INSN_MNEMONIC ("bal")) | 
|  | { | 
|  | relative = 1; | 
|  | longdisp = a->constant; | 
|  | /* REVISIT: To sync with WinIDEA and CR16 4.1tools, the below | 
|  | line commented */ | 
|  | /* longdisp <<= 1; */ | 
|  | mask = ((LONGLONG)1 << a->size) - 1; | 
|  | switch (a->size) | 
|  | { | 
|  | case 8  : | 
|  | { | 
|  | longdisp <<= 1; | 
|  | if (longdisp & ((LONGLONG)1 << a->size)) | 
|  | { | 
|  | sign_flag = 1; | 
|  | longdisp = ~(longdisp) + 1; | 
|  | } | 
|  | break; | 
|  | } | 
|  | case 16 : | 
|  | case 24 : | 
|  | { | 
|  | if (longdisp & 1) | 
|  | { | 
|  | sign_flag = 1; | 
|  | longdisp = ~(longdisp) + 1; | 
|  | } | 
|  | break; | 
|  | } | 
|  | default: | 
|  | func (stream, "Wrong offset used in branch/bal instruction"); | 
|  | break; | 
|  | } | 
|  | a->constant = (unsigned long int) (longdisp & mask); | 
|  | } | 
|  | /* For branch Neq instruction it is 2*offset + 2.  */ | 
|  | else if (IS_INSN_TYPE (BRANCH_NEQ_INS)) | 
|  | a->constant = 2 * a->constant + 2; | 
|  |  | 
|  | if ((!IS_INSN_TYPE (CSTBIT_INS)) && (!IS_INSN_TYPE (LD_STOR_INS))) | 
|  | (sign_flag) ? func (stream, "%s", "*-"): func (stream, "%s","*+"); | 
|  |  | 
|  | /* PR 10173: Avoid printing the 0x prefix twice.  */ | 
|  | if (info->symtab_size > 0) | 
|  | func (stream, "%s", "0x"); | 
|  | number = ((relative ? memaddr : 0) + | 
|  | (sign_flag ? ((- a->constant) & 0xffffffe) : a->constant)); | 
|  |  | 
|  | (*info->print_address_func) ((number & ((1 << 24) - 1)), info); | 
|  |  | 
|  | func (stream, "%s", print_exp_len (instruction->size * 16)); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Print all the arguments of CURRINSN instruction.  */ | 
|  |  | 
|  | static void | 
|  | print_arguments (ins *currentInsn, bfd_vma memaddr, struct disassemble_info *info) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* For "pop/push/popret RA instruction only.  */ | 
|  | if ((IS_INSN_MNEMONIC ("pop") | 
|  | || (IS_INSN_MNEMONIC ("popret") | 
|  | || (IS_INSN_MNEMONIC ("push")))) | 
|  | && currentInsn->nargs == 1) | 
|  | { | 
|  | info->fprintf_func (info->stream, "RA"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < currentInsn->nargs; i++) | 
|  | { | 
|  | processing_argument_number = i; | 
|  |  | 
|  | /* For "bal (ra), disp17" instruction only.  */ | 
|  | if ((IS_INSN_MNEMONIC ("bal")) && (i == 0) && instruction->size == 2) | 
|  | { | 
|  | info->fprintf_func (info->stream, "(ra),"); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if ((INST_HAS_REG_LIST) && (i == 2)) | 
|  | info->fprintf_func (info->stream, "RA"); | 
|  | else | 
|  | print_arg (¤tInsn->arg[i], memaddr, info); | 
|  |  | 
|  | if ((i != currentInsn->nargs - 1) && (!IS_INSN_MNEMONIC ("b"))) | 
|  | info->fprintf_func (info->stream, ","); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Build the instruction's arguments.  */ | 
|  |  | 
|  | void | 
|  | cr16_make_instruction (void) | 
|  | { | 
|  | int i; | 
|  | unsigned int shift; | 
|  |  | 
|  | for (i = 0; i < cr16_currInsn.nargs; i++) | 
|  | { | 
|  | argument a; | 
|  |  | 
|  | memset (&a, 0, sizeof (a)); | 
|  | a.type = getargtype (instruction->operands[i].op_type); | 
|  | a.size = getbits (instruction->operands[i].op_type); | 
|  | shift = instruction->operands[i].shift; | 
|  |  | 
|  | make_argument (&a, shift); | 
|  | cr16_currInsn.arg[i] = a; | 
|  | } | 
|  |  | 
|  | /* Calculate instruction size (in bytes).  */ | 
|  | cr16_currInsn.size = instruction->size + (size_changed ? 1 : 0); | 
|  | /* Now in bits.  */ | 
|  | cr16_currInsn.size *= 2; | 
|  | } | 
|  |  | 
|  | /* Retrieve a single word from a given memory address.  */ | 
|  |  | 
|  | static wordU | 
|  | get_word_at_PC (bfd_vma memaddr, struct disassemble_info *info) | 
|  | { | 
|  | bfd_byte buffer[4]; | 
|  | int status; | 
|  | wordU insn = 0; | 
|  |  | 
|  | status = info->read_memory_func (memaddr, buffer, 2, info); | 
|  |  | 
|  | if (status == 0) | 
|  | insn = (wordU) bfd_getl16 (buffer); | 
|  |  | 
|  | return insn; | 
|  | } | 
|  |  | 
|  | /* Retrieve multiple words (3) from a given memory address.  */ | 
|  |  | 
|  | static void | 
|  | get_words_at_PC (bfd_vma memaddr, struct disassemble_info *info) | 
|  | { | 
|  | int i; | 
|  | bfd_vma mem; | 
|  |  | 
|  | for (i = 0, mem = memaddr; i < 3; i++, mem += 2) | 
|  | cr16_words[i] = get_word_at_PC (mem, info); | 
|  |  | 
|  | cr16_allWords =  ((ULONGLONG) cr16_words[0] << 32) | 
|  | + ((unsigned long) cr16_words[1] << 16) + cr16_words[2]; | 
|  | } | 
|  |  | 
|  | /* Prints the instruction by calling print_arguments after proper matching.  */ | 
|  |  | 
|  | int | 
|  | print_insn_cr16 (bfd_vma memaddr, struct disassemble_info *info) | 
|  | { | 
|  | int is_decoded;     /* Nonzero means instruction has a match.  */ | 
|  |  | 
|  | /* Initialize global variables.  */ | 
|  | imm4flag = 0; | 
|  | size_changed = 0; | 
|  |  | 
|  | /* Retrieve the encoding from current memory location.  */ | 
|  | get_words_at_PC (memaddr, info); | 
|  | /* Find a matching opcode in table.  */ | 
|  | is_decoded = cr16_match_opcode (); | 
|  | /* If found, print the instruction's mnemonic and arguments.  */ | 
|  | if (is_decoded > 0 && (cr16_words[0] != 0 || cr16_words[1] != 0)) | 
|  | { | 
|  | if (startswith (instruction->mnemonic, "cinv")) | 
|  | info->fprintf_func (info->stream,"%s", | 
|  | getcinvstring (instruction->mnemonic)); | 
|  | else | 
|  | info->fprintf_func (info->stream, "%s", instruction->mnemonic); | 
|  |  | 
|  | if (((cr16_currInsn.nargs = get_number_of_operands ()) != 0) | 
|  | && ! (IS_INSN_MNEMONIC ("b"))) | 
|  | info->fprintf_func (info->stream, "\t"); | 
|  | cr16_make_instruction (); | 
|  | /* For push/pop/pushrtn with RA instructions.  */ | 
|  | if ((INST_HAS_REG_LIST) && ((cr16_words[0] >> 7) & 0x1)) | 
|  | cr16_currInsn.nargs +=1; | 
|  | print_arguments (&cr16_currInsn, memaddr, info); | 
|  | return cr16_currInsn.size; | 
|  | } | 
|  |  | 
|  | /* No match found.  */ | 
|  | info->fprintf_func (info->stream,"%s ",ILLEGAL); | 
|  | return 2; | 
|  | } |