update to ida 7.6, add builds
This commit is contained in:
489
idasdk76/module/st9/emu.cpp
Normal file
489
idasdk76/module/st9/emu.cpp
Normal file
@@ -0,0 +1,489 @@
|
||||
|
||||
#include "st9.hpp"
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
static sel_t calc_page(ea_t insn_ea, ushort addr)
|
||||
{
|
||||
return get_sreg(insn_ea, rDPR0+(addr>>14));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
static ea_t calc_data_mem_without_mapping(ea_t insn_ea, ea_t addr)
|
||||
{
|
||||
sel_t page = calc_page(insn_ea, (ushort)addr);
|
||||
if ( page == BADSEL )
|
||||
return BADADDR;
|
||||
ea_t ea = use_mapping((page<<14) + (addr & 0x3FFF));
|
||||
return ea;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
ea_t get_dest_addr(const insn_t &insn, const op_t &x)
|
||||
{
|
||||
if ( x.type == o_far )
|
||||
return x.addr;
|
||||
else if ( x.type == o_mem )
|
||||
return calc_data_mem_without_mapping(insn.ea, x.addr);
|
||||
else if ( x.type == o_near )
|
||||
return to_ea(insn.cs, x.addr);
|
||||
else
|
||||
return BADADDR;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Emulate an operand.
|
||||
void st9_t::handle_operand(const insn_t &insn, const op_t &op, bool lwrite)
|
||||
{
|
||||
switch ( op.type )
|
||||
{
|
||||
// Code address
|
||||
case o_near:
|
||||
case o_far:
|
||||
{
|
||||
cref_t mode;
|
||||
ea_t ea = get_dest_addr(insn, op);
|
||||
|
||||
// call or jump ?
|
||||
if ( is_call_insn(insn) )
|
||||
{
|
||||
if ( !func_does_return(ea) )
|
||||
flow = false;
|
||||
mode = op.type == o_near ? fl_CN: fl_CF;
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = op.type == o_near ? fl_JN: fl_JF;
|
||||
}
|
||||
insn.add_cref(ea, op.offb, mode);
|
||||
}
|
||||
break;
|
||||
|
||||
// Memory address
|
||||
case o_mem:
|
||||
{
|
||||
ea_t ea = get_dest_addr(insn, op);
|
||||
insn.add_dref(ea, op.offb, lwrite ? dr_W : dr_R);
|
||||
insn.create_op_data(ea, op);
|
||||
}
|
||||
break;
|
||||
|
||||
// Immediate value
|
||||
case o_imm:
|
||||
{
|
||||
set_immd(insn.ea);
|
||||
flags_t F = get_flags(insn.ea);
|
||||
// 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);
|
||||
}
|
||||
// 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, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
// Displacement
|
||||
case o_displ:
|
||||
{
|
||||
set_immd(insn.ea);
|
||||
flags_t F = get_flags(insn.ea);
|
||||
if ( op_adds_xrefs(F, op.n) )
|
||||
{
|
||||
ea_t ea = insn.add_off_drefs(op, dr_O, OOF_ADDR);
|
||||
insn.create_op_data(ea, op);
|
||||
}
|
||||
|
||||
// create stack variables if required
|
||||
if ( may_create_stkvars() && !is_defarg(F, op.n) && op.reg == rrr14 )
|
||||
{
|
||||
func_t *pfn = get_func(insn.ea);
|
||||
if ( pfn != NULL && pfn->flags & FUNC_FRAME )
|
||||
{
|
||||
adiff_t displ = (int16)op.addr;
|
||||
if ( insn.create_stkvar(op, displ, STKVAR_VALID_SIZE) )
|
||||
{
|
||||
op_stkvar(insn.ea, op.n);
|
||||
if ( insn.Op2.type == o_reg )
|
||||
{
|
||||
regvar_t *r = find_regvar(pfn, insn.ea, ph.reg_names[insn.Op2.reg]);
|
||||
if ( r != NULL )
|
||||
{
|
||||
struc_t *s = get_frame(pfn);
|
||||
member_t *m = get_stkvar(NULL, insn, op, displ);
|
||||
if ( s != NULL && m != NULL )
|
||||
{
|
||||
char b[20];
|
||||
qsnprintf(b, sizeof b, "%scopy", r->user);
|
||||
set_member_name(s, m->soff, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Register - Phrase - Void: do nothing
|
||||
case o_reg:
|
||||
case o_phrase:
|
||||
case o_void:
|
||||
break;
|
||||
|
||||
default:
|
||||
INTERR(10076);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Emulate an instruction.
|
||||
int st9_t::st9_emu(const insn_t &insn)
|
||||
{
|
||||
uint32 feature = insn.get_canon_feature(ph);
|
||||
flow = ((feature & CF_STOP) == 0);
|
||||
// is it "jump always"?
|
||||
if ( is_jmp_cc(insn.itype) && insn.auxpref == cT )
|
||||
flow = false;
|
||||
|
||||
if ( insn.Op1.type != o_void) handle_operand(insn, insn.Op1, (feature & CF_CHG1) != 0);
|
||||
if ( insn.Op2.type != o_void) handle_operand(insn, insn.Op2, (feature & CF_CHG2) != 0);
|
||||
if ( insn.Op3.type != o_void) handle_operand(insn, insn.Op3, (feature & CF_CHG3) != 0);
|
||||
|
||||
if ( flow )
|
||||
add_cref(insn.ea, insn.ea + insn.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 ( insn.itype )
|
||||
{
|
||||
case st9_srp:
|
||||
{
|
||||
sel_t val = insn.Op1.value;
|
||||
if ( val % 2 )
|
||||
val--; // even reduced
|
||||
split_sreg_range(insn.ea+insn.size, rRW, val | (val << 8), SR_auto);
|
||||
}
|
||||
rw_has_changed = true;
|
||||
break;
|
||||
|
||||
case st9_srp0:
|
||||
{
|
||||
sel_t RW = get_sreg(insn.ea, rRW);
|
||||
split_sreg_range(insn.ea+insn.size, rRW, insn.Op1.value | (RW & 0xFF00), SR_auto);
|
||||
}
|
||||
rw_has_changed = true;
|
||||
break;
|
||||
|
||||
case st9_srp1:
|
||||
{
|
||||
sel_t RW = get_sreg(insn.ea, rRW);
|
||||
split_sreg_range(insn.ea+insn.size, rRW, (insn.Op1.value << 8) | (RW & 0x00FF), SR_auto);
|
||||
}
|
||||
rw_has_changed = true;
|
||||
break;
|
||||
|
||||
case st9_spp:
|
||||
split_sreg_range(insn.ea+insn.size, rRP, insn.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.
|
||||
|
||||
flags_t F = get_flags(insn.ea);
|
||||
if ( rw_has_changed && !has_cmt(F) )
|
||||
{
|
||||
char buf[MAXSTR];
|
||||
sel_t RW = get_sreg(insn.ea+insn.size, rRW);
|
||||
int low = RW & 0x00FF;
|
||||
int high = (RW & 0xFF00) >> 8;
|
||||
|
||||
low *= 8;
|
||||
high *= 8;
|
||||
|
||||
const char *const 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(insn.ea, buf, false);
|
||||
}
|
||||
|
||||
if ( rp_has_changed && !has_cmt(F) )
|
||||
{
|
||||
char buf[MAXSTR];
|
||||
int rpval = get_sreg(insn.ea+insn.size, rRP);
|
||||
qsnprintf(buf, sizeof buf, "Registers R240-R255 will now be referred to the page %d of paged registers",
|
||||
rpval);
|
||||
set_cmt(insn.ea, buf, false);
|
||||
}
|
||||
|
||||
// reanalyze switch info
|
||||
if ( insn.itype == st9_jp && get_auto_state() == AU_USED )
|
||||
{
|
||||
switch_info_t si;
|
||||
if ( get_switch_info(&si, insn.ea) > 0 && !si.is_user_defined() )
|
||||
{
|
||||
delete_switch_table(insn.ea, si);
|
||||
if ( st9_is_switch(&si, insn) )
|
||||
{
|
||||
set_switch_info(insn.ea, si);
|
||||
create_switch_table(insn.ea, si);
|
||||
}
|
||||
else
|
||||
{
|
||||
del_switch_info(insn.ea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Analyze an instruction
|
||||
static ea_t next_insn(insn_t *insn, ea_t ea)
|
||||
{
|
||||
if ( decode_insn(insn, ea) == 0 )
|
||||
return 0;
|
||||
ea += insn->size;
|
||||
return ea;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// does a far return instruction precede 'ea'?
|
||||
static bool is_far_return(ea_t ea)
|
||||
{
|
||||
insn_t insn;
|
||||
if ( decode_prev_insn(&insn, ea) != BADADDR )
|
||||
return insn.itype == st9_rets;
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// if a function ends with a far return, mark it as such
|
||||
// NB: we only handle regular (non-chunked) functions
|
||||
static void setup_far_func(func_t *pfn)
|
||||
{
|
||||
if ( (pfn->flags & FUNC_FAR) == 0 )
|
||||
{
|
||||
if ( is_far_return(pfn->end_ea) )
|
||||
{
|
||||
pfn->flags |= FUNC_FAR;
|
||||
update_func(pfn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Create a function frame
|
||||
bool st9_t::create_func_frame(func_t *pfn) const
|
||||
{
|
||||
setup_far_func(pfn);
|
||||
|
||||
ea_t ea = pfn->start_ea;
|
||||
|
||||
insn_t insn;
|
||||
ea = next_insn(&insn, ea);
|
||||
if ( !ea )
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Get the total frame size
|
||||
*
|
||||
* LINK rr14, #size
|
||||
*/
|
||||
|
||||
if ( insn.itype != st9_link )
|
||||
return 0;
|
||||
|
||||
int link_register = insn.Op1.reg;
|
||||
size_t total_size = (size_t)insn.Op2.value;
|
||||
|
||||
/*
|
||||
* Get arguments size
|
||||
*
|
||||
* LDW 0x??(rr14), RR??? a word
|
||||
* LD '' a byte
|
||||
*/
|
||||
|
||||
int args_size = 0;
|
||||
|
||||
for ( int i = 0; true; i++ )
|
||||
{
|
||||
insn_t ldi;
|
||||
ea = next_insn(&ldi, ea);
|
||||
if ( !ea )
|
||||
return 0;
|
||||
|
||||
if ( ldi.Op1.type != o_displ || ldi.Op2.type != o_reg )
|
||||
break;
|
||||
|
||||
if ( ldi.Op1.reg != link_register )
|
||||
break;
|
||||
|
||||
if ( ldi.itype == st9_ld ) // byte
|
||||
args_size++;
|
||||
else if ( ldi.itype == st9_ldw ) // word
|
||||
args_size += 2;
|
||||
else
|
||||
break;
|
||||
|
||||
char regvar[10];
|
||||
qsnprintf(regvar, sizeof regvar, "arg_%d", i);
|
||||
int err = add_regvar(pfn, ldi.ea, ldi.ea + ldi.size,
|
||||
ph.reg_names[ldi.Op2.reg], regvar, NULL);
|
||||
if ( err )
|
||||
msg("add_regvar() failed : error %d\n", err);
|
||||
}
|
||||
|
||||
//msg("LOCAL: %d\nARGS: %d\n", total_size - args_size, args_size);
|
||||
|
||||
pfn->flags |= FUNC_FRAME;
|
||||
return add_frame(pfn, total_size - args_size, 0, args_size);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/*
|
||||
GCC?-produced switch:
|
||||
|
||||
ldw ridx, rin [optional]
|
||||
cpw rin, #n
|
||||
jpugt default | jrugt default
|
||||
addw ridx, ridx
|
||||
spm
|
||||
ldw rjmp, jtbl(ridx)
|
||||
sdm
|
||||
jp (rjmp)
|
||||
jtbl: .word case0, case1, ...
|
||||
*/
|
||||
|
||||
static ea_t check_prev_insn(int itype, insn_t &insn)
|
||||
{
|
||||
ea_t ea = decode_prev_insn(&insn, insn.ea);
|
||||
if ( ea == BADADDR || insn.itype != itype )
|
||||
return BADADDR;
|
||||
return ea;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static bool is_gcc_switch(switch_info_t *_si, insn_t &insn)
|
||||
{
|
||||
switch_info_t &si = *_si;
|
||||
int rjmp, ridx;
|
||||
// si.flags |= SWI_J32;
|
||||
ea_t ea, jtbl_insn;
|
||||
//
|
||||
// Check jump insn and get register number
|
||||
// jp (rjmp)
|
||||
if ( insn.itype != st9_jp
|
||||
|| insn.Op1.type != o_reg
|
||||
|| !is_ind(insn.Op1) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
rjmp = insn.Op1.reg;
|
||||
// sdm
|
||||
ea = check_prev_insn(st9_sdm, insn);
|
||||
if ( ea == BADADDR )
|
||||
return false;
|
||||
// ldw rjmp, jtbl(ridx)
|
||||
ea = check_prev_insn(st9_ldw, insn);
|
||||
if ( ea == BADADDR
|
||||
|| !insn.Op1.is_reg(rjmp)
|
||||
|| insn.Op2.type != o_displ )
|
||||
return false;
|
||||
ridx = insn.Op2.reg;
|
||||
jtbl_insn = ea;
|
||||
// this addr is offset in current code segment because of spm
|
||||
si.jumps = to_ea(insn.cs, insn.Op2.addr);
|
||||
// spm
|
||||
ea = check_prev_insn(st9_spm, insn);
|
||||
if ( ea == BADADDR )
|
||||
return false;
|
||||
|
||||
// addw ridx, ridx
|
||||
ea = check_prev_insn(st9_addw, insn);
|
||||
if ( ea == BADADDR
|
||||
|| !insn.Op1.is_reg(ridx)
|
||||
|| !insn.Op2.is_reg(ridx) )
|
||||
return false;
|
||||
|
||||
// jpugt default | jrugt default
|
||||
ea = decode_prev_insn(&insn, ea);
|
||||
if ( ea != BADADDR
|
||||
&& is_jmp_cc(insn.itype)
|
||||
&& insn.auxpref == cUGT )
|
||||
{
|
||||
si.defjump = get_dest_addr(insn, insn.Op1);
|
||||
// cpw rin, #n
|
||||
ea = check_prev_insn(st9_cpw, insn);
|
||||
if ( ea == BADADDR
|
||||
|| insn.Op2.type != o_imm )
|
||||
return false;
|
||||
int rin = insn.Op1.reg;
|
||||
si.ncases = ushort(insn.Op2.value+1);
|
||||
// is rin the same as ridx?
|
||||
bool ok = insn.Op1.is_reg(ridx);
|
||||
if ( !ok )
|
||||
{
|
||||
// check for preceding ldw ridx, rin
|
||||
ea_t ea2 = decode_prev_insn(&insn, ea);
|
||||
if ( ea2 != BADADDR
|
||||
&& insn.itype == st9_ldw
|
||||
&& insn.Op1.is_reg(ridx)
|
||||
&& insn.Op2.is_reg(rin) )
|
||||
ok = true;
|
||||
}
|
||||
if ( !ok )
|
||||
return false;
|
||||
si.set_expr(rin, insn.Op1.dtype);
|
||||
}
|
||||
//
|
||||
// Everything ok.
|
||||
//
|
||||
msg("SWITCH %a: gcc_switch\n", insn.ea);
|
||||
si.startea = ea;
|
||||
si.set_jtable_element_size(2);
|
||||
si.set_shift(0);
|
||||
op_num(ea, 1); // cpw rin, #n
|
||||
op_plain_offset(jtbl_insn, 1, to_ea(insn.cs, 0)); // ldw rjmp, jtbl(ridx)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool st9_is_switch(switch_info_t *si, const insn_t &insn)
|
||||
{
|
||||
if ( insn.itype == st9_jp )
|
||||
{
|
||||
insn_t copy = insn;
|
||||
return is_gcc_switch(si, copy);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user