Files
2021-10-31 21:20:46 +02:00

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;
}