update to ida 7.6, add builds
This commit is contained in:
11
idasdk76/plugins/z80dbg/makefile
Normal file
11
idasdk76/plugins/z80dbg/makefile
Normal file
@@ -0,0 +1,11 @@
|
||||
PROC=z80dbg
|
||||
|
||||
include ../plugin.mak
|
||||
|
||||
# MAKEDEP dependency list ------------------
|
||||
$(F)z80dbg$(O) : $(I)allins.hpp $(I)bitrange.hpp $(I)bytes.hpp \
|
||||
$(I)config.hpp $(I)fpro.h $(I)funcs.hpp $(I)ida.hpp \
|
||||
$(I)idd.hpp $(I)idp.hpp $(I)ieee.h $(I)kernwin.hpp \
|
||||
$(I)lines.hpp $(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \
|
||||
$(I)netnode.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
|
||||
$(I)ua.hpp $(I)xref.hpp z80dbg.cpp
|
||||
352
idasdk76/plugins/z80dbg/z80dbg.cpp
Normal file
352
idasdk76/plugins/z80dbg/z80dbg.cpp
Normal file
@@ -0,0 +1,352 @@
|
||||
#include <ida.hpp>
|
||||
#include <idd.hpp>
|
||||
#include <idp.hpp>
|
||||
#include <loader.hpp>
|
||||
|
||||
#include <allins.hpp>
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
struct plugin_ctx_t : public plugmod_t, public event_listener_t
|
||||
{
|
||||
plugin_ctx_t()
|
||||
{
|
||||
hook_event_listener(HT_IDP, this);
|
||||
}
|
||||
~plugin_ctx_t()
|
||||
{
|
||||
// listeners are uninstalled automatically
|
||||
// when the owner module is unloaded
|
||||
}
|
||||
virtual bool idaapi run(size_t) override { return false; }
|
||||
virtual ssize_t idaapi on_event(ssize_t code, va_list va) override;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
static bool z80_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;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
typedef const regval_t &idaapi getreg_t(const char *name, const regval_t *regvalues);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
static sval_t named_regval(
|
||||
const char *regname,
|
||||
getreg_t *getreg,
|
||||
const regval_t *rv)
|
||||
{
|
||||
// Get register info.
|
||||
const char *main_regname;
|
||||
bitrange_t bitrange;
|
||||
if ( !z80_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;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
static sval_t regval(
|
||||
const op_t &op,
|
||||
getreg_t *getreg,
|
||||
const regval_t *rv)
|
||||
{
|
||||
// Check for bad register number.
|
||||
processor_t &ph = PH;
|
||||
if ( op.reg > ph.regs_num )
|
||||
return 0;
|
||||
return named_regval(ph.reg_names[op.reg], getreg, rv);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
static bool z80_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.
|
||||
processor_t &ph = PH;
|
||||
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;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
opinf->value._set_int(v);
|
||||
opinf->value_size = get_dtype_size(op.dtype);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
enum opcond_t // condition code types
|
||||
{
|
||||
oc_nz,
|
||||
oc_z,
|
||||
oc_nc,
|
||||
oc_c,
|
||||
oc_po,
|
||||
oc_pe,
|
||||
oc_p,
|
||||
oc_m,
|
||||
oc_not
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
static bool z80_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;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
static ea_t z80_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 ( z80_check_cond(insn.Op1.reg, 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 ( !z80_check_cond(insn.Op1.reg, 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;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static ea_t z80_calc_step_over(ea_t ip)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
ssize_t idaapi plugin_ctx_t::on_event(ssize_t code, va_list va)
|
||||
{
|
||||
switch ( code )
|
||||
{
|
||||
case processor_t::ev_next_exec_insn:
|
||||
{
|
||||
ea_t *target = va_arg(va, ea_t *);
|
||||
ea_t ea = va_arg(va, ea_t);
|
||||
int tid = va_arg(va, int);
|
||||
getreg_t *getreg = va_arg(va, getreg_t *);
|
||||
const regval_t *regvalues = va_arg(va, const regval_t *);
|
||||
qnotused(tid);
|
||||
*target = z80_next_exec_insn(ea, getreg, regvalues);
|
||||
return 1;
|
||||
}
|
||||
case processor_t::ev_calc_step_over:
|
||||
{
|
||||
ea_t *target = va_arg(va, ea_t *);
|
||||
ea_t ip = va_arg(va, ea_t);
|
||||
*target = z80_calc_step_over(ip);
|
||||
return 1;
|
||||
}
|
||||
case processor_t::ev_get_idd_opinfo:
|
||||
{
|
||||
idd_opinfo_t *opinf = va_arg(va, idd_opinfo_t *);
|
||||
ea_t ea = va_arg(va, ea_t);
|
||||
int n = va_arg(va, int);
|
||||
int thread_id = va_arg(va, int);
|
||||
getreg_t *getreg = va_arg(va, getreg_t *);
|
||||
const regval_t *regvalues = va_arg(va, const regval_t *);
|
||||
qnotused(thread_id);
|
||||
return z80_get_operand_info(opinf, ea, n, getreg, regvalues) ? 1 : 0;
|
||||
}
|
||||
case processor_t::ev_get_reg_info:
|
||||
{
|
||||
const char **main_regname = va_arg(va, const char **);
|
||||
bitrange_t *bitrange = va_arg(va, bitrange_t *);
|
||||
const char *regname = va_arg(va, const char *);
|
||||
return z80_get_reg_info(main_regname, bitrange, regname) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
return 0; // event is not processed
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static plugmod_t *idaapi init()
|
||||
{
|
||||
processor_t &ph = PH;
|
||||
if ( ph.id != PLFM_Z80 )
|
||||
return nullptr;
|
||||
return new plugin_ctx_t;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static const char comment[] = "Z80 debugger processor extension";
|
||||
static const char help[] =
|
||||
"Z80 debugger module\n"
|
||||
"\n"
|
||||
"This plugin extends the Z80 processor module to support debugging.\n";
|
||||
static const char wanted_name[] = "Z80 debugger processor extension";
|
||||
static const char wanted_hotkey[] = "";
|
||||
plugin_t PLUGIN =
|
||||
{
|
||||
IDP_INTERFACE_VERSION,
|
||||
PLUGIN_MULTI // The plugin can work with multiple idbs in parallel
|
||||
| PLUGIN_PROC, // Load plugin when a processor module is loaded
|
||||
init, // Initialize plugin
|
||||
nullptr,
|
||||
nullptr,
|
||||
comment, // Long comment about the plugin
|
||||
help, // Multiline help about the plugin
|
||||
wanted_name, // The preferred short name of the plugin
|
||||
wanted_hotkey // The preferred hotkey to run the plugin
|
||||
};
|
||||
Reference in New Issue
Block a user