update to ida 7.6, add builds
This commit is contained in:
429
idasdk76/module/z80/emu.cpp
Normal file
429
idasdk76/module/z80/emu.cpp
Normal file
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Version 3.06
|
||||
* Copyright (c) 1990-96 by Ilfak Guilfanov.
|
||||
* ALL RIGHTS RESERVED.
|
||||
* FIDO: 2:5020/209
|
||||
* E-mail: ig@estar.msk.su
|
||||
*
|
||||
*/
|
||||
|
||||
#include "i5.hpp"
|
||||
#include <idd.hpp>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static void set_immd_bit(const insn_t &insn, int n)
|
||||
{
|
||||
set_immd(insn.ea);
|
||||
if ( !is_defarg(get_flags(insn.ea), n) )
|
||||
{
|
||||
switch ( insn.itype )
|
||||
{
|
||||
case I5_ani:
|
||||
case I5_xri:
|
||||
case I5_ori:
|
||||
case I5_in:
|
||||
case I5_out:
|
||||
case I5_rst:
|
||||
|
||||
case HD_in0:
|
||||
case HD_out0:
|
||||
case HD_tstio:
|
||||
op_num(insn.ea,-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
void z80_t::load_operand(const insn_t &insn, const op_t &x)
|
||||
{
|
||||
dref_t xreftype;
|
||||
switch ( x.type )
|
||||
{
|
||||
case o_reg:
|
||||
case o_phrase:
|
||||
default:
|
||||
break;
|
||||
|
||||
case o_imm:
|
||||
xreftype = dr_O;
|
||||
MakeImm:
|
||||
set_immd_bit(insn, x.n);
|
||||
if ( op_adds_xrefs(get_flags(insn.ea), x.n) )
|
||||
insn.add_off_drefs(x, xreftype, 0);
|
||||
break;
|
||||
case o_displ:
|
||||
xreftype = dr_R;
|
||||
goto MakeImm;
|
||||
|
||||
case o_mem:
|
||||
{
|
||||
ea_t ea = map_data_ea(insn, x);
|
||||
insn.add_dref(ea, x.offb, dr_R);
|
||||
insn.create_op_data(ea, x);
|
||||
}
|
||||
break;
|
||||
|
||||
case o_near:
|
||||
{
|
||||
ea_t ea = map_code_ea(insn, x);
|
||||
ea_t segbase = (ea - x.addr) >> 4;
|
||||
ea_t thisseg = insn.cs;
|
||||
int iscall = has_insn_feature(insn.itype,CF_CALL);
|
||||
insn.add_cref(
|
||||
ea,
|
||||
x.offb,
|
||||
iscall ? (segbase == thisseg ? fl_CN : fl_CF)
|
||||
: (segbase == thisseg ? fl_JN : fl_JF));
|
||||
if ( iscall && !func_does_return(ea) )
|
||||
flow = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
static void save_operand(const insn_t &insn, const op_t &x)
|
||||
{
|
||||
switch ( x.type )
|
||||
{
|
||||
case o_reg:
|
||||
break;
|
||||
case o_mem:
|
||||
{
|
||||
ea_t ea = map_data_ea(insn, x);
|
||||
insn.create_op_data(ea, x);
|
||||
insn.add_dref(ea, x.offb, dr_W);
|
||||
}
|
||||
break;
|
||||
case o_displ:
|
||||
set_immd_bit(insn, x.n);
|
||||
if ( op_adds_xrefs(get_flags(insn.ea), x.n) )
|
||||
insn.add_off_drefs(x, dr_W, OOF_ADDR);
|
||||
case o_phrase:
|
||||
break;
|
||||
default:
|
||||
switch ( insn.itype )
|
||||
{
|
||||
case Z80_in0:
|
||||
case Z80_outaw:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
int z80_t::i5_emu(const insn_t &insn)
|
||||
{
|
||||
uint32 Feature = insn.get_canon_feature(ph);
|
||||
flow = ((Feature & CF_STOP) == 0);
|
||||
|
||||
if ( (Feature & CF_USE1) )
|
||||
load_operand(insn, insn.Op1);
|
||||
if ( (Feature & CF_USE2) )
|
||||
load_operand(insn, insn.Op2);
|
||||
|
||||
if ( Feature & CF_JUMP )
|
||||
remember_problem(PR_JUMP, insn.ea);
|
||||
|
||||
switch ( insn.itype )
|
||||
{
|
||||
case I5_mov:
|
||||
case I5_mvi:
|
||||
case Z80_ld:
|
||||
break;
|
||||
case Z80_jp:
|
||||
case Z80_jr: // Z80
|
||||
case Z80_ret: // Z80
|
||||
if ( insn.Op1.Cond != oc_not )
|
||||
break;
|
||||
// no break
|
||||
case I5_jmp:
|
||||
if ( insn.Op2.type == o_phrase )
|
||||
remember_problem(PR_JUMP, insn.ea);
|
||||
// no break
|
||||
case I5_ret:
|
||||
flow = false;
|
||||
break;
|
||||
case I5_rstv:
|
||||
add_cref(insn.ea, map_code_ea(insn, 0x40, 0), fl_CN);
|
||||
break;
|
||||
case I5_rst:
|
||||
{
|
||||
int mul = isZ80() ? 1 : 8;
|
||||
ushort offset = ushort(insn.Op1.value * mul);
|
||||
add_cref(insn.ea, map_code_ea(insn, offset, 0), fl_CN);
|
||||
}
|
||||
case I5_call:
|
||||
case I5_cc:
|
||||
case I5_cnc:
|
||||
case I5_cz:
|
||||
case I5_cnz:
|
||||
case I5_cpe:
|
||||
case I5_cpo:
|
||||
case I5_cp:
|
||||
case I5_cm:
|
||||
case Z80_exx: // Z80
|
||||
// i5_CPUregs.bc.undef();
|
||||
// i5_CPUregs.de.undef();
|
||||
// i5_CPUregs.hl.undef();
|
||||
// i5_CPUregs.af.undef();
|
||||
// i5_CPUregs.ix.undef();
|
||||
// i5_CPUregs.iy.undef();
|
||||
break;
|
||||
default:
|
||||
// R1.undef();
|
||||
// R2.undef();
|
||||
break;
|
||||
}
|
||||
|
||||
if ( Feature & CF_CHG1 )
|
||||
save_operand(insn, insn.Op1);
|
||||
if ( Feature & CF_CHG2 )
|
||||
save_operand(insn, insn.Op2);
|
||||
|
||||
if ( flow )
|
||||
add_cref(insn.ea, insn.ea+insn.size, fl_F);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
sval_t z80_t::named_regval(
|
||||
const char *regname,
|
||||
getreg_t *getreg,
|
||||
const regval_t *rv)
|
||||
{
|
||||
// Get register info.
|
||||
const char *main_regname;
|
||||
bitrange_t bitrange;
|
||||
if ( !get_reg_info(&main_regname, &bitrange, regname) )
|
||||
return 0;
|
||||
|
||||
// Get main register value and apply bitrange.
|
||||
sval_t ret = getreg(main_regname, rv).ival;
|
||||
ret >>= bitrange.bitoff();
|
||||
ret &= (1ULL << bitrange.bitsize()) - 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
sval_t z80_t::regval(const op_t &op, getreg_t *getreg, const regval_t *rv)
|
||||
{
|
||||
// Check for bad register number.
|
||||
if ( op.reg > R_a2 )
|
||||
return 0;
|
||||
return named_regval(ph.reg_names[op.reg], getreg, rv);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
bool z80_t::check_cond(
|
||||
uint16_t cc,
|
||||
getreg_t *getreg,
|
||||
const regval_t *regvalues)
|
||||
{
|
||||
uint16_t F = named_regval("F", getreg, regvalues);
|
||||
bool C = (F & (1 << 0)) != 0;
|
||||
bool PV = (F & (1 << 2)) != 0;
|
||||
bool Z = (F & (1 << 6)) != 0;
|
||||
bool S = (F & (1 << 7)) != 0;
|
||||
switch ( cc )
|
||||
{
|
||||
case oc_nz: return !Z; // non-zero
|
||||
case oc_z: return Z; // zero
|
||||
case oc_nc: return !C; // no carry
|
||||
case oc_c: return C; // carry
|
||||
case oc_po: return !PV; // parity odd
|
||||
case oc_pe: return PV; // parity even
|
||||
case oc_p: return !S; // sign positive
|
||||
case oc_m: return S; // sign negative
|
||||
case oc_not: return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
ea_t z80_t::next_exec_insn(
|
||||
ea_t ea,
|
||||
getreg_t *getreg,
|
||||
const regval_t *regvalues)
|
||||
{
|
||||
// Decode instruction at ea.
|
||||
insn_t insn;
|
||||
if ( decode_insn(&insn, ea) < 1 )
|
||||
return BADADDR;
|
||||
|
||||
// Get next address to be executed.
|
||||
ea_t target = BADADDR;
|
||||
switch ( insn.itype )
|
||||
{
|
||||
case Z80_jp:
|
||||
case Z80_jr:
|
||||
case Z80_call:
|
||||
if ( check_cond(insn.Op1.Cond, getreg, regvalues) )
|
||||
{
|
||||
if ( insn.Op2.type == o_near )
|
||||
target = insn.Op2.addr;
|
||||
else if ( insn.Op2.type == o_phrase )
|
||||
target = regval(insn.Op2, getreg, regvalues);
|
||||
}
|
||||
break;
|
||||
|
||||
case Z80_djnz:
|
||||
{
|
||||
uint8_t B = named_regval("B", getreg, regvalues);
|
||||
if ( (B-1) != 0 )
|
||||
target = insn.Op1.addr;
|
||||
}
|
||||
break;
|
||||
|
||||
case Z80_ret:
|
||||
if ( !check_cond(insn.Op1.Cond, getreg, regvalues) )
|
||||
break;
|
||||
// fallthrough
|
||||
case Z80_reti:
|
||||
case Z80_retn:
|
||||
{
|
||||
uint16_t SP = named_regval("SP", getreg, regvalues);
|
||||
target = get_word(SP);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
ea_t z80_t::calc_step_over(ea_t ip) const
|
||||
{
|
||||
insn_t insn;
|
||||
if ( ip == BADADDR || decode_insn(&insn, ip) < 1 )
|
||||
return BADADDR;
|
||||
|
||||
// Allow stepping over call instructions and djnz.
|
||||
bool step_over = is_call_insn(insn)
|
||||
|| insn.itype == Z80_djnz;
|
||||
if ( step_over )
|
||||
return insn.ea + insn.size;
|
||||
|
||||
return BADADDR;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
bool z80_t::get_operand_info(
|
||||
idd_opinfo_t *opinf,
|
||||
ea_t ea,
|
||||
int n,
|
||||
getreg_t *getreg,
|
||||
const regval_t *regvalues)
|
||||
{
|
||||
// No Z80 instruction has operand number greater than 2.
|
||||
if ( n < 0 || n > 2 )
|
||||
return false;
|
||||
|
||||
// Decode instruction at ea.
|
||||
insn_t insn;
|
||||
if ( decode_insn(&insn, ea) < 1 )
|
||||
return false;
|
||||
|
||||
// Check the instruction features to see if the operand is modified.
|
||||
opinf->modified = has_cf_chg(insn.get_canon_feature(ph), n);
|
||||
|
||||
// Get operand value (possibly an ea).
|
||||
uint64 v = 0;
|
||||
const op_t &op = insn.ops[n];
|
||||
switch ( op.type )
|
||||
{
|
||||
case o_reg:
|
||||
// We use the getreg function (along with regvalues) to retrieve
|
||||
// the value of the register specified in op.reg.
|
||||
v = regval(op, getreg, regvalues);
|
||||
break;
|
||||
case o_mem:
|
||||
case o_near:
|
||||
// Memory addresses are stored in op.addr.
|
||||
opinf->ea = op.addr;
|
||||
break;
|
||||
case o_phrase:
|
||||
// Memory references using register value.
|
||||
opinf->ea = regval(op, getreg, regvalues);
|
||||
break;
|
||||
case o_displ:
|
||||
// Memory references using register and address value.
|
||||
opinf->ea = regval(op, getreg, regvalues) + op.addr;
|
||||
break;
|
||||
case o_imm:
|
||||
// Immediates are stored in op.value.
|
||||
v = op.value;
|
||||
break;
|
||||
case o_cond:
|
||||
// This is just a condition code type (see opcond_t). There is no
|
||||
// meaningful value to return.
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
opinf->value._set_int(v);
|
||||
opinf->value_size = get_dtype_size(op.dtype);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
bool z80_t::get_reg_info(
|
||||
const char **main_regname,
|
||||
bitrange_t *bitrange,
|
||||
const char *regname)
|
||||
{
|
||||
// Sanity checks.
|
||||
if ( regname == NULL || regname[0] == '\0' )
|
||||
return false;
|
||||
|
||||
static const char *const subregs[][3] =
|
||||
{
|
||||
{ "af", "a", "f" },
|
||||
{ "bc", "b", "c" },
|
||||
{ "de", "d", "e" },
|
||||
{ "hl", "h", "l" },
|
||||
{ "af'", "a'", "f'" },
|
||||
{ "bc'", "b'", "c'" },
|
||||
{ "de'", "d'", "e'" },
|
||||
{ "hl'", "h'", "l'" },
|
||||
{ "ix", NULL, NULL },
|
||||
{ "iy", NULL, NULL },
|
||||
{ "sp", NULL, NULL },
|
||||
{ "pc", NULL, NULL },
|
||||
};
|
||||
|
||||
// Check if we are dealing with paired or single registers and return
|
||||
// the appropriate information.
|
||||
for ( size_t i = 0; i < qnumber(subregs); i++ )
|
||||
{
|
||||
for ( size_t j = 0; j < 3; j++ )
|
||||
{
|
||||
if ( subregs[i][j] == NULL )
|
||||
break;
|
||||
if ( strieq(regname, subregs[i][j]) )
|
||||
{
|
||||
if ( main_regname != NULL )
|
||||
*main_regname = subregs[i][0];
|
||||
if ( bitrange != NULL )
|
||||
{
|
||||
switch ( j )
|
||||
{
|
||||
case 0: *bitrange = bitrange_t(0, 16); break;
|
||||
case 1: *bitrange = bitrange_t(8, 8); break;
|
||||
case 2: *bitrange = bitrange_t(0, 8); break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user