394 lines
9.6 KiB
C++
394 lines
9.6 KiB
C++
|
|
#include "m32r.hpp"
|
|
|
|
// handle immediate values
|
|
static void handle_imm(const insn_t &insn)
|
|
{
|
|
set_immd(insn.ea);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// handle the custom switch format
|
|
// ....
|
|
// bl.s next || nop <- insn_ea
|
|
// next:
|
|
// add lr, R0
|
|
// jmp lr
|
|
// si.jumps:
|
|
// bra.s case0 || nop
|
|
// bra.l case1
|
|
// ...
|
|
int m32r_create_switch_xrefs(ea_t insn_ea, const switch_info_t &si)
|
|
{
|
|
if ( (si.flags & SWI_CUSTOM) != 0 )
|
|
{
|
|
insn_t insn;
|
|
decode_insn(&insn, insn_ea);
|
|
ea_t ea = si.jumps;
|
|
for ( int i = 0; i < si.ncases; i++, ea += insn.size )
|
|
{
|
|
add_cref(insn_ea, ea, fl_JN);
|
|
decode_insn(&insn, ea);
|
|
if ( insn.Op1.type == o_near )
|
|
{
|
|
ea_t target = to_ea(insn.cs, insn.Op1.addr);
|
|
// xrefs are from "bl" -> branch target.
|
|
add_cref(insn_ea, target, fl_JN);
|
|
}
|
|
}
|
|
}
|
|
return 1; // ok
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int m32r_calc_switch_cases(casevec_t *casevec, eavec_t *targets, ea_t insn_ea, const switch_info_t &si)
|
|
{
|
|
if ( (si.flags & SWI_CUSTOM) == 0 )
|
|
return 0;
|
|
|
|
insn_t insn;
|
|
decode_insn(&insn, insn_ea);
|
|
|
|
ea_t ea = si.jumps;
|
|
svalvec_t vals;
|
|
vals.push_back(0); // add one item
|
|
for ( int i=0; i < si.ncases; i++, ea += insn.size )
|
|
{
|
|
decode_insn(&insn, ea);
|
|
if ( targets != NULL )
|
|
{
|
|
if ( insn.itype == m32r_bra && insn.Op1.type == o_near )
|
|
targets->push_back(insn.Op1.addr);
|
|
else
|
|
targets->push_back(insn.ea);
|
|
}
|
|
if ( casevec != NULL )
|
|
{
|
|
vals[0] = i;
|
|
casevec->push_back(vals);
|
|
}
|
|
}
|
|
return 1; // ok
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static bool handle_switch(const insn_t &insn)
|
|
{
|
|
switch_info_t si;
|
|
bool was_switch = (get_flags(insn.ea) & FF_JUMP) != 0
|
|
&& get_switch_info(&si, insn.ea) > 0;
|
|
// do not overwrite the existing switch
|
|
// FIXME: reanalyze non user defined switches
|
|
if ( was_switch )
|
|
return true;
|
|
|
|
// ldi8 R1, #0x21 ; '!'
|
|
// cmpu R1, R0
|
|
// bc.l loc_67F8C
|
|
// slli R0, #2
|
|
// addi R0, #4
|
|
// bl.s next || nop
|
|
// next:
|
|
// add lr, R0
|
|
// jmp lr
|
|
// bra.s loc_67CDC || nop
|
|
// bra.s loc_67D34 || nop
|
|
// bra.l loc_67F8C
|
|
// ...
|
|
if ( insn.itype != m32r_bl )
|
|
return false;
|
|
|
|
// bl should be to next address
|
|
ea_t tgt = to_ea(insn.cs, insn.Op1.addr);
|
|
if ( tgt != insn.ea + insn.size )
|
|
return false;
|
|
|
|
insn_t insn2;
|
|
// check for add lr, R0; jmp lr
|
|
if ( decode_insn(&insn2, tgt) == 0
|
|
|| insn2.itype != m32r_add
|
|
|| !insn2.Op1.is_reg(rLR)
|
|
|| insn2.Op2.type != o_reg )
|
|
{
|
|
BAD_MATCH:
|
|
return false;
|
|
}
|
|
|
|
int switch_reg = insn2.Op2.reg;
|
|
|
|
// jmp lr
|
|
if ( decode_insn(&insn2, insn2.ea + insn2.size) == 0
|
|
|| insn2.itype != m32r_jmp
|
|
|| !insn2.Op1.is_reg(rLR) )
|
|
{
|
|
goto BAD_MATCH;
|
|
}
|
|
|
|
// addi R0, #4
|
|
if ( decode_prev_insn(&insn2, insn.ea) == BADADDR
|
|
|| insn2.itype != m32r_addi
|
|
|| !insn2.Op1.is_reg(switch_reg)
|
|
|| insn2.Op2.type != o_imm )
|
|
{
|
|
goto BAD_MATCH;
|
|
}
|
|
|
|
ea_t jumps = insn.ea + insn.size + insn2.Op2.value;
|
|
|
|
// slli R0, #2
|
|
if ( decode_prev_insn(&insn2, insn2.ea) == BADADDR
|
|
|| insn2.itype != m32r_slli
|
|
|| !insn2.Op1.is_reg(switch_reg)
|
|
|| !insn2.Op2.is_imm(2) )
|
|
{
|
|
goto BAD_MATCH;
|
|
}
|
|
|
|
// bc.l default
|
|
if ( decode_prev_insn(&insn2, insn2.ea) == BADADDR
|
|
|| insn2.itype != m32r_bc )
|
|
{
|
|
goto BAD_MATCH;
|
|
}
|
|
|
|
ea_t defea = to_ea(insn2.cs, insn2.Op1.addr);
|
|
|
|
// cmpu R1, R0
|
|
if ( decode_prev_insn(&insn2, insn2.ea) == BADADDR
|
|
|| insn2.itype != m32r_cmpu
|
|
|| !insn2.Op2.is_reg(switch_reg)
|
|
|| insn2.Op1.type != o_reg )
|
|
{
|
|
goto BAD_MATCH;
|
|
}
|
|
|
|
int cmpreg = insn2.Op1.reg;
|
|
|
|
// ldi8 R1, #max
|
|
if ( decode_prev_insn(&insn2, insn2.ea) == BADADDR
|
|
|| insn2.itype != m32r_ldi
|
|
|| !insn2.Op1.is_reg(cmpreg)
|
|
|| insn2.Op2.type != o_imm )
|
|
{
|
|
goto BAD_MATCH;
|
|
}
|
|
|
|
// looks good
|
|
|
|
si.flags |= SWI_CUSTOM | SWI_J32;
|
|
si.ncases = insn2.Op2.value + 1;
|
|
si.jumps = jumps;
|
|
si.lowcase = 0;
|
|
si.startea = insn2.ea;
|
|
si.set_expr(switch_reg, dt_dword);
|
|
si.defjump = defea;
|
|
set_switch_info(insn.ea, si);
|
|
create_switch_table(insn.ea, si);
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// emulate operand
|
|
void m32r_t::handle_operand(const insn_t &insn, const op_t &op, bool loading)
|
|
{
|
|
flags_t F = get_flags(insn.ea);
|
|
switch ( op.type )
|
|
{
|
|
// Address
|
|
case o_near:
|
|
// branch label - create code reference (call or jump
|
|
// according to the instruction)
|
|
{
|
|
ea_t ea = to_ea(insn.cs, op.addr);
|
|
cref_t ftype = fl_JN;
|
|
if ( insn.itype == m32r_bl && !handle_switch(insn) )
|
|
{
|
|
if ( !func_does_return(ea) )
|
|
flow = false;
|
|
ftype = fl_CN;
|
|
}
|
|
insn.add_cref(ea, op.offb, ftype);
|
|
}
|
|
break;
|
|
|
|
// Immediate
|
|
case o_imm:
|
|
QASSERT(10135, loading);
|
|
handle_imm(insn);
|
|
// if the value was converted to an offset, then create a data xref:
|
|
if ( op_adds_xrefs(F, op.n) )
|
|
insn.add_off_drefs(op, dr_O, OOFW_IMM|OOF_SIGNED);
|
|
|
|
// create a comment if this immediate is represented in the .cfg file
|
|
{
|
|
const ioport_t *port = find_sym(op.value);
|
|
if ( port != NULL && !has_cmt(F) )
|
|
set_cmt(insn.ea, port->cmt.c_str(), false);
|
|
}
|
|
break;
|
|
|
|
// Displ
|
|
case o_displ:
|
|
handle_imm(insn);
|
|
// if the value was converted to an offset, then create a data xref:
|
|
if ( op_adds_xrefs(F, op.n) )
|
|
insn.add_off_drefs(op, loading ? dr_R : dr_W, OOF_SIGNED|OOF_ADDR|OOFW_32);
|
|
|
|
// create stack variables if required
|
|
if ( may_create_stkvars() && !is_defarg(F, op.n) )
|
|
{
|
|
func_t *pfn = get_func(insn.ea);
|
|
if ( pfn != NULL && (op.reg == rFP || op.reg == rSP) && pfn->flags & FUNC_FRAME )
|
|
{
|
|
if ( insn.create_stkvar(op, op.addr, STKVAR_VALID_SIZE) )
|
|
op_stkvar(insn.ea, op.n);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case o_phrase:
|
|
/* create stack variables if required */
|
|
if ( op.specflag1 == fRI && may_create_stkvars() && !is_defarg(F, op.n) )
|
|
{
|
|
func_t *pfn = get_func(insn.ea);
|
|
if ( pfn != NULL
|
|
&& (op.reg == rFP || op.reg == rSP)
|
|
&& (pfn->flags & FUNC_FRAME) != 0 )
|
|
{
|
|
if ( insn.create_stkvar(op, 0, STKVAR_VALID_SIZE) )
|
|
op_stkvar(insn.ea, op.n);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Phrase - register - void : do nothing
|
|
case o_reg:
|
|
case o_void:
|
|
break;
|
|
|
|
// Others types should never be called
|
|
default:
|
|
INTERR(10136);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// emulate an instruction
|
|
int m32r_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_JUMP )
|
|
remember_problem(PR_JUMP, insn.ea);
|
|
|
|
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);
|
|
|
|
if ( flow )
|
|
add_cref(insn.ea, insn.ea + insn.size, fl_F);
|
|
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool idaapi create_func_frame(func_t *pfn)
|
|
{
|
|
if ( pfn == NULL )
|
|
return 0;
|
|
|
|
ea_t ea = pfn->start_ea;
|
|
insn_t insn[4];
|
|
int i;
|
|
|
|
for ( i = 0; i < 4; i++ )
|
|
{
|
|
decode_insn(&insn[i], ea);
|
|
ea += insn[i].size;
|
|
}
|
|
|
|
i = 0;
|
|
ushort regsize = 0; // number of saved registers
|
|
|
|
// first insn is not either push fp OR st fp, @-sp
|
|
if ( (insn[i].itype != m32r_push
|
|
|| insn[i].Op1.reg != rFP)
|
|
&& (insn[i].itype != m32r_st
|
|
|| insn[i].Op1.reg != rFP
|
|
|| insn[i].Op2.reg != rSP
|
|
|| insn[i].Op2.specflag1 != fRIAS) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
regsize += 4;
|
|
i++;
|
|
|
|
// next insn is push lr OR st lr, @-sp
|
|
if ( (insn[i].itype == m32r_push
|
|
&& insn[i].Op1.reg == rLR)
|
|
|| (insn[i].itype == m32r_st
|
|
&& insn[i].Op1.reg == rFP
|
|
&& insn[i].Op2.reg == rLR
|
|
&& insn[i].Op2.specflag1 != fRIAS) )
|
|
{
|
|
regsize += 4;
|
|
i++;
|
|
}
|
|
|
|
// next insn is not addi sp, #imm
|
|
if ( insn[i].itype != m32r_addi || insn[i].Op1.reg != rSP )
|
|
return 0;
|
|
|
|
sval_t offset = - (sval_t) insn[i].Op2.value;
|
|
|
|
// toggle to the negative sign of the immediate operand of the addi insn
|
|
if ( !is_invsign(insn[i].ea, get_flags(insn[i].ea), 2) )
|
|
toggle_sign(insn[i].ea, 2);
|
|
|
|
i++;
|
|
|
|
// next insn is not mv fp, sp
|
|
if ( insn[i].itype != m32r_mv || insn[i].Op1.reg != rFP || insn[i].Op2.reg != rSP )
|
|
return 0;
|
|
|
|
pfn->flags |= (FUNC_FRAME | FUNC_BOTTOMBP);
|
|
return add_frame(pfn, offset, regsize, 0);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// should always returns 0
|
|
int idaapi m32r_get_frame_retsize(const func_t *)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// check is the specified operand is relative to the SP register
|
|
int idaapi is_sp_based(const insn_t &/*insn*/, const op_t &op)
|
|
{
|
|
return OP_SP_ADD | (op.reg == rSP ? OP_SP_BASED : OP_FP_BASED);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool idaapi can_have_type(const op_t &x)
|
|
{
|
|
switch ( x.type )
|
|
{
|
|
case o_imm:
|
|
case o_displ:
|
|
return 1;
|
|
|
|
case o_phrase:
|
|
if ( x.specflag1 == fRI )
|
|
return 1;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|