Files
sigmaker-ida/idasdk76/module/65816/emu.cpp
2021-10-31 21:20:46 +02:00

295 lines
8.2 KiB
C++

#include "m65816.hpp"
#include "bt.hpp"
//----------------------------------------------------------------------
void m65816_t::handle_operand(const op_t &x, bool read_access, const insn_t &insn)
{
ea_t ea;
dref_t dreftype;
switch ( x.type )
{
case o_void:
case o_reg:
break;
case o_imm:
QASSERT(557, read_access);
dreftype = dr_O;
MAKE_IMMD:
set_immd(insn.ea);
if ( is_off(get_flags(insn.ea), x.n) )
insn.add_off_drefs(x, dreftype, x.type == o_imm ? 0 : OOF_ADDR);
break;
case o_displ:
dreftype = read_access ? dr_R : dr_W;
switch ( x.phrase )
{
case rD: // "dp"
case rDX: // "dp, X"
case rDY: // "dp, Y"
case riDX: // "(dp, X)"
case rDi: // "(dp,n)"
case rDiL: // "long(dp,n)"
case rDiY: // "(dp,n), Y"
case rDiLY: // "long(dp,n), Y"
{
sel_t dp = get_sreg(insn.ea, rD);
if ( dp != BADSEL )
{
ea_t orig_ea = dp + x.addr;
ea = xlat(orig_ea);
goto MAKE_DREF;
}
else
{
goto MAKE_IMMD;
}
}
case rAbsi: // "(abs)"
case rAbsX: // "abs, X"
case rAbsY: // "abs, Y"
case rAbsiL: // "long(abs)"
ea = xlat(map_data_ea(insn, x));
goto MAKE_DREF;
case rAbsXi: // "(abs,X)"
ea = xlat(map_code_ea(insn, x)); // jmp, jsr
goto MAKE_DREF;
case rAbsLX: // "long abs, X"
ea = x.addr;
goto MAKE_DREF;
default:
goto MAKE_IMMD;
}
case o_mem:
case o_mem_far:
ea = calc_addr(x, NULL, insn);
MAKE_DREF:
insn.create_op_data(ea, x);
insn.add_dref(ea, x.offb, read_access ? dr_R : dr_W);
break;
case o_near:
case o_far:
{
ea_t orig_ea;
ea = calc_addr(x, &orig_ea, insn);
if ( insn.itype == M65816_per )
{
insn.add_dref(ea, x.offb, dr_O);
}
else
{
bool iscall = has_insn_feature(insn.itype, CF_CALL);
cref_t creftype = x.type == o_near
? iscall ? fl_CN : fl_JN
: iscall ? fl_CF : fl_JF;
insn.add_cref(ea, x.offb, creftype);
if ( flow && iscall )
flow = func_does_return(ea);
}
}
break;
default:
INTERR(558);
}
}
//----------------------------------------------------------------------
/**
* Get what is known of the status flags register,
* at address 'ea'.
*
* ea : The effective address.
*
* returns : A 9-bit value, composed with what is known of the
* status register at the 'ea' effective address. Its
* layout is the following:
* +----------------------------------------------------------------+
* | 0 | 0 | 0 | 0 | 0 | 0 | 0 | e || n | v | m | x | d | i | z | c |
* +----------------------------------------------------------------+
* 15 7 0
* Note that a 16-bit value is returned, in order to
* take the emulation-mode flag into consideration.
*/
static uint16 get_cpu_status(ea_t ea)
{
return (get_sreg(ea, rFe) << 8) | (get_sreg(ea, rFm) << 5) | (get_sreg(ea, rFx) << 4);
}
//----------------------------------------------------------------------
int m65816_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.Op1, 1, insn);
if ( Feature & CF_USE2 ) handle_operand(insn.Op2, 1, insn);
if ( Feature & CF_CHG1 ) handle_operand(insn.Op1, 0, insn);
if ( Feature & CF_CHG2 ) handle_operand(insn.Op2, 0, insn);
if ( Feature & CF_JUMP )
remember_problem(PR_JUMP, insn.ea);
if ( flow )
add_cref(insn.ea, insn.ea + insn.size, fl_F);
uint8 code = get_byte(insn.ea);
const struct opcode_info_t &opinfo = get_opcode_info(code);
if ( opinfo.itype == M65816_jmp || opinfo.itype == M65816_jsr )
{
if ( opinfo.addr == ABS_INDIR
|| opinfo.addr == ABS_INDIR_LONG
|| opinfo.addr == ABS_IX_INDIR )
{
remember_problem(PR_JUMP, insn.ea);
}
}
#if 0
switch ( opinfo.addr )
{
case ABS_LONG_IX:
{
ea_t orig_ea = insn.Op1.addr;
ea_t ea = xlat(orig_ea);
bool read_access;
if ( insn.itype == M65816_sta )
read_access = false;
else
read_access = true;
insn.add_dref(ea, insn.Op1.offb, read_access ? dr_R : dr_W);
break;
}
case DP:
{
bool read_access;
if ( insn.itype == M65816_tsb || insn.itype == M65816_asl || insn.itype == M65816_trb
|| insn.itype == M65816_rol || insn.itype == M65816_lsr || insn.itype == M65816_ror
|| insn.itype == M65816_dec || insn.itype == M65816_inc )
read_access = false;
else
read_access = true;
int32 val = backtrack_value(insn.ea, 2, BT_DP);
if ( val != -1 )
{
ea_t orig_ea = val + insn.Op1.addr;
ea_t ea = xlat(orig_ea);
insn.create_op_data(ea, insn.Op1);
insn.add_dref(ea, insn.Op1.offb, read_access ? dr_R : dr_W);
}
}
break;
}
#endif
switch ( insn.itype )
{
case M65816_sep:
case M65816_rep:
{
// Switching 8 -> 16 bits modes.
uint8 flag_data = get_byte(insn.ea + 1);
uint8 m_flag = flag_data & 0x20;
uint8 x_flag = flag_data & 0x10;
uint8 val = (insn.itype == M65816_rep) ? 0 : 1;
if ( m_flag )
split_sreg_range(insn.ea + 2, rFm, val, SR_auto);
if ( x_flag )
split_sreg_range(insn.ea + 2, rFx, val, SR_auto);
}
break;
case M65816_xce:
{
// Switching to native mode?
uint8 prev = get_byte(insn.ea - 1);
const struct opcode_info_t &opinf = get_opcode_info(prev);
if ( opinf.itype == M65816_clc )
split_sreg_range(insn.ea + 1, rFe, 0, SR_auto);
else if ( opinf.itype == M65816_sec )
split_sreg_range(insn.ea + 1, rFe, 1, SR_auto);
}
break;
case M65816_jmp:
case M65816_jml:
case M65816_jsl:
case M65816_jsr:
{
if ( insn.Op1.full_target_ea )
{
ea_t ftea = insn.Op1.full_target_ea;
if ( insn.itype != M65816_jsl && insn.itype != M65816_jml )
ftea = map_code_ea(insn, ftea, 0);
else
ftea = xlat(ftea);
split_sreg_range(ftea, rFm, get_sreg(insn.ea, rFm), SR_auto);
split_sreg_range(ftea, rFx, get_sreg(insn.ea, rFx), SR_auto);
split_sreg_range(ftea, rFe, get_sreg(insn.ea, rFe), SR_auto);
split_sreg_range(ftea, rPB, ftea >> 16, SR_auto);
split_sreg_range(ftea, rB, get_sreg(insn.ea, rB), SR_auto);
split_sreg_range(ftea, rDs, get_sreg(insn.ea, rDs), SR_auto);
split_sreg_range(ftea, rD, get_sreg(insn.ea, rD), SR_auto);
}
}
break;
case M65816_plb:
{
int32 val = backtrack_value(insn.ea, 1, BT_STACK);
if ( val != -1 )
{
split_sreg_range(insn.ea + insn.size, rB, val, SR_auto);
split_sreg_range(insn.ea + insn.size, rDs, val << 12, SR_auto);
}
}
break;
case M65816_pld:
{
int32 val = backtrack_value(insn.ea, 2, BT_STACK);
if ( val != -1 )
split_sreg_range(insn.ea + insn.size, rD, val, SR_auto);
}
break;
case M65816_plp:
{
// Ideally, should pass another parameter, specifying when to stop
// backtracking.
// For example, in order to avoid this:
// PHP
// PLP <-- this one is causing interference
// (dunno if that even happens, though)
// PLP
ea_t ea = backtrack_prev_ins(insn.ea, M65816_php);
if ( ea != BADADDR )
{
uint16 p = get_cpu_status(ea);
split_sreg_range(insn.ea + insn.size, rFm, (p >> 5) & 0x1, SR_auto);
split_sreg_range(insn.ea + insn.size, rFx, (p >> 4) & 0x1, SR_auto);
}
}
break;
}
return 1;
}