/* Instruction printing code for the MAXQ Copyright 2004, 2005 Free Software Foundation, Inc. Written by Vineet Sharma(vineets@noida.hcltech.com) Inderpreet S.(inderpreetb@noida.hcltech.com) This file is part of GDB. 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 2 of the License, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "sysdep.h" #include "dis-asm.h" #include "opcode/maxq.h" struct _group_info { unsigned char group_no; unsigned char sub_opcode; unsigned char src; unsigned char dst; unsigned char fbit; unsigned char bit_no; unsigned char flag; }; typedef struct _group_info group_info; #define SRC 0x01 #define DST 0x02 #define FORMAT 0x04 #define BIT_NO 0x08 #define SUB_OP 0x10 #define MASK_LOW_BYTE 0x0f #define MASK_HIGH_BYTE 0xf0 /* Flags for retrieving the bits from the op-code. */ #define _DECODE_LOWNIB_LOWBYTE 0x000f #define _DECODE_HIGHNIB_LOWBYTE 0x00f0 #define _DECODE_LOWNIB_HIGHBYTE 0x0f00 #define _DECODE_HIGHNIB_HIGHBYTE 0xf000 #define _DECODE_HIGHBYTE 0xff00 #define _DECODE_LOWBYTE 0x00ff #define _DECODE_4TO6_HIGHBYTE 0x7000 #define _DECODE_4TO6_LOWBYTE 0x0070 #define _DECODE_0TO6_HIGHBYTE 0x7f00 #define _DECODE_0TO2_HIGHBYTE 0x0700 #define _DECODE_GET_F_HIGHBYTE 0x8000 #define _DECODE_BIT7_HIGHBYTE 0x8000 #define _DECODE_BIT7_LOWBYTE 0x0080 #define _DECODE_GET_CARRY 0x10000 #define _DECODE_BIT0_LOWBYTE 0x1 #define _DECODE_BIT6AND7_HIGHBYTE 0xc000 /* Module and Register Indexed of System Registers. */ #define _CURR_ACC_MODINDEX 0xa #define _CURR_ACC_REGINDEX 0x0 #define _PSF_REG_MODINDEX 0x8 #define _PSF_REG_REGINDEX 0x4 #define _PFX_REG_MODINDEX 0xb #define _PFX0_REG_REGINDEX 0x0 #define _PFX2_REG_REGINDEX 0x2 #define _DP_REG_MODINDEX 0xf #define _DP0_REG_REGINDEX 0x3 #define _DP1_REG_REGINDEX 0x7 #define _IP_REG_MODINDEX 0xc #define _IP_REG_REGINDEX 0x0 #define _IIR_REG_MODINDEX 0x8 #define _IIR_REG_REGINDEX 0xb #define _SP_REG_MODINDEX 0xd #define _SP_REG_REGINDEX 0x1 #define _IC_REG_MODINDEX 0x8 #define _IC_REG_REGINDEX 0x5 #define _LC_REG_MODINDEX 0xe #define _LC0_REG_REGINDEX 0x0 #define _LC1_REG_REGINDEX 0x1 #define _LC2_REG_REGINDEX 0x2 #define _LC3_REG_REGINDEX 0x3 /* Flags for finding the bits in PSF Register. */ #define SIM_ALU_DECODE_CARRY_BIT_POS 0x2 #define SIM_ALU_DECODE_SIGN_BIT_POS 0x40 #define SIM_ALU_DECODE_ZERO_BIT_POS 0x80 #define SIM_ALU_DECODE_EQUAL_BIT_POS 0x1 #define SIM_ALU_DECODE_IGE_BIT_POS 0x1 /* Number Of Op-code Groups. */ unsigned char const SIM_ALU_DECODE_OPCODE_GROUPS = 11; /* Op-code Groups. */ unsigned char const SIM_ALU_DECODE_LOGICAL_XCHG_OP_GROUP = 1; /* Group1: AND/OR/XOR/ADD/SUB Operations: fxxx 1010 ssss ssss. */ unsigned char const SIM_ALU_DECODE_AND_OR_ADD_SUB_OP_GROUP = 2; /* Group2: Logical Operations: 1000 1010 xxxx 1010. */ unsigned char const SIM_ALU_DECODE_BIT_OP_GROUP = 3; /* XCHG/Bit Operations: 1xxx 1010 xxxx 1010. */ unsigned char const SIM_ALU_DECODE_SET_DEST_BIT_GROUP = 4; /* Move value in bit of destination register: 1ddd dddd xbbb 0111. */ unsigned char const SIM_ALU_DECODE_JUMP_OP_GROUP = 5; #define JUMP_CHECK(insn) \ ( ((insn & _DECODE_4TO6_HIGHBYTE) == 0x0000) \ || ((insn & _DECODE_4TO6_HIGHBYTE) == 0x2000) \ || ((insn & _DECODE_4TO6_HIGHBYTE) == 0x6000) \ || ((insn & _DECODE_4TO6_HIGHBYTE) == 0x1000) \ || ((insn & _DECODE_4TO6_HIGHBYTE) == 0x5000) \ || ((insn & _DECODE_4TO6_HIGHBYTE) == 0x3000) \ || ((insn & _DECODE_4TO6_HIGHBYTE) == 0x7000) \ || ((insn & _DECODE_4TO6_HIGHBYTE) == 0x4000) ) /* JUMP operations: fxxx 1100 ssss ssss */ unsigned char const SIM_ALU_DECODE_RET_OP_GROUP = 6; /* RET Operations: 1xxx 1100 0000 1101 */ unsigned char const SIM_ALU_DECODE_MOVE_SRC_DST_GROUP = 7; /* Move src into dest register: fddd dddd ssss ssss */ unsigned char const SIM_ALU_DECODE_SET_SRC_BIT_GROUP = 8; /* Move value in bit of source register: fbbb 0111 ssss ssss */ unsigned char const SIM_ALU_DECODE_DJNZ_CALL_PUSH_OP_GROUP = 9; /* PUSH, DJNZ and CALL operations: fxxx 1101 ssss ssss */ unsigned char const SIM_ALU_DECODE_POP_OP_GROUP = 10; /* POP operation: 1ddd dddd 0000 1101 */ unsigned char const SIM_ALU_DECODE_CMP_SRC_OP_GROUP = 11; /* GLOBAL */ char unres_reg_name[20]; static char * get_reg_name (unsigned char reg_code, type1 arg_pos) { unsigned char module; unsigned char index; int ix = 0; reg_entry const *reg_x; mem_access_syntax const *syntax; mem_access *mem_acc; module = 0; index = 0; module = (reg_code & MASK_LOW_BYTE); index = (reg_code & MASK_HIGH_BYTE); index = index >> 4; /* Search the system register table. */ for (reg_x = &system_reg_table[0]; reg_x->reg_name != NULL; ++reg_x) if ((reg_x->Mod_name == module) && (reg_x->Mod_index == index)) return reg_x->reg_name; /* Serch pheripheral table. */ for (ix = 0; ix < num_of_reg; ix++) { reg_x = &new_reg_table[ix]; if ((reg_x->Mod_name == module) && (reg_x->Mod_index == index)) return reg_x->reg_name; } for (mem_acc = &mem_table[0]; mem_acc->name != NULL || !mem_acc; ++mem_acc) { if (reg_code == mem_acc->opcode) { for (syntax = mem_access_syntax_table; mem_access_syntax_table != NULL || mem_access_syntax_table->name; ++syntax) if (!strcmp (mem_acc->name, syntax->name)) { if ((arg_pos == syntax->type) || (syntax->type == BOTH)) return mem_acc->name; break; } } } memset (unres_reg_name, 0, 20); sprintf (unres_reg_name, "%01x%01xh", index, module); return unres_reg_name; } static bfd_boolean check_move (unsigned char insn0, unsigned char insn8) { bfd_boolean first = FALSE; bfd_boolean second = FALSE; char *first_reg; char *second_reg; reg_entry const *reg_x; const unsigned char module1 = insn0 & MASK_LOW_BYTE; const unsigned char index1 = ((insn0 & 0x70) >> 4); const unsigned char module2 = insn8 & MASK_LOW_BYTE; const unsigned char index2 = ((insn8 & MASK_HIGH_BYTE) >> 4); /* DST */ if (((insn0 & MASK_LOW_BYTE) == MASK_LOW_BYTE) && ((index1 == 0) || (index1 == 1) || (index1 == 2) || (index1 == 5) || (index1 == 4) || (index1 == 6))) first = TRUE; else if (((insn0 & MASK_LOW_BYTE) == 0x0D) && (index1 == 0)) first = TRUE; else if ((module1 == 0x0E) && ((index1 == 0) || (index1 == 1) || (index1 == 2))) first = TRUE; else { for (reg_x = &system_reg_table[0]; reg_x->reg_name != NULL && reg_x; ++reg_x) { if ((reg_x->Mod_name == module1) && (reg_x->Mod_index == index1) && ((reg_x->rtype == Reg_16W) || (reg_x->rtype == Reg_8W))) { /* IP not allowed. */ if ((reg_x->Mod_name == 0x0C) && (reg_x->Mod_index == 0x00)) continue; /* A[AP] not allowed. */ if ((reg_x->Mod_name == 0x0A) && (reg_x->Mod_index == 0x01)) continue; first_reg = reg_x->reg_name; first = TRUE; break; } } } if (!first) /* No need to check further. */ return FALSE; if (insn0 & 0x80) { /* SRC */ if (((insn8 & MASK_LOW_BYTE) == MASK_LOW_BYTE) && ((index2 == 0) || (index2 == 1) || (index2 == 2) || (index2 == 4) || (index2 == 5) || (index2 == 6))) second = TRUE; else if (((insn8 & MASK_LOW_BYTE) == 0x0D) && (index2 == 0)) second = TRUE; else if ((module2 == 0x0E) && ((index2 == 0) || (index2 == 1) || (index2 == 2))) second = TRUE; else { for (reg_x = &system_reg_table[0]; reg_x->reg_name != NULL && reg_x; ++reg_x) { if ((reg_x->Mod_name == (insn8 & MASK_LOW_BYTE)) && (reg_x->Mod_index == (((insn8 & 0xf0) >> 4)))) { second = TRUE; second_reg = reg_x->reg_name; break; } } } if (second) { if ((module1 == 0x0A && index1 == 0x0) && (module2 == 0x0A && index2 == 0x01)) return FALSE; return TRUE; } return FALSE; } return first; } static void maxq_print_arg (MAX_ARG_TYPE arg, struct disassemble_info * info, group_info grp) { switch (arg) { case FLAG_C: info->fprintf_func (info->stream, "C"); break; case FLAG_NC: info->fprintf_func (info->stream, "NC"); break; case FLAG_Z: info->fprintf_func (info->stream, "Z"); break; case FLAG_NZ: info->fprintf_func (info->stream, "NZ"); break; case FLAG_S: info->fprintf_func (info->stream, "S"); break; case FLAG_E: info->fprintf_func (info->stream, "E"); break; case FLAG_NE: info->fprintf_func (info->stream, "NE"); break; case ACC_BIT: info->fprintf_func (info->stream, "Acc"); if ((grp.flag & BIT_NO) == BIT_NO) info->fprintf_func (info->stream, ".%d", grp.bit_no); break; case A_BIT_0: info->fprintf_func (info->stream, "#0"); break; case A_BIT_1: info->fprintf_func (info->stream, "#1"); break; default: break; } } static unsigned char get_group (const unsigned int insn) { if (check_move ((insn >> 8), (insn & _DECODE_LOWBYTE))) return 8; if ((insn & _DECODE_LOWNIB_HIGHBYTE) == 0x0A00) { /* && condition with sec part added on 26 May for resolving 2 & 3 grp conflict. */ if (((insn & _DECODE_LOWNIB_LOWBYTE) == 0x000A) && ((insn & _DECODE_GET_F_HIGHBYTE) == 0x8000)) { if ((insn & _DECODE_HIGHNIB_HIGHBYTE) == 0x8000) return 2; else return 3; } return 1; } else if ((insn & _DECODE_LOWNIB_HIGHBYTE) == 0x0C00) { if (((insn & _DECODE_LOWBYTE) == 0x000D) && JUMP_CHECK (insn) && ((insn & _DECODE_GET_F_HIGHBYTE) == 0x8000)) return 6; else if ((insn & _DECODE_LOWBYTE) == 0x008D) return 7; return 5; } else if (((insn & _DECODE_LOWNIB_HIGHBYTE) == 0x0D00) && (((insn & _DECODE_4TO6_HIGHBYTE) == 0x3000) || ((insn & _DECODE_4TO6_HIGHBYTE) == 0x4000) || ((insn & _DECODE_4TO6_HIGHBYTE) == 0x5000) || ((insn & _DECODE_4TO6_HIGHBYTE) == 0x0000))) return 10; else if ((insn & _DECODE_LOWBYTE) == 0x000D) return 11; else if ((insn & _DECODE_LOWBYTE) == 0x008D) return 12; else if ((insn & _DECODE_0TO6_HIGHBYTE) == 0x7800) return 13; else if ((insn & _DECODE_LOWNIB_HIGHBYTE) == 0x0700) return 9; else if (((insn & _DECODE_LOWNIB_LOWBYTE) == 0x0007) && ((insn & _DECODE_GET_F_HIGHBYTE) == 0x8000)) return 4; return 8; } static void get_insn_opcode (const unsigned int insn, group_info *i) { static unsigned char pfx_flag = 0; static unsigned char count_for_pfx = 0; i->flag ^= i->flag; i->bit_no ^= i->bit_no; i->dst ^= i->dst; i->fbit ^= i->fbit; i->group_no ^= i->group_no; i->src ^= i->src; i->sub_opcode ^= i->sub_opcode; if (count_for_pfx > 0) count_for_pfx++; if (((insn >> 8) == 0x0b) || ((insn >> 8) == 0x2b)) { pfx_flag = 1; count_for_pfx = 1; } i->group_no = get_group (insn); if (pfx_flag && (i->group_no == 0x0D) && (count_for_pfx == 2) && ((insn & _DECODE_0TO6_HIGHBYTE) == 0x7800)) { i->group_no = 0x08; count_for_pfx = 0; pfx_flag ^= pfx_flag; } switch (i->group_no) { case 1: i->sub_opcode = ((insn & _DECODE_4TO6_HIGHBYTE) >> 12); i->flag |= SUB_OP; i->src = ((insn & _DECODE_LOWBYTE)); i->flag |= SRC; i->fbit = ((insn & _DECODE_GET_F_HIGHBYTE) >> 15); i->flag |= FORMAT; break; case 2: i->sub_opcode = ((insn & _DECODE_HIGHNIB_LOWBYTE) >> 4); i->flag |= SUB_OP; break; case 3: i->sub_opcode = ((insn & _DECODE_HIGHNIB_HIGHBYTE) >> 12); i->flag |= SUB_OP; i->bit_no = ((insn & _DECODE_HIGHNIB_LOWBYTE) >> 4); i->flag |= BIT_NO; break; case 4: i->sub_opcode = ((insn & _DECODE_BIT7_LOWBYTE) >> 7); i->flag |= SUB_OP; i->dst = ((insn & _DECODE_0TO6_HIGHBYTE) >> 8); i->flag |= DST; i->bit_no = ((insn & _DECODE_4TO6_LOWBYTE) >> 4); i->flag |= BIT_NO; break; case 5: i->sub_opcode = ((insn & _DECODE_4TO6_HIGHBYTE) >> 12); i->flag |= SUB_OP; i->src = ((insn & _DECODE_LOWBYTE)); i->flag |= SRC; i->fbit = ((insn & _DECODE_GET_F_HIGHBYTE) >> 15); i->flag |= FORMAT; break; case 6: i->sub_opcode = ((insn & _DECODE_HIGHNIB_HIGHBYTE) >> 12); i->flag |= SUB_OP; break; case 7: i->sub_opcode = ((insn & _DECODE_HIGHNIB_HIGHBYTE) >> 12); i->flag |= SUB_OP; break; case 8: i->dst = ((insn & _DECODE_0TO6_HIGHBYTE) >> 8); i->flag |= DST; i->src = ((insn & _DECODE_LOWBYTE)); i->flag |= SRC; i->fbit = ((insn & _DECODE_GET_F_HIGHBYTE) >> 15); i->flag |= FORMAT; break; case 9: i->sub_opcode = ((insn & _DECODE_0TO2_HIGHBYTE) >> 8); i->flag |= SUB_OP; i->bit_no = ((insn & _DECODE_4TO6_HIGHBYTE) >> 12); i->flag |= BIT_NO; i->fbit = ((insn & _DECODE_GET_F_HIGHBYTE) >> 15); i->flag |= FORMAT; i->src = ((insn & _DECODE_LOWBYTE)); i->flag |= SRC; break; case 10: i->sub_opcode = ((insn & _DECODE_4TO6_HIGHBYTE) >> 12); i->flag |= SUB_OP; i->src = ((insn & _DECODE_LOWBYTE)); i->flag |= SRC; i->fbit = ((insn & _DECODE_GET_F_HIGHBYTE) >> 15); i->flag |= FORMAT; break; case 11: i->dst = ((insn & _DECODE_0TO6_HIGHBYTE) >> 8); i->flag |= DST; break; case 12: i->dst = ((insn & _DECODE_0TO6_HIGHBYTE) >> 8); i->flag |= DST; break; case 13: i->sub_opcode = ((insn & _DECODE_4TO6_HIGHBYTE) >> 12); i->flag |= SUB_OP; i->src = ((insn & _DECODE_LOWBYTE)); i->flag |= SRC; i->fbit = ((insn & _DECODE_GET_F_HIGHBYTE) >> 15); i->flag |= FORMAT; break; } return; } /* Print one instruction from MEMADDR on INFO->STREAM. Return the size of the instruction (always 2 on MAXQ20). */ static int print_insn (bfd_vma memaddr, struct disassemble_info *info, enum bfd_endian endianess) { /* The raw instruction. */ unsigned char insn[2], insn0, insn8, derived_code; unsigned int format; unsigned int actual_operands; unsigned int i; /* The group_info collected/decoded. */ group_info grp; MAXQ20_OPCODE_INFO const *opcode; int status; format = 0; status = info->read_memory_func (memaddr, (bfd_byte *) & insn[0], 2, info); if (status != 0) { info->memory_error_func (status, memaddr, info); return -1; } insn8 = insn[1]; insn0 = insn[0]; /* FIXME: Endianness always little. */ if (endianess == BFD_ENDIAN_BIG) get_insn_opcode (((insn[0] << 8) | (insn[1])), &grp); else get_insn_opcode (((insn[1] << 8) | (insn[0])), &grp); derived_code = ((grp.group_no << 4) | grp.sub_opcode); if (insn[0] == 0 && insn[1] == 0) { info->fprintf_func (info->stream, "00 00"); return 2; } /* The opcode is always in insn0. */ for (opcode = &op_table[0]; opcode->name != NULL; ++opcode) { if (opcode->instr_id == derived_code) { if (opcode->instr_id == 0x3D) { if ((grp.bit_no == 0) && (opcode->arg[1] != A_BIT_0)) continue; if ((grp.bit_no == 1) && (opcode->arg[1] != A_BIT_1)) continue; if ((grp.bit_no == 3) && (opcode->arg[0] != 0)) continue; } info->fprintf_func (info->stream, "%s ", opcode->name); actual_operands = 0; if ((grp.flag & SRC) == SRC) actual_operands++; if ((grp.flag & DST) == DST) actual_operands++; /* If Implict FLAG in the Instruction. */ if ((opcode->op_number > actual_operands) && !((grp.flag & SRC) == SRC) && !((grp.flag & DST) == DST)) { for (i = 0; i < opcode->op_number; i++) { if (i == 1 && (opcode->arg[1] != NO_ARG)) info->fprintf_func (info->stream, ","); maxq_print_arg (opcode->arg[i], info, grp); } } /* DST is ABSENT in the grp. */ if ((opcode->op_number > actual_operands) && ((grp.flag & SRC) == SRC)) { maxq_print_arg (opcode->arg[0], info, grp); info->fprintf_func (info->stream, " "); if (opcode->instr_id == 0xA4) info->fprintf_func (info->stream, "LC[0]"); if (opcode->instr_id == 0xA5) info->fprintf_func (info->stream, "LC[1]"); if ((grp.flag & SRC) == SRC) info->fprintf_func (info->stream, ","); } if ((grp.flag & DST) == DST) { if ((grp.flag & BIT_NO) == BIT_NO) { info->fprintf_func (info->stream, " %s.%d", get_reg_name (grp.dst, (type1) 0 /*DST*/), grp.bit_no); } else info->fprintf_func (info->stream, " %s", get_reg_name (grp.dst, (type1) 0)); } /* SRC is ABSENT in the grp. */ if ((opcode->op_number > actual_operands) && ((grp.flag & DST) == DST)) { info->fprintf_func (info->stream, ","); maxq_print_arg (opcode->arg[1], info, grp); info->fprintf_func (info->stream, " "); } if ((grp.flag & SRC) == SRC) { if ((grp.flag & DST) == DST) info->fprintf_func (info->stream, ","); if ((grp.flag & BIT_NO) == BIT_NO) { format = opcode->format; if ((grp.flag & FORMAT) == FORMAT) format = grp.fbit; if (format == 1) info->fprintf_func (info->stream, " %s.%d", get_reg_name (grp.src, (type1) 1 /*SRC*/), grp.bit_no); if (format == 0) info->fprintf_func (info->stream, " #%02xh.%d", grp.src, grp.bit_no); } else { format = opcode->format; if ((grp.flag & FORMAT) == FORMAT) format = grp.fbit; if (format == 1) info->fprintf_func (info->stream, " %s", get_reg_name (grp.src, (type1) 1 /*SRC*/)); if (format == 0) info->fprintf_func (info->stream, " #%02xh", (grp.src)); } } return 2; } } info->fprintf_func (info->stream, "Unable to Decode : %02x %02x", insn[0], insn[1]); return 2; } int print_insn_maxq_little (bfd_vma memaddr, struct disassemble_info *info) { return print_insn (memaddr, info, BFD_ENDIAN_LITTLE); }