/* * Interactive disassembler (IDA). * Copyright (c) Hex-Rays * ALL RIGHTS RESERVED. * * Processor emulator * */ #include #include #include #include #include "ins.hpp" #include "necv850.hpp" //---------------------------------------------------------------------- //#notify.is_sane_insn // is the instruction sane for the current file type? // arg: int no_crefs // 1: the instruction has no code refs to it. // ida just tries to convert unexplored bytes // to an instruction (but there is no other // reason to convert them into an instruction) // 0: the instruction is created because // of some coderef, user request or another // weighty reason. // The instruction is in 'cmd' // returns: 1-ok, <=0-no, the instruction isn't likely to appear in the program int nec850_t::nec850_is_sane_insn(const insn_t &insn, int /*no_crefs*/) const { #define CHECK_R0_WRITE(n) \ if ( ((Feature & CF_CHG ## n) != 0) \ && insn.Op ## n.is_reg(rZERO) ) \ { \ return 0; \ } int Feature = insn.get_canon_feature(ph); CHECK_R0_WRITE(1); CHECK_R0_WRITE(2); return 1; } //---------------------------------------------------------------------- int idaapi nec850_is_sp_based(const insn_t &insn, const op_t &x) { int res = OP_SP_ADD; if ( x.type == o_displ && x.reg == rSP ) return res | OP_SP_BASED; // check for movea 8, sp, r28 if ( insn.itype == NEC850_MOVEA && insn.Op2.is_reg(rSP) && x.type == o_imm ) return res | OP_SP_BASED; return res | OP_FP_BASED; } //---------------------------------------------------------------------- bool idaapi nec850_create_func_frame(func_t *pfn) { asize_t frsize; insn_t insn; if ( decode_insn(&insn, pfn->start_ea) != 0 && (insn.itype == NEC850_PREPARE_i || insn.itype == NEC850_PREPARE_sp) ) { frsize = insn.Op2.value * 4; } else { frsize = 0; } return add_frame(pfn, frsize, 0, 0); } //---------------------------------------------------------------------- int idaapi nec850_get_frame_retsize(const func_t * /*pfn*/) { return 0; } //---------------------------------------------------------------------- static bool spoils(const insn_t &insn, uint16 reg) { int n; switch ( insn.itype ) { case NEC850_ZXB: case NEC850_SXB: case NEC850_ZXH: case NEC850_SXH: n = 0; break; case NEC850_XOR: case NEC850_SUBR: case NEC850_SUB: case NEC850_STSR: case NEC850_SLD_B: case NEC850_SLD_H: case NEC850_SLD_W: case NEC850_SHR: case NEC850_SHL: case NEC850_SATSUBR: case NEC850_SATSUB: case NEC850_SATADD: case NEC850_SAR: case NEC850_OR: case NEC850_NOT: case NEC850_MULH: case NEC850_MOV: case NEC850_LD_B: case NEC850_LD_H: case NEC850_LD_W: case NEC850_JARL: case NEC850_AND: case NEC850_ADD: case NEC850_DIVH: case NEC850_BSW: case NEC850_BSH: case NEC850_HSW: case NEC850_SLD_BU: case NEC850_SLD_HU: case NEC850_LD_BU: case NEC850_LD_HU: n = 1; break; case NEC850_XORI: case NEC850_SATSUBI: case NEC850_ORI: case NEC850_MULHI: case NEC850_MOVHI: case NEC850_MOVEA: case NEC850_ANDI: case NEC850_ADDI: case NEC850_SETF: case NEC850_SASF: n = 2; break; case NEC850_CMOV: n = 4; break; case NEC850_MUL: case NEC850_MULU: case NEC850_DIVH_r3: case NEC850_DIVHU: case NEC850_DIV: case NEC850_DIVU: return insn.ops[1].is_reg(reg) || insn.ops[2].is_reg(reg); case NEC850_DISPOSE_r0: case NEC850_DISPOSE_r: return reg == rSP || reg_in_list12(reg, insn.Op2.value); case NEC850_PREPARE_sp: return reg == rSP; case NEC850_PREPARE_i: return reg == rSP || reg == rEP; default: return false; } return insn.ops[n].is_reg(reg); } //---------------------------------------------------------------------- // does the instruction spoil the flags? static bool spoils_flags(const insn_t &insn) { switch ( insn.itype ) { case NEC850_ADD: case NEC850_ADDI: case NEC850_ADF: case NEC850_AND: case NEC850_ANDI: case NEC850_BSH: case NEC850_BSW: case NEC850_CAXI: case NEC850_CLR1: case NEC850_CMP: case NEC850_CTRET: case NEC850_DIV: case NEC850_DIVH: case NEC850_DIVHU: case NEC850_DIVH_r3: case NEC850_DIVQ: case NEC850_DIVQU: case NEC850_DIVU: case NEC850_EIRET: case NEC850_FERET: case NEC850_HSH: case NEC850_HSW: case NEC850_NOT: case NEC850_NOT1: case NEC850_OR: case NEC850_ORI: case NEC850_RETI: case NEC850_SAR: case NEC850_SATADD: case NEC850_SATSUB: case NEC850_SATSUBI: case NEC850_SATSUBR: case NEC850_SBF: case NEC850_SCH0L: case NEC850_SCH0R: case NEC850_SCH1L: case NEC850_SCH1R: case NEC850_SET1: case NEC850_SHL: case NEC850_SHR: case NEC850_SUB: case NEC850_SUBR: case NEC850_TST: case NEC850_TST1: case NEC850_XOR: case NEC850_XORI: case NEC850_BINS: case NEC850_ROTL: return true; default: // other insns don't spoil fixed point flags return false; } } //---------------------------------------------------------------------- bool nec850_t::get_gp_based_addr(ea_t *target, const insn_t &_insn, const op_t &op) const { if ( g_gp_ea == BADADDR ) return false; if ( op.phrase == rGP ) { *target = g_gp_ea; return true; } uint16 op_phrase = op.phrase; *target = BADADDR; insn_t tmp = _insn; while ( true ) { flags_t F = get_flags(tmp.ea); if ( !is_flow(F) || has_xref(F) ) break; if ( decode_prev_insn(&tmp, tmp.ea) == BADADDR ) break; if ( tmp.itype == NEC850_MOVEA && tmp.Op2.reg == rGP && tmp.Op3.reg == op_phrase ) { *target = g_gp_ea + tmp.Op1.value; break; } if ( spoils(tmp, op_phrase) ) break; } return *target != BADADDR; } //---------------------------------------------------------------------- void nec850_t::handle_operand(const insn_t &insn, const op_t &op, bool isRead) { ea_t ea; flags_t F = get_flags(insn.ea); switch ( op.type ) { case o_imm: if ( op_adds_xrefs(F, op.n) ) insn.add_off_drefs(op, dr_O, 0); break; case o_displ: set_immd(insn.ea); if ( !is_defarg(F, op.n) ) { if ( may_create_stkvars() && op.reg == rSP ) { func_t *pfn = get_func(insn.ea); if ( pfn != NULL && insn.create_stkvar(op, op.addr, STKVAR_VALID_SIZE) ) op_stkvar(insn.ea, op.n); } else if ( get_gp_based_addr(&ea, insn, op) ) { refinfo_t ri; ri.flags = REF_OFF32|REFINFO_PASTEND|REFINFO_NOBASE|REFINFO_SIGNEDOP; ri.target = BADADDR; ri.base = ea; ri.tdelta = 0; op_offset_ex(insn.ea, op.n, &ri); F = get_flags(insn.ea); } } if ( op_adds_xrefs(F, op.n) ) { // create data xrefs ea_t base_ea; if ( get_gp_based_addr(&base_ea, insn, op) ) { ea = base_ea + op.addr; insn.add_dref(ea, op.offb, isRead ? dr_R : dr_W); } else { int outf = get_displ_outf(insn, op, F); ea = insn.add_off_drefs(op, isRead ? dr_R : dr_W, outf); } if ( ea != BADADDR ) insn.create_op_data(ea, op); } break; case o_near: { bool iscall = has_insn_feature(insn.itype, CF_CALL); ea_t dest = to_ea(insn.cs, op.addr); if ( dest == insn.ea + insn.size ) { // PIC pattern: // jarl nextaddr, r29 // nextaddr: iscall = false; } insn.add_cref(dest, op.offb, iscall ? fl_CN : fl_JN); if ( flow && iscall ) { if ( !func_does_return(dest) ) flow = false; } } break; case o_mem: { ea = to_ea(insn.cs, op.addr); insn.create_op_data(ea, op); insn.add_dref(op.addr, op.offb, isRead ? dr_R : dr_W); } break; } } //---------------------------------------------------------------------- static void idaapi trace_stack(func_t *pfn, const insn_t &insn) { sval_t delta; switch ( insn.itype ) { case NEC850_PREPARE_i: case NEC850_PREPARE_sp: { delta = -((bitcount(insn.Op1.value) * 4) + (insn.Op2.value << 2)); // PATTERN #1 /* 00000030 _func3: 00000030 000 br loc_5E 00000032 00000032 loc_32: -- CODE XREF: _func3+32j 00000032 000 st.w r6, 4[sp] 0000005A 0000005A loc_5A: -- CODE XREF: _func3+10j 0000005A -- _func3+14j ... 0000005A 000 dispose 2, {lp}, [lp] 0000005E -- --------------------------------------------------------------------------- 0000005E 0000005E loc_5E: -- CODE XREF: _func3 0000005E -0C prepare {lp}, 2 00000062 000 br loc_32 00000062 -- End of function _func3 */ bool farref; insn_t tmp; if ( decode_preceding_insn(&tmp, insn.ea, &farref) != BADADDR && (tmp.itype == NEC850_BR || tmp.itype == NEC850_JR) && tmp.Op1.addr == insn.ea && func_contains(pfn, tmp.ea) ) { add_auto_stkpnt(pfn, tmp.ea + tmp.size, delta); } } break; case NEC850_DISPOSE_r: case NEC850_DISPOSE_r0: // count registers in LIST12 and use the imm5 for local vars delta = (bitcount(insn.Op2.value) * 4) + (insn.Op1.value << 2); break; case NEC850_ADD: case NEC850_ADDI: case NEC850_MOVEA: delta = insn.Op1.value; break; default: return; } add_auto_stkpnt(pfn, insn.ea + insn.size, delta); } //---------------------------------------------------------------------- // pattern: // mov #address, lp // jmp [reg1] // address: // flow to the next instruction static bool indirect_function_call(const insn_t &_insn) { if ( _insn.itype != NEC850_JMP || _insn.Op1.is_reg(rLP) ) return false; insn_t insn = _insn; ea_t ret_addr = insn.ea + insn.size; // after the jmp bool flows = false; while ( decode_prev_insn(&insn, insn.ea) != BADADDR ) { if ( insn.itype == NEC850_MOV && insn.Op1.type == o_imm && insn.Op1.dtype == dt_dword && insn.Op2.is_reg(rLP) ) { // MOV #address, lp op_offset(insn.ea, 0, REF_OFF32); if ( insn.Op1.value == ret_addr ) { // normal return, after the jmp instruction flows = true; } else { // add xref to return address add_cref(_insn.ea, insn.Op1.value, fl_JN); } break; } if ( spoils(insn, rLP) ) break; flags_t F = get_flags(insn.ea); if ( !is_flow(F) || has_xref(F) ) break; } return flows; } //---------------------------------------------------------------------- int nec850_t::nec850_emu(const insn_t &insn) { int aux = insn.auxpref; int Feature = insn.get_canon_feature(ph); flow = (Feature & CF_STOP) == 0; if ( Feature & CF_USE1 ) handle_operand(insn, insn.Op1, true); if ( Feature & CF_CHG1 ) handle_operand(insn, insn.Op1, false); if ( Feature & CF_USE2 ) handle_operand(insn, insn.Op2, true); if ( Feature & CF_CHG2 ) handle_operand(insn, insn.Op2, false); if ( Feature & CF_USE3 ) handle_operand(insn, insn.Op3, true); if ( Feature & CF_CHG3 ) handle_operand(insn, insn.Op3, false); if ( Feature & CF_JUMP ) remember_problem(PR_JUMP, insn.ea); flags_t F = get_flags(insn.ea); if ( insn.itype == NEC850_MOVEA && insn.Op1.type == o_imm && !is_defarg(F, insn.Op1.n) ) { // movea imm16, sp, reg (reg != sp) if ( insn.Op2.is_reg(rSP) && !insn.Op3.is_reg(rSP) && may_create_stkvars() && insn.create_stkvar(insn.Op1, insn.Op1.value, 0) ) { op_stkvar(insn.ea, insn.Op1.n); } else if ( insn.Op2.is_reg(rGP) && g_gp_ea != BADADDR ) { ea_t ea = g_gp_ea + insn.Op1.value; refinfo_t ri; ri.flags = REF_OFF32|REFINFO_PASTEND|REFINFO_SIGNEDOP|REFINFO_NOBASE; ri.target = BADADDR; ri.base = g_gp_ea; ri.tdelta = 0; op_offset_ex(insn.ea, insn.Op1.n, &ri); F = get_flags(insn.ea); if ( op_adds_xrefs(F, insn.Op1.n) ) insn.add_dref(ea, insn.Op1.offb, dr_O); } } // add dref to callt table entry address if ( insn.itype == NEC850_CALLT && g_ctbp_ea != BADADDR ) { ea_t ea = g_ctbp_ea + (insn.Op1.value << 1); insn.create_op_data(ea, insn.Op1.offb, dt_word); insn.add_dref(ea, insn.Op1.offb, dr_R); } if ( indirect_function_call(insn) ) flow = true; if ( (aux & N850F_SP) && may_trace_sp() ) { func_t *pfn = get_func(insn.ea); if ( pfn != NULL ) trace_stack(pfn, insn); } // add flow if ( flow ) add_cref(insn.ea, insn.ea + insn.size, fl_F); return 1; } //---------------------------------------------------------------------- int nec850_may_be_func(const insn_t &insn) { int prop = 0; if ( insn.itype == NEC850_PREPARE_i || insn.itype == NEC850_PREPARE_sp ) prop = 100; return prop; } //---------------------------------------------------------------------- inline bool is_ret_itype(const insn_t &insn) { return insn.itype == NEC850_RETI || insn.itype == NEC850_DBRET || insn.itype == NEC850_CTRET || insn.itype == NEC850_DISPOSE_r || insn.itype == NEC850_JMP && insn.Op1.is_reg(rLP); } //---------------------------------------------------------------------- bool nec850_is_return(const insn_t &insn, bool strict) { if ( is_ret_itype(insn) ) return true; if ( insn.itype == NEC850_DISPOSE_r0 ) return !strict; return false; } //------------------------------------------------------------------------- static bool find_set( ea_t *value, ea_t *valea, insn_t insn, // make a copy int reg) { simple_bfi_t bfi(insn.ea); while ( true ) { cref_t prev_insn_ref = bfi.prev_insn(); if ( prev_insn_ref == fl_U || decode_insn(&insn, bfi.cur_ea) <= 0 ) break; switch ( insn.itype ) { case NEC850_MOV: if ( insn.Op2.is_reg(reg) && insn.Op1.type == o_reg ) { reg = insn.Op1.reg; continue; } if ( insn.Op2.is_reg(reg) && insn.Op1.type == o_imm ) { *value = insn.Op1.value; *valea = insn.ea; return true; } break; case NEC850_ADDI: case NEC850_MOVEA: if ( insn.Op3.is_reg(reg) && insn.Op1.type == o_imm && insn.Op2.is_reg(rZERO) ) { *value = insn.Op1.value; *valea = insn.ea; return true; } break; } if ( spoils(insn, reg) ) break; } return false; } //------------------------------------------------------------------------- struct nec850_jump_pattern_t : public jump_pattern_t { protected: enum { rA, rC }; nec850_jump_pattern_t(switch_info_t *_si, const char (*_depends)[4]) : jump_pattern_t(_si, _depends, rC) { modifying_r32_spoils_r64 = false; non_spoiled_reg = rA; } public: virtual bool handle_mov(tracked_regs_t &_regs) override; virtual void check_spoiled(tracked_regs_t *_regs) const override; protected: // movea -minv, rA', rA | add -minv, rA bool jpi_sub_lowcase(); // cmp followed by the conditional jump // it calls jpi_condjump() and jpi_cmp_ncases() that can be redefined in // the derived class. bool jpi_cmp_ncases_condjump(); // switch rA bool jpi_jump(); // bh default virtual bool jpi_condjump() newapi; // cmp ncases, rA virtual bool jpi_cmp_ncases() newapi; }; //------------------------------------------------------------------------- bool nec850_jump_pattern_t::handle_mov(tracked_regs_t &_regs) { if ( insn.itype != NEC850_MOV && insn.Op1.type != o_reg && insn.Op2.type != o_reg ) { return false; } return set_moved(insn.Op2, insn.Op1, _regs); } //------------------------------------------------------------------------- #define PROC_MAXCHGOP 3 void nec850_jump_pattern_t::check_spoiled(tracked_regs_t *__regs) const { tracked_regs_t &_regs = *__regs; for ( uint i = 0; i < _regs.size(); ++i ) { const op_t &x = _regs[i]; if ( x.type == o_reg && spoils(insn, x.reg) || x.type == o_condjump && spoils_flags(insn) ) { set_spoiled(&_regs, x); } } check_spoiled_not_reg(&_regs, PROC_MAXCHGOP); } //---------------------------------------------------------------------- // movea -minv, rA', rA | add -minv, rA bool nec850_jump_pattern_t::jpi_sub_lowcase() { if ( insn.itype == NEC850_MOVEA ) { if ( insn.Op1.type != o_imm || insn.Op2.type != o_reg || !is_equal(insn.Op3, rA) ) { return false; } trackop(insn.Op2, rA); } else if ( insn.itype == NEC850_ADD ) { if ( insn.Op1.type != o_imm || !is_equal(insn.Op2, rA) ) return false; } else { return false; } si->lowcase = uval_t(-uint32(insn.Op1.value)); return true; } //------------------------------------------------------------------------- // cmp followed by the conditional jump bool nec850_jump_pattern_t::jpi_cmp_ncases_condjump(void) { // var should not be spoiled QASSERT(10317, !is_spoiled(rA)); if ( jpi_condjump() // continue matching if found || is_spoiled(rC) || !jpi_cmp_ncases() ) { return false; } op_t &op = regs[rC]; // assert: op.type == o_condjump if ( (op.value & cc_inc_ncases) != 0 ) ++si->ncases; si->defjump = op.specval; si->set_expr(insn.Op1.reg, insn.Op1.dtype); return true; } //---------------------------------------------------------------------- // switch rA bool nec850_jump_pattern_t::jpi_jump() { if ( insn.itype != NEC850_SWITCH || insn.Op1.type != o_reg || insn.Op1.reg == rZERO ) { return false; } si->jumps = insn.ea + insn.size; si->set_elbase(si->jumps); si->flags |= SWI_SIGNED; si->set_jtable_element_size(2); si->set_shift(1); si->set_expr(insn.Op1.reg, dt_dword); trackop(insn.Op1, rA); return true; } //---------------------------------------------------------------------- // bh default bool nec850_jump_pattern_t::jpi_condjump() { op_t op; op.type = o_condjump; op.value = 0; switch ( insn.itype ) { case NEC850_BH: // higher case NEC850_BNH: // not higher op.value |= cc_inc_ncases; break; case NEC850_BL: // lower case NEC850_BNC: // no carry (not lower) break; default: return false; } ea_t jump = to_ea(insn.cs, insn.Op1.addr); switch ( insn.itype ) { case NEC850_BH: case NEC850_BNC: op.specval = jump; break; case NEC850_BL: case NEC850_BNH: // we have conditional jump to the switch body // assert: eas[0] != BADADDR if ( jump > eas[0] ) return false; op.specval = insn.ea + insn.size; // possibly followed by 'jr default' { insn_t deflt; if ( decode_insn(&deflt, op.specval) > 0 && deflt.itype == NEC850_JR && deflt.Op1.type == o_near ) { op.specval = deflt.Op1.addr; } } break; default: return false; } op.addr = insn.ea; trackop(op, rC); return true; } //---------------------------------------------------------------------- // cmp ncases, rA bool nec850_jump_pattern_t::jpi_cmp_ncases() { if ( insn.itype != NEC850_CMP || insn.Op1.type != o_imm && insn.Op1.type != o_reg || !same_value(insn.Op2, rA) ) { return false; } const op_t &x = insn.Op1; uval_t val; ea_t dummy; if ( x.type == o_imm ) val = x.value; // assert: x.type == o_reg else if ( !find_set(&val, &dummy, insn, x.reg) ) return false; si->ncases = ushort(val); return true; } //---------------------------------------------------------------------- // jump pattern #1 // 2 movea -minv, rA', rA | add -minv, rA (optional) // 1 cmp ncases, rA | cmp rNcases, rA // bh default (nearest to "cmp") // 0 switch rA // 0 -> 1 -> 2 static const char nec850_depends1[][4] = { { 1 }, // 0 { 2 | JPT_OPT | JPT_NEAR }, // 1 { 0 }, // 2 optional, near }; //------------------------------------------------------------------------- class nec850_jump_pattern1_t : public nec850_jump_pattern_t { public: nec850_jump_pattern1_t(switch_info_t *_si) : nec850_jump_pattern_t(_si, nec850_depends1) {} virtual bool jpi2(void) override { return jpi_sub_lowcase(); } virtual bool jpi1(void) override { return jpi_cmp_ncases_condjump(); } virtual bool jpi0(void) override { return jpi_jump(); } }; //---------------------------------------------------------------------- static int is_jump_pattern1(switch_info_t *si, const insn_t &insn, procmod_t *) { nec850_jump_pattern1_t jp(si); if ( !jp.match(insn) ) return JT_NONE; return JT_SWITCH; } //---------------------------------------------------------------------- // jump pattern #2 (addi instead of cmp) // 2 movea -minv, rA', rA | add -minv, rA (optional) // 1 addi -ncases, rA, r0 // bl default (nearest to "cmp") // 0 switch rA // 0 -> 1 -> 2 static const char nec850_depends2[][4] = { { 1 }, // 0 { 2 | JPT_OPT | JPT_NEAR }, // 1 { 0 }, // 2 optional, near }; //------------------------------------------------------------------------- class nec850_jump_pattern2_t : public nec850_jump_pattern_t { public: nec850_jump_pattern2_t(switch_info_t *_si) : nec850_jump_pattern_t(_si, nec850_depends2) {} bool jpi2(void) override { return jpi_sub_lowcase(); } bool jpi1(void) override { return jpi_cmp_ncases_condjump(); } bool jpi0(void) override { return jpi_jump(); } protected: // bl default bool jpi_condjump() override; // addi -ncases, rA, r0 bool jpi_cmp_ncases() override; }; //---------------------------------------------------------------------- // bl default bool nec850_jump_pattern2_t::jpi_condjump() { op_t op; op.type = o_condjump; op.value = 0; switch ( insn.itype ) { case NEC850_BH: // higher op.value |= cc_inc_ncases; break; case NEC850_BL: // lower break; default: return false; } ea_t jump = to_ea(insn.cs, insn.Op1.addr); switch ( insn.itype ) { case NEC850_BL: op.specval = jump; break; case NEC850_BH: // we have conditional jump to the switch body // assert: eas[0] != BADADDR if ( jump > eas[0] ) return false; op.specval = insn.ea + insn.size; break; default: return false; } op.addr = insn.ea; trackop(op, rC); return true; } //---------------------------------------------------------------------- // addi -ncases, rA, r0 bool nec850_jump_pattern2_t::jpi_cmp_ncases() { if ( insn.itype != NEC850_ADDI || insn.Op1.type != o_imm || !insn.Op3.is_reg(rZERO) || !same_value(insn.Op2, rA) ) { return false; } si->ncases = ushort(-uint32(insn.Op1.value)); return true; } //---------------------------------------------------------------------- static int is_jump_pattern2(switch_info_t *si, const insn_t &insn, procmod_t *) { nec850_jump_pattern2_t jp(si); if ( !jp.match(insn) ) return JT_NONE; return JT_SWITCH; } //---------------------------------------------------------------------- // jump pattern #3 (without 'switch' insn) // 3 movea -minv, rA', rA | add -minv, rA (optional) // 2 cmp ncases, rA | cmp rNcases, rA // bh default (nearest to "cmp") // 1 shl 2, rA | shl 1, rA // 0 jmp jumps[rA] // // jumps: jr case0 (4 bytes) | (2 bytes) // jr case1 // ... // // 0 -> 1 -> 2 -> 3 static const char nec850_depends3[][4] = { { 1 }, // 0 { 2 }, // 1 { 3 | JPT_OPT | JPT_NEAR }, // 2 { 0 }, // 3 optional, near }; //------------------------------------------------------------------------- class nec850_jump_pattern3_t : public nec850_jump_pattern_t { public: nec850_jump_pattern3_t(switch_info_t *_si) : nec850_jump_pattern_t(_si, nec850_depends3) { si->flags |= SWI_JMPINSN; } virtual bool jpi3(void) override { return jpi_sub_lowcase(); } virtual bool jpi2(void) override { return jpi_cmp_ncases_condjump(); } virtual bool jpi1(void) override; // shl shift, rA virtual bool jpi0(void) override; // jmp jumps[rA] }; //---------------------------------------------------------------------- // shl shift, rA bool nec850_jump_pattern3_t::jpi1() { if ( insn.itype != NEC850_SHL || insn.Op1.type != o_imm || !same_value(insn.Op2, rA) ) { return false; } int elsize; if ( insn.Op1.value == 1 ) elsize = 2; else if ( insn.Op1.value == 2 ) elsize = 4; else return false; si->set_jtable_element_size(elsize); return true; } //---------------------------------------------------------------------- // jmp jumps[rA] bool nec850_jump_pattern3_t::jpi0() { if ( insn.itype != NEC850_JMP || insn.Op1.type != o_displ ) return false; si->jumps = insn.Op1.addr; track(insn.Op1.phrase, rA, dt_dword); return true; } //---------------------------------------------------------------------- static int is_jump_pattern3(switch_info_t *si, const insn_t &insn, procmod_t *) { nec850_jump_pattern3_t jp(si); if ( !jp.match(insn) ) return JT_NONE; op_offset(jp.eas[0], 0, REFINFO_NOBASE | REF_OFF32); // rollback data created in handle_operand() del_items(si->jumps, DELIT_SIMPLE); return JT_SWITCH; } //---------------------------------------------------------------------- bool idaapi nec850_is_switch(switch_info_t *si, const insn_t &insn) { if ( insn.itype != NEC850_SWITCH && insn.itype != NEC850_JMP ) return false; static is_pattern_t *const patterns[] = { is_jump_pattern1, is_jump_pattern2, is_jump_pattern3, }; return check_for_table_jump(si, insn, patterns, qnumber(patterns)); } //------------------------------------------------------------------------- sval_t nec850_t::regval( const op_t &op, getreg_t *getreg, const regval_t *rv) const { if ( op.reg > rSR31 ) { warning("Bad register number passed to nec850.get_register_value: %d", op.reg); return 0; } return sval_t(getreg(ph.reg_names[op.reg], rv).ival); } //------------------------------------------------------------------------- static bool is_bcond(int itype) { return itype == NEC850_BV || itype == NEC850_BL || itype == NEC850_BZ || itype == NEC850_BNH || itype == NEC850_BN || itype == NEC850_BR || itype == NEC850_BLT || itype == NEC850_BLE || itype == NEC850_BNV || itype == NEC850_BNC || itype == NEC850_BNZ || itype == NEC850_BH || itype == NEC850_BP || itype == NEC850_BSA || itype == NEC850_BGE || itype == NEC850_BGT; } //------------------------------------------------------------------------- ea_t nec850_t::nec850_next_exec_insn( ea_t ea, getreg_t *getreg, const regval_t *regvalues) const { insn_t insn; if ( decode_insn(&insn, ea) < 1 ) return BADADDR; // First check for Bcond. if ( is_bcond(insn.itype) ) { uint32_t PSW = getreg("PSW", regvalues).ival; bool Z = (PSW & (1 << 0)) != 0; bool S = (PSW & (1 << 1)) != 0; bool OV = (PSW & (1 << 2)) != 0; bool CY = (PSW & (1 << 3)) != 0; bool SAT = (PSW & (1 << 4)) != 0; bool condition = false; switch ( insn.itype ) { case NEC850_BV: condition = OV; break; case NEC850_BL: condition = CY; break; case NEC850_BZ: condition = Z; break; case NEC850_BNH: condition = (CY || Z); break; case NEC850_BN: condition = S; break; case NEC850_BR: condition = true; break; case NEC850_BLT: condition = (S != OV); break; case NEC850_BLE: condition = ((S != OV) || Z); break; case NEC850_BNV: condition = !OV; break; case NEC850_BNC: condition = !CY; break; case NEC850_BNZ: condition = !Z; break; case NEC850_BH: condition = !(CY || Z); break; case NEC850_BP: condition = !S; break; case NEC850_BSA: condition = SAT; break; case NEC850_BGE: condition = !(S != OV); break; case NEC850_BGT: condition = !((S != OV) || Z); break; } ea_t target = condition ? insn.Op1.addr : BADADDR; return target; } // Then check for other instructions. ea_t target = BADADDR; switch ( insn.itype ) { case NEC850_RETI: { uint32_t PSW = getreg("PSW", regvalues).ival; if ( (PSW & (1 << 6)) != 0 ) // PSW.EP { target = getreg("EIPC", regvalues).ival; } else { if ( (PSW & (1 << 7)) != 0 ) // PSW.NP target = getreg("FEPC", regvalues).ival; else target = getreg("EIPC", regvalues).ival; } } break; case NEC850_JR: target = insn.Op1.addr; break; case NEC850_JMP: target = regval(insn.Op1, getreg, regvalues) + insn.Op1.addr; break; case NEC850_JARL: if ( insn.Op1.type == o_reg ) target = regval(insn.Op1, getreg, regvalues); else target = insn.Op1.addr; break; case NEC850_SWITCH: // TODO break; case NEC850_DISPOSE_r: target = regval(insn.Op3, getreg, regvalues); break; case NEC850_CALLT: target = insn.Op1.addr; break; case NEC850_CTRET: target = getreg("CTPC", regvalues).ival; break; case NEC850_EIRET: target = getreg("EIPC", regvalues).ival; break; case NEC850_FERET: target = getreg("FEPC", regvalues).ival; break; case NEC850_LOOP: if ( regval(insn.Op1, getreg, regvalues) - 1 != 0 ) target = insn.Op2.addr; break; case NEC850_DBHVTRAP: case NEC850_DBRET: case NEC850_DBTRAP: case NEC850_FETRAP: case NEC850_HALT: case NEC850_HVCALL: case NEC850_HVTRAP: case NEC850_RIE: case NEC850_RMTRAP: case NEC850_SYSCALL: case NEC850_TRAP: // TODO break; } return target; } //------------------------------------------------------------------------- ea_t nec850_t::nec850_calc_step_over(ea_t ip) const { insn_t insn; if ( ip == BADADDR || decode_insn(&insn, ip) < 1 ) return BADADDR; bool step_over = is_call_insn(insn) || insn.itype == NEC850_LOOP; if ( step_over ) return insn.ea + insn.size; return BADADDR; } //------------------------------------------------------------------------- bool nec850_t::nec850_get_operand_info( idd_opinfo_t *opinf, ea_t ea, int n, getreg_t *getreg, const regval_t *regvalues) { if ( n < 0 || n > 4 ) // check the operand number return false; insn_t insn; if ( decode_insn(&insn, ea) < 1 ) return false; // TODO check for op.type == o_cond? opinf->modified = has_cf_chg(insn.get_canon_feature(ph), n); uint64 v = 0; const op_t &op = insn.ops[n]; switch ( op.type ) { case o_imm: v = op.value; break; case o_mem: case o_near: opinf->ea = op.addr; break; case o_reg: v = regval(op, getreg, regvalues); break; case o_displ: // TODO break; case o_reglist: case o_regrange: // TODO how to represent multiple registers? break; default: return false; } opinf->value._set_int(v); opinf->value_size = get_dtype_size(op.dtype); return true; } //-------------------------------------------------------------------------- int nec850_t::nec850_get_reg_index(const char *name) const { if ( name == NULL || name[0] == '\0' ) return -1; for ( size_t i = 0; i < ph.regs_num; i++ ) if ( stricmp(ph.reg_names[i], name) == 0 ) return i; return -1; } //-------------------------------------------------------------------------- bool nec850_t::nec850_get_reg_info( const char **main_regname, bitrange_t *bitrange, const char *regname) { int regnum = nec850_get_reg_index(regname); if ( regnum == -1 ) return false; if ( bitrange != NULL ) *bitrange = bitrange_t(0, 32); if ( main_regname != NULL ) *main_regname = ph.reg_names[regnum]; return true; }