1470 lines
38 KiB
C++
1470 lines
38 KiB
C++
/*
|
|
* Interactive disassembler (IDA).
|
|
* Copyright (c) 1990-99 by Ilfak Guilfanov.
|
|
* ALL RIGHTS RESERVED.
|
|
* E-mail: ig@datarescue.com
|
|
*
|
|
*
|
|
*/
|
|
|
|
#include "h8.hpp"
|
|
#include <frame.hpp>
|
|
#include <segregs.hpp>
|
|
#include <jumptable.hpp>
|
|
|
|
//------------------------------------------------------------------------
|
|
static void process_immediate_number(const insn_t &insn, int n, flags_t F)
|
|
{
|
|
set_immd(insn.ea);
|
|
if ( is_defarg(F, n) )
|
|
return;
|
|
switch ( insn.itype )
|
|
{
|
|
case H8_shal:
|
|
case H8_shar:
|
|
case H8_shll:
|
|
case H8_shlr:
|
|
case H8_rotl:
|
|
case H8_rotr:
|
|
case H8_rotxl:
|
|
case H8_rotxr:
|
|
if ( n == 0 )
|
|
op_dec(insn.ea, n);
|
|
break;
|
|
case H8_and:
|
|
case H8_or:
|
|
case H8_xor:
|
|
op_num(insn.ea, n);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline bool issp(int x)
|
|
{
|
|
return x == R7 || x == ER7;
|
|
}
|
|
|
|
inline bool isbp(int x)
|
|
{
|
|
return x == R6 || x == ER6;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
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, ssize_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);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static const int RegBySize[8][3] =
|
|
{
|
|
{ R0L, R0, ER0 },
|
|
{ R1L, R1, ER1 },
|
|
{ R2L, R2, ER2 },
|
|
{ R3L, R3, ER3 },
|
|
{ R4L, R4, ER4 },
|
|
{ R5L, R5, ER5 },
|
|
{ R6L, R6, ER6 },
|
|
{ R7L, R7, ER7 },
|
|
};
|
|
|
|
inline op_dtype_t get_regsize(int reg)
|
|
{
|
|
switch ( reg / 8 )
|
|
{
|
|
case 0: // Rn
|
|
case 1: // En
|
|
return dt_word;
|
|
case 2: // RnH
|
|
case 3: // RnL
|
|
return dt_byte;
|
|
case 4: // ERn
|
|
return dt_dword;
|
|
default: // not general registers
|
|
return dt_void;
|
|
}
|
|
}
|
|
|
|
inline int cvt_to_wholereg(int reg, op_dtype_t dtype = dt_word)
|
|
{
|
|
if ( dtype > dt_dword )
|
|
return -1;
|
|
return RegBySize[reg & 7][size_t(dtype)]; //lint !e571 Suspicious cast
|
|
}
|
|
|
|
inline bool is_high_bits_reg(int reg)
|
|
{
|
|
switch ( reg / 8 )
|
|
{
|
|
case 1: // En
|
|
case 2: // RnH
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// a) is_same_reg(R0, R0L) -> true
|
|
// b) is_same_reg(R0, R0H) -> false
|
|
inline bool is_same_reg(int r1, int r2)
|
|
{
|
|
op_dtype_t dt1 = get_regsize(r1);
|
|
op_dtype_t dt2 = get_regsize(r2);
|
|
// assert: dt1 != dt_void && dt2 != o_void
|
|
if ( dt1 < dt2 )
|
|
r2 = cvt_to_wholereg(r2, dt1);
|
|
else if ( dt1 > dt2 )
|
|
r1 = cvt_to_wholereg(r1, dt2);
|
|
// a) R0L == R0L; b) R0L != R0H
|
|
return r1 == r2;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// a) regs_have_common_bits(R0, R0L) -> true
|
|
// b) regs_have_common_bits(R0, R0H) -> true
|
|
// b) regs_have_common_bits(E0, R0H) -> false
|
|
inline bool regs_have_common_bits(
|
|
int r1,
|
|
int r2,
|
|
op_dtype_t dt1,
|
|
op_dtype_t dt2)
|
|
{
|
|
// assert: dt1 != dt_void && dt2 != o_void
|
|
if ( dt1 < dt2 )
|
|
r1 = cvt_to_wholereg(r1, dt2);
|
|
else if ( dt1 > dt2 )
|
|
r2 = cvt_to_wholereg(r2, dt1);
|
|
// a) R0 == R0; b) R0 == R0; c) E0 != R0
|
|
return r1 == r2;
|
|
}
|
|
inline bool regs_have_common_bits(int r1, int r2)
|
|
{
|
|
return regs_have_common_bits(r1, r2, get_regsize(r1), get_regsize(r2));
|
|
}
|
|
inline bool regs_have_common_bits(int r1, int r2, op_dtype_t dt1)
|
|
{
|
|
return regs_have_common_bits(r1, r2, dt1, get_regsize(r2));
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// does the instruction spoil the register?
|
|
#define PROC_MAXCHGOP 3
|
|
bool h8_t::spoils(const insn_t &insn, int reg) const
|
|
{
|
|
op_dtype_t dtype = get_regsize(reg);
|
|
switch ( insn.itype )
|
|
{
|
|
case H8_bsr:
|
|
case H8_bsrbc:
|
|
case H8_bsrbs:
|
|
case H8_jsr:
|
|
case H8_trapa:
|
|
return true; // TODO take in account ABI
|
|
case H8_eepmov:
|
|
case H8_movmd:
|
|
case H8_movsd:
|
|
return true; // TODO check R4/L, ER5, ER6
|
|
case H8_push:
|
|
case H8_pop:
|
|
case H8_rts:
|
|
case H8_rte:
|
|
case H8_rtsl:
|
|
case H8_rtel:
|
|
if ( regs_have_common_bits(reg, SP, dtype, dt_word) )
|
|
return true;
|
|
break;
|
|
}
|
|
uint32 feature = insn.get_canon_feature(ph);
|
|
if ( feature == 0 )
|
|
return false;
|
|
for ( int i = 0; i < PROC_MAXCHGOP; ++i )
|
|
{
|
|
if ( !has_cf_use(feature, i) )
|
|
continue;
|
|
const op_t &x = insn.ops[i];
|
|
if ( x.type == o_phrase
|
|
&& x.phtype != ph_normal
|
|
&& regs_have_common_bits(reg, x.phrase, dtype) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
for ( int i = 0; i < PROC_MAXCHGOP; ++i )
|
|
{
|
|
if ( !has_cf_chg(feature, i) )
|
|
continue;
|
|
const op_t &x = insn.ops[i];
|
|
if ( x.type == o_reg && regs_have_common_bits(reg, x.reg, dtype)
|
|
|| x.type == o_reglist ) // TODO check reg in list
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// does the instruction spoil the flags?
|
|
static bool spoils_flags(const insn_t &insn)
|
|
{
|
|
switch ( insn.itype )
|
|
{
|
|
case H8_ldm:
|
|
case H8_stm:
|
|
case H8_movab:
|
|
case H8_movaw:
|
|
case H8_eepmov:
|
|
case H8_movmd:
|
|
case H8_movsd:
|
|
return false;
|
|
|
|
case H8_adds:
|
|
case H8_subs:
|
|
case H8_mulxu:
|
|
case H8_mulu:
|
|
case H8_muluu:
|
|
case H8_mac:
|
|
case H8_clrmac:
|
|
case H8_ldmac:
|
|
return false;
|
|
|
|
case H8_bset:
|
|
case H8_bclr:
|
|
case H8_bnot:
|
|
case H8_bsetne:
|
|
case H8_bseteq:
|
|
case H8_bclrne:
|
|
case H8_bclreq:
|
|
case H8_bst:
|
|
case H8_bist:
|
|
case H8_bstz:
|
|
case H8_bistz:
|
|
case H8_bfld:
|
|
case H8_bfst:
|
|
return false;
|
|
|
|
case H8_brabs:
|
|
case H8_brabc:
|
|
case H8_bra:
|
|
case H8_brn:
|
|
case H8_bhi:
|
|
case H8_bls:
|
|
case H8_bcc:
|
|
case H8_bcs:
|
|
case H8_bne:
|
|
case H8_beq:
|
|
case H8_bvc:
|
|
case H8_bvs:
|
|
case H8_bpl:
|
|
case H8_bmi:
|
|
case H8_bge:
|
|
case H8_blt:
|
|
case H8_bgt:
|
|
case H8_ble:
|
|
case H8_bras:
|
|
case H8_jmp:
|
|
case H8_rts:
|
|
case H8_rtsl:
|
|
return false;
|
|
|
|
case H8_ldc:
|
|
case H8_andc:
|
|
case H8_orc:
|
|
case H8_xorc:
|
|
return insn.Op2.is_reg(CCR);
|
|
case H8_stc:
|
|
case H8_sleep:
|
|
case H8_nop:
|
|
return false;
|
|
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool h8_t::get_op_value(uval_t *value, const insn_t &_insn, const op_t &x) const
|
|
{
|
|
if ( x.type == o_imm )
|
|
{
|
|
*value = x.value;
|
|
return true;
|
|
}
|
|
|
|
if ( x.type != o_reg
|
|
&& (x.type != o_displ
|
|
|| x.displtype != dt_normal && x.displtype != dt_regidx)
|
|
&& x.type != o_phrase
|
|
&& x.type != o_pcidx )
|
|
{
|
|
return false;
|
|
}
|
|
uint16 reg = x.reg;
|
|
|
|
bool ok = false;
|
|
insn_t insn;
|
|
ea_t next_ea = _insn.ea;
|
|
while ( (!has_xref(get_flags(next_ea)) || get_first_cref_to(next_ea) == BADADDR)
|
|
&& decode_prev_insn(&insn, next_ea) != BADADDR )
|
|
{
|
|
if ( insn.itype == H8_mov
|
|
&& insn.Op1.type == o_imm
|
|
&& insn.Op2.type == o_reg
|
|
&& insn.Op2.reg == reg )
|
|
{
|
|
*value = insn.Op1.value;
|
|
ok = true;
|
|
break;
|
|
}
|
|
|
|
if ( spoils(insn, reg) )
|
|
break;
|
|
|
|
next_ea = insn.ea;
|
|
}
|
|
|
|
if ( ok )
|
|
{
|
|
if ( x.type == o_phrase )
|
|
{
|
|
if ( x.phtype == ph_pre_inc )
|
|
*value += 1;
|
|
else if ( x.phtype == ph_pre_dec )
|
|
*value -= 1;
|
|
}
|
|
else if ( x.type == o_displ )
|
|
{
|
|
if ( x.displtype == dt_regidx )
|
|
{
|
|
if ( (_insn.auxpref == aux_long) != 0 )
|
|
*value <<= 2;
|
|
else if ( (_insn.auxpref == aux_word) != 0 )
|
|
*value <<= 1;
|
|
}
|
|
*value += x.addr;
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void h8_t::trace_sp(const insn_t &insn) const
|
|
{
|
|
// @sp++
|
|
if ( insn.Op1.type == o_phrase
|
|
&& issp(insn.Op1.reg)
|
|
&& insn.Op1.phtype == ph_post_inc )
|
|
{
|
|
ssize_t size = get_dtype_size(insn.Op2.dtype);
|
|
if ( insn.Op2.type == o_reglist )
|
|
size *= insn.Op2.nregs;
|
|
add_stkpnt(insn, size);
|
|
return;
|
|
}
|
|
|
|
// @--sp
|
|
if ( insn.Op2.type == o_phrase
|
|
&& issp(insn.Op2.reg)
|
|
&& insn.Op2.phtype == ph_pre_dec )
|
|
{
|
|
ssize_t size = get_dtype_size(insn.Op1.dtype);
|
|
if ( insn.Op1.type == o_reglist )
|
|
size *= insn.Op1.nregs;
|
|
add_stkpnt(insn, -size);
|
|
return;
|
|
}
|
|
|
|
uval_t v;
|
|
switch ( insn.itype )
|
|
{
|
|
case H8_add:
|
|
case H8_adds:
|
|
if ( !issp(insn.Op2.reg) )
|
|
break;
|
|
if ( get_op_value(&v, insn, insn.Op1) )
|
|
add_stkpnt(insn, v);
|
|
break;
|
|
case H8_sub:
|
|
case H8_subs:
|
|
if ( !issp(insn.Op2.reg) )
|
|
break;
|
|
if ( get_op_value(&v, insn, insn.Op1) )
|
|
add_stkpnt(insn, 0-v);
|
|
break;
|
|
case H8_push:
|
|
add_stkpnt(insn, 0-get_dtype_size(insn.Op1.dtype));
|
|
break;
|
|
case H8_pop:
|
|
add_stkpnt(insn, get_dtype_size(insn.Op1.dtype));
|
|
break;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void h8_t::add_code_xref(const insn_t &insn, const op_t &x, ea_t ea)
|
|
{
|
|
cref_t ftype = fl_JN;
|
|
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);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void h8_t::handle_operand(const insn_t &insn, const op_t &x, bool is_forced, bool isload)
|
|
{
|
|
uval_t op_value;
|
|
flags_t F = get_flags(insn.ea);
|
|
switch ( x.type )
|
|
{
|
|
case o_reg:
|
|
case o_reglist:
|
|
return;
|
|
|
|
case o_imm:
|
|
QASSERT(10094, isload);
|
|
process_immediate_number(insn, x.n, F);
|
|
if ( op_adds_xrefs(F, x.n) )
|
|
insn.add_off_drefs(x, dr_O, OOFS_IFSIGN|OOFW_IMM);
|
|
break;
|
|
|
|
case o_phrase:
|
|
if ( is_forced )
|
|
break;
|
|
if ( !is_defarg(F, x.n) && get_op_value(&op_value, insn, x) )
|
|
{
|
|
op_offset(insn.ea, x.n, REF_OFF32 | REFINFO_NOBASE, BADADDR, op_value);
|
|
}
|
|
if ( op_adds_xrefs(F, x.n) )
|
|
{
|
|
ea_t ea = insn.add_off_drefs(x, isload ? dr_R : dr_W, get_displ_outf(x, F));
|
|
if ( ea != BADADDR )
|
|
{
|
|
insn.create_op_data(ea, x);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case o_displ:
|
|
if ( is_forced )
|
|
break;
|
|
if ( op_adds_xrefs(F, x.n) )
|
|
{
|
|
ea_t ea = insn.add_off_drefs(x, isload ? dr_R : dr_W, get_displ_outf(x, F));
|
|
if ( ea != BADADDR )
|
|
insn.create_op_data(ea, x);
|
|
if ( (x.flags & OF_OUTER_DISP) != 0 )
|
|
{
|
|
ea = insn.add_off_drefs(x, isload ? dr_R : dr_W, OOF_OUTER | OOF_SIGNED | OOFW_32);
|
|
if ( ea != BADADDR )
|
|
insn.create_op_data(ea, x.offo, x.szfl & idx_byte ? dt_byte : dt_word);
|
|
}
|
|
}
|
|
// create stack variables if required
|
|
if ( 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:
|
|
add_code_xref(insn, x, calc_mem(insn, x.addr));
|
|
break;
|
|
case o_mem:
|
|
{
|
|
ea_t ea = x.memtype == mem_sbr
|
|
? calc_mem_sbr_based(insn, x.addr)
|
|
: calc_mem(insn, x.addr);
|
|
if ( !is_mapped(ea) )
|
|
{
|
|
const char *name = find_sym(ea);
|
|
if ( name != NULL && name[0] != '\0' )
|
|
break; // address not here
|
|
}
|
|
insn.add_dref(ea, x.offb, isload ? dr_R : dr_W);
|
|
insn.create_op_data(ea, x);
|
|
if ( x.memtype == mem_ind || x.memtype == mem_vec7 )
|
|
{
|
|
ssize_t size = get_dtype_size(x.dtype);
|
|
flags_t eaF = get_flags(ea);
|
|
if ( (is_word(eaF) || is_dword(eaF))
|
|
&& (!is_defarg0(eaF) || is_off0(eaF)) )
|
|
{
|
|
ea_t target = calc_mem(
|
|
insn,
|
|
size == 2 ? get_word(ea) : trim_ea_branch(get_dword(ea)));
|
|
if ( is_mapped(target) )
|
|
add_code_xref(insn, x, target);
|
|
if ( !is_off0(eaF) )
|
|
op_plain_offset(ea, 0, calc_mem(insn, 0));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case o_pcidx:
|
|
{
|
|
uval_t value;
|
|
bool ok = get_op_value(&value, insn, x);
|
|
if ( ok )
|
|
{
|
|
ea_t ea = insn.ea + insn.size + (value << 1);
|
|
add_code_xref(insn, x, ea);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
INTERR(10095);
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
void h8_t::check_base_reg_change_value(const insn_t &insn) const
|
|
{
|
|
if ( insn.itype == H8_ldc
|
|
&& insn.Op2.type == o_reg
|
|
&& (insn.Op2.reg == SBR || insn.Op2.reg == VBR) )
|
|
{
|
|
sel_t value = BADSEL;
|
|
bool ok = get_op_value(&value, insn, insn.Op1);
|
|
split_sreg_range(insn.ea + insn.size, insn.Op2.reg, value, ok ? SR_autostart : SR_user);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int h8_t::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);
|
|
|
|
//
|
|
// Check for SBR, VBR change value
|
|
//
|
|
if ( is_h8sx() )
|
|
check_base_reg_change_value(insn);
|
|
|
|
//
|
|
// 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);
|
|
|
|
|
|
//
|
|
// Handle SP modifications
|
|
//
|
|
if ( may_trace_sp() )
|
|
{
|
|
if ( !flow )
|
|
recalc_spd(insn.ea); // recalculate SP register for the next insn
|
|
else
|
|
trace_sp(insn);
|
|
}
|
|
|
|
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 &insn) // can a function start here?
|
|
// returns: probability 0..100
|
|
// 'insn' structure is filled upon the entrace
|
|
// the idp module is allowed to modify 'insn'
|
|
{
|
|
if ( insn.itype == H8_push && isbp(insn.Op1.reg) )
|
|
return 100; // push.l er6
|
|
if ( insn.itype == H8_push && insn.Op1.reg == ER3 )
|
|
return 100; // push.l er3
|
|
if ( insn.itype == H8_push && insn.Op1.reg == R3 )
|
|
return 100; // push.w r3
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int is_sane_insn(const insn_t &insn, int /*nocrefs*/)
|
|
{
|
|
if ( insn.itype == H8_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 h8_is_align_insn(ea_t ea)
|
|
{
|
|
insn_t insn;
|
|
if ( decode_insn(&insn, ea) < 1 )
|
|
return 0;
|
|
switch ( insn.itype )
|
|
{
|
|
case H8_nop:
|
|
break;
|
|
case H8_mov:
|
|
case H8_or:
|
|
if ( insn.Op1.type == insn.Op2.type && insn.Op1.reg == insn.Op2.reg )
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
return insn.size;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool idaapi is_return_insn(const insn_t &insn)
|
|
{
|
|
return insn.itype == H8_rte
|
|
|| insn.itype == H8_rts
|
|
|| insn.itype == H8_rtel
|
|
|| insn.itype == H8_rtsl;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool idaapi create_func_frame(func_t *pfn)
|
|
{
|
|
if ( pfn->frame == BADNODE )
|
|
{
|
|
size_t regs = 0;
|
|
ea_t ea = pfn->start_ea;
|
|
bool bpused = false;
|
|
insn_t insn;
|
|
while ( ea < pfn->end_ea ) // skip all pushregs
|
|
{ // (must test that ea is lower
|
|
// than pfn->end_ea)
|
|
decode_insn(&insn, ea);
|
|
ea += insn.size;
|
|
switch ( insn.itype )
|
|
{
|
|
case H8_nop:
|
|
continue;
|
|
case H8_push:
|
|
regs += get_dtype_size(insn.Op1.dtype);
|
|
continue;
|
|
case H8_stm:
|
|
if ( !issp(insn.Op2.reg) )
|
|
break;
|
|
regs += insn.Op1.nregs * get_dtype_size(insn.Op1.dtype);
|
|
continue;
|
|
case H8_mov: // mov.l er6, sp
|
|
if ( insn.Op1.type == o_reg && issp(insn.Op1.reg)
|
|
&& insn.Op2.type == o_reg && isbp(insn.Op2.reg) )
|
|
{
|
|
bpused = true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if ( regs != 0 || bpused )
|
|
{
|
|
setflag((uint32 &)pfn->flags, FUNC_FRAME, bpused);
|
|
return add_frame(pfn, 0, (ushort)regs, 0);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int h8_t::h8_get_frame_retsize(const func_t *)
|
|
{
|
|
return advanced() ? 4 : 2;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
inline bool is_clear_reg_insn(const insn_t &insn)
|
|
{
|
|
return (insn.itype == H8_sub || insn.itype == H8_xor)
|
|
&& insn.Op2.type == o_reg
|
|
&& insn.Op1.is_reg(insn.Op2.reg)
|
|
|| insn.itype == H8_mov
|
|
&& insn.Op2.type == o_reg
|
|
&& insn.Op1.is_imm(0);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
struct h8_jump_pattern_t : public jump_pattern_t
|
|
{
|
|
protected:
|
|
enum { rA, rC };
|
|
|
|
h8_t ±
|
|
op_dtype_t jump_displ_size;
|
|
int shift; //lint !e958 padding is required
|
|
int sub_lowcase2_njpi; // njpi of tied 'sub.b #low minv, rA'
|
|
op_dtype_t sub_lowcase_size; // too pass to jpi_sub_lowcase_tied()
|
|
|
|
h8_jump_pattern_t(
|
|
procmod_t *_pm,
|
|
switch_info_t *_si,
|
|
const char (*_depends)[4],
|
|
int sub_lowcase2_njpi_)
|
|
: jump_pattern_t(_si, _depends, rC),
|
|
pm(*(h8_t*)_pm),
|
|
jump_displ_size(dt_void),
|
|
shift(-1),
|
|
sub_lowcase2_njpi(sub_lowcase2_njpi_),
|
|
sub_lowcase_size(dt_void)
|
|
{
|
|
modifying_r32_spoils_r64 = false;
|
|
non_spoiled_reg = rA;
|
|
}
|
|
|
|
public:
|
|
virtual bool equal_ops(const op_t &x, const op_t &y) const override;
|
|
virtual bool handle_mov(tracked_regs_t &_regs) override;
|
|
virtual void check_spoiled(tracked_regs_t *_regs) const override;
|
|
virtual op_dtype_t extend_dtype(const op_t &op) const override;
|
|
|
|
bool finish(int ld_njpi);
|
|
|
|
protected:
|
|
static inline bool optype_supported(const op_t &x);
|
|
void h8_track(int reg, int r_i) { track(reg, r_i, get_regsize(reg)); }
|
|
|
|
bool jpi_ld(int shl_njpi);
|
|
bool jpi_shl();
|
|
bool jpi_cmp_ncases_condjump(int body_njpi);
|
|
bool jpi_sub_lowcase();
|
|
bool jpi_sub_lowcase_tied() const;
|
|
|
|
// helpers
|
|
bool jpi_add_sub(
|
|
uval_t *value,
|
|
op_dtype_t *add_size,
|
|
int tied_jpi);
|
|
bool jpi_add_sub_tied(uval_t *value, op_dtype_t add_size) const;
|
|
bool jpi_cmp_ncases();
|
|
bool jpi_condjump(int body_njpi);
|
|
|
|
static inline reftype_t get_off_reftype(op_dtype_t dtype);
|
|
static inline reftype_t get_lo_reftype(op_dtype_t dtype);
|
|
static inline reftype_t get_hi_reftype(op_dtype_t dtype);
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool h8_jump_pattern_t::equal_ops(const op_t &x, const op_t &y) const
|
|
{
|
|
if ( x.type != y.type )
|
|
return false;
|
|
// ignore difference in the data size of registers
|
|
switch ( x.type )
|
|
{
|
|
case o_void:
|
|
// consider spoiled values as not equal
|
|
return false;
|
|
case o_reg:
|
|
// the data size of registers will be taken into account as the
|
|
// operand size
|
|
return is_same_reg(x.reg, y.reg);
|
|
case o_displ:
|
|
return x.phrase == y.phrase
|
|
&& x.addr == y.addr
|
|
&& x.displtype == y.displtype;
|
|
case o_phrase:
|
|
return x.phrase == y.phrase && x.phtype == y.phtype;
|
|
case o_mem:
|
|
return x.addr == y.addr && x.memtype == y.memtype;
|
|
case o_condjump:
|
|
// we do not track the condition flags
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
inline bool h8_jump_pattern_t::optype_supported(const op_t &x)
|
|
{
|
|
// we can work with the following types only
|
|
return x.type == o_reg && x.reg < MACL
|
|
|| x.type == o_displ
|
|
&& (x.displtype == dt_normal || x.displtype == dt_regidx)
|
|
|| x.type == o_phrase && x.phtype == ph_normal
|
|
|| x.type == o_mem
|
|
&& (x.memtype == mem_sbr || x.memtype == mem_direct);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool h8_jump_pattern_t::handle_mov(tracked_regs_t &_regs)
|
|
{
|
|
// some binaries use 'sub.b r0h, r0h' instead of 'extu.w r0'
|
|
if ( is_clear_reg_insn(insn) && is_high_bits_reg(insn.Op2.reg) )
|
|
{
|
|
const op_t &x = insn.Op2;
|
|
op_t src;
|
|
src.type = o_reg;
|
|
src.dtype = x.dtype;
|
|
src.reg = cvt_to_wholereg(x.reg, src.dtype);
|
|
// assert: src.reg != x.reg because !is_high_bits_reg(src.reg)
|
|
op_t dst;
|
|
dst.type = o_reg;
|
|
dst.dtype = op_dtype_t(x.dtype + 1);
|
|
dst.reg = cvt_to_wholereg(x.reg, dst.dtype);
|
|
return set_moved(dst, src, _regs);
|
|
}
|
|
|
|
op_dtype_t dst_dtype = dt_void;
|
|
op_dtype_t src_dtype = dt_void;
|
|
switch ( insn.itype )
|
|
{
|
|
case H8_mov:
|
|
// some binaries use the following pattern
|
|
// sub.b r0h, r0h
|
|
// mov.b @(jpt_XXX:16,r0), r0l
|
|
// instead of
|
|
// mov.b @(jpt_XXX:16,r0), r0l
|
|
// sub.b r0h, r0h
|
|
// or
|
|
// mov.b @(jpt_XXX:16,r0), r0l
|
|
// extu.w r0
|
|
dst_dtype = extend_dtype(insn.Op2);
|
|
break;
|
|
case H8_extu:
|
|
// assert: insn.Op1.type == o_imm
|
|
if ( insn.Op2.dtype == dt_word )
|
|
src_dtype = dt_byte; // extu.w r0
|
|
else if ( insn.Op1.value == 1 )
|
|
src_dtype = dt_word; // extu.l er0
|
|
else if ( insn.Op1.value == 2 )
|
|
src_dtype = dt_byte; // extu.l #2, er0
|
|
else
|
|
return false;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
bool is_mov_insn = src_dtype == dt_void;
|
|
if ( !optype_supported(insn.Op2)
|
|
|| is_mov_insn && !optype_supported(insn.Op1) )
|
|
{
|
|
return false;
|
|
}
|
|
return set_moved(insn.Op2,
|
|
is_mov_insn ? insn.Op1 : insn.Op2,
|
|
_regs,
|
|
dst_dtype,
|
|
src_dtype);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void h8_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 && pm.spoils(insn, x.reg)
|
|
|| x.type == o_condjump && spoils_flags(insn) )
|
|
{
|
|
set_spoiled(&_regs, x);
|
|
}
|
|
}
|
|
check_spoiled_not_reg(&_regs, PROC_MAXCHGOP);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
op_dtype_t h8_jump_pattern_t::extend_dtype(const op_t &op) const
|
|
{
|
|
if ( op.type == o_reg && (op.dtype == dt_byte || op.dtype == dt_word) )
|
|
{
|
|
insn_t prev;
|
|
if ( decode_prev_insn(&prev, insn.ea) != BADADDR
|
|
&& is_clear_reg_insn(prev) )
|
|
{
|
|
// sub.w r0, r0 | mov.b ..., r0l
|
|
if ( prev.Op2.dtype > op.dtype
|
|
&& cvt_to_wholereg(prev.Op2.reg, op.dtype) == op.reg )
|
|
{
|
|
return prev.Op1.dtype;
|
|
}
|
|
// sub.b r0h, r0h | mov.b ..., r0l
|
|
if ( prev.Op2.dtype == op.dtype
|
|
&& is_high_bits_reg(prev.Op2.reg)
|
|
&& cvt_to_wholereg(prev.Op2.reg) == cvt_to_wholereg(op.reg) )
|
|
{
|
|
return op_dtype_t(prev.Op1.dtype + 1);
|
|
}
|
|
}
|
|
}
|
|
return op.dtype;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool h8_jump_pattern_t::finish(int ld_njpi)
|
|
{
|
|
if ( eas[ld_njpi] != BADADDR && jump_displ_size != dt_void )
|
|
{
|
|
reftype_t rtype = get_off_reftype(jump_displ_size);
|
|
op_offset(eas[ld_njpi], 0, rtype);
|
|
}
|
|
if ( eas[sub_lowcase2_njpi - 1] != BADADDR )
|
|
si->lowcase = -si->lowcase;
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// mov.X @(jumps:size,rA'), rA
|
|
bool h8_jump_pattern_t::jpi_ld(int shl_njpi)
|
|
{
|
|
if ( insn.itype != H8_mov
|
|
|| insn.Op1.type != o_displ
|
|
|| insn.Op1.displtype != dt_normal && insn.Op1.displtype != dt_regidx
|
|
|| !is_equal(insn.Op2, rA) )
|
|
{
|
|
return false;
|
|
}
|
|
shift = insn.Op2.dtype; // shift == log2size(insn.Op2)
|
|
si->set_jtable_element_size(1 << shift);
|
|
if ( insn.Op1.displtype == dt_regidx )
|
|
shift = 0; // register is shifted by the address mode
|
|
if ( shift == 0 )
|
|
skip[shl_njpi] = true; // no need for an additional shift
|
|
h8_track(insn.Op1.phrase, rA);
|
|
si->jumps = insn.Op1.addr;
|
|
if ( (insn.Op1.szfl & disp_16) != 0 )
|
|
jump_displ_size = dt_word;
|
|
else if ( (insn.Op1.szfl & disp_32) != 0 )
|
|
jump_displ_size = dt_dword;
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// add.X rA, rA | shll.X #shift, rA
|
|
bool h8_jump_pattern_t::jpi_shl()
|
|
{
|
|
if ( shift <= 0 || !is_equal(insn.Op2, rA) )
|
|
return false;
|
|
if ( insn.itype == H8_shll )
|
|
{
|
|
if ( !insn.Op1.is_imm(shift) )
|
|
return false;
|
|
// continue to track rA
|
|
}
|
|
else if ( insn.itype == H8_add )
|
|
{
|
|
if ( shift != 1
|
|
|| insn.Op1.type != o_reg
|
|
|| !insn.Op2.is_reg(insn.Op1.reg) )
|
|
{
|
|
return false;
|
|
}
|
|
// continue to track rA
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool h8_jump_pattern_t::jpi_cmp_ncases_condjump(int body_njpi)
|
|
{
|
|
// assert: !is_spoiled(rA) because rA is non spoiled register
|
|
|
|
// look for the conditional jump
|
|
if ( jpi_condjump(body_njpi) )
|
|
return false;
|
|
|
|
// the condition between 'cmp' and the conditional jump should not be
|
|
// spoiled
|
|
if ( is_spoiled(rC) )
|
|
return false;
|
|
|
|
// look for the 'cmp' insn
|
|
if ( jpi_cmp_ncases() )
|
|
{
|
|
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.Op2.reg, insn.Op2.dtype);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool h8_jump_pattern_t::jpi_sub_lowcase()
|
|
{
|
|
// continue to track rA
|
|
return jpi_add_sub(&si->lowcase, &sub_lowcase_size, sub_lowcase2_njpi);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool h8_jump_pattern_t::jpi_sub_lowcase_tied() const
|
|
{
|
|
// continue to track rA
|
|
return jpi_add_sub_tied(&si->lowcase, sub_lowcase_size);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// add.b #value, rAl/h | sub.b #value, rAl/h
|
|
// add.w #value, rA/eA | sub.w #value, rA/eA
|
|
// add.l #value, erA | sub.l #value, erA
|
|
// inc.b rAl/h | dec.b rAl/h
|
|
// inc.w #value, rA/eA | dec.w #value, rA/eA
|
|
// inc.l #value, erA | dec.l #value, erA
|
|
// addx.b #high value, rAh
|
|
// addx.w #hword value, eA
|
|
bool h8_jump_pattern_t::jpi_add_sub(
|
|
uval_t *value,
|
|
op_dtype_t *add_size,
|
|
int tied_jpi)
|
|
{
|
|
if ( insn.itype != H8_add
|
|
&& insn.itype != H8_addx
|
|
&& insn.itype != H8_sub
|
|
&& insn.itype != H8_inc
|
|
&& insn.itype != H8_dec
|
|
|| insn.Op1.type != o_imm )
|
|
{
|
|
return false;
|
|
}
|
|
const op_t &op = insn.Op2;
|
|
if ( insn.itype != H8_addx )
|
|
{
|
|
if ( !is_equal(op, rA) )
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
const op_t &dst = regs[rA];
|
|
// assert: !is_spoiled(rA) because rA is non spoiled register
|
|
// example:
|
|
// addx #high value, r0h <-- op
|
|
// jmp @r0 <-- dst
|
|
if ( op.dtype + 1 != dst.dtype
|
|
|| op.type != o_reg
|
|
|| dst.type != o_reg
|
|
|| !is_high_bits_reg(op.reg)
|
|
|| cvt_to_wholereg(op.reg, dst.dtype) != dst.reg )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
*value = insn.Op1.value;
|
|
*add_size = op.dtype;
|
|
if ( insn.itype == H8_addx )
|
|
{
|
|
*value <<= (8 * get_dtype_size(op.dtype));
|
|
skip[tied_jpi] = false;
|
|
}
|
|
else if ( insn.itype == H8_sub || insn.itype == H8_dec )
|
|
{
|
|
*value = -*value;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// add.b #low value, rAl
|
|
// add.w #lword value, rA
|
|
bool h8_jump_pattern_t::jpi_add_sub_tied(
|
|
uval_t *value,
|
|
op_dtype_t add_size) const
|
|
{
|
|
if ( insn.itype != H8_add || insn.Op1.type != o_imm )
|
|
return false;
|
|
// assert: !is_spoiled(rA) because rA is non spoiled register
|
|
const op_t &op = insn.Op2;
|
|
const op_t ® = regs[rA];
|
|
if ( op.dtype != add_size
|
|
|| op.dtype + 1 != reg.dtype
|
|
|| op.type != o_reg
|
|
|| reg.type != o_reg
|
|
|| is_high_bits_reg(op.reg)
|
|
|| cvt_to_wholereg(op.reg, reg.dtype) != reg.reg )
|
|
{
|
|
return false;
|
|
}
|
|
*value += insn.Op1.value;
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// cmp.X rSize, rA
|
|
// cmp.X #size, rA
|
|
bool h8_jump_pattern_t::jpi_cmp_ncases()
|
|
{
|
|
// assert: !is_spoiled(rA) because rA is non spoiled register
|
|
if ( insn.itype != H8_cmp
|
|
|| insn.Op1.type != o_imm && insn.Op1.type != o_reg
|
|
|| !same_value(insn.Op2, rA) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uval_t value;
|
|
if ( insn.Op1.type == o_imm )
|
|
value = insn.Op1.value;
|
|
// assert: insn.Op1.type == o_reg
|
|
else if ( !pm.get_op_value(&value, insn, insn.Op1) )
|
|
return false;
|
|
si->ncases = ushort(value);
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// bhi default
|
|
// bls switch_body
|
|
bool h8_jump_pattern_t::jpi_condjump(int body_njpi)
|
|
{
|
|
op_t op;
|
|
op.type = o_condjump;
|
|
op.value = 0;
|
|
switch ( insn.itype )
|
|
{
|
|
case H8_bhi: // higher
|
|
case H8_bls: // lower or same
|
|
op.value |= cc_inc_ncases;
|
|
break;
|
|
case H8_bcs: // lower
|
|
case H8_bcc: // higher or same
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
if ( insn.Op1.type != o_near )
|
|
return false;
|
|
ea_t jump = to_ea(insn.cs, insn.Op1.addr);
|
|
switch ( insn.itype )
|
|
{
|
|
case H8_bhi: // higher
|
|
case H8_bcc: // higher or same
|
|
op.specval = jump;
|
|
break;
|
|
case H8_bcs: // lower
|
|
case H8_bls: // lower or same
|
|
// we have conditional jump to the switch body
|
|
{
|
|
int njpi;
|
|
for ( njpi = body_njpi; njpi > 0 && eas[njpi] == BADADDR; --njpi )
|
|
;
|
|
// assert: eas[njpi] != BADADDR because eas[0] != BADADDR
|
|
if ( jump > eas[njpi] )
|
|
return false;
|
|
op.specval = insn.ea + insn.size;
|
|
|
|
// possibly followed by 'B default'
|
|
insn_t dflt;
|
|
if ( decode_insn(&dflt, op.specval) > 0
|
|
&& (dflt.itype == H8_bra || dflt.itype == H8_jmp)
|
|
&& dflt.Op1.type == o_near )
|
|
{
|
|
op.specval = to_ea(dflt.cs, dflt.Op1.addr);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
op.addr = insn.ea;
|
|
trackop(op, rC);
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static const reftype_t off_reftype[3] = { REF_OFF8, REF_OFF16, REF_OFF32 };
|
|
static const reftype_t lo_reftype[2] = { REF_LOW8, REF_LOW16 };
|
|
static const reftype_t hi_reftype[2] = { REF_HIGH8, REF_HIGH16 };
|
|
inline reftype_t h8_jump_pattern_t::get_off_reftype(op_dtype_t dtype)
|
|
{
|
|
// assert: dtype <= op_dword
|
|
return off_reftype[size_t(dtype)]; //lint !e571 Suspicious cast
|
|
}
|
|
inline reftype_t h8_jump_pattern_t::get_lo_reftype(op_dtype_t dtype)
|
|
{
|
|
// assert: dtype <= op_word
|
|
return lo_reftype[size_t(dtype)]; //lint !e571 Suspicious cast
|
|
}
|
|
inline reftype_t h8_jump_pattern_t::get_hi_reftype(op_dtype_t dtype)
|
|
{
|
|
// assert: dtype <= op_word
|
|
return hi_reftype[size_t(dtype)]; //lint !e571 Suspicious cast
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// jump pattern #1
|
|
// 7 add.b #low minv, rAl | add.X/sub.X #minv, rA (.X: .b/.w/.l)
|
|
// 6 addx #high minv, rAh
|
|
// 5 cmp.w rSize, rA | cmp.X #size, rA
|
|
// bhi default | bls switch_body (nearest to "cmp")
|
|
// 4 add.X rA, rA (if 3 is .w)
|
|
// 3 mov.b/w @(jumps,rA'), rA
|
|
// 2 add.b #low elbase, rAl | add.X #elbase, rA
|
|
// 1 addx #high elbase, rAh
|
|
// 0 jmp @rA
|
|
|
|
static const char depends1[][4] =
|
|
{
|
|
{ 1 | JPT_OPT }, // 0
|
|
{ 2 }, // 1 optional
|
|
{ 3 }, // 2 tied to 1
|
|
{ 4 }, // 3
|
|
{ 5 }, // 4 tied to 3
|
|
{ 6 | JPT_OPT }, // 5
|
|
{ 7 }, // 6 optional
|
|
{ 0 }, // 7 tied to 6
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
class h8_jump_pattern1_t : public h8_jump_pattern_t
|
|
{
|
|
protected:
|
|
enum
|
|
{
|
|
JPI_SUB_LOWCASE2 = 7,
|
|
JPI_SHIFT = 4,
|
|
JPI_LD = 3,
|
|
JPI_ADD_ELBASE2 = 2,
|
|
JPI_ADD_ELBASE = 1,
|
|
};
|
|
op_dtype_t add_elbase_size;
|
|
|
|
public:
|
|
h8_jump_pattern1_t(procmod_t *_pm, switch_info_t *_si)
|
|
: h8_jump_pattern_t(_pm, _si, depends1, JPI_SUB_LOWCASE2),
|
|
add_elbase_size(dt_void) {}
|
|
|
|
virtual bool jpi7(void) override { return jpi_sub_lowcase_tied(); }
|
|
virtual bool jpi6(void) override { return jpi_sub_lowcase(); }
|
|
virtual bool jpi5(void) override { return jpi_cmp_ncases_condjump(JPI_SHIFT); }
|
|
virtual bool jpi4(void) override { return jpi_shl(); }
|
|
virtual bool jpi3(void) override { return jpi_ld(JPI_SHIFT); }
|
|
virtual bool jpi2(void) override; // (tied to 1)
|
|
virtual bool jpi1(void) override; // add #elbase, rA
|
|
virtual bool jpi0(void) override; // jmp @rA
|
|
|
|
bool finish(); //lint !e1511 Member hides non-virtual member
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// jmp @rA
|
|
bool h8_jump_pattern1_t::jpi0()
|
|
{
|
|
if ( insn.itype != H8_jmp
|
|
|| insn.Op1.type != o_phrase
|
|
|| insn.Op1.phtype != ph_normal )
|
|
{
|
|
return false;
|
|
}
|
|
h8_track(insn.Op1.phrase, rA);
|
|
skip[JPI_SUB_LOWCASE2] = true;
|
|
skip[JPI_ADD_ELBASE2] = true;
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// add.X #elbase, rA
|
|
// addx.b #high elbase, rAh
|
|
// addx.w #hword elbase, rA
|
|
bool h8_jump_pattern1_t::jpi1()
|
|
{
|
|
// continue to track rA
|
|
return jpi_add_sub(&si->elbase, &add_elbase_size, JPI_ADD_ELBASE2);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// add.b #low elbase, rAh
|
|
// add.w #lword elbase, rA
|
|
bool h8_jump_pattern1_t::jpi2()
|
|
{
|
|
// continue to track rA
|
|
return jpi_add_sub_tied(&si->elbase, add_elbase_size);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool h8_jump_pattern1_t::finish()
|
|
{
|
|
if ( eas[JPI_ADD_ELBASE] != BADADDR )
|
|
{
|
|
reftype_t rtype = get_off_reftype(add_elbase_size);
|
|
if ( eas[JPI_ADD_ELBASE2] == BADADDR )
|
|
{
|
|
op_offset(eas[JPI_ADD_ELBASE], 0, rtype);
|
|
}
|
|
else
|
|
{
|
|
// low part
|
|
rtype = get_lo_reftype(add_elbase_size);
|
|
op_offset(eas[JPI_ADD_ELBASE2], 0, rtype, si->elbase);
|
|
// high part
|
|
rtype = get_hi_reftype(add_elbase_size);
|
|
op_offset(eas[JPI_ADD_ELBASE], 0, rtype, si->elbase);
|
|
}
|
|
si->flags |= SWI_ELBASE;
|
|
}
|
|
return h8_jump_pattern_t::finish(JPI_LD);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static int is_jump_pattern1(switch_info_t *si, const insn_t &insn, procmod_t *pm)
|
|
{
|
|
h8_jump_pattern1_t jp(pm, si);
|
|
if ( !jp.match(insn) || !jp.finish() )
|
|
return JT_NONE;
|
|
return JT_SWITCH;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// jump pattern #2
|
|
// 6 add.b #low minv, rAl
|
|
// 5 addx #high minv, rAh
|
|
// 4 cmp.X #size, rA
|
|
// bhi default (nearest to "cmp")
|
|
// 3 add.X rA, rA (if needed)
|
|
// 2 mov.b/w @(jumps,rA'), rA
|
|
// 1 shlr.X rA (optional)
|
|
// 0 bra rA
|
|
|
|
static const char depends2[][4] =
|
|
{
|
|
{ 1 | JPT_OPT }, // 0
|
|
{ 2 }, // 1 optional
|
|
{ 3 }, // 2
|
|
{ 4 }, // 3 tied to 2
|
|
{ 5 | JPT_OPT }, // 4
|
|
{ 6 }, // 5 optional
|
|
{ 0 }, // 6 tied to 5
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
class h8_jump_pattern2_t : public h8_jump_pattern_t
|
|
{
|
|
protected:
|
|
enum
|
|
{
|
|
JPI_SUB_LOWCASE2 = 6,
|
|
JPI_SHIFT = 3,
|
|
JPI_LD = 2,
|
|
JPI_EL_SHIFT = 1,
|
|
};
|
|
|
|
public:
|
|
h8_jump_pattern2_t(procmod_t *_pm, switch_info_t *_si)
|
|
: h8_jump_pattern_t(_pm, _si, depends2, JPI_SUB_LOWCASE2) {}
|
|
|
|
virtual bool jpi6(void) override { return jpi_sub_lowcase_tied(); }
|
|
virtual bool jpi5(void) override { return jpi_sub_lowcase(); }
|
|
virtual bool jpi4(void) override { return jpi_cmp_ncases_condjump(JPI_SHIFT); }
|
|
virtual bool jpi3(void) override { return jpi_shl(); }
|
|
virtual bool jpi2(void) override { return jpi_ld(JPI_SHIFT); }
|
|
virtual bool jpi1(void) override; // shlr.X rA
|
|
virtual bool jpi0(void) override; // bra rA
|
|
|
|
bool finish(); //lint !e1511 Member hides non-virtual member
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// bra rA
|
|
bool h8_jump_pattern2_t::jpi0()
|
|
{
|
|
if ( insn.itype != H8_bra || insn.Op1.type != o_pcidx )
|
|
return false;
|
|
|
|
h8_track(insn.Op1.reg, rA);
|
|
skip[JPI_SUB_LOWCASE2] = true;
|
|
si->set_elbase(insn.ea + insn.size);
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// shlr.X rA
|
|
bool h8_jump_pattern2_t::jpi1()
|
|
{
|
|
if ( insn.itype != H8_shlr
|
|
|| !insn.Op1.is_imm(1)
|
|
|| !is_equal(insn.Op2, rA) )
|
|
{
|
|
return false;
|
|
}
|
|
// continue to track rA
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool h8_jump_pattern2_t::finish()
|
|
{
|
|
if ( eas[JPI_EL_SHIFT] == BADADDR )
|
|
si->set_shift(1); // register is shifted left by the 'bra' insn
|
|
return h8_jump_pattern_t::finish(JPI_LD);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static int is_jump_pattern2(switch_info_t *si, const insn_t &insn, procmod_t *pm)
|
|
{
|
|
h8_jump_pattern2_t jp(pm, si);
|
|
if ( !jp.match(insn) || !jp.finish() )
|
|
return JT_NONE;
|
|
return JT_SWITCH;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool idaapi h8_is_switch(switch_info_t *si, const insn_t &insn)
|
|
{
|
|
if ( insn.itype != H8_jmp && insn.itype != H8_bra )
|
|
return false;
|
|
|
|
static is_pattern_t *const patterns[] =
|
|
{
|
|
is_jump_pattern1,
|
|
is_jump_pattern2,
|
|
};
|
|
return check_for_table_jump(si, insn, patterns, qnumber(patterns));
|
|
}
|
|
|