//---------------------------------------------------------------------- static bool get_op_value(op_t &x, int *value) { if ( x.type == o_imm ) { *value = (int)x.value; return true; } bool ok = false; if ( x.type == o_reg ) { int reg = x.reg; insn_t saved = cmd; if ( decode_prev_insn(cmd.ea) != BADADDR && is_mov(cmd.itype) && cmd.Op1.type == o_imm && cmd.Op2.type == o_reg && cmd.Op2.reg == reg ) { *value = (int)cmd.Op1.value; ok = true; } cmd = saved; } return ok; }
//---------------------------------------------------------------------- // These are the recognized jump table sizing patterns //0100 cmp.b #7, r0l //0102 bls loc_108:8 //0104 bra def_200:8 //0106 loc_108: ; jump table lookup //0200 def_200: ; default jump target // Or //0100 cmp.b #7, r0l //0102 bls loc_108:8 //0104 jmp def_2000:16 //0108 loc_108: ; jump table lookup //2000 def_2000: ; default jump target // Or //0100 mov.w #7, r3 //0104 cmp.w r3, r0 //0106 bls loc_10C:8 //0108 bra def_200:8 //010A loc_10C: ; jump table lookup //0200 def_200: ; default jump target // Or //0100 mov.w #7, r3 //0104 cmp.w r3, r0 //0106 bls loc_10C:8 //0108 jmp def_2000:16 //010C loc_10C: ; jump table lookup //2000 def_2000: ; default jump target // Or //0100 cmp.b #7, r0l //0102 bhi def_200:8 //0104 ; jump table lookup //0200 def_200: ; default jump target // Or //0100 mov.w #7, r3 //0104 cmp.w r3, r0 //0106 bhi def_200:8 //0108 ; jump table lookup //0200 def_200: ; default jump target //---------------------------------------------------------------------- static bool find_table_size(ea_t *defea, int *size, int rlx, ea_t code_ip) { *defea = BADADDR; *size = INT_MAX; if ( decode_prev_insn(cmd.ea) == BADADDR ) return true; if ( cmd.itype == H8_bhi ) // bhi default { *defea = cmd.Op1.addr; } else { if ( cmd.itype != H8_jmp // jmp default && cmd.itype != H8_bra ) return true; // bra default *defea = cmd.Op1.addr; if ( decode_prev_insn(cmd.ea) == BADADDR || cmd.itype != H8_bls // bls code_ip || cmd.Op1.addr != code_ip ) return true; } if ( decode_prev_insn(cmd.ea) == BADADDR || cmd.itype != H8_cmp // cmp.b #size, rlx || cmd.Op2.type != o_reg ) return true; if ( cmd.Op1.type == o_imm ) { if ( (cmd.auxpref & aux_byte) == 0 || cmd.Op2.reg != rlx ) return true; } else { if ( cmd.Op1.type != o_reg // cmp.w RX, rx || cmd.Op2.reg != (rlx - 24) ) return true; int rx = cmd.Op1.reg; if ( decode_prev_insn(cmd.ea) == BADADDR || cmd.itype != H8_mov // mov.w #size, RX || cmd.Op2.type != o_reg || cmd.Op2.reg != rx || cmd.Op1.type != o_imm ) return true; } *size = int(cmd.Op1.value + 1); return true; }
//---------------------------------------------------------------------- // This is jump table pattern #2 // (*1* may be omitted...IE, this logic is located above jump table sizing instructions) //0100 *1* sub.b r0h, r0h //0102 add.w r0, r0 //0104 mov.w @(jpt_108:16,r0), r0 //0108 jmp @r0 //---------------------------------------------------------------------- static bool is_jump_pattern2(ea_t *base, ea_t *table, ea_t *defea, int *size, int *elsize) { int reg = cmd.Op1.phrase; if ( decode_prev_insn(cmd.ea) == BADADDR || cmd.itype != H8_mov // mov.w @(table:16,r0), r0 || (cmd.auxpref & aux_word) == 0 || cmd.Op1.type != o_displ || cmd.Op2.reg != reg ) return false; *table = cmd.Op1.addr; int rx = cmd.Op1.reg; *base = 0; ea_t bea = toEA(cmd.cs, 0); set_offset(cmd.ea, 0, bea); if ( decode_prev_insn(cmd.ea) == BADADDR || cmd.itype != H8_add // add.w r0, r0 || (cmd.auxpref & aux_word) == 0 || cmd.Op1.type != o_reg || cmd.Op1.reg != rx || cmd.Op2.reg != rx ) return false; int rhx = rx + 16; int rlx = rhx + 8; ea_t oldea = cmd.ea; ea_t oldip = cmd.ip; if ( decode_prev_insn(cmd.ea) == BADADDR || (cmd.itype != H8_sub && cmd.itype != H8_xor) // sub.b rhx, rhx || (cmd.auxpref & aux_byte) == 0 || cmd.Op1.type != o_reg || cmd.Op2.type != o_reg || cmd.Op1.reg != rhx || cmd.Op2.reg != rhx ) // return false; { cmd.ea = oldea; // forgive this... cmd.ip = oldip; } // the jump table is found, try to determine its size *elsize = 2; return find_table_size(defea, size, rlx, cmd.ip); }
// Emulate an instruction. int idaapi emu(void) { bool flow = !is_stop() || (cmd.auxpref & INSN_DELAY_SHOT); if ( flow ) { insn_t cmd_backup = cmd; if ( decode_prev_insn(cmd.ea) != BADADDR ) { flow = !(is_stop() && (cmd.auxpref & INSN_DELAY_SHOT)); } cmd = cmd_backup; } if ( cmd.Op1.type != o_void) handle_operand(cmd.Op1 ); if ( cmd.Op2.type != o_void) handle_operand(cmd.Op2 ); if ( cmd.Op3.type != o_void) handle_operand(cmd.Op3 ); if ( cmd.Op4.type != o_void) handle_operand(cmd.Op4 ); if ( flow ) ua_add_cref(0, cmd.ea + cmd.size, fl_F); return 1; }
//---------------------------------------------------------------------- // This is jump table pattern #1 //0100 sub.b r0h, r0h //0102 mov.b @(jpt_10a:16,r0), r0l //0106 add.b #loc_10C & 0xFF, r0l //0108 addx #loc_10C >> 8, r0h //010A jmp @r0 //010C loc_10C: ; base address of jump table // Or //0100 mov.b @(jpt_10a:16,r0), r0l //0104 sub.b r0h, r0h //0106 add.b #loc_10C & 0xFF, r0l //0108 addx #loc_10C >> 8, r0h //010A jmp @r0 //010C loc_10C: ; base address of jump table //---------------------------------------------------------------------- static bool is_jump_pattern1(ea_t *base, ea_t *table, ea_t *defea, int *size, int *elsize) { int reg = cmd.Op1.phrase; int rh = reg + 16; int rl = rh + 8; if ( decode_prev_insn(cmd.ea) == BADADDR || cmd.itype != H8_addx // addx #baseh, rh || cmd.Op1.type != o_imm || cmd.Op2.reg != rh ) return false; int baseh = (int)cmd.Op1.value; // msb of base ea_t eah = cmd.ea; if ( decode_prev_insn(cmd.ea) == BADADDR || cmd.itype != H8_add // add.b #basel, rl || (cmd.auxpref & aux_byte) == 0 || cmd.Op1.type != o_imm || cmd.Op2.reg != rl ) return false; int basel = (int)cmd.Op1.value; // lsb of base ea_t eal = cmd.ea; int rx, rhx, rlx; ea_t obase; if ( decode_prev_insn(cmd.ea) == BADADDR ) return false; else { if ( cmd.itype == H8_mov ) // mov.b @(table:16,rx), rl { if ( (cmd.auxpref & aux_byte) == 0 || cmd.Op1.type != o_displ || cmd.Op2.reg != rl ) return false; *table = cmd.Op1.addr; rx = cmd.Op1.reg; rhx = rx + 16; rlx = rhx + 8; obase = toEA(cmd.cs, 0); set_offset(cmd.ea, 0, obase); if ( decode_prev_insn(cmd.ea) == BADADDR || (cmd.itype != H8_sub && cmd.itype != H8_xor) // sub.b rhx, rhx || (cmd.auxpref & aux_byte) == 0 || cmd.Op1.type != o_reg || cmd.Op2.type != o_reg || cmd.Op1.reg != rhx || cmd.Op2.reg != rhx ) return false; } else if ( cmd.itype == H8_sub || cmd.itype == H8_xor ) // sub.b rhx, rhx { if ( (cmd.auxpref & aux_byte) == 0 || cmd.Op1.type != o_reg || cmd.Op2.type != o_reg || cmd.Op1.reg != cmd.Op2.reg ) return false; rhx = cmd.Op1.reg; rlx = rhx + 8; rx = rhx - 16; if ( decode_prev_insn(cmd.ea) == BADADDR || (cmd.itype != H8_mov) // mov.b @(table:16,rx), rl || (cmd.auxpref & aux_byte) == 0 || cmd.Op1.type != o_displ || cmd.Op2.reg != rl || cmd.Op1.reg != rx ) return false; *table = cmd.Op1.addr; obase = toEA(cmd.cs, 0); set_offset(cmd.ea, 0, obase); } else return false; } *base = (baseh<<8) | basel; ea_t bea = toEA(cmd.cs, *base); op_offset(eah, 0, REF_HIGH8, bea, obase); op_offset(eal, 0, REF_LOW8, bea, obase); // the jump table is found, try to determine its size *elsize = 1; return find_table_size(defea, size, rlx, cmd.ip); }
int idaapi emu() { char szLabel[MAXSTR]; insn_t saved; segment_t* pSegment; ea_t ea, length, offset; flags_t flags; uint32 dwFeature, i; dwFeature = cmd.get_canon_feature(); fFlow = !(dwFeature & CF_STOP); if (dwFeature & CF_USE1) op_emu(cmd.Op1, 1); if (dwFeature & CF_USE2) op_emu(cmd.Op2, 1); if (dwFeature & CF_CHG1) op_emu(cmd.Op1, 0); if (dwFeature & CF_CHG2) op_emu(cmd.Op2, 0); saved = cmd; switch (cmd.itype) { case M8B_MOV: if (!cmd.Op1.is_reg(rPSP)) break; case M8B_SWAP: if (cmd.itype == M8B_SWAP && !cmd.Op2.is_reg(rDSP)) break; for (i = 0; i < 5; ++i) { ea = decode_prev_insn(cmd.ea); if (ea == BADADDR) break; if (cmd.itype == M8B_MOV && cmd.Op1.is_reg(rA) && cmd.Op2.type == o_imm) { ea = toRAM(cmd.Op2.value); if (ea != BADADDR) { qsnprintf(szLabel, sizeof(szLabel), "%s_%0.2X", cmd.itype == M8B_MOV ? "psp" : "dsp", cmd.Op2.value); ua_add_dref(cmd.Op2.offb, ea, dr_O); set_name(ea, szLabel, SN_NOWARN); } break; } } break; case M8B_JACC: pSegment = getseg(cmd.ea); if (!pSegment) break; length = pSegment->endEA - cmd.ea; if (length > 256) length = 256; for (offset = 2; offset < length; offset += 2) { ea = toROM(saved.Op1.addr + offset); if (ea == BADADDR) break; flags = getFlags(ea); if (!hasValue(flags) || (has_any_name(flags) || hasRef(flags)) || !create_insn(ea)) break; switch (cmd.itype) { case M8B_JMP: case M8B_RET: case M8B_RETI: case M8B_IPRET: add_cref(saved.ea, ea, fl_JN); break; default: offset = length; } } break; case M8B_IORD: case M8B_IOWR: case M8B_IOWX: for (i = 0; i < 5; ++i) { ea = (saved.itype == M8B_IORD) ? decode_insn(cmd.ea + cmd.size) : decode_prev_insn(cmd.ea); if (ea == BADADDR) break; if (cmd.Op1.is_reg(rA) && cmd.Op2.type == o_imm) { qsnprintf(szLabel, sizeof(szLabel), "[A=%0.2Xh] ", cmd.Op2.value); if (get_portbits_sym(szLabel + qstrlen(szLabel), saved.Op1.addr, cmd.Op2.value)) set_cmt(saved.ea, szLabel, false); break; } } } cmd = saved; if ((cmd.ea & 0xFF) == 0xFF) { switch (cmd.itype) { case M8B_RET: case M8B_RETI: case M8B_XPAGE: break; default: QueueMark(Q_noValid, cmd.ea); } } if (fFlow) ua_add_cref(0, cmd.ea + cmd.size, fl_F); return 1; }
//---------------------------------------------------------------------- int emu(void) { if ( segtype(cmd.ea) == SEG_XTRN ) return 1; //uint32 Feature = cmd.get_canon_feature(); int flag1 = is_forced_operand(cmd.ea, 0); int flag2 = is_forced_operand(cmd.ea, 1); int flag3 = is_forced_operand(cmd.ea, 2); // Determine if the next instruction should be executed flow = (InstrIsSet(cmd.itype, CF_STOP) != true); if ( InstrIsSet(cmd.itype,CF_USE1) ) process_operand(cmd.Op1, flag1, 1); if ( InstrIsSet(cmd.itype,CF_USE2) ) process_operand(cmd.Op2, flag2, 1); if ( InstrIsSet(cmd.itype,CF_USE3) ) process_operand(cmd.Op3, flag3, 1); if ( InstrIsSet(cmd.itype,CF_CHG1) ) process_operand(cmd.Op1, flag1, 0); if ( InstrIsSet(cmd.itype,CF_CHG2) ) process_operand(cmd.Op2, flag2, 0); if ( InstrIsSet(cmd.itype,CF_CHG3) ) process_operand(cmd.Op3, flag3, 0); // check for DP changes if ( cmd.itype == OAK_Dsp_lpg ) splitSRarea1(get_item_end(cmd.ea), PAGE, cmd.Op1.value & 0xFF, SR_auto); if ( ( cmd.itype == OAK_Dsp_mov ) && (cmd.Op1.type == o_imm) && (cmd.Op2.type == o_reg) && (cmd.Op2.reg == ST1) ) splitSRarea1(get_item_end(cmd.ea), PAGE, cmd.Op1.value & 0xFF, SR_auto); //Delayed Return insn_t saved = cmd; cycles = cmd.cmd_cycles; delayed = false; if ( decode_prev_insn(cmd.ea) != BADADDR ) { if ( (cmd.itype == OAK_Dsp_retd) || (cmd.itype == OAK_Dsp_retid) ) delayed = true; else cycles += cmd.cmd_cycles; if (!delayed) if ( decode_prev_insn(cmd.ea) != BADADDR ) if ( (cmd.itype == OAK_Dsp_retd) || (cmd.itype == OAK_Dsp_retid) ) delayed = true; } if (delayed && (cycles > 1) ) flow = 0; cmd = saved; //mov #imm, pc if ( ( cmd.itype == OAK_Dsp_mov ) && (cmd.Op2.type == o_reg) && (cmd.Op2.reg == PC) ) flow = 0; if ( flow ) ua_add_cref(0,cmd.ea+cmd.size,fl_F); if ( may_trace_sp() ) { if ( !flow ) recalc_spd(cmd.ea); // recalculate SP register for the next insn else trace_sp(); } return 1; }
//---------------------------------------------------------------------- bool jump_pattern_t::follow_tree(ea_t ea, int n) { if ( n == 0 ) return true; int rsaved[sizeof(r)]; bool ssaved[sizeof(spoiled)]; memcpy(rsaved, r, sizeof(r)); memcpy(ssaved, spoiled, sizeof(spoiled)); bool success = false; if ( n < 0 ) { success = true; n = -n; } jmsg("follow_tree(%a, %d)\n", ea, n); if ( !skip[n] ) { if ( eas[n] == BADADDR ) { cmd.ea = ea; bool found_insn = false; while ( true ) { if ( cmd.ea < minea ) break; farref = false; ea_t prev = BADADDR; if ( allow_noflows || isFlow(get_flags_novalue(cmd.ea)) ) prev = decode_prev_insn(cmd.ea); if ( prev == BADADDR ) { if ( !allow_farrefs ) break; ea_t cur_addr = cmd.ea; if ( decode_preceding_insn(cmd.ea, &farref) == BADADDR ) break; // skip branches which are used to glue blocks together if ( farref && is_branch_to(cur_addr) ) continue; } if ( handle_mov() ) continue; if ( (this->*check[n])() ) { found_insn = true; break; } if ( failed ) return false; jmsg("%a: can't be %d.", cmd.ea, n); jmsg(" rA=%d%s rB=%d%s rC=%d%s rD=%d%s rE=%d%s\n", r[1], spoiled[1] ? "*" : "", r[2], spoiled[2] ? "*" : "", r[3], spoiled[3] ? "*" : "", r[4], spoiled[4] ? "*" : "", r[5], spoiled[5] ? "*" : ""); check_spoiled(); } if ( !found_insn ) { memcpy(r, rsaved, sizeof(r)); if ( success ) goto SUCC; return false; } eas[n] = cmd.ea; } if ( eas[n] >= ea ) { jmsg("%a: depends on %a\n", ea, eas[n]); return success; } ea = eas[n]; jmsg("%a: found %d\n", cmd.ea, n); } SUCC: if ( depends[n][0] && !follow_tree(ea, depends[n][0]) ) return success; if ( depends[n][1] && !follow_tree(ea, depends[n][1]) ) return success; jmsg("follow_tree(%d) - ok\n", n); memcpy(spoiled, ssaved, sizeof(spoiled)); return true; }
//---------------------------------------------------------------------- int idaapi emu(void) { uint32 Feature = cmd.get_canon_feature(); int flag1 = is_forced_operand(cmd.ea, 0); int flag2 = is_forced_operand(cmd.ea, 1); int flag3 = is_forced_operand(cmd.ea, 2); flow = (Feature & CF_STOP) == 0; if ( Feature & CF_USE1 ) process_operand(cmd.Op1, flag1, 1); if ( Feature & CF_USE2 ) process_operand(cmd.Op2, flag2, 1); if ( Feature & CF_USE3 ) process_operand(cmd.Op3, flag3, 1); if ( Feature & CF_CHG1 ) process_operand(cmd.Op1, flag1, 0); if ( Feature & CF_CHG2 ) process_operand(cmd.Op2, flag2, 0); if ( Feature & CF_CHG3 ) process_operand(cmd.Op3, flag3, 0); // // Check for: // - the register bank changes // - PCLATH changes // - PCL changes // for ( int i=0; i < 3; i++ ) { int reg = 0; switch ( i ) { case 0: reg = BANK; if ( !is_bank() ) continue; break; case 1: reg = PCLATH; if ( !is_pclath() ) continue; break; case 2: reg = -1; if ( !is_pcl() ) continue; break; } sel_t v = (reg == -1) ? cmd.ip : getSR(cmd.ea, reg); if ( cmd.Op2.type == o_reg && cmd.Op2.reg == F ) { // split(reg, v); } else { switch ( cmd.itype ) { case PIC_bcf: case PIC_bcf3: case PIC_bsf: case PIC_bsf3: if ( ((ptype == PIC12) && (cmd.Op2.value == 5) ) // bank selector || ((ptype == PIC14) && ( (reg == BANK && (cmd.Op2.value == 5 || cmd.Op2.value == 6)) || (reg == PCLATH && (cmd.Op2.value == 3 || cmd.Op2.value == 4)))) || ((ptype == PIC16) && (sval_t(cmd.Op2.value) >= 0 && cmd.Op2.value <= 3))) { if ( v == BADSEL ) v = 0; int shift = 0; if ( ptype == PIC14 && reg == BANK ) shift = 5; if ( cmd.itype == PIC_bcf ) v = v & ~(1 << (cmd.Op2.value-shift)); else v = v | (1 << (cmd.Op2.value-shift)); split(reg, v); } break; case PIC_clrf: case PIC_clrf2: split(reg, 0); break; case PIC_swapf: case PIC_swapf3: split(reg, ((v>>4) & 15) | ((v & 15) << 4)); break; case PIC_movwf: case PIC_movwf2: case PIC_addlw: case PIC_andlw: case PIC_iorlw: case PIC_sublw: case PIC_xorlw: { insn_t saved = cmd; if ( decode_prev_insn(cmd.ea) != BADADDR && ( cmd.itype == PIC_movlw ) ) { switch ( saved.itype ) { case PIC_movwf: case PIC_movwf2: v = cmd.Op1.value; break; case PIC_addlw: v += cmd.Op1.value; break; case PIC_andlw: v &= cmd.Op1.value; break; case PIC_iorlw: v |= cmd.Op1.value; break; case PIC_sublw: v -= cmd.Op1.value; break; case PIC_xorlw: v ^= cmd.Op1.value; break; } } else { v = BADSEL; } cmd = saved; } split(reg, v); break; case PIC_movlw: split(reg, cmd.Op2.value); break; } } } // Such as , IDA doesn't seem to convert the following: // tris 6 // into // tris PORTB ( or whatever ) if ( cmd.itype == PIC_tris && !isDefArg0(uFlag) ) set_offset(cmd.ea, 0, dataseg); // movlw value // followed by a // movwf FSR // should convert value into an offset , because FSR is used as a pointer to // the INDF (indirect addressing file) if ( ptype == PIC12 || ptype == PIC14 && cmd.itype == PIC_movwf && cmd.Op1.type == o_mem && (cmd.Op1.addr & 0x7F) == 0x4 ) // FSR { insn_t saved = cmd; if ( decode_prev_insn(cmd.ea) != BADADDR && cmd.itype == PIC_movlw ) { set_offset(cmd.ea, 0, dataseg); } cmd = saved; } // Also - it seems to make sense to me that a // movlw value // followed by a // tris PORTn (or movwf TRISn) // should convert value into a binary , because the bits indicate whether a // port is defined for input or output. if ( is_load_tris_reg() ) { insn_t saved = cmd; if ( decode_prev_insn(cmd.ea) != BADADDR && cmd.itype == PIC_movlw ) { op_bin(cmd.ea, 0); } cmd = saved; } // Move litteral to BSR if ( cmd.itype == PIC_movlb1 ) split(BANK, cmd.Op1.value); // // Determine if the next instruction should be executed // if ( !flow ) flow = conditional_insn(); if ( segtype(cmd.ea) == SEG_XTRN ) flow = 0; if ( flow ) ua_add_cref(0,cmd.ea+cmd.size,fl_F); return 1; }
//---------------------------------------------------------------------- static void process_operand(op_t &x,int ,int isload) { if ( cmd.Op2.type == o_reg && cmd.Op2.reg == F || cmd.itype == PIC_swapf ) isload = 0; switch ( x.type ) { case o_reg: return; case o_imm: if ( !isload ) error("interr: emu"); process_immediate_number(x.n); if ( isOff(uFlag, x.n) ) ua_add_off_drefs2(x, dr_O, calc_outf(x)); break; case o_near: { cref_t ftype = fl_JN; ea_t ea = calc_code_mem(x.addr); if ( InstrIsSet(cmd.itype, CF_CALL) ) { if ( !func_does_return(ea) ) flow = false; ftype = fl_CN; } ua_add_cref(x.offb, ea, ftype); } break; case o_mem: { ea_t ea = calc_data_mem(x.addr); destroy_if_unnamed_array(ea); ua_add_dref(x.offb, ea, isload ? dr_R : dr_W); ua_dodata2(x.offb, ea, x.dtyp); if ( !isload ) doVar(ea); if ( may_create_stkvars()) { if ( x.addr == PIC16_INDF2 ) { func_t *pfn = get_func(cmd.ea); if ( pfn != NULL && (pfn->flags & FUNC_FRAME) != 0 ) { ua_stkvar2(cmd.Op1, 0, STKVAR_VALID_SIZE); } } else if ( x.addr == PIC16_PLUSW2 ) { insn_t saved = cmd; if ( decode_prev_insn(cmd.ea) != BADADDR && cmd.itype == PIC_movlw ) { func_t *pfn = get_func(cmd.ea); if ( pfn != NULL && (pfn->flags & FUNC_FRAME) != 0 ) { if ( ua_stkvar2(cmd.Op1, cmd.Op1.value, STKVAR_VALID_SIZE) ) op_stkvar(cmd.ea, cmd.Op1.n); } } cmd = saved; } } } break; default: warning("interr: emu2 %a", cmd.ea); } }