/* * Interactive disassembler (IDA). * Copyright (c) Hex-Rays * ALL RIGHTS RESERVED. * * Instruction decoder * */ #include "ins.hpp" static const int bcond_map[16] = { NEC850_BV, NEC850_BL, NEC850_BZ, NEC850_BNH, NEC850_BN, NEC850_BR, NEC850_BLT, NEC850_BLE, NEC850_BNV, NEC850_BNC, NEC850_BNZ, NEC850_BH, NEC850_BP, NEC850_BSA, NEC850_BGE, NEC850_BGT }; //------------------------------------------------------------------------ // The instruction formats 5 to 10 have bit10 and bit9 on and are a word // The rest of the instructions are half-word and their format is 1 to 4 int detect_inst_len(uint16 w) { return ((w & 0x600) == 0x600) ? 4 : 2; } //------------------------------------------------------------------------ // Fetchs an instruction (uses ua_next_xxx(insn)) of a correct size (ready for decoding) // Returns the size of the instruction int fetch_instruction(uint32 *w, insn_t &insn) { uint16 hw = insn.get_next_word(); int r = detect_inst_len(hw); if ( r == 4 ) *w = (insn.get_next_word() << 16) | hw; else *w = hw; return r; } //------------------------------------------------------------------------ static sval_t fetch_disp32(const uint32 w, insn_t &ins) { // 15 0 31 16 47 32 // xxxxxxxxxxxxxxxx ddddddddddddddd0 DDDDDDDDDDDDDDDD uint32 d_low = (w >> 16);// ddddddddddddddd0 if ( ins.size == 2 ) d_low = ins.get_next_word(); else if ( ins.size != 4 ) { // bad format ins.size = 0; ins.itype = 0; return 1; } uint16 d_high = ins.get_next_word(); // DDDDDDDDDDDDDDDD int32 addr = (d_high<<16) | d_low; return sval_t(addr); } //------------------------------------------------------------------------ static bool decode_disp23(const uint32 w, insn_t &ins, int opidx, op_dtype_t dt) { // LD.B disp23 [reg1] , reg3 // 00000111100RRRRR wwwwwddddddd0101 DDDDDDDDDDDDDDDD // ddddddd is the lower 7 bits of disp23. // DDDDDDDDDDDDDDDD is the higher 16 bits of disp23 // LD.H disp23[reg1], reg3 // 00000111100RRRRR wwwwwdddddd00111 DDDDDDDDDDDDDDDD // dddddd is the lower side bits 6 to 1 of disp23. // DDDDDDDDDDDDDDDD is the higher 16 bits of disp23. // we need at least 32 bits of opcode here if ( ins.size != 4 ) return false; uint16 d_low = ( w >> 20 ) & 0x7F; // ddddddd if ( dt != dt_byte && ( d_low & 1 ) != 0 ) return false; uint16 d_high = ins.get_next_word(); // DDDDDDDDDDDDDDDD sval_t addr = ( d_high << 7 ) | d_low; SIGN_EXTEND(sval_t, addr, 23); op_t &op = ins.ops[opidx]; op.type = o_displ; op.reg = w & 0x1F; op.addr = addr; op.dtype = dt; op.specflag1 = N850F_USEBRACKETS | N850F_OUTSIGNED | N850F_VAL32; return true; } //------------------------------------------------------------------------ static void set_opreg(op_t &op, int reg, op_dtype_t dtyp = dt_dword) { op.type = o_reg; op.dtype = dtyp; op.reg = reg; } //---------------------------------------------------------------------- // Create operand of condition type inline void set_opcond(op_t &x, uval_t value) { x.type = o_cond; x.dtype = dt_qword; x.value = value; } //------------------------------------------------------------------------ static void set_opimm(op_t &op, uval_t value, int dtyp = dt_dword) { op.type = o_imm; op.dtype = dtyp; op.value = value; } //------------------------------------------------------------------------ // Decodes an instruction "w" into cmd structure bool nec850_t::decode_coprocessor(const uint32 w, insn_t &ins) const { // 11111 1 33222 2 222 22 2111 1 // 54321 098765 43210 10987 6 543 21 0987 6 // reg2 |opcode|reg1 |reg3 |b|cat|ty|subo|b| // ..... |111111|.....|.....|1|...|..|....|0| int r1 = w & 0x1F; int r2 = ( w & 0xF800 ) >> 11; int r3 = ( w & 0xF8000000 ) >> 27; int cat = ( w >> 23 ) & 7; int typ = ( w >> 21 ) & 3; int subop = ( w >> 17 ) & 0xF; ins.itype = NEC850_NULL; // we only support V850E2M and RH850 FP instructions if ( !is_v850e2m() ) return false; if ( typ == 0 && cat == 0 ) { // CMOVF.D: cat = 000, type = 00, subop = 1fff, reg3 != 0 // CMOVF.S : cat = 000, type = 00, subop = 0fff, reg3 != 0 // TRFSR: cat = 000, type = 00, subop = 0fff, reg1 = 0, reg3 = 0 if ( r3 != 0 ) { // CMOVF.S|D fcbit, reg1, reg2, reg3 ins.itype = ( subop & 8 ) ? NEC850_CMOVF_D : NEC850_CMOVF_S; int fcbit = subop & 7; set_opimm(ins.Op1, fcbit); set_opreg(ins.Op2, r1); set_opreg(ins.Op3, r2); set_opreg(ins.Op4, r3); } else if ( subop < 8 ) { ins.itype = NEC850_TRFSR; int fcbit = subop & 7; set_opimm(ins.Op1, fcbit); } } else if ( typ == 1 && cat == 0 && r3 < 0x10 ) { // CMPF.D: cat = 000, type = 01, subop = 1fff, reg3 = 0FFFF // CMPF.S : cat = 000, type = 01, subop = 0fff, reg3 = 0FFFF // CMPF.S|D fcond, reg2, reg1, fcbit ins.itype = ( subop & 8 ) ? NEC850_CMPF_D : NEC850_CMPF_S; int fcbit = subop & 7; set_opcond(ins.Op1, r3); set_opreg(ins.Op2, r2); set_opreg(ins.Op3, r1); set_opimm(ins.Op4, fcbit); } else if ( typ == 3 ) { // reg1, reg2, reg3 if ( cat == 0 ) { switch ( subop & 7 ) { case 0: ins.itype = ( subop & 8 ) ? NEC850_ADDF_D : NEC850_ADDF_S; break; case 1: ins.itype = ( subop & 8 ) ? NEC850_SUBF_D : NEC850_SUBF_S; break; case 2: ins.itype = ( subop & 8 ) ? NEC850_MULF_D : NEC850_MULF_S; break; case 4: ins.itype = ( subop & 8 ) ? NEC850_MAXF_D : NEC850_MAXF_S; break; case 5: ins.itype = ( subop & 8 ) ? NEC850_MINF_D : NEC850_MINF_S; break; case 7: ins.itype = ( subop & 8 ) ? NEC850_DIVF_D : NEC850_DIVF_S; break; } } else if ( cat == 1 && subop < 4 ) { if ( is_rh850() ) { uint16 itypes[] = { NEC850_FMAF_S, NEC850_FMSF_S, NEC850_FNMAF_S, NEC850_FNMSF_S }; ins.itype = itypes[subop]; } } if ( ins.itype != NEC850_NULL ) { bool dbl = ( subop & 8 ) != 0; op_dtype_t dt = dbl ? dt_double : dt_float; set_opreg(ins.Op1, r1, dt); set_opreg(ins.Op2, r2, dt); set_opreg(ins.Op3, r3, dt); } } else if ( typ == 2 && cat == 0 ) { // reg2, reg3 conversions op_dtype_t dtsrc = dt_float, dtdst = dt_float; switch ( subop ) { case 0: { // TRNCF.SW cat = 0 type = 2 subop = 0 reg1 = 1 // CEILF.SW cat = 0 type = 2 subop = 0 reg1 = 2 // FLOORF.SW cat = 0 type = 2 subop = 0 reg1 = 3 // CVTF.SW cat = 0 type = 2 subop = 0 reg1 = 4 // TRNCF.SUW cat = 0 type = 2 subop = 0 reg1 = 17 // CEILF.SUW cat = 0 type = 2 subop = 0 reg1 = 18 // FLOORF.SUW cat = 0 type = 2 subop = 0 reg1 = 19 // CVTF.SUW cat = 0 type = 2 subop = 0 reg1 = 20 static const int ops[] = { NEC850_NULL, NEC850_TRNCF_SW, NEC850_CEILF_SW, NEC850_FLOORF_SW, // 0-3 NEC850_CVTF_SW, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 4-7 NEC850_NULL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 8-11 NEC850_NULL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 12-15 NEC850_NULL, NEC850_TRNCF_SUW, NEC850_CEILF_SUW, NEC850_FLOORF_SUW, // 16-19 NEC850_CVTF_SUW // 20 }; if ( r1 < qnumber(ops) ) ins.itype = ops[r1]; dtsrc = dt_float; dtdst = dt_dword; } break; case 1: { // CVTF.WS cat=0 type=2 subop=1 reg1=0 dw f // CVTF.LS cat=0 type=2 subop=1 reg1=1 dq f // CVTF.HS cat=0 type=2 subop=1 reg1=2 h f // CVTF.SH cat=0 type=2 subop=1 reg1=3 f h // CVTF.UWS cat=0 type=2 subop=1 reg1=16 dw f // CVTF.ULS cat=0 type=2 subop=1 reg1=17 dq f static const int ops[] = { NEC850_CVTF_WS, NEC850_CVTF_LS, NEC850_CVTF_HS, NEC850_CVTF_SH, // 0-3 NEC850_CVTF_SW, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 4-7 NEC850_NULL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 8-11 NEC850_NULL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 12-15 NEC850_CVTF_UWS, NEC850_CVTF_ULS // 16-17 }; if ( r1 < qnumber(ops) ) ins.itype = ops[r1]; // NB: we use dt_float for half-precision op_dtype_t srct[] = { dt_dword, dt_qword, dt_float, dt_float }; dtsrc = srct[r1&3]; dtdst = dt_float; } break; case 2: { // TRNCF.SL cat=0 type=2 subop=2 reg1=1 // CEILF.SL cat=0 type=2 subop=2 reg1=2 // FLOORF.SL cat=0 type=2 subop=2 reg1=3 // CVTF.SL cat=0 type=2 subop=2 reg1=4 // TRNCF.SUL cat=0 type=2 subop=2 reg1=17 // CEILF.SUL cat=0 type=2 subop=2 reg1=18 // FLOORF.SUL cat=0 type=2 subop=2 reg1=19 // CVTF.SUL cat=0 type=2 subop=2 reg1=20 static const int ops[] = { NEC850_NULL, NEC850_TRNCF_SL, NEC850_CEILF_SL, NEC850_FLOORF_SL, // 0-3 NEC850_CVTF_SL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 4-7 NEC850_NULL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 8-11 NEC850_NULL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 12-15 NEC850_NULL, NEC850_TRNCF_SUL, NEC850_CEILF_SUL, NEC850_FLOORF_SUL, // 16-19 NEC850_CVTF_SUL // 20 }; if ( r1 < qnumber(ops) ) ins.itype = ops[r1]; dtsrc = dt_float; dtdst = dt_qword; } break; case 4: case 12: { // ABSF.S cat = 0 type = 2 subop = 4 reg1 = 0 // NEGF.S cat = 0 type = 2 subop = 4 reg1 = 1 // ABSF.D cat = 0 type = 2 subop = 12 reg1 = 0 // NEGF.D cat = 0 type = 2 subop = 12 reg1 = 1 if ( r1 == 0 ) ins.itype = subop == 4 ? NEC850_ABSF_S : NEC850_ABSF_D; else if ( r1 == 1 ) ins.itype = subop == 4 ? NEC850_NEGF_S : NEC850_NEGF_D; dtsrc = subop == 4 ? dt_float: dt_double; dtdst = dtsrc; } break; case 7: case 15: { // SQRTF.S cat=0 type=2 subop=7 reg1=0 // RECIPF.S cat=0 type=2 subop=7 reg1=1 // RSQRTF.S cat=0 type=2 subop=7 reg1=2 // SQRTF.D cat=0 type=2 subop=15 reg1=0 // RECIPF.D cat=0 type=2 subop=15 reg1=1 // RSQRTF.D cat=0 type=2 subop=15 reg1=2 if ( r1 == 0 ) ins.itype = subop == 7 ? NEC850_SQRTF_S : NEC850_SQRTF_D; else if ( r1 == 1 ) ins.itype = subop == 7 ? NEC850_RECIPF_S : NEC850_RECIPF_D; else if ( r1 == 2 ) ins.itype = subop == 7 ? NEC850_RSQRTF_S : NEC850_RSQRTF_D; dtsrc = subop == 7 ? dt_float : dt_double; dtdst = dtsrc; } break; case 8: { // TRNCF.DW cat=0 type=2 subop=8 reg1=1 // CEILF.DW cat=0 type=2 subop=8 reg1=2 // FLOORF.DW cat=0 type=2 subop=8 reg1=3 // CVTF.DW cat=0 type=2 subop=8 reg1=4 // TRNCF.DUW cat=0 type=2 subop=8 reg1=17 // CEILF.DUW cat=0 type=2 subop=8 reg1=18 // FLOORF.DUW cat=0 type=2 subop=8 reg1=19 // CVTF.DUW cat=0 type=2 subop=8 reg1=20 static const int ops[] = { NEC850_NULL, NEC850_TRNCF_DW, NEC850_CEILF_DW, NEC850_FLOORF_DW, // 0-3 NEC850_CVTF_DW, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 4-7 NEC850_NULL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 8-11 NEC850_NULL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 12-15 NEC850_NULL, NEC850_TRNCF_DUW, NEC850_CEILF_DUW, NEC850_FLOORF_DUW, // 16-19 NEC850_CVTF_DUW // 20 }; if ( r1 < qnumber(ops) ) ins.itype = ops[r1]; dtsrc = dt_double; dtdst = dt_dword; } break; case 9: { // CVTF.WD cat=0 type=2 subop=9 reg1=0 dw d // CVTF.LD cat=0 type=2 subop=9 reg1=1 dq d // CVTF.SD cat=0 type=2 subop=9 reg1=2 f d // CVTF.DS cat=0 type=2 subop=9 reg1=3 d f // CVTF.UWD cat=0 type=2 subop=9 reg1=16 dw d // CVTF.ULD cat=0 type=2 subop=9 reg1=17 dq d static const int ops[] = { NEC850_CVTF_WD, NEC850_CVTF_LD, NEC850_CVTF_SD, NEC850_CVTF_DS, // 0-3 NEC850_NULL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 4-7 NEC850_NULL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 8-11 NEC850_NULL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 12-15 NEC850_CVTF_UWD, NEC850_CVTF_ULD // 16-17 }; if ( r1 < qnumber(ops) ) ins.itype = ops[r1]; op_dtype_t srct[] = { dt_dword, dt_qword, dt_float, dt_double }; dtsrc = srct[r1 & 3]; dtdst = r1 == 3 ? dt_float : dt_double; } break; case 10: { // TRNCF.DL cat=0 type=2 subop=10 reg1=1 // CEILF.DL cat=0 type=2 subop=10 reg1=2 // FLOORF.DL cat=0 type=2 subop=10 reg1=3 // CVTF.DL cat=0 type=2 subop=10 reg1=4 // TRNCF.DUL cat=0 type=2 subop=10 reg1=17 // CEILF.DUL cat=0 type=2 subop=10 reg1=18 // FLOORF.DUL cat=0 type=2 subop=10 reg1=19 // CVTF.DUL cat=0 type=2 subop=10 reg1=20 static const int ops[] = { NEC850_NULL, NEC850_TRNCF_DL, NEC850_CEILF_DL, NEC850_FLOORF_DL, // 0-3 NEC850_CVTF_DL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 4-7 NEC850_NULL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 8-11 NEC850_NULL, NEC850_NULL, NEC850_NULL, NEC850_NULL, // 12-15 NEC850_NULL, NEC850_TRNCF_DUL, NEC850_CEILF_DUL, NEC850_FLOORF_DUL, // 16-19 NEC850_CVTF_DUL // 20 }; if ( r1 < qnumber(ops) ) ins.itype = ops[r1]; dtsrc = dt_double; dtdst = dt_qword; } break; } if ( ins.itype != NEC850_NULL ) { set_opreg(ins.Op1, r2, dtsrc); set_opreg(ins.Op2, r3, dtdst); } } if ( ins.itype == NEC850_NULL && is_v850e2m() && ( cat >> 1 ) == 1 ) { // reg1, reg2, reg3, reg4 // MADDF.S: cat = 01W type = 00, subop = WWWW // MSUBF.S : cat = 01W type = 01, subop = WWWW // NMADDF.S : cat = 01W type = 10, subop = WWWW // NMSUBF.S : cat = 01W type = 11, subop = WWWW // WWWWW: reg4. (The least significant bit of reg4 is bit 23.) int r4 = (subop << 1) | (cat & 1); static const uint16 itypes[] = { NEC850_MADDF_S, NEC850_MSUBF_S, NEC850_NMADDF_S, NEC850_NMSUBF_S }; ins.itype = itypes[typ]; set_opreg(ins.Op1, r1, dt_float); set_opreg(ins.Op2, r2, dt_float); set_opreg(ins.Op3, r3, dt_float); set_opreg(ins.Op4, r4, dt_float); } if ( ins.itype != NEC850_NULL ) { ins.auxpref |= N850F_FP; return true; } return false; } //------------------------------------------------------------------------ // Decodes an instruction "w" into cmd structure bool nec850_t::decode_instruction(const uint32 w, insn_t &ins) { #define PARSE_L12 (((w & 1) << 11) | (w >> 21)) #define PARSE_R1 (w & 0x1F) #define PARSE_R2 ((w & 0xF800) >> 11) typedef struct { int itype; int flags; } itype_flags_t; // If an instruction deals with displacement it should // initialize this pointer to the operand location. // At the end we will transform the operand to o_mem // if we know how to resolve its address op_t *displ_op = NULL; do { uint32 op; // // Format I // op = (w & 0x7E0) >> 5; // Take bit5->bit10 if ( op <= 0xF ) { static const int inst_1[] = { /* MOV reg1, reg2 */ NEC850_MOV, /* NOT reg1, reg2 */ NEC850_NOT, /* DIVH reg1, reg2 */ NEC850_DIVH, /* JMP [reg1] */ NEC850_JMP, /* SATSUBR reg1, reg2 */ NEC850_SATSUBR, /* SATSUB reg1, reg2 */ NEC850_SATSUB, /* SATADD reg1, reg2 */ NEC850_SATADD, /* MULH reg1, reg2 */ NEC850_MULH, /* OR reg1, reg2 */ NEC850_OR, /* XOR reg1, reg2 */ NEC850_XOR, /* AND reg1, reg2 */ NEC850_AND, /* TST reg1, reg2 */ NEC850_TST, /* SUBR reg1, reg2 */ NEC850_SUBR, /* SUB reg1, reg2 */ NEC850_SUB, /* ADD reg1, reg2 */ NEC850_ADD, /* CMP reg1, reg2 */ NEC850_CMP }; // // NOP, Equivalent to MOV R, r (where R=r=0) if ( w == 0 ) { ins.itype = NEC850_NOP; ins.Op1.type = o_void; ins.Op1.dtype = dt_void; break; } uint16 r1 = PARSE_R1; uint16 r2 = PARSE_R2; if ( is_v850e() && op == 2 && r1 == 0 ) { switch ( r2 ) { case 0: if ( is_v850e2m() ) ins.itype = NEC850_RIE; break; case 0x1C: if ( is_rh850() ) ins.itype = NEC850_DBHVTRAP; break; case 0x1D: if ( is_rh850() ) ins.itype = NEC850_DBCP; break; case 0x1E: if ( is_v850e2m() ) ins.itype = NEC850_RMTRAP; break; case 0x1F: ins.itype = NEC850_DBTRAP; break; default: if ( is_v850e2() && r2 < 0x10 ) { ins.itype = NEC850_FETRAP; set_opimm(ins.Op1, r2); } break; } if ( ins.itype != 0 ) break; } ins.itype = inst_1[op]; set_opreg(ins.Op1, r1); if ( is_v850e() ) { if ( r2 == 0 ) { if ( is_v850e2m() && op == 0 ) { switch ( r1 ) { case 0x1C: if ( is_rh850() ) ins.itype = NEC850_SYNCI; else ins.itype = NEC850_NULL; break; case 0x1D: ins.itype = NEC850_SYNCE; break; case 0x1E: ins.itype = NEC850_SYNCM; break; case 0x1F: ins.itype = NEC850_SYNCP; break; default: ins.itype = NEC850_NULL; break; } if ( ins.itype != NEC850_NULL ) { ins.Op1.type = o_void; ins.Op2.type = o_void; break; } } else if ( ins.itype == NEC850_DIVH ) { ins.itype = NEC850_SWITCH; break; } else if ( ins.itype == NEC850_SATSUBR ) { ins.itype = NEC850_ZXB; break; } else if ( ins.itype == NEC850_SATSUB ) { ins.itype = NEC850_SXB; break; } else if ( ins.itype == NEC850_SATADD ) { ins.itype = NEC850_ZXH; break; } else if ( ins.itype == NEC850_MULH ) { ins.itype = NEC850_SXH; break; } } // case when r2 != 0 else { // SLD.BU / SLD.HU if ( ins.itype == NEC850_JMP ) { bool sld_hu = (w >> 4) & 1; uint32 addr = w & 0xF; if ( sld_hu ) { ins.itype = NEC850_SLD_HU; ins.Op1.dtype = dt_word; addr <<= 1; } else { ins.itype = NEC850_SLD_BU; ins.Op1.dtype = dt_byte; } ins.Op1.type = o_displ; displ_op = &ins.Op1; ins.Op1.reg = rEP; ins.Op1.addr = addr; ins.Op1.specflag1 = N850F_USEBRACKETS; set_opreg(ins.Op2, r2); break; } } } if ( ins.itype == NEC850_JMP && r2 == 0 ) { ins.Op1.specflag1 = N850F_USEBRACKETS; } else { set_opreg(ins.Op2, r2); } break; } // Format II else if ( op <= 0x17 ) { if ( PARSE_R2 == 0 && op == 0x17 && is_v850e2m() ) { // 48-bit Format VI jr/jarl // JARL disp32, reg1: 00000010111RRRRR ddddddddddddddd0 DDDDDDDDDDDDDDDD // JR disp32: 0000001011100000 ddddddddddddddd0 DDDDDDDDDDDDDDDD uint16 reg = PARSE_R1; sval_t addr = fetch_disp32(w, ins); if ( (addr & 1) != 0 ) return false; ins.Op1.addr = ins.ip + addr; ins.Op1.type = o_near; ins.Op1.specflag1 = N850F_VAL32; if ( reg == 0 ) { ins.itype = NEC850_JR; } else { ins.itype = NEC850_JARL; set_opreg(ins.Op2, reg); } break; } // flag used for sign extension static const itype_flags_t inst_2[] = { { NEC850_MOV, 1 }, /* MOV imm5, reg2 */ { NEC850_SATADD, 1 }, /* SATADD imm5, reg2 */ { NEC850_ADD, 1 }, /* ADD imm5, reg2 */ { NEC850_CMP, 1 }, /* CMP imm5, reg2 */ { NEC850_SHR, 0 }, /* SHR imm5, reg2 */ { NEC850_SAR, 0 }, /* SAR imm5, reg2 */ { NEC850_SHL, 0 }, /* SHL imm5, reg2 */ { NEC850_MULH, 1 }, /* MULH imm5, reg2 */ }; op -= 0x10; ins.itype = inst_2[op].itype; uint16 r2 = PARSE_R2; if ( is_v850e() ) { // // CALLT // if ( r2 == 0 && (ins.itype == NEC850_SATADD || ins.itype == NEC850_MOV) ) { ins.itype = NEC850_CALLT; set_opimm(ins.Op1, w & 0x3F, dt_byte); if ( g_ctbp_ea != BADADDR ) { // resolve callt addr using ctbp ea_t ctp = g_ctbp_ea + (ins.Op1.value << 1); ins.Op1.type = o_near; ins.Op1.addr = g_ctbp_ea + get_word(ctp); } break; } } sval_t v = PARSE_R1; if ( inst_2[op].flags == 1 ) { SIGN_EXTEND(sval_t, v, 5); ins.Op1.specflag1 |= N850F_OUTSIGNED; } set_opimm(ins.Op1, v, dt_byte); set_opreg(ins.Op2, r2); // ADD imm, reg -> reg = reg + imm if ( ins.itype == NEC850_ADD && r2 == rSP ) ins.auxpref |= N850F_SP; break; } // Format VI else if ( op >= 0x30 && op <= 0x37 ) { static const itype_flags_t inst_6[] = { // itype flags (1=signed) { NEC850_ADDI, 1 }, /* ADDI imm16, reg1, reg2 */ { NEC850_MOVEA, 1 }, /* MOVEA imm16, reg1, reg2 */ { NEC850_MOVHI, 0 }, /* MOVHI imm16, reg1, reg2 */ { NEC850_SATSUBI, 1 }, /* SATSUBI imm16, reg1, reg2 */ { NEC850_ORI, 0 }, /* ORI imm16, reg1, reg2 */ { NEC850_XORI, 0 }, /* XORI imm16, reg1, reg2 */ { NEC850_ANDI, 0 }, /* ANDI imm16, reg1, reg2 */ { NEC850_MULHI, 0 }, /* MULHI imm16, reg1, reg2 */ }; op -= 0x30; ins.itype = inst_6[op].itype; uint16 r1 = PARSE_R1; uint16 r2 = PARSE_R2; uint32 imm = w >> 16; // // V850E instructions if ( is_v850e() && r2 == 0 ) { if ( ins.itype == NEC850_MULHI ) { if ( !is_v850e2() ) return false; // "Do not specify r0 as the destination register reg2." if ( ( imm & 1 ) != 0 ) { // RH850: LOOP reg1,disp16 // 00000110111RRRRR ddddddddddddddd1 if ( !is_rh850() || r1 == 0 ) return false; // "Do not specify r0 for reg1." ins.itype = NEC850_LOOP; set_opreg(ins.Op1, r1); imm ^= 1; // clear bit 0 sval_t addr = ins.ip - imm; ins.Op2.addr = addr; ins.Op2.type = o_near; } else { // V850E2: jmp disp32 [reg1] // 00000110111RRRRR ddddddddddddddd0 DDDDDDDDDDDDDDDD sval_t addr = fetch_disp32(w, ins); if ( ( addr & 1 ) != 0 ) return false; ins.Op1.addr = addr; ins.Op1.type = o_displ; ins.Op1.specflag1 = N850F_OUTSIGNED | N850F_VAL32 | N850F_USEBRACKETS; ins.Op1.reg = r1; ins.itype = NEC850_JMP; } break; } // MOV imm32, R if ( ins.itype == NEC850_MOVEA ) { imm |= ins.get_next_word() << 16; set_opimm(ins.Op1, imm); ins.itype = NEC850_MOV; set_opreg(ins.Op2, r1); break; } // DISPOSE imm5, list12 (reg1 == 0) // DISPOSE imm5, list12, [reg1] else if ( ins.itype == NEC850_SATSUBI || ins.itype == NEC850_MOVHI ) { r1 = (w >> 16) & 0x1F; uint16 L = PARSE_L12; ins.auxpref |= N850F_SP; // SP reference set_opimm(ins.Op1, (w & 0x3E) >> 1, dt_byte); ins.Op2.value = L; ins.Op2.type = o_reglist; ins.Op2.dtype = dt_word; if ( r1 != 0 ) { set_opreg(ins.Op3, r1); ins.Op3.specflag1 = N850F_USEBRACKETS; ins.itype = NEC850_DISPOSE_r; } else { ins.itype = NEC850_DISPOSE_r0; } break; } } bool is_signed = inst_6[op].flags == 1; set_opimm(ins.Op1, is_signed ? sval_t(int16(imm)) : imm); ins.Op1.specflag1 |= N850F_OUTSIGNED; set_opreg(ins.Op2, r1); set_opreg(ins.Op3, r2); // (ADDI|MOVEA) imm, sp, sp -> sp = sp + imm if ( (ins.itype == NEC850_ADDI || ins.itype == NEC850_MOVEA) && ((r1 == rSP) && (r2 == rSP)) ) { ins.auxpref |= N850F_SP; } break; } // Format VII - LD.x else if ( op == 0x38 || op == 0x39 ) { displ_op = &ins.Op1; ins.Op1.type = o_displ; ins.Op1.phrase = PARSE_R1; // R set_opreg(ins.Op2, PARSE_R2); uint32 addr; // LD.B if ( op == 0x38 ) { addr = w >> 16; ins.itype = NEC850_LD_B; ins.Op1.dtype = dt_byte; } else { // Bit16 is cleared for LD.H if ( (w & (1 << 16)) == 0 ) { ins.itype = NEC850_LD_H; ins.Op1.dtype = dt_word; } // LD.W else { ins.itype = NEC850_LD_W; ins.Op1.dtype = dt_dword; } addr = ((w & 0xFFFE0000) >> 17) << 1; } ins.Op1.specflag1 = N850F_USEBRACKETS | N850F_OUTSIGNED; ins.Op1.addr = int16(addr); break; } // Format VII - ST.x else if ( op == 0x3A || op == 0x3B ) { // (1) ST.B reg2, disp16 [reg1] // (2) ST.H reg2, disp16 [reg1] // (3) ST.W reg2, disp16 [reg1] set_opreg(ins.Op1, PARSE_R2); ins.Op2.type = o_displ; displ_op = &ins.Op2; ins.Op2.reg = PARSE_R1; ins.Op2.specflag1 = N850F_USEBRACKETS | N850F_OUTSIGNED; // ST.B uint32 addr; if ( op == 0x3A ) { addr = w >> 16; ins.itype = NEC850_ST_B; ins.Op2.dtype = dt_byte; } else { // Bit16 is cleared for ST.H if ( (w & (1 << 16)) == 0 ) { ins.itype = NEC850_ST_H; ins.Op2.dtype = dt_word; } else { ins.itype = NEC850_ST_W; ins.Op2.dtype = dt_dword; } addr = ((w & 0xFFFE0000) >> 17) << 1; } ins.Op2.addr = int16(addr); break; } // Format XIII - PREPARE / LD.BU else if ( is_v850e() && ((w >> 16) & 0x1) // this bit is important to differentiate between JARL/JR instructions && (op == 0x3C || op == 0x3D) ) { uint16 r2 = PARSE_R2; uint16 subop = (w >> 16) & 0x1F; // PREPARE if ( r2 == 0 && (subop == 1 || (subop & 7) == 3) ) { ins.auxpref |= N850F_SP; ins.Op1.value = PARSE_L12; ins.Op1.type = o_reglist; ins.Op1.dtype = dt_word; set_opimm(ins.Op2, (w & 0x3E) >> 1, dt_byte); if ( subop == 1 ) { ins.itype = NEC850_PREPARE_i; } else { ins.itype = NEC850_PREPARE_sp; uint16 ff = subop >> 3; switch ( ff ) { case 0: // disassembles as: PREPARE list12, imm5, sp // meaning: load sp into ep set_opreg(ins.Op3, rSP); break; // the other cases disassemble with imm (the 3rd operand) directly processed: // f=1->ep=sign_extend(imm16), f=2->ep=imm16 shl 16, f=3->ep=imm32 case 1: // c: a8 07 0b 80 prepare {r24}, 20, 0x1 // 10: 01 00 set_opimm(ins.Op3, sval_t(int16(ins.get_next_word()))); break; case 2: // 2: a8 07 13 80 prepare {r24}, 20, 0x10000 // 6: 01 00 set_opimm(ins.Op3, ins.get_next_word() << 16); break; case 3: // 2: a8 07 1b 80 prepare {r24}, 20, 0x1 // 6: 01 00 00 00 set_opimm(ins.Op3, ins.get_next_dword()); break; } } } else if ( r2 == 0 && is_v850e2m() ) { // disp23 variants ( Format XIV) // LD.BU disp23 [reg1] , reg3 // 00000111101RRRRR wwwwwddddddd0101 DDDDDDDDDDDDDDDD // LD.HU disp23 [reg1] , reg3 // 00000111101RRRRR wwwwwdddddd00111 DDDDDDDDDDDDDDDD // ST.H reg3, disp23 [reg1] // 00000111101RRRRR wwwwwdddddd01101 DDDDDDDDDDDDDDDD // ST.H reg3, disp23 [reg1] // 00000111101RRRRR wwwwwdddddd01101 DDDDDDDDDDDDDDDD // LD.B disp23 [reg1] , reg3 // 00000111100RRRRR wwwwwddddddd0101 DDDDDDDDDDDDDDDD // LD.H disp23 [reg1] , reg3 // 00000111100RRRRR wwwwwdddddd00111 DDDDDDDDDDDDDDDD // LD.W disp23 [reg1] , reg3 // 00000111100RRRRR wwwwwdddddd01001 DDDDDDDDDDDDDDDD // LD.DW disp23[reg1], reg3 // 00000111101RRRRR wwwwwdddddd01001 DDDDDDDDDDDDDDDD // ST.B reg3, disp23 [reg1] // 00000111100RRRRR wwwwwddddddd1101 DDDDDDDDDDDDDDDD // ST.W reg3, disp23 [reg1] // 00000111100RRRRR wwwwwdddddd01111 DDDDDDDDDDDDDDDD // ST.DW reg3, disp23[reg1] // 00000111101RRRRR wwwwwdddddd01111 DDDDDDDDDDDDDDDD // RRRRR = reg1, wwwww = reg3. // ddddddd is the lower 7 bits of disp23. // DDDDDDDDDDDDDDDD is the higher 16 bits of disp23. subop = ( w >> 16 ) & 0xF; bool sign = ( op & 1 ) == 0; uint32 r3 = ( w & 0xF8000000 ) >> 27; switch ( subop ) { case 5: ins.itype = sign ? NEC850_LD_B : NEC850_LD_BU; if ( !decode_disp23(w, ins, 0, dt_byte) ) return false; set_opreg(ins.Op2, r3); break; case 7: ins.itype = sign ? NEC850_LD_H : NEC850_LD_HU; if ( !decode_disp23(w, ins, 0, dt_word) ) return false; set_opreg(ins.Op2, r3); break; case 9: if ( !sign && !is_rh850() ) return false; ins.itype = sign ? NEC850_LD_W : NEC850_LD_DW; if ( !decode_disp23(w, ins, 0, dt_dword) ) return false; set_opreg(ins.Op2, r3); break; case 13: ins.itype = sign ? NEC850_ST_B : NEC850_ST_H; if ( !decode_disp23(w, ins, 1, sign ? dt_byte : dt_word) ) return false; set_opreg(ins.Op1, r3); break; case 15: if ( !sign && !is_rh850() ) return false; ins.itype = sign ? NEC850_ST_W : NEC850_ST_DW; if ( !decode_disp23(w, ins, 1, dt_dword) ) return false; set_opreg(ins.Op1, r3); break; } } else { // LD.BU disp16 [reg1] , reg2 // rrrrr11110bRRRRR ddddddddddddddd1 // ddddddddddddddd is the higher 15 bits of disp16, and b is bit 0 of disp16. // rrrrr != 00000 ( Do not specify r0 for reg2. ) if ( r2 == 0 ) return false; uint16 r1 = PARSE_R1; ins.itype = NEC850_LD_BU; ins.Op1.type = o_displ; displ_op = &ins.Op1; ins.Op1.reg = r1; ins.Op1.addr = int16(((w >> 16) & ~1) | ((w & 0x20) >> 5)); ins.Op1.dtype = dt_byte; ins.Op1.specflag1 = N850F_USEBRACKETS | N850F_OUTSIGNED; set_opreg(ins.Op2, r2); } break; } // Format VIII else if ( op == 0x3E ) { // parse sub-opcode (b15..b14) op = ((w & 0xC000) >> 14); static const int inst_8[] = { NEC850_SET1, NEC850_NOT1, NEC850_CLR1, NEC850_TST1 }; ins.itype = inst_8[op]; set_opimm(ins.Op1, ((w & 0x3800) >> 11), dt_byte); ins.Op2.type = o_displ; displ_op = &ins.Op2; ins.Op2.addr = int16(w >> 16); ins.Op2.offb = 2; ins.Op2.dtype = dt_byte; ins.Op2.reg = PARSE_R1; // R ins.Op2.specflag1 = N850F_USEBRACKETS | N850F_OUTSIGNED; break; } // // Format IX, X // else if ( op == 0x3F ) { if ( (w & ( 1 << 16 )) == 0 && ( w & ( 1 << 26 ) ) != 0 ) // coprocessor insn return decode_coprocessor(w, ins); // // Format X // // Const opcodes if ( w == 0x16087E0 ) // EI ins.itype = NEC850_EI; else if ( w == 0x16007E0 ) // DI ins.itype = NEC850_DI; else if ( w == 0x14007E0 ) // RETI ins.itype = NEC850_RETI; else if ( w == 0x12007E0 ) // HALT ins.itype = NEC850_HALT; else if ( w == 0xffffffff ) ins.itype = NEC850_BREAKPOINT; else if ( (w >> 5) == 0x8003F ) //lint !e587 predicate always false // TRAP { ins.itype = NEC850_TRAP; set_opimm(ins.Op1, PARSE_R1, dt_byte); break; } if ( ins.itype != 0 ) break; if ( is_v850e1f() && !is_v850e2m() ) { // E1F opcodes (ref. U16374EJ1V0UM) int subop = ( w >> 16 ) & 0x7FF; int r3 = ( w & 0xF8000000 ) >> 27; switch ( subop ) { // Format F:I reg1, reg2, reg3 case 0x3E0: ins.itype = NEC850_DIVF_S; goto OPS_FI; case 0x3E4: ins.itype = NEC850_SUBF_S; goto OPS_FI; case 0x3E8: ins.itype = NEC850_ADDF_S; goto OPS_FI; case 0x3EC: ins.itype = NEC850_MULF_S; goto OPS_FI; case 0x3F0: ins.itype = NEC850_MINF_S; goto OPS_FI; case 0x3F4: ins.itype = NEC850_MAXF_S; OPS_FI: set_opreg(ins.Op1, PARSE_R1); set_opreg(ins.Op2, PARSE_R2); set_opreg(ins.Op3, r3); break; // Format F:II reg2, reg3 case 0x360: ins.itype = NEC850_CVT_SW; OPS_FII: set_opreg(ins.Op1, PARSE_R2); set_opreg(ins.Op2, r3); break; case 0x368: ins.itype = NEC850_TRNC_SW; goto OPS_FII; case 0x370: ins.itype = NEC850_CVT_WS; goto OPS_FII; case 0x3F8: ins.itype = NEC850_NEGF_S; goto OPS_FII; case 0x3FC: ins.itype = NEC850_ABSF_S; goto OPS_FII; // Format F:IV reg2 or reg3 case 0x378: if ( r3 != 0 ) { // STFF EFG,reg2 ins.itype = NEC850_STFF; set_opreg(ins.Op1, EFG); set_opreg(ins.Op2, r3); } else { ins.itype = NEC850_TRFF; // no operands } break; case 0x37C: // STFC ECT,reg2 ins.itype = NEC850_STFC; set_opreg(ins.Op1, ECT); set_opreg(ins.Op2, r3); break; case 0x37A: if ( r3 == 0 ) { // LDFF reg2,EFG ins.itype = NEC850_LDFF; set_opreg(ins.Op1, PARSE_R2); set_opreg(ins.Op2, EFG); } break; case 0x37E: if ( r3 == 0 ) { // LDFC reg2,ECT ins.itype = NEC850_LDFC; set_opreg(ins.Op1, PARSE_R2); set_opreg(ins.Op2, ECT); } break; } if ( ins.itype != 0 ) break; } // Still in format 10 (op = 0x3F) if ( is_v850e() ) { if ( is_v850e2m() ) { if ( w == 0x14807E0 ) ins.itype = NEC850_EIRET; else if ( w == 0x14a07E0 ) ins.itype = NEC850_FERET; else if ( ( w & 0xc7ffffe0 ) == 0x0160d7e0 ) { ins.itype = NEC850_SYSCALL; int v8 = (w & 0x1f) | ((w >> (27 - 5)) & 0xe0); set_opimm(ins.Op1, v8); } else if ( is_rh850() ) { int subop = ( w >> 16 ) & 0x7FF; switch ( subop ) { case 0x20: case 0x40: { // LDSR reg2, regID, selID // rrrrr111111RRRRR sssss00000100000 // rrrrr: regID, sssss: selID, RRRRR: reg2 // STSR regID, reg2, selID // rrrrr111111RRRRR sssss00001000000 // rrrrr: regID, sssss: selID, RRRRR: reg2 bool is_ld = subop == 0x20; ins.itype = is_ld ? NEC850_LDSR : NEC850_STSR; uint32 selid = ( w & 0xF8000000 ) >> 27; uint32 regid = PARSE_R1; uint32 r2 = PARSE_R2; if ( is_ld ) { // In this instruction, general-purpose register reg2 is used as the source register, but, for // mnemonic description convenience, the general - purpose register reg1 field is used in the // opcode.The meanings of the register specifications in the mnemonic descriptions and // opcode therefore differ from those of other instructions. set_opreg(ins.Op1, regid); set_opreg(ins.Op2, r2 + rSR0); } else { set_opreg(ins.Op1, regid + rSR0); set_opreg(ins.Op2, r2); } if ( selid != 0 ) set_opimm(ins.Op3, selid); } break; case 0x30: case 0x50: { bool is_ld = subop == 0x30; if ( is_ld ) { ins.itype = NEC850_LDTC_SR; set_opreg(ins.Op1, PARSE_R1); set_opimm(ins.Op2, PARSE_R2); } else { ins.itype = NEC850_STTC_SR; set_opimm(ins.Op1, PARSE_R1); set_opreg(ins.Op2, PARSE_R2); } uint32 selid = ( w & 0xF8000000 ) >> 27; set_opimm(ins.Op3, selid); } break; case 0x32: case 0x52: { bool is_ld = subop == 0x32; uint32 selid = ( w & 0xF8000000 ) >> 27; switch ( selid ) { case 0: ins.itype = is_ld ? NEC850_LDTC_GR: NEC850_STTC_GR; set_opreg(ins.Op2, PARSE_R2); set_opreg(ins.Op1, PARSE_R1); break; case 1: ins.itype = is_ld ? NEC850_LDTC_VR : NEC850_STTC_VR; set_opreg(ins.Op1, is_ld ? PARSE_R2 : PARSE_R1); set_opreg(ins.Op2, is_ld ? PARSE_R1 : PARSE_R2); break; case 31: ins.itype = is_ld ? NEC850_LDTC_PC : NEC850_STTC_PC; set_opreg(ins.Op1, is_ld ? PARSE_R1 : PARSE_R2); break; default: break; } } break; case 0x34: case 0x54: { bool is_ld = subop == 0x34; if ( is_ld ) { ins.itype = NEC850_LDVC_SR; set_opreg(ins.Op1, PARSE_R1); set_opimm(ins.Op2, PARSE_R2); } else { ins.itype = NEC850_STVC_SR; set_opimm(ins.Op1, PARSE_R1); set_opreg(ins.Op2, PARSE_R2); } uint32 selid = ( w & 0xF8000000 ) >> 27; set_opimm(ins.Op3, selid); } break; case 0xC4: case 0xC6: { // ROTL imm5, reg2, reg3 // rrrrr111111iiiii wwwww00011000100 // ROTL reg1, reg2, reg3 // rrrrr111111RRRRR wwwww00011000110 ins.itype = NEC850_ROTL; uint32 r1 = PARSE_R1; uint32 r2 = PARSE_R2; uint32 r3 = (w & 0xF8000000 ) >> 27; if ( subop == 0xC4 ) { set_opimm(ins.Op1, r1); } else { set_opreg(ins.Op1, r1); } set_opreg(ins.Op2, r2); set_opreg(ins.Op3, r3); } break; case 0x110: ins.itype = NEC850_HVTRAP; set_opimm(ins.Op1, PARSE_R1, dt_byte); break; case 0x132: ins.itype = NEC850_EST; break; case 0x134: ins.itype = NEC850_DST; break; case 0x378: { // LDL.W [reg1], reg3 // 00000111111RRRRR wwwww01101111000 ins.itype = NEC850_LDL_W; uint32 r3 = ( w & 0xF8000000 ) >> 27; set_opreg(ins.Op1, PARSE_R1); ins.Op1.specflag1 = N850F_USEBRACKETS; set_opreg(ins.Op2, r3); break; } case 0x37A: { // STC.W reg3, [reg1] // 00000111111RRRRR wwwww01101111010 ins.itype = NEC850_STC_W; uint32 r3 = ( w & 0xF8000000 ) >> 27; set_opreg(ins.Op1, r3); set_opreg(ins.Op2, PARSE_R1); ins.Op2.specflag1 = N850F_USEBRACKETS; break; } case 0x160: { uint32 r1 = PARSE_R1; uint32 r2 = PARSE_R2; uint32 r3 = ( w & 0xF8000000 ) >> 27; int w1 = w >> 16; switch ( r2 ) { case 8: // pushsp case 0xB: // dbpush case 0xC: // popsp { // PUSHSP rh-rt // 01000111111RRRRR wwwww00101100000 // POPSP rh-rt // 01100111111RRRRR wwwww00101100000 // RRRRR indicates rh. wwwww indicates rt. ins.itype = r2 == 8 ? NEC850_PUSHSP : r2 == 0xB ? NEC850_DBPUSH : NEC850_POPSP; ins.Op1.type = o_regrange; ins.Op1.regrange_high = r1; ins.Op1.regrange_low = r3; } break; case 0x10: switch ( w1 ) { case 0x8960: ins.itype = NEC850_TLBAI; break; case 0x8160: ins.itype = NEC850_TLBVI; break; case 0xC160: ins.itype = NEC850_TLBS; break; case 0xE960: ins.itype = NEC850_TLBR; break; case 0xE160: ins.itype = NEC850_TLBW; break; } break; case 0x18: { // JARL [reg1], reg3 // 11000111111RRRRR WWWWW00101100000 set_opreg(ins.Op1, r1); ins.Op1.specflag1 = N850F_USEBRACKETS; set_opreg(ins.Op2, r3); ins.itype = NEC850_JARL; } break; case 0x19: { ins.itype = NEC850_DBTAG; int v8 = (w & 0x1f) | ((w1 >> 6) & 0xe0); set_opimm(ins.Op1, v8); } break; case 0x1A: { ins.itype = NEC850_HVCALL; int v8 = (w & 0x1f) | ((w1 >> 6) & 0xe0); set_opimm(ins.Op1, v8); } break; case 0x1B: { // PREF prefop, [reg1] // 11011111111RRRRR PPPPP00101100000 // PPPPP indicates prefop ins.itype = NEC850_PREF; set_opimm(ins.Op1, r3); set_opreg(ins.Op2, r1); } break; case 0x1Cu: case 0x1Du: case 0x1Eu: case 0x1Fu: { // CACHE cacheop, [reg1] // 111pp111111RRRRR PPPPP00101100000 // ppPPPPP indicates cacheop int cacheop = ( ( r2 & 3 ) << 5 ) | r3; if ( r1 == 0x1f && cacheop == 0x7E ) { ins.itype = NEC850_CLL; } else { ins.itype = NEC850_CACHE; set_opimm(ins.Op1, cacheop); set_opreg(ins.Op2, r1); } } break; } } break; default: if ( w == 0x1200FE0 ) ins.itype = NEC850_SNOOZE; else if ( (w&0x10000) == 0 ) { uint o0 = ( w >> 20 ) & 0x7F; if ( o0 == 9 || o0 == 11 || o0 == 13 ) { // BINS reg1, pos, width, reg2 // rrrrr111111RRRRR MMMMK 0001001 LLL0 msb >= 16, lsb >= 16 // rrrrr111111RRRRR MMMMK 0001011 LLL0 msb >= 16, lsb < 16 // rrrrr111111RRRRR MMMMK 0001101 LLL0 msb < 16, lsb < 16 // Most significant bit of field to be updated : msb = pos + width - 1 // Least significant bit of field to be updated : lsb = pos // MMMM = lower 4 bits of msb, KLLL = lower 4 bits of lsb uint16 whi = w >> 16; uint lsb = ( whi >> 1 ) & 7; lsb |= ( whi >> 8 ) & 8; uint msb = ( whi >> 12 ) & 0xF; if ( o0 == 9 || o0 == 11 ) msb += 16; if ( o0 == 9 ) lsb += 16; uint width = msb - lsb + 1; ins.itype = NEC850_BINS; set_opreg(ins.Op1, PARSE_R1); set_opimm(ins.Op2, lsb); set_opimm(ins.Op3, width); set_opreg(ins.Op4, PARSE_R2); } } break; } if ( ins.itype != 0 ) break; } if ( ins.itype != 0 ) break; } if ( w == 0x14607E0 ) { ins.itype = NEC850_DBRET; break; } else if ( w == 0x14407E0 ) { ins.itype = NEC850_CTRET; break; } else if ( (w >> 16) & 0x1 ) { int r2 = PARSE_R2; int r1 = PARSE_R1; if ( r2 != 0 ) { // V850E: LD.HU disp16 [reg1], reg2 // rrrrr111111RRRRR ddddddddddddddd1 ins.itype = NEC850_LD_HU; ins.Op1.type = o_displ; displ_op = &ins.Op1; ins.Op1.reg = r1; ins.Op1.addr = uint32(( w >> 17 ) << 1); ins.Op1.dtype = dt_word; ins.Op1.specflag1 = N850F_USEBRACKETS | N850F_OUTSIGNED; set_opreg(ins.Op2, r2); } else if ( is_rh850() ) { // RH850: Bcond disp17 // 00000111111DCCCC ddddddddddddddd1 sval_t dest = uint32(( w >> 17 ) << 1); if ( (w & 0x10) != 0 ) dest += 0x10000; // D SIGN_EXTEND(sval_t, dest, 17); ins.itype = bcond_map[w & 0xF]; ins.Op1.dtype = dt_word; ins.Op1.type = o_near; ins.Op1.addr = ea_t(dest + ins.ip); } break; } // // XI Group match (reg1, reg2, reg3) // uint32 r1 = PARSE_R1; uint32 r2 = PARSE_R2; uint32 r3 = ( w & 0xF8000000 ) >> 27; op = (w & 0x7FF0000) >> 16; if ( op == 0x220 ) ins.itype = NEC850_MUL; else if ( op == 0x222 ) ins.itype = NEC850_MULU; else if ( op == 0x280 ) ins.itype = NEC850_DIVH_r3; else if ( op == 0x282 ) ins.itype = NEC850_DIVHU; else if ( op == 0x2C0 ) ins.itype = NEC850_DIV; else if ( op == 0x2C2 ) ins.itype = NEC850_DIVU; else if ( is_v850e2() ) { if ( ( op & 1 ) == 0 ) { if ( ( op >> 5 ) == 0x1D ) { // ADF int cc = ( op >> 1 ) & 0xF; if ( cc == CC_SAT ) { ins.itype = NEC850_SATADD; } else { ins.itype = NEC850_ADF; set_opcond(ins.Op1, cc); set_opreg(ins.Op2, r1); set_opreg(ins.Op3, r2); set_opreg(ins.Op4, r3); break; } } else if ( ( op >> 5 ) == 0x1C ) { // SBF int cc = ( op >> 1 ) & 0xF; if ( cc == CC_SAT ) { ins.itype = NEC850_SATSUB; } else { ins.itype = NEC850_SBF; set_opcond(ins.Op1, cc); set_opreg(ins.Op2, r1); set_opreg(ins.Op3, r2); set_opreg(ins.Op4, r3); break; } } else if ( ( op >> 6 ) == 0xF ) { // MAC rrrrr111111RRRRR wwww0011110mmmm0 // MACU rrrrr111111RRRRR wwww0011111mmmm0 ins.itype = ( op & 0x20 ) ? NEC850_MACU : NEC850_MAC; int r4 = op&0x1F; set_opreg(ins.Op1, r1); set_opreg(ins.Op2, r2); set_opreg(ins.Op3, r3); set_opreg(ins.Op4, r4); break; } } switch ( op ) { case 0x82: ins.itype = NEC850_SHR; break; case 0xa2: ins.itype = NEC850_SAR; break; case 0xc2: ins.itype = NEC850_SHL; break; case 0xEE: ins.itype = NEC850_CAXI; ins.Op1.specflag1 |= N850F_USEBRACKETS; break; case 0x2FE: ins.itype = NEC850_DIVQU; break; case 0x2FC: ins.itype = NEC850_DIVQ; break; } } // process the match if ( ins.itype != 0 ) { set_opreg(ins.Op1, r1); set_opreg(ins.Op2, r2); set_opreg(ins.Op3, r3); break; } // // XII/IX Group match (reg2, reg3) // if ( op == 0x340 ) ins.itype = NEC850_BSW; else if ( op == 0x342 ) ins.itype = NEC850_BSH; else if ( op == 0x344 ) ins.itype = NEC850_HSW; else if ( is_v850e2() ) { switch ( op ) { case 0x346: ins.itype = NEC850_HSH; break; case 0x360: ins.itype = NEC850_SCH0R; break; case 0x362: ins.itype = NEC850_SCH1R; break; case 0x364: ins.itype = NEC850_SCH0L; break; case 0x366: ins.itype = NEC850_SCH1L; break; } } // process the match if ( ins.itype != 0 ) { set_opreg(ins.Op1, r2); set_opreg(ins.Op2, r3); break; } // // match CMOV // op = w >> 16; op = ((op & 0x7E0) >> 4) | (op & 0x1); if ( op == 0x30 || op == 0x32 ) { uint32 cc = (w & 0x1E0000) >> 17; ins.itype = NEC850_CMOV; set_opcond(ins.Op1, cc); r1 = PARSE_R1; r2 = PARSE_R2; r3 = (w & 0xF8000000) >> 27; if ( op == 0x32 ) // CMOV cc, reg1, reg2, reg3 { set_opreg(ins.Op2, r1); } else { // CMOV cc, imm5, reg2, reg3 sval_t v = r1; SIGN_EXTEND(sval_t, v, 5); set_opimm(ins.Op2, v, dt_byte); ins.Op2.specflag1 |= N850F_OUTSIGNED; } set_opreg(ins.Op3, r2); set_opreg(ins.Op4, r3); break; } // // match MUL[U]_i9 // op = w >> 16; op = ((op & 0x7C0) >> 4) | (op & 0x3); if ( op == 0x24 || op == 0x26 ) { sval_t imm = (((w & 0x3C0000) >> 18) << 5) | (w & 0x1F); if ( op == 0x24 ) { ins.itype = NEC850_MUL; SIGN_EXTEND(sval_t, imm, 9); ins.Op1.specflag1 |= N850F_OUTSIGNED; } else ins.itype = NEC850_MULU; set_opimm(ins.Op1, imm); set_opreg(ins.Op2, PARSE_R2); set_opreg(ins.Op3, (w & 0xF8000000) >> 27); break; } } // // Format IX // op = w >> 16; // take 2nd half-word as the opcode uint32 reg1 = PARSE_R1; uint32 reg2 = PARSE_R2; // SETF if ( op == 0 ) { if ( ( w & 0x10 ) == 0 ) { ins.itype = NEC850_SETF; set_opcond(ins.Op1, w & 0xF); set_opreg(ins.Op2, reg2); } else if ( is_v850e2m() ) { ins.itype = NEC850_RIE; uint imm5 = ( w >> 11 ) & 0x1F; uint imm4 = w & 0xF; set_opimm(ins.Op1, imm5); set_opimm(ins.Op2, imm4); } break; } switch ( op ) { case 0x20: // LDSR ins.itype = NEC850_LDSR; ins.Op2.reg = rSR0; // designate system register break; case 0x40: // STSR ins.itype = NEC850_STSR; ins.Op1.reg = rSR0; // designate system register break; case 0x80: // SHR ins.itype = NEC850_SHR; break; case 0xA0: // SAR ins.itype = NEC850_SAR; break; case 0xC0: // SHL ins.itype = NEC850_SHL; break; } if ( ins.itype != 0 ) { // Common stuff for the rest of Format 9 instructions ins.Op1.dtype = ins.Op2.dtype = dt_dword; ins.Op1.type = ins.Op2.type = o_reg; ins.Op1.reg += reg1; ins.Op2.reg += reg2; break; } // -> ins.itype == 0 // No match? Try V850E if ( is_v850e() ) { // SASF if ( op == 0x200 ) { ins.itype = NEC850_SASF; set_opcond(ins.Op1, w & 0xF); set_opreg(ins.Op2, reg2); break; } switch ( op ) { case 0xE0: // NOT1 ins.itype = NEC850_SET1; break; case 0xE2: // NOT1 ins.itype = NEC850_NOT1; break; case 0xE4: // CLR1 ins.itype = NEC850_CLR1; break; case 0xE6: // TST1 ins.itype = NEC850_TST1; break; default: return 0; // No match! } // Common set_opreg(ins.Op1, reg2, dt_byte); ins.Op2.dtype = dt_byte; displ_op = &ins.Op2; ins.Op2.type = o_displ; ins.Op2.addr = 0; ins.Op2.reg = reg1; ins.Op2.specflag1 = N850F_USEBRACKETS; } if ( ins.itype == 0 ) return 0; // unknown instruction break; } // // Format V // op = (w & 0x780) >> 6; // Take bit6->bit10 // JARL and JR if ( op == 0x1E ) { uint32 reg = PARSE_R2; sval_t addr = uint32((((w & 0x3F) << 15) | ((w & 0xFFFE0000) >> 17)) << 1); SIGN_EXTEND(sval_t, addr, 22); ins.Op1.addr = ins.ip + addr; ins.Op1.type = o_near; // per the docs, if reg is zero then JARL turns to JR if ( reg == 0 ) { ins.itype = NEC850_JR; } else { ins.itype = NEC850_JARL; set_opreg(ins.Op2, reg); } break; } // // Format III // op = (w & 0x780) >> 7; // Take bit7->bit10 // assert: op in [0, 0xF] // Bcond disp9 if ( op == 0xB ) { sval_t dest = ( ((w & 0x70) >> 4) | ((w & 0xF800) >> 8) ) << 1; SIGN_EXTEND(sval_t, dest, 9); ins.itype = bcond_map[w & 0xF]; ins.Op1.dtype = dt_word; ins.Op1.type = o_near; ins.Op1.addr = ea_t(dest + ins.ip); break; } // // Format IV // else if ( op >= 6 ) { uint32 reg2 = PARSE_R2; uint32 addr = (w & 0x7F); // zero extended int idx_d(-1), idx_r(-1); char dtyp_d(-1); // SLD.B if ( op == 6 ) { ins.itype = NEC850_SLD_B; idx_d = 0; idx_r = 1; dtyp_d = dt_byte; } // SLD.H else if ( op == 8 ) { ins.itype = NEC850_SLD_H; idx_d = 0; idx_r = 1; dtyp_d = dt_word; addr <<= 1; } // SLD.W else if ( op == 10 && ((w & 1) == 0) ) { ins.itype = NEC850_SLD_W; idx_d = 0; idx_r = 1; dtyp_d = dt_dword; addr <<= 1; } // SST.B else if ( op == 7 ) { ins.itype = NEC850_SST_B; idx_d = 1; idx_r = 0; dtyp_d = dt_byte; } // SST.H else if ( op == 9 ) { ins.itype = NEC850_SST_H; idx_d = 1; idx_r = 0; dtyp_d = dt_byte; // bit0 is already cleared, so the 7bit addr we read // can be shifted by one to transform it to 8bit addr <<= 1; } // SST.W else if ( op == 10 && ((w & 1) == 1) ) { ins.itype = NEC850_SST_W; idx_d = 1; idx_r = 0; dtyp_d = dt_dword; // clear lower bit because it is set, and shift by one // bit 15 0 // rrrrr1010dddddd1 addr = (addr & ~1) << 1; } if ( idx_d == -1 || idx_r == -1 || dtyp_d == -1 ) return false; // could not decode set_opreg(ins.ops[idx_r], reg2); ins.ops[idx_d].type = o_displ; displ_op = &ins.ops[idx_d]; ins.ops[idx_d].reg = rEP; ins.ops[idx_d].addr = addr; ins.ops[idx_d].dtype = dtyp_d; ins.ops[idx_d].specflag1 = N850F_USEBRACKETS; break; } // Unknown instructions ins.itype = NEC850_NULL; } while ( false ); // special cases when we have memory access through displacement if ( displ_op != NULL ) { // A displacement with GP and GP is set? if ( displ_op->reg == rGP && g_gp_ea != BADADDR ) { displ_op->type = o_mem; if ( ins.itype == NEC850_SLD_BU || ins.itype == NEC850_LD_BU || ins.itype == NEC850_SLD_HU || ins.itype == NEC850_LD_HU ) { displ_op->addr = short(displ_op->addr) + g_gp_ea; } else { displ_op->addr += g_gp_ea; } } // register zero access? else if ( displ_op->reg == rZERO ) { // since r0 is always 0, we can replace the operand by the complete address displ_op->type = o_mem; displ_op->specflag1 &= ~N850F_OUTSIGNED; if ( ins.itype == NEC850_LD_BU || ins.itype == NEC850_LD_HU ) displ_op->addr = short(displ_op->addr); } #ifdef __EA64__ if ( displ_op->type == o_mem ) { // truncate address to 32 bits if needed segment_t *s = getseg(displ_op->addr); if ( s == NULL || !s->is_64bit() ) displ_op->addr = uint32(displ_op->addr); } #endif } return ins.itype != 0; } //------------------------------------------------------------------------ // Analyze one instruction and fill 'insn' structure. // insn.ea contains address of instruction to analyze. // Return length of the instruction in bytes, 0 if instruction can't be decoded. // This function shouldn't change the database, flags or anything else. // All these actions should be performed only by u_emu() function. int nec850_t::nec850_ana(insn_t *pinsn) { insn_t &insn = *pinsn; if ( insn.ea & 0x1 ) return 0; uint32 w; fetch_instruction(&w, insn); if ( decode_instruction(w, insn) ) return insn.size; else return 0; }