Files
2021-10-31 21:20:46 +02:00

515 lines
12 KiB
C++

/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-99 by Ilfak Guilfanov.
* ALL RIGHTS RESERVED.
* E-mail: ig@datarescue.com
*
*
*/
#include "h8500.hpp"
#include <frame.hpp>
//------------------------------------------------------------------------
static void process_immediate_number(const insn_t &insn, int n)
{
set_immd(insn.ea);
if ( is_defarg(get_flags(insn.ea), n) )
return;
switch ( insn.itype )
{
case H8500_add_q:
case H8500_bclr:
case H8500_bnot:
case H8500_bset:
case H8500_btst:
op_dec(insn.ea, n);
break;
case H8500_and:
case H8500_or:
case H8500_xor:
case H8500_andc:
case H8500_orc:
case H8500_xorc:
op_num(insn.ea, n);
break;
}
}
//----------------------------------------------------------------------
inline bool issp(int x)
{
return x == SP;
}
inline bool isbp(int x)
{
return x == FP;
}
//----------------------------------------------------------------------
int idaapi is_sp_based(const insn_t &, const op_t &x)
{
return OP_SP_ADD
| ((x.type == o_displ || x.type == o_phrase) && issp(x.phrase)
? OP_SP_BASED
: OP_FP_BASED);
}
//----------------------------------------------------------------------
static void add_stkpnt(const insn_t &insn, sval_t value)
{
func_t *pfn = get_func(insn.ea);
if ( pfn == NULL )
return;
if ( value & 1 )
value++;
add_auto_stkpnt(pfn, insn.ea+insn.size, value);
}
//----------------------------------------------------------------------
inline bool is_mov(int itype)
{
return itype >= H8500_mov_g && itype <= H8500_mov_s;
}
//----------------------------------------------------------------------
static bool get_op_value(const insn_t &insn, const 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 movi;
if ( decode_prev_insn(&movi, insn.ea) != BADADDR
&& is_mov(movi.itype)
&& movi.Op1.type == o_imm
&& movi.Op2.type == o_reg
&& movi.Op2.reg == reg )
{
*value = (int)movi.Op1.value;
ok = true;
}
}
return ok;
}
//----------------------------------------------------------------------
static int calc_reglist_count(int regs)
{
int count = 0;
for ( int i=0; i < 8; i++,regs>>=1 )
if ( regs & 1 )
count++;
return count;
}
//----------------------------------------------------------------------
// @--sp
inline bool is_sp_dec(const op_t &x)
{
return x.type == o_phrase
&& issp(x.reg)
&& x.phtype == ph_pre;
}
//----------------------------------------------------------------------
// @sp++
inline bool is_sp_inc(const op_t &x)
{
return x.type == o_phrase
&& issp(x.reg)
&& x.phtype == ph_post;
}
//----------------------------------------------------------------------
static void trace_sp(const insn_t &insn)
{
// @sp++
if ( is_sp_inc(insn.Op1) )
{
int size = 2;
if ( insn.Op2.type == o_reglist )
size *= calc_reglist_count(insn.Op2.reg);
add_stkpnt(insn, size);
return;
}
// @--sp
if ( is_sp_dec(insn.Op2) )
{
int size = 2;
if ( insn.Op1.type == o_reglist )
size *= calc_reglist_count(insn.Op1.reg);
add_stkpnt(insn, -size);
return;
}
// xxx @--sp
if ( is_sp_dec(insn.Op1) )
{
add_stkpnt(insn, -2);
return;
}
int v;
switch ( insn.itype )
{
case H8500_add_g:
case H8500_add_q:
case H8500_adds:
if ( issp(insn.Op2.reg) && get_op_value(insn, insn.Op1, &v) )
add_stkpnt(insn, v);
break;
case H8500_sub:
case H8500_subs:
if ( issp(insn.Op2.reg) && get_op_value(insn, insn.Op1, &v) )
add_stkpnt(insn, -v);
break;
}
}
//----------------------------------------------------------------------
static sval_t calc_func_call_delta(const insn_t &insn, ea_t callee)
{
sval_t delta;
func_t *pfn = get_func(callee);
if ( pfn != NULL )
{
delta = pfn->argsize;
if ( (pfn->flags & FUNC_FAR) != 0 && insn.Op1.type == o_near )
delta += 2; // function will pop the code segment
}
else
{
delta = get_ind_purged(callee);
if ( delta == -1 )
delta = 0;
}
return delta;
}
//----------------------------------------------------------------------
// trace a function call.
// adjuct the stack, determine the execution flow
// returns:
// true - the called function returns to the caller
// false - the called function doesn't return to the caller
static bool handle_function_call(const insn_t &insn, ea_t callee)
{
bool funcflow = true;
if ( !func_does_return(callee) )
funcflow = false;
if ( inf_should_trace_sp() )
{
func_t *caller = get_func(insn.ea);
if ( func_contains(caller, insn.ea+insn.size) )
{
sval_t delta = calc_func_call_delta(insn, callee);
if ( delta != 0 )
add_stkpnt(insn, delta);
}
}
return funcflow;
}
//----------------------------------------------------------------------
inline ea_t find_callee(const insn_t &insn)
{
return get_first_fcref_from(insn.ea);
}
//----------------------------------------------------------------------
void h8500_t::handle_operand(const insn_t &insn, const op_t &x, bool is_forced, bool isload)
{
switch ( x.type )
{
case o_reg:
case o_reglist:
return;
case o_imm:
QASSERT(10090, isload);
process_immediate_number(insn, x.n);
if ( op_adds_xrefs(get_flags(insn.ea), x.n) )
insn.add_off_drefs(x, dr_O, calc_opimm_flags(insn));
break;
case o_phrase:
case o_displ:
{
process_immediate_number(insn, x.n);
if ( is_forced )
break;
flags_t F = get_flags(insn.ea);
if ( op_adds_xrefs(F, x.n) )
{
ea_t ea = insn.add_off_drefs(x, isload ? dr_R : dr_W, calc_opdispl_flags(insn));
if ( ea != BADADDR )
insn.create_op_data(ea, x);
}
// create stack variables if required
if ( x.type == o_displ
&& may_create_stkvars()
&& !is_defarg(F, x.n) )
{
func_t *pfn = get_func(insn.ea);
if ( pfn != NULL
&& (issp(x.phrase)
|| isbp(x.phrase) && (pfn->flags & FUNC_FRAME) != 0) )
{
if ( insn.create_stkvar(x, x.addr, STKVAR_VALID_SIZE) )
op_stkvar(insn.ea, x.n);
}
}
}
break;
case o_near:
case o_far:
{
cref_t ftype = x.type == o_near ? fl_JN : fl_JF;
ea_t ea = calc_mem(insn, x);
if ( has_insn_feature(insn.itype, CF_CALL) )
{
if ( !func_does_return(ea) )
flow = false;
ftype = x.type == o_near ? fl_CN : fl_CF;
}
insn.add_cref(ea, x.offb, ftype);
}
break;
case o_mem:
{
ea_t ea = calc_mem(insn, x);
insn.add_dref(ea, x.offb, isload ? dr_R : dr_W);
insn.create_op_data(ea, x);
}
break;
default:
INTERR(10091);
}
}
//----------------------------------------------------------------------
inline bool is_far_ending(const insn_t &insn)
{
return insn.itype == H8500_prts
|| insn.itype == H8500_prtd;
}
//----------------------------------------------------------------------
int h8500_t::h8500_emu(const insn_t &insn)
{
uint32 Feature = insn.get_canon_feature(ph);
bool flag1 = is_forced_operand(insn.ea, 0);
bool flag2 = is_forced_operand(insn.ea, 1);
bool flag3 = is_forced_operand(insn.ea, 2);
flow = ((Feature & CF_STOP) == 0);
if ( Feature & CF_USE1 ) handle_operand(insn, insn.Op1, flag1, true);
if ( Feature & CF_USE2 ) handle_operand(insn, insn.Op2, flag2, true);
if ( Feature & CF_USE3 ) handle_operand(insn, insn.Op3, flag3, true);
if ( Feature & CF_CHG1 ) handle_operand(insn, insn.Op1, flag1, false);
if ( Feature & CF_CHG2 ) handle_operand(insn, insn.Op2, flag2, false);
if ( Feature & CF_CHG3 ) handle_operand(insn, insn.Op3, flag3, false);
//
// Determine if the next instruction should be executed
//
if ( segtype(insn.ea) == SEG_XTRN )
flow = false;
//
// Handle loads to segment registers
//
sel_t v = BADSEL;
switch ( insn.itype )
{
case H8500_andc:
if ( insn.Op1.value == 0 )
v = 0;
goto SPLIT;
case H8500_orc:
if ( insn.Op1.value == 0xFF )
v = 0xFF;
goto SPLIT;
case H8500_ldc:
if ( insn.Op1.type == o_imm )
v = insn.Op1.value;
// fallthrough
case H8500_xorc:
SPLIT:
if ( insn.Op2.reg >= BR && insn.Op2.reg <= TP )
split_sreg_range(insn.ea+insn.size, insn.Op2.reg, v, SR_auto);
break;
}
if ( (Feature & CF_CALL) != 0 )
{
ea_t callee = find_callee(insn);
if ( !handle_function_call(insn, callee) )
flow = false;
}
//
// Handle SP modifications
//
if ( may_trace_sp() )
{
func_t *pfn = get_func(insn.ea);
if ( pfn != NULL )
{
if ( (pfn->flags & FUNC_USERFAR) == 0
&& (pfn->flags & FUNC_FAR) == 0
&& is_far_ending(insn) )
{
pfn->flags |= FUNC_FAR;
update_func(pfn);
reanalyze_callers(pfn->start_ea, 0);
}
if ( !flow )
recalc_spd(insn.ea); // recalculate SP register for the next insn
else
trace_sp(insn);
}
}
if ( flow )
add_cref(insn.ea, insn.ea+insn.size, fl_F);
return 1;
}
//----------------------------------------------------------------------
int is_jump_func(const func_t * /*pfn*/, ea_t *jump_target)
{
*jump_target = BADADDR;
return 0; // means "don't know"
}
//----------------------------------------------------------------------
int may_be_func(const insn_t &)
{
// if ( insn.itype == H8_push && isbp(insn.Op1.reg) ) return 100; // push.l er6
return 0;
}
//----------------------------------------------------------------------
int is_sane_insn(const insn_t &insn, int /*nocrefs*/)
{
if ( insn.itype == H8500_nop )
{
for ( int i=0; i < 8; i++ )
if ( get_word(insn.ea-i*2) != 0 )
return 1;
return 0; // too many nops in a row
}
return 1;
}
//----------------------------------------------------------------------
int idaapi is_align_insn(ea_t ea)
{
insn_t insn;
if ( decode_insn(&insn, ea) < 1 )
return 0;
switch ( insn.itype )
{
case H8500_nop:
break;
case H8500_mov_g: // B/W Move data
case H8500_mov_e: // B Move data
case H8500_mov_i: // W Move data
case H8500_mov_f: // B/W Move data
case H8500_mov_l: // B/W Move data
case H8500_mov_s: // B/W Move data
case H8500_or:
case H8500_and:
if ( insn.Op1.type == insn.Op2.type && insn.Op1.reg == insn.Op2.reg )
break;
default:
return 0;
}
return insn.size;
}
//----------------------------------------------------------------------
int idaapi h8500_get_frame_retsize(const func_t *pfn)
{
return pfn->flags & FUNC_FAR ? 4 : 2;
}
//----------------------------------------------------------------------
static uval_t find_ret_purged(const func_t *pfn)
{
uval_t argsize = 0;
ea_t ea = pfn->start_ea;
insn_t insn;
while ( ea < pfn->end_ea )
{
decode_insn(&insn, ea);
if ( insn.itype == H8500_rtd || insn.itype == H8500_prtd )
{
argsize = insn.Op1.value;
break;
}
ea = next_that(ea, pfn->end_ea, f_is_code);
}
// could not find any ret instructions
// but the function ends with a jump
if ( ea >= pfn->end_ea
&& (insn.itype == H8500_jmp || insn.itype == H8500_pjmp) )
{
ea_t target = calc_mem(insn, insn.Op1);
pfn = get_func(target);
if ( pfn != NULL )
argsize = pfn->argsize;
}
return argsize;
}
//----------------------------------------------------------------------
static void setup_far_func(func_t *pfn)
{
if ( (pfn->flags & FUNC_FAR) == 0 )
{
ea_t ea1 = pfn->start_ea;
ea_t ea2 = pfn->end_ea;
while ( ea1 < ea2 )
{
if ( is_code(get_flags(ea1)) )
{
insn_t insn;
decode_insn(&insn, ea1);
if ( is_far_ending(insn) )
{
pfn->flags |= FUNC_FAR;
update_func(pfn);
break;
}
}
ea1 = next_head(ea1, ea2);
}
}
}
//----------------------------------------------------------------------
bool idaapi create_func_frame(func_t *pfn)
{
if ( pfn != NULL )
{
setup_far_func(pfn);
uval_t argsize = find_ret_purged(pfn);
add_frame(pfn, 0, 0, argsize);
}
return true;
}