//---------------------------------------------------------------------- bool outop( op_t &x ) { uval_t v, v1; // const char *ptr; switch( x.type ) { case o_imm: out_symbol( '#' ); OutValue( x, OOF_SIGNED | OOFW_IMM ); break; case o_indexed: OutValue( x, OOF_ADDR|OOF_SIGNED|(is_ext_insn() ? OOFW_32 : OOFW_16) ); //.addr v = x.value; out_symbol( '[' ); if ( v != 0 ) goto OUTPHRASE; out_symbol( ']' ); break; case o_indirect: case o_indirect_inc: out_symbol( '[' ); case o_mem: case o_near: v = x.addr; OUTPHRASE: v1 = toEA( getSR(cmd.ea, (x.type == o_near) ? rVcs : rVds), v); if( !out_name_expr( x, v1, v ) ) { OutValue( x, (x.type == o_indexed ? 0 : OOF_ADDR) | OOF_NUMBER|OOFS_NOSIGN| ((x.type == o_near) ? (is_ext_insn() ? OOFW_32 : OOFW_16) : OOFW_8) ); QueueMark( Q_noName, cmd.ea ); } if( x.type == o_indirect || x.type == o_indirect_inc || x.type == o_indexed ) { out_symbol( ']' ); if( x.type == o_indirect_inc ) out_symbol( '+' ); } break; case o_void: return 0; case o_bit: out_symbol( char('0' + x.reg) ); break; default: warning( "out: %a: bad optype %d", cmd.ea, x.type ); } return 1; }
//-------------------------------------------------------------------------- // if bit0 is set, ensure that thumb mode // if bit0 is clear, ensure that arm mode static void handle_arm_thumb_modes(ea_t ea) { bool should_be_thumb = (ea & 1) != 0; bool is_thumb = getSR(ea, ARM_T); if ( should_be_thumb != is_thumb ) { int code = processor_t::loader + (should_be_thumb ? 0 : 1); ph.notify(processor_t::idp_notify(code), ea & ~1); } }
//-------------------------------------------------------------------------- // function to produce assume directives void idaapi assumes(ea_t ea) { segreg_t *Darea = getSRarea(ea); segment_t *Sarea = getseg(ea); if ( Sarea == NULL || Darea == NULL || !inf.s_assume ) return; for ( int i=ph.regFirstSreg; i <= ph.regLastSreg; i++ ) { if ( i == ph.regCodeSreg ) continue; sel_t now = getSR(ea, i); bool show = (ea == Sarea->startEA); if ( show || Darea->startEA == ea ) { segreg_t *prev = getSRarea(ea-1); if ( show || (prev != NULL && getSR(prev->startEA, i) != now) ) print_segment_register(i, now); } } }
//------------------------------------------------------------------------ ea_t calc_data_mem(op_t &op) { ea_t addr = op.addr; sel_t dph = 0; if (op.tms_regH == DPH) { dph = getSR(toEA(cmd.cs, cmd.ip), DPH); if ( dph == BADSEL ) return BADSEL; addr &= 0xFFFF; } sel_t dp = 0; if (op.tms_regP == DP) { dp = getSR(toEA(cmd.cs, cmd.ip), DP); if ( dp == BADSEL ) return BADSEL; addr &= 0xFFFF; } return (((dph & 0x7F) << 16) | (dp + addr)) << 1; }
//------------------------------------------------------------------------ ea_t calc_code_mem(ea_t ea, bool is_near) { if (is_near) { sel_t xpc = getSR(cmd.ea, XPC); if ( xpc == BADSEL ) xpc = 0; return ((xpc & 0x7F) << 16) | (ea & 0xFFFF); } return toEA(cmd.cs, ea); }
ea_t calc_io_mem(op_t &op) { ea_t addr = op.addr; sel_t pdp = 0; if (op.tms_regP == PDP) { pdp = getSR(toEA(cmd.cs, cmd.ip), PDP); if ( pdp == BADSEL ) return BADSEL; addr &= 0x7F; } ea_t ea = ((pdp & 0x1FF) << 7) | addr; return toEA(cmd.cs, ea); }
ea_t calc_data_mem(ea_t ea, bool is_mem) { if (is_mem) { sel_t dp = getSR(cmd.ea, DP); if ( dp == BADSEL ) return BADSEL; return ((dp & 0x1FF) << 7) | (ea & 0x7F) + dataseg; } else { return dataseg + ea; } }
//---------------------------------------------------------------------- ea_t calc_mem(op_t &x) { if ( x.type == o_near ) return toEA(cmd.cs, x.addr); // Before this was simply toEA, now we do it like this: // (if someone complains, both methods should be retained) ea_t ea = x.addr; switch ( calc_sizer(x) ) { case 8: if ( cmd.auxpref & aux_page ) { ea &= 0xFF; sel_t br = getSR(cmd.ea, BR); if ( br != BADSEL ) ea |= br << 8; else ea = BADADDR; } break; case 16: ea &= 0xFFFF; if ( x.type == o_mem ) { sel_t dp = getSR(cmd.ea, DP); if ( dp != BADSEL ) ea |= dp << 16; else ea = BADADDR; } else { ea |= cmd.ea & ~0xFFFF; } break; } return ea; }
//---------------------------------------------------------------------- ea_t calc_mem(op_t &x) { uint xaddr; if ( x.amode & amode_x ) { if (x.amode & amode_short) { sel_t dpage = getSR(cmd.ea, PAGE); if ( dpage == BADSEL ) return BADSEL; xaddr = ((dpage & 0xFF) << 8) | uint(x.addr); } else xaddr = (uint)x.addr; return xmem == BADADDR ? BADADDR : xmem + xaddr; } return toEA(cmd.cs, x.addr); }
void idaapi run(int) { static const char * nname; if ( ph.id == PLFM_MIPS ) nname = "$ mips"; else if ( ph.id == PLFM_ARM ) nname = " $arm"; else nname = "$ vmm functions"; netnode n(nname); ea_t ea = get_screen_ea(); // get current address if ( !isCode(get_flags_novalue(ea)) ) return; // not an instruction ea_t callee = n.altval(ea)-1; // get the callee address from the database // remove thumb bit for arm if ( ph.id == PLFM_ARM ) callee &= ~1; char buf[MAXSTR]; qsnprintf(buf, sizeof(buf), form, help); if ( AskUsingForm_c(buf, &callee) ) { if ( callee == BADADDR ) { n.altdel(ea); } else { if ( ph.id == PLFM_ARM && (callee & 1) == 0 ) { // if we're calling a thumb function, set bit 0 sel_t tbit = getSR(callee, T); if ( tbit != 0 && tbit != BADSEL ) callee |= 1; } n.altset(ea, callee+1); // save the new address } noUsed(ea); // reanalyze the current instruction } }
//---------------------------------------------------------------------- // change value of virtual register "BANK" and switch to another bank static void split(int reg, sel_t v) { if ( reg == -1 ) { flow = 0; if ( v != BADSEL ) { sel_t pclath = getSR(cmd.ea, PCLATH) & 0x1F; ea_t ea = calc_code_mem(uchar(v) | (pclath<<8)); ua_add_cref(0, ea, fl_JN); } } else { if ( v == BADSEL ) v = 0; // assume bank0 if bank is unknown if ( reg == BANK ) { if ( ptype != PIC16 ) v &= 3; else v &= 0xF; } splitSRarea1(get_item_end(cmd.ea), reg, v, SR_auto); } }
// Get description for a given general register. // Description may change according to the current number of the registers page. static const char * get_general_register_description(const ushort reg) { if ( reg < rR240 || reg > rR255 ) return NULL; switch ( getSR(cmd.ea, rRP) ) { // page: N/A case BADSEL: switch ( reg ) { case rR230: return "Central Interrupt Control Register"; case rR231: return "Flag Register"; case rR232: return "Pointer 0 Register"; case rR233: return "Pointer 1 Register"; case rR234: return "Page Pointer Register"; case rR235: return "Mode Register"; case rR236: return "User Stack Pointer High Register"; case rR237: return "User Stack Pointer Low Register"; case rR238: return "System Stack Pointer High Register"; case rR239: return "System Stack Pointer Low Register"; } break; // page: 0 case 0: switch ( reg ) { case rR241: return "Minor Register"; case rR242: return "External Interrupt Trigger Register"; case rR243: return "External Interrupt Pending Register"; case rR244: return "External Interrupt Mask-bit Register"; case rR245: return "External Interrupt Priority Level Register"; case rR246: return "External Interrupt Vector Register"; case rR247: return "Nested Interrupt Control"; case rR248: return "Watchdog Timer High Register"; case rR249: return "Watchdog Timer Low Register"; case rR250: return "Watchdog Timer Prescaler Register"; case rR251: return "Watchdog Timer Control Register"; case rR252: return "Wait Control Register"; case rR253: return "SPI Data Register"; case rR254: return "SPI Control Register"; } break; // page: 2 case 2: switch ( reg ) { case rR240: return "Port 0 Configuration Register 0"; case rR241: return "Port 0 Configuration Register 1"; case rR242: return "Port 0 Configuration Register 2"; case rR244: return "Port 1 Configuration Register 0"; case rR245: return "Port 1 Configuration Register 1"; case rR246: return "Port 1 Configuration Register 2"; case rR248: return "Port 2 Configuration Register 0"; case rR249: return "Port 2 Configuration Register 1"; case rR250: return "Port 2 Configuration Register 2"; } break; // page: 3 case 3: switch ( reg ) { case rR240: return "Port 4 Configuration Register 0"; case rR241: return "Port 4 Configuration Register 1"; case rR242: return "Port 4 Configuration Register 2"; case rR244: return "Port 5 Configuration Register 0"; case rR245: return "Port 5 Configuration Register 1"; case rR246: return "Port 5 Configuration Register 2"; case rR248: return "Port 6 Configuration Register 0"; case rR249: return "Port 6 Configuration Register 1"; case rR250: return "Port 6 Configuration Register 2"; case rR251: return "Port 6 Data Register"; case rR252: return "Port 7 Configuration Register 0"; case rR253: return "Port 7 Configuration Register 1"; case rR254: return "Port 7 Configuration Register 2"; case rR255: return "Port 7 Data Register"; } break; // page: 8, 10 or 12 case 8: case 10: case 12: switch ( reg ) { case rR240: return "Capture Load Register 0 High"; case rR241: return "Capture Load Register 0 Low"; case rR242: return "Capture Load Register 1 High"; case rR243: return "Capture Load Register 1 Low"; case rR244: return "Compare 0 Register High"; case rR245: return "Compare 0 Register Low"; case rR246: return "Compare 1 Register High"; case rR247: return "Compare 1 Register Low"; case rR248: return "Timer Control Register"; case rR249: return "Timer Mode Register"; case rR250: return "External Input Control Register"; case rR251: return "Prescaler Register"; case rR252: return "Output A Control Register"; case rR253: return "Output B Control Register"; case rR254: return "Flags Register"; case rR255: return "Interrupt/DMA Mask Register"; } break; // page: 9 case 9: switch ( reg ) { case rR240: case rR244: return "DMA Counter Pointer Register"; case rR241: case rR245: return "DMA Address Pointer Register"; case rR242: case rR246: return "Interrupt Vector Register"; case rR243: case rR247: return "Interrupt/DMA Control Register"; case rR248: return "I/O Connection Register"; } break; // page: 11 case 11: switch ( reg ) { case rR240: return "Counter High Byte Register"; case rR241: return "Counter Low Byte Register"; case rR242: return "Standard Timer Prescaler Register"; case rR243: return "Standard Timer Control Register"; } break; // page: 13 case 13: switch ( reg ) { case rR244: return "DMA Counter Pointer Register"; case rR245: return "DMA Address Pointer Register"; case rR246: return "Interrupt Vector Register"; case rR247: return "Interrupt/DMA Control Register"; } break; // page: 21 case 21: switch ( reg ) { case rR240: return "Data Page Register 0"; case rR241: return "Data Page Register 1"; case rR242: return "Data Page Register 2"; case rR243: return "Data Page Register 3"; case rR244: return "Code Segment Register"; case rR248: return "Interrupt Segment Register"; case rR249: return "DMA Segment Register"; case rR245: return "External Memory Register 1"; case rR246: return "External Memory Register 2"; } // page: 24 or 25 case 24: case 25: switch ( reg ) { case rR240: return "Receiver DMA Transaction Counter Pointer"; case rR241: return "Receiver DMA Source Address Pointer"; case rR242: return "Transmitter DMA Transaction Counter Pointer"; case rR243: return "Transmitter DMA Source Address Pointer"; case rR244: return "Interrupt Vector Register"; case rR245: return "Address/Data Compare Register"; case rR246: return "Interrupt Mask Register"; case rR247: return "Interrupt Status Register"; case rR248: return "Receive/Transmitter Buffer Register"; case rR249: return "Interrupt/DMA Priority Register"; case rR250: return "Character Configuration Register"; case rR251: return "Clock Configuration Register"; case rR252: return "Baud Rate Generator High Register"; case rR253: return "Baud Rate Generator Low Register"; case rR254: return "Synchronous Input Control"; case rR255: return "Synchronous Output Control"; } break; // page: 43 case 43: switch ( reg ) { case rR248: return "Port 8 Configuration Register 0"; case rR249: return "Port 8 Configuration Register 1"; case rR250: return "Port 8 Configuration Register 2"; case rR251: return "Port 8 Data Register"; case rR252: return "Port 9 Configuration Register 0"; case rR253: return "Port 9 Configuration Register 1"; case rR254: return "Port 9 Configuration Register 2"; case rR255: return "Port 9 Data Register"; } break; // page: 55 case 55: switch ( reg ) { case rR240: return "Clock Control Register"; case rR242: return "Clock Flag Register"; case rR246: return "PLL Configuration Register"; } break; // page: 63 case 63: switch ( reg ) { case rR240: return "Channel 0 Data Register"; case rR241: return "Channel 1 Data Register"; case rR242: return "Channel 2 Data Register"; case rR243: return "Channel 3 Data Register"; case rR244: return "Channel 4 Data Register"; case rR245: return "Channel 5 Data Register"; case rR246: return "Channel 6 Data Register"; case rR247: return "Channel 7 Data Register"; case rR248: return "Channel 6 Lower Threshold Register"; case rR249: return "Channel 6 Lower Threshold Register"; case rR250: return "Channel 7 Upper Threshold Register"; case rR251: return "Channel 7 Upper Threshold Register"; case rR252: return "Compare Result Register"; case rR253: return "Control Logic Register"; case rR254: return "Interrupt Control Register"; case rR255: return "Interrupt Vector Register"; } break; } return NULL; }
// Emulate an instruction. int emu(void) { uint32 feature = cmd.get_canon_feature(); flow = ((feature & CF_STOP) == 0); if ( cmd.Op1.type != o_void) handle_operand(cmd.Op1, (feature & CF_CHG1) != 0 ); if ( cmd.Op2.type != o_void) handle_operand(cmd.Op2, (feature & CF_CHG2) != 0 ); if ( cmd.Op3.type != o_void) handle_operand(cmd.Op3, (feature & CF_CHG3) != 0 ); if ( flow ) ua_add_cref(0, cmd.ea + cmd.size, fl_F); // Following code will update the current value of the two virtual // segment registers: RW (register window) and RP (register page). bool rw_has_changed = false; bool rp_has_changed = false; switch ( cmd.itype ) { case st9_srp: { sel_t val = cmd.Op1.value; if ( val % 2 ) val--; // even reduced splitSRarea1(cmd.ea, rRW, val | (val << 8), SR_auto); } rw_has_changed = true; break; case st9_srp0: { sel_t RW = getSR(cmd.ea, rRW); splitSRarea1(cmd.ea, rRW, cmd.Op1.value | (RW & 0xFF00), SR_auto); } rw_has_changed = true; break; case st9_srp1: { sel_t RW = getSR(cmd.ea, rRW); splitSRarea1(cmd.ea, rRW, (cmd.Op1.value << 8) | (RW & 0x00FF), SR_auto); } rw_has_changed = true; break; case st9_spp: splitSRarea1(cmd.ea, rRP, cmd.Op1.value, SR_auto); rp_has_changed = true; break; } // If RW / RP registers have changed, print a comment which explains the new mapping of // the general registers. if ( rw_has_changed && !has_cmt(uFlag) ) { char buf[MAXSTR]; sel_t RW = getSR(cmd.ea, rRW); int low = RW & 0x00FF; int high = (RW & 0xFF00) >> 8; low *= 8; high *= 8; const char *fmt = "r0 -> R%d, r1 -> R%d, r2 -> R%d, r3 -> R%d, r4 -> R%d, r5 -> R%d, r6 -> R%d, r7 -> R%d,\n" "r8 -> R%d, r9 -> R%d, r10 -> R%d, r11 -> R%d, r12 -> R%d, r13 -> R%d, r14 -> R%d, r15 -> R%d"; qsnprintf(buf, sizeof buf, fmt, 0 + low, 1 + low, 2 + low, 3 + low, 4 + low, 5 + low, 6 + low, 7 + low, 8 + high, 9 + high, 10 + high, 11 + high, 12 + high, 13 + high, 14 + high, 15 + high ); set_cmt(cmd.ea, buf, false); }
//------------------------------------------------------------------------ inline uint16 get_rp(ea_t ea) { sel_t t = getSR(ea, rRp); return t != BADSEL ? t : 0; }
//------------------------------------------------------------------------ ea_t calc_data_mem(op_t &x) { sel_t dpage = getSR(cmd.ea, dp); if ( dpage == BADSEL ) return BADSEL; return ((dpage & 0xFF) << 16) | (x.addr); }
//---------------------------------------------------------------------- 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; }