430 lines
10 KiB
C++
430 lines
10 KiB
C++
/*
|
|
* 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;
|
|
}
|