298 lines
7.3 KiB
C++
298 lines
7.3 KiB
C++
/*
|
|
* Interactive disassembler (IDA).
|
|
* Copyright (c) 1990-99 by Ilfak Guilfanov.
|
|
* ALL RIGHTS RESERVED.
|
|
* E-mail: ig@datarescue.com
|
|
*
|
|
*
|
|
*/
|
|
|
|
#include "f2mc.hpp"
|
|
#include <segregs.hpp>
|
|
#include <frame.hpp>
|
|
|
|
//------------------------------------------------------------------------
|
|
static int get_reglist_size(ushort reglist)
|
|
{
|
|
int size = 0;
|
|
for ( int i = 0; i < 8; i++ )
|
|
if ( (reglist >> i) & 1 )
|
|
size++;
|
|
return size;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
static bool is_bank(const op_t &op)
|
|
{
|
|
if ( op.type != o_reg )
|
|
return false;
|
|
|
|
return op.reg == DTB
|
|
|| op.reg == ADB
|
|
|| op.reg == SSB
|
|
|| op.reg == USB
|
|
|| op.reg == DPR
|
|
|| op.reg == PCB;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static void process_imm(const insn_t &insn, const op_t &x)
|
|
{
|
|
set_immd(insn.ea);
|
|
|
|
if ( op_adds_xrefs(get_flags(insn.ea), x.n) )
|
|
insn.add_off_drefs(x, dr_O, calc_outf(x));
|
|
|
|
if ( is_defarg(get_flags(insn.ea), x.n) )
|
|
return; // if already defined by user
|
|
|
|
switch ( insn.itype )
|
|
{
|
|
case F2MC_add:
|
|
case F2MC_addl:
|
|
case F2MC_addsp:
|
|
case F2MC_addw2:
|
|
case F2MC_and:
|
|
case F2MC_andw2:
|
|
case F2MC_callv:
|
|
case F2MC_cbne:
|
|
case F2MC_cmp2:
|
|
case F2MC_cmpl:
|
|
case F2MC_cmpw2:
|
|
case F2MC_cwbne:
|
|
case F2MC_int:
|
|
case F2MC_link:
|
|
case F2MC_mov:
|
|
case F2MC_movl:
|
|
case F2MC_movn:
|
|
case F2MC_movw:
|
|
case F2MC_movx:
|
|
case F2MC_or:
|
|
case F2MC_orw2:
|
|
case F2MC_sub:
|
|
case F2MC_subl:
|
|
case F2MC_subw2:
|
|
case F2MC_xor:
|
|
case F2MC_xorw2:
|
|
op_num(insn.ea, x.n);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void f2mc_t::handle_operand(const insn_t &insn, const op_t &x, bool use)
|
|
{
|
|
switch ( x.type )
|
|
{
|
|
case o_reg:
|
|
case o_phrase:
|
|
case o_reglist:
|
|
return;
|
|
|
|
case o_near:
|
|
case o_far:
|
|
{
|
|
cref_t ftype = fl_JN;
|
|
ea_t ea = x.addr;
|
|
// 24-bit (far) operands store the full address.
|
|
// so this calculation is needed only for near jumps/calls
|
|
if ( x.type == o_near )
|
|
ea = calc_code_mem(insn, x.addr);
|
|
|
|
if ( has_insn_feature(insn.itype, CF_CALL) )
|
|
{
|
|
if ( !func_does_return(ea) )
|
|
flow = false;
|
|
ftype = fl_CN;
|
|
}
|
|
insn.add_cref(ea, x.offb, ftype);
|
|
}
|
|
break;
|
|
|
|
case o_imm:
|
|
QASSERT(10102, use);
|
|
process_imm(insn, x);
|
|
break;
|
|
|
|
case o_mem:
|
|
{
|
|
ea_t ea = calc_data_mem(x.addr);
|
|
insn.add_dref(ea, x.offb, use ? dr_R : dr_W);
|
|
insn.create_op_data(ea, x);
|
|
}
|
|
break;
|
|
case o_displ:
|
|
process_imm(insn, x);
|
|
if ( may_create_stkvars() && x.reg == RW3 )
|
|
{
|
|
func_t *pfn = get_func(insn.ea);
|
|
if ( pfn != NULL
|
|
&& (pfn->flags & FUNC_FRAME) != 0
|
|
&& insn.create_stkvar(x, x.addr, STKVAR_VALID_SIZE) )
|
|
{
|
|
op_stkvar(insn.ea, x.n);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
warning("%a: %s,%d: bad optype %d", insn.ea, insn.get_canon_mnem(ph), x.n, x.type);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline bool add_stkpnt(func_t *pfn, sval_t delta, const insn_t &insn)
|
|
{
|
|
return add_auto_stkpnt(pfn, insn.ea + insn.size, delta);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static void trace_sp(const insn_t &insn)
|
|
{
|
|
func_t *pfn = get_func(insn.ea);
|
|
if ( pfn == NULL )
|
|
return;
|
|
|
|
switch ( insn.itype )
|
|
{
|
|
case F2MC_int:
|
|
case F2MC_intp:
|
|
case F2MC_int9:
|
|
add_stkpnt(pfn, -6*2, insn);
|
|
break;
|
|
case F2MC_reti:
|
|
add_stkpnt(pfn, 6*2, insn);
|
|
break;
|
|
case F2MC_link:
|
|
add_stkpnt(pfn, -2-insn.Op1.value, insn);
|
|
break;
|
|
case F2MC_unlink:
|
|
add_stkpnt(pfn, -get_spd(pfn, insn.ea), insn);
|
|
break;
|
|
case F2MC_ret:
|
|
add_stkpnt(pfn, 2, insn);
|
|
break;
|
|
case F2MC_retp:
|
|
add_stkpnt(pfn, 2*2, insn);
|
|
break;
|
|
case F2MC_pushw:
|
|
if ( insn.Op1.type == o_reglist )
|
|
add_stkpnt(pfn, -get_reglist_size(insn.Op1.reg)*2, insn);
|
|
else
|
|
add_stkpnt(pfn, -2, insn);
|
|
break;
|
|
case F2MC_popw:
|
|
if ( insn.Op1.type == o_reglist )
|
|
add_stkpnt(pfn, get_reglist_size(insn.Op1.reg)*2, insn);
|
|
else
|
|
add_stkpnt(pfn, 2, insn);
|
|
break;
|
|
case F2MC_addsp:
|
|
add_stkpnt(pfn, insn.Op1.value, insn);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
int f2mc_t::emu(const insn_t &insn)
|
|
{
|
|
uint32 feature = insn.get_canon_feature(ph);
|
|
flow = (feature & CF_STOP) == 0;
|
|
|
|
if ( feature & CF_USE1 ) handle_operand(insn, insn.Op1, true);
|
|
if ( feature & CF_USE2 ) handle_operand(insn, insn.Op2, true);
|
|
if ( feature & CF_USE3 ) handle_operand(insn, insn.Op3, true);
|
|
|
|
if ( feature & CF_CHG1 ) handle_operand(insn, insn.Op1, false);
|
|
if ( feature & CF_CHG2 ) handle_operand(insn, insn.Op2, false);
|
|
if ( feature & CF_CHG3 ) handle_operand(insn, insn.Op3, false);
|
|
|
|
// check for CCR changes
|
|
if ( insn.Op1.type == o_reg && insn.Op1.reg == CCR )
|
|
{
|
|
op_bin(insn.ea, 1);
|
|
|
|
sel_t ccr = get_sreg(insn.ea, CCR);
|
|
if ( ccr == BADSEL )
|
|
ccr = 0;
|
|
|
|
if ( insn.itype == F2MC_and )
|
|
ccr &= insn.Op2.value; // and ccr,imm8
|
|
else if ( insn.itype == F2MC_or )
|
|
ccr |= insn.Op2.value; // or ccr,imm8
|
|
split_sreg_range(get_item_end(insn.ea), CCR, ccr, SR_auto);
|
|
}
|
|
|
|
|
|
// check for DTB,ADB,SSB,USB,DPR changes
|
|
if ( insn.itype == F2MC_mov && is_bank(insn.Op1)
|
|
&& insn.Op2.type == o_reg && insn.Op2.reg == A ) // mov dtb|adb|ssb|usb|dpr,a
|
|
{
|
|
sel_t bank = BADSEL;
|
|
insn_t l;
|
|
if ( decode_prev_insn(&l, insn.ea) != BADADDR && l.itype == F2MC_mov
|
|
&& l.Op1.type == o_reg && l.Op1.reg == A )
|
|
{
|
|
if ( l.Op2.type == o_imm ) // mov a,imm8
|
|
bank = l.Op2.value;
|
|
else if ( is_bank(l.Op2) ) // mov a,dtb|adb|ssb|usb|dpr|pcb
|
|
{
|
|
bank = get_sreg(l.ea, l.Op2.reg);
|
|
if ( bank == BADSEL )
|
|
bank = 0;
|
|
}
|
|
}
|
|
if ( bank != BADSEL )
|
|
split_sreg_range(get_item_end(insn.ea), insn.Op1.reg, bank, SR_auto);
|
|
}
|
|
|
|
|
|
// determine if the next instruction should be executed
|
|
if ( segtype(insn.ea) == SEG_XTRN )
|
|
flow = false;
|
|
if ( flow )
|
|
add_cref(insn.ea, insn.ea + insn.size, fl_F);
|
|
|
|
if ( may_trace_sp() )
|
|
{
|
|
if ( !flow )
|
|
recalc_spd(insn.ea); // recalculate SP register for the next insn
|
|
else
|
|
trace_sp(insn);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool idaapi create_func_frame(func_t *pfn)
|
|
{
|
|
if ( pfn != NULL )
|
|
{
|
|
if ( pfn->frame == BADNODE )
|
|
{
|
|
ea_t ea = pfn->start_ea;
|
|
if ( ea + 4 < pfn->end_ea ) // minimum 2+1+1 bytes needed
|
|
{
|
|
insn_t insn;
|
|
decode_insn(&insn, ea);
|
|
if ( insn.itype == F2MC_link )
|
|
{
|
|
size_t localsize = (size_t)insn.Op1.value;
|
|
ushort regsize = 2;
|
|
decode_insn(&insn, ea + 2);
|
|
pfn->flags |= FUNC_FRAME;
|
|
return add_frame(pfn, localsize, regsize, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int idaapi is_sp_based(const insn_t &, const op_t &)
|
|
{
|
|
return OP_SP_ADD | OP_FP_BASED;
|
|
}
|