529 lines
14 KiB
C++
529 lines
14 KiB
C++
/*
|
|
* Interactive disassembler (IDA).
|
|
* Copyright (c) Hex-Rays
|
|
* ALL RIGHTS RESERVED.
|
|
*
|
|
* Processor description structures
|
|
*
|
|
*/
|
|
#include "necv850.hpp"
|
|
#include "ins.hpp"
|
|
#include <loader.hpp>
|
|
#include <segregs.hpp>
|
|
|
|
#define GP_EA_IDX 1
|
|
#define CTBP_EA_IDX 2
|
|
|
|
//------------------------------------------------------------------
|
|
void nec850_t::save_settings()
|
|
{
|
|
prog_pointers.altset(GP_EA_IDX, ea2node(g_gp_ea));
|
|
prog_pointers.altset(CTBP_EA_IDX, ea2node(g_ctbp_ea));
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
void nec850_t::load_settings()
|
|
{
|
|
g_gp_ea = node2ea(prog_pointers.altval(GP_EA_IDX));
|
|
g_ctbp_ea = node2ea(prog_pointers.altval(CTBP_EA_IDX));
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
const char *nec850_t::set_idp_options(
|
|
const char *keyword,
|
|
int value_type,
|
|
const void * value,
|
|
bool idb_loaded)
|
|
{
|
|
if ( keyword != NULL )
|
|
{
|
|
if ( streq(keyword, "GP_EA") )
|
|
{
|
|
if ( value_type != IDPOPT_NUM )
|
|
return IDPOPT_BADTYPE;
|
|
g_gp_ea = *((uval_t *)value);
|
|
goto SAVE;
|
|
}
|
|
if ( streq(keyword, "CTBP_EA") )
|
|
{
|
|
if ( value_type != IDPOPT_NUM )
|
|
return IDPOPT_BADTYPE;
|
|
g_ctbp_ea = *((uval_t *)value);
|
|
goto SAVE;
|
|
}
|
|
return IDPOPT_BADKEY;
|
|
}
|
|
|
|
static const char form[] =
|
|
"NEC V850x analyzer options\n"
|
|
"\n"
|
|
" <~G~lobal Pointer address:$::18::>\n"
|
|
" <CALLT ~B~ase pointer :$::18::>\n"
|
|
"\n"
|
|
"\n"
|
|
"\n";
|
|
CASSERT(sizeof(g_gp_ea) == sizeof(ea_t));
|
|
CASSERT(sizeof(g_ctbp_ea) == sizeof(ea_t));
|
|
if ( ask_form(form, &g_gp_ea, &g_ctbp_ea) == ASKBTN_YES )
|
|
{
|
|
SAVE:
|
|
if ( idb_loaded )
|
|
save_settings();
|
|
}
|
|
|
|
return IDPOPT_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static const asm_t nec850_asm =
|
|
{
|
|
ASH_HEXF3 | AS_UNEQU | AS_COLON | ASB_BINF4 | AS_N2CHR, // flags
|
|
0, // user flags
|
|
"NEC V850 Assembler", // assembler name
|
|
0, // help
|
|
NULL, // array of automatically generated header lines
|
|
".org", // org directive
|
|
".end", // end directive
|
|
"--", // comment string
|
|
'"', // string delimiter
|
|
'\'', // char delimiter
|
|
"'\"", // special symbols in char and string constants
|
|
".str", // ascii string directive
|
|
".byte", // byte directive
|
|
".hword", // halfword (16 bits) [IDA: word]
|
|
".word", // word (32 bits) [IDA: dword]
|
|
".dword", // doubleword (64 bits) [IDA: qword]
|
|
NULL, // oword (16 bytes)
|
|
".float", // float (4-byte)
|
|
".double", // double (8-byte)
|
|
NULL, // no tbytes
|
|
NULL, // no packreal
|
|
"#d dup(#v)", //".db.#s(b,w) #d,#v"
|
|
".byte (%s) ?", // uninited data (reserve space) ;?
|
|
".set", // 'equ' Used if AS_UNEQU is set
|
|
NULL, // seg prefix
|
|
"PC", // a_curip
|
|
NULL, // returns function header line
|
|
NULL, // returns function footer line
|
|
".globl", // public
|
|
NULL, // weak
|
|
".extern", // extrn
|
|
".comm", // comm
|
|
NULL, // get_type_name
|
|
".align", // align
|
|
'(', // lbrace
|
|
')', // rbrace
|
|
NULL, // mod
|
|
"&", // bit-and
|
|
"|", // or
|
|
"^", // xor
|
|
"!", // not
|
|
"<<", // shl
|
|
">>", // shr
|
|
NULL, // sizeof
|
|
0, // flags2
|
|
NULL, // cmnt2
|
|
NULL, // low8 operation, should contain %s for the operand
|
|
NULL, // high8
|
|
NULL, // low16
|
|
NULL, // high16
|
|
".include %s", // a_include_fmt
|
|
NULL, // if a named item is a structure and displayed
|
|
NULL // 'rva' keyword for image based offsets
|
|
};
|
|
|
|
static const asm_t *const asms[] = { &nec850_asm, NULL };
|
|
|
|
//----------------------------------------------------------------------
|
|
#define FAMILY "NEC/Renesas 850 series:"
|
|
|
|
static const char *const shnames[] =
|
|
{
|
|
"V850",
|
|
"V850E",
|
|
"V850E1",
|
|
"V850E2M",
|
|
"RH850",
|
|
NULL
|
|
};
|
|
|
|
static const char *const lnames[] =
|
|
{
|
|
FAMILY"NEC V850",
|
|
"NEC V850E",
|
|
"NEC/Renesas V850E1/ES",
|
|
"NEC/Renesas V850E2/E2M",
|
|
"Renesas RH850",
|
|
NULL
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
ssize_t idaapi idb_listener_t::on_event(ssize_t code, va_list)
|
|
{
|
|
switch ( code )
|
|
{
|
|
// save database
|
|
case idb_event::closebase:
|
|
case idb_event::savebase:
|
|
pm.save_settings();
|
|
break;
|
|
|
|
case idb_event::segm_moved: // A segment is moved
|
|
// Fix processor dependent address sensitive information
|
|
// {
|
|
// ea_t from = va_arg(va, ea_t);
|
|
// ea_t to = va_arg(va, ea_t);
|
|
// asize_t size = va_arg(va, asize_t);
|
|
// bool changed_netmap = va_argi(va, bool);
|
|
// // adjust gp_ea
|
|
// }
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// This old-style callback only returns the processor module object.
|
|
static ssize_t idaapi notify(void *, int msgid, va_list)
|
|
{
|
|
if ( msgid == processor_t::ev_get_procmod )
|
|
return size_t(new nec850_t);
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
ssize_t idaapi nec850_t::on_event(ssize_t msgid, va_list va)
|
|
{
|
|
int code = 0;
|
|
switch ( msgid )
|
|
{
|
|
case processor_t::ev_init:
|
|
hook_event_listener(HT_IDB, &idb_listener, &LPH);
|
|
inf_set_be(false);
|
|
prog_pointers.create("$ prog pointers");
|
|
break;
|
|
|
|
case processor_t::ev_is_sane_insn:
|
|
{
|
|
const insn_t &insn = *va_arg(va, insn_t *);
|
|
int no_crefs = va_arg(va, int);
|
|
code = nec850_is_sane_insn(insn, no_crefs) == 1 ? 1 : -1;
|
|
break;
|
|
}
|
|
|
|
case processor_t::ev_newprc:
|
|
{
|
|
int procnum = va_arg(va, int);
|
|
// bool keep_cfg = va_argi(va, bool);
|
|
ptype = procnum;
|
|
break;
|
|
}
|
|
case processor_t::ev_term:
|
|
unhook_event_listener(HT_IDB, &idb_listener);
|
|
break;
|
|
case processor_t::ev_ending_undo:
|
|
// restore ptype
|
|
ptype = ph.get_proc_index();
|
|
//fall through
|
|
case processor_t::ev_oldfile:
|
|
// old file loaded
|
|
load_settings();
|
|
break;
|
|
|
|
case processor_t::ev_creating_segm:
|
|
{
|
|
segment_t *s = va_arg(va, segment_t *);
|
|
// Set default value of DS register for all segments
|
|
set_default_dataseg(s->sel);
|
|
}
|
|
break;
|
|
|
|
case processor_t::ev_may_be_func:
|
|
{
|
|
const insn_t &insn = *va_arg(va, insn_t *);
|
|
code = nec850_may_be_func(insn);
|
|
}
|
|
break;
|
|
|
|
case processor_t::ev_is_ret_insn:
|
|
{
|
|
const insn_t &insn = *va_arg(va, insn_t *);
|
|
bool strict = va_argi(va, bool);
|
|
code = nec850_is_return(insn, strict) ? 1 : -1;
|
|
}
|
|
break;
|
|
|
|
case processor_t::ev_out_header:
|
|
{
|
|
outctx_t *ctx = va_arg(va, outctx_t *);
|
|
nec850_header(*ctx);
|
|
return 1;
|
|
}
|
|
|
|
case processor_t::ev_out_footer:
|
|
{
|
|
outctx_t *ctx = va_arg(va, outctx_t *);
|
|
nec850_footer(*ctx);
|
|
return 1;
|
|
}
|
|
|
|
case processor_t::ev_out_segstart:
|
|
{
|
|
outctx_t *ctx = va_arg(va, outctx_t *);
|
|
segment_t *seg = va_arg(va, segment_t *);
|
|
nec850_segstart(*ctx, seg);
|
|
return 1;
|
|
}
|
|
|
|
case processor_t::ev_out_segend:
|
|
{
|
|
outctx_t *ctx = va_arg(va, outctx_t *);
|
|
segment_t *seg = va_arg(va, segment_t *);
|
|
nec850_segend(*ctx, seg);
|
|
return 1;
|
|
}
|
|
|
|
case processor_t::ev_ana_insn:
|
|
{
|
|
insn_t *out = va_arg(va, insn_t *);
|
|
return nec850_ana(out);
|
|
}
|
|
|
|
case processor_t::ev_emu_insn:
|
|
{
|
|
const insn_t *insn = va_arg(va, const insn_t *);
|
|
return nec850_emu(*insn) ? 1 : -1;
|
|
}
|
|
|
|
case processor_t::ev_out_insn:
|
|
{
|
|
outctx_t *ctx = va_arg(va, outctx_t *);
|
|
out_insn(*ctx);
|
|
return 1;
|
|
}
|
|
|
|
case processor_t::ev_out_operand:
|
|
{
|
|
outctx_t *ctx = va_arg(va, outctx_t *);
|
|
const op_t *op = va_arg(va, const op_t *);
|
|
return out_opnd(*ctx, *op) ? 1 : -1;
|
|
}
|
|
|
|
case processor_t::ev_is_switch:
|
|
{
|
|
switch_info_t *si = va_arg(va, switch_info_t *);
|
|
const insn_t *insn = va_arg(va, const insn_t *);
|
|
return nec850_is_switch(si, *insn) ? 1 : 0;
|
|
}
|
|
|
|
case processor_t::ev_is_sp_based:
|
|
{
|
|
int *mode = va_arg(va, int *);
|
|
const insn_t *insn = va_arg(va, const insn_t *);
|
|
const op_t *op = va_arg(va, const op_t *);
|
|
*mode = nec850_is_sp_based(*insn, *op);
|
|
return 1;
|
|
}
|
|
|
|
case processor_t::ev_create_func_frame:
|
|
{
|
|
func_t *pfn = va_arg(va, func_t *);
|
|
nec850_create_func_frame(pfn);
|
|
return 1;
|
|
}
|
|
|
|
case processor_t::ev_get_frame_retsize:
|
|
{
|
|
int *frsize = va_arg(va, int *);
|
|
const func_t *pfn = va_arg(va, const func_t *);
|
|
*frsize = nec850_get_frame_retsize(pfn);
|
|
return 1;
|
|
}
|
|
|
|
case processor_t::ev_set_idp_options:
|
|
{
|
|
const char *keyword = va_arg(va, const char *);
|
|
int value_type = va_arg(va, int);
|
|
const char *value = va_arg(va, const char *);
|
|
const char **errmsg = va_arg(va, const char **);
|
|
bool idb_loaded = va_argi(va, bool);
|
|
const char *ret = set_idp_options(keyword, value_type, value, idb_loaded);
|
|
if ( ret == IDPOPT_OK )
|
|
return 1;
|
|
if ( errmsg != NULL )
|
|
*errmsg = ret;
|
|
return -1;
|
|
}
|
|
|
|
// START OF DEBUGGER CALLBACKS
|
|
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 = nec850_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 = nec850_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 nec850_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 nec850_get_reg_info(main_regname, bitrange, regname) ? 1 : -1;
|
|
}
|
|
// END OF DEBUGGER CALLBACKS
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return code;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
// Registers Definition
|
|
//-----------------------------------------------------------------------
|
|
const char *const RegNames[rLastRegister] =
|
|
{
|
|
"r0",
|
|
"r1",
|
|
"r2",
|
|
"sp",
|
|
"gp",
|
|
"r5", // text pointer - tp
|
|
"r6",
|
|
"r7",
|
|
"r8",
|
|
"r9",
|
|
"r10",
|
|
"r11",
|
|
"r12",
|
|
"r13",
|
|
"r14",
|
|
"r15",
|
|
"r16",
|
|
"r17",
|
|
"r18",
|
|
"r19",
|
|
"r20",
|
|
"r21",
|
|
"r22",
|
|
"r23",
|
|
"r24",
|
|
"r25",
|
|
"r26",
|
|
"r27",
|
|
"r28",
|
|
"r29",
|
|
"ep",
|
|
"lp",
|
|
// system registers start here
|
|
"eipc",
|
|
"eipsw",
|
|
"fepc",
|
|
"fepsw",
|
|
"ecr",
|
|
"psw",
|
|
"sr6",
|
|
"sr7",
|
|
"sr8",
|
|
"sr9",
|
|
"sr10",
|
|
"sr11",
|
|
"sr12",
|
|
"sr13",
|
|
"sr14",
|
|
"sr15",
|
|
"sr16",
|
|
"sr17",
|
|
"sr18",
|
|
"sr19",
|
|
"sr20",
|
|
"sr21",
|
|
"sr22",
|
|
"sr23",
|
|
"sr24",
|
|
"sr25",
|
|
"sr26",
|
|
"sr27",
|
|
"sr28",
|
|
"sr29",
|
|
"sr30",
|
|
"sr31",
|
|
|
|
"EFG", "ECT",
|
|
|
|
//
|
|
"ep", "cs", "ds"
|
|
};
|
|
CASSERT(qnumber(RegNames) == rLastRegister);
|
|
|
|
//-----------------------------------------------------------------------
|
|
// Processor Definition
|
|
//-----------------------------------------------------------------------
|
|
processor_t LPH =
|
|
{
|
|
IDP_INTERFACE_VERSION, // version
|
|
PLFM_NEC_V850X, // id
|
|
// flag
|
|
PR_DEFSEG32
|
|
| PR_USE32
|
|
| PRN_HEX
|
|
| PR_RNAMESOK,
|
|
// flag2
|
|
PR2_IDP_OPTS, // the module has processor-specific configuration options
|
|
8, // 8 bits in a byte for code segments
|
|
8, // 8 bits in a byte for other segments
|
|
|
|
shnames, // short processor names
|
|
lnames, // long processor names
|
|
|
|
asms, // assemblers
|
|
|
|
notify,
|
|
|
|
RegNames, // Regsiter names
|
|
rLastRegister, // Number of registers
|
|
|
|
rVcs/*rVep*/, // number of first segment register
|
|
rVds/*rVcs*/, // number of last segment register
|
|
0 /*4*/, // size of a segment register
|
|
rVcs,
|
|
rVds,
|
|
NULL, // No known code start sequences
|
|
NULL, // Array of 'return' instruction opcodes
|
|
NEC850_NULL,
|
|
NEC850_LAST_INSTRUCTION,
|
|
Instructions, // instruc
|
|
0, // size of tbyte
|
|
{0,7,15,0}, // real width
|
|
0, // icode_return
|
|
NULL, // Micro virtual machine description
|
|
};
|