416 lines
11 KiB
C++
416 lines
11 KiB
C++
/*
|
|
* Interactive disassembler (IDA).
|
|
* Copyright (c) 1990-98 by Ilfak Guilfanov.
|
|
* ALL RIGHTS RESERVED.
|
|
* E-mail: ig@estar.msk.su
|
|
* FIDO: 2:5020/209
|
|
*
|
|
*
|
|
* TMS320C6xx - VLIW (very long instruction word) architecture
|
|
*
|
|
*/
|
|
|
|
#include "tms6.hpp"
|
|
int data_id;
|
|
|
|
//--------------------------------------------------------------------------
|
|
// B14 - data page pointer
|
|
// B15 - stack pointer
|
|
static const char *const RegNames[] =
|
|
{
|
|
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
|
|
"A8", "A9", "A10", "A11", "A12", "A13", "A14", "A15",
|
|
"A16", "A17", "A18", "A19", "A20", "A21", "A22", "A23",
|
|
"A24", "A25", "A26", "A27", "A28", "A29", "A30", "A31",
|
|
"B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7",
|
|
"B8", "B9", "B10", "B11", "B12", "B13", "B14", "B15",
|
|
"B16", "B17", "B18", "B19", "B20", "B21", "B22", "B23",
|
|
"B24", "B25", "B26", "B27", "B28", "B29", "B30", "B31",
|
|
"AMR",
|
|
"CSR",
|
|
"IFR",
|
|
"ISR",
|
|
"ICR",
|
|
"IER",
|
|
"ISTP",
|
|
"IRP",
|
|
"NRP",
|
|
"ACR", // undocumented, info from Jeff Bailey <jeff_bailey@infinitek.com>
|
|
"ADR", // undocumented, info from Jeff Bailey <jeff_bailey@infinitek.com>
|
|
"PCE1",
|
|
"FADCR",
|
|
"FAUCR",
|
|
"FMCR",
|
|
"TSCL",
|
|
"TSCH",
|
|
"ILC",
|
|
"RILC",
|
|
"REP",
|
|
"DNUM",
|
|
"SSR",
|
|
"GPLYA",
|
|
"GPLYB",
|
|
"GFPGFR",
|
|
"TSR",
|
|
"ITSR",
|
|
"NTSR",
|
|
"ECR",
|
|
"EFR",
|
|
"IERR",
|
|
"CS", "DS"
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
ssize_t idaapi idb_listener_t::on_event(ssize_t code, va_list va)
|
|
{
|
|
switch ( code )
|
|
{
|
|
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);
|
|
if ( !changed_netmap )
|
|
{
|
|
nodeidx_t ndx1 = ea2node(from);
|
|
nodeidx_t ndx2 = ea2node(to);
|
|
pm.helper.altshift(ndx1, ndx2, size);
|
|
// like altadjust()
|
|
for ( nodeidx_t ndx = pm.helper.supfirst();
|
|
ndx != BADADDR;
|
|
ndx = pm.helper.supnext(ndx) )
|
|
{
|
|
tgtinfo_t tgt;
|
|
ea_t ea = node2ea(ndx);
|
|
tgt.restore_from_idb(pm, ea);
|
|
if ( tgt.has_target() )
|
|
{
|
|
tgt.target = correct_address(tgt.target, from, to, size);
|
|
tgt.save_to_idb(pm, ea);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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(SET_MODULE_DATA(tms6_t));
|
|
return 0;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
ssize_t idaapi tms6_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);
|
|
helper.create(PROCMOD_NODE_NAME);
|
|
break;
|
|
|
|
case processor_t::ev_term:
|
|
unhook_event_listener(HT_IDB, &idb_listener);
|
|
clr_module_data(data_id);
|
|
break;
|
|
|
|
case processor_t::ev_oldfile:
|
|
{
|
|
netnode old_tnode("$ tms node");
|
|
if ( old_tnode != BADNODE )
|
|
{
|
|
upgrade_tnode(old_tnode);
|
|
old_tnode.kill();
|
|
}
|
|
}
|
|
// no break
|
|
case processor_t::ev_ending_undo:
|
|
case processor_t::ev_newfile:
|
|
break;
|
|
|
|
case processor_t::ev_out_header:
|
|
{
|
|
outctx_t *ctx = va_arg(va, outctx_t *);
|
|
header(*ctx);
|
|
return 1;
|
|
}
|
|
|
|
case processor_t::ev_out_footer:
|
|
{
|
|
outctx_t *ctx = va_arg(va, outctx_t *);
|
|
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 *);
|
|
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 *);
|
|
segend(*ctx, seg);
|
|
return 1;
|
|
}
|
|
|
|
case processor_t::ev_ana_insn:
|
|
{
|
|
insn_t *out = va_arg(va, insn_t *);
|
|
return ana(out);
|
|
}
|
|
|
|
case processor_t::ev_emu_insn:
|
|
{
|
|
const insn_t *insn = va_arg(va, const insn_t *);
|
|
return 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_out_data:
|
|
{
|
|
outctx_t *ctx = va_arg(va, outctx_t *);
|
|
bool analyze_only = va_argi(va, bool);
|
|
data(*ctx, analyze_only);
|
|
return 1;
|
|
}
|
|
|
|
case processor_t::ev_out_special_item:
|
|
{
|
|
outctx_t *ctx = va_arg(va, outctx_t *);
|
|
uchar seg_type = va_argi(va, uchar);
|
|
outspec(*ctx, seg_type);
|
|
return 1;
|
|
}
|
|
|
|
case processor_t::ev_is_align_insn:
|
|
{
|
|
ea_t ea = va_arg(va, ea_t);
|
|
return is_align_insn(ea);
|
|
}
|
|
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return code;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void tms6_t::upgrade_tnode(const netnode &old_tnode)
|
|
{
|
|
// copy branch/call info to HELPER
|
|
for ( nodeidx_t ndx = old_tnode.altfirst();
|
|
ndx != BADADDR;
|
|
ndx = old_tnode.altnext(ndx) )
|
|
{
|
|
nodeidx_t ndx2 = old_tnode.altval(ndx);
|
|
if ( ndx2 == 0 )
|
|
continue;
|
|
tgtinfo_t tgt;
|
|
switch ( ndx2 )
|
|
{
|
|
case 1:
|
|
tgt.type = tgtinfo_t::IND_BRANCH;
|
|
break;
|
|
case 2:
|
|
tgt.type = tgtinfo_t::IND_CALL;
|
|
break;
|
|
default:
|
|
{
|
|
ea_t target = node2ea(ndx2);
|
|
tgt.type = (target & 1) != 0
|
|
? tgtinfo_t::BRANCH
|
|
: tgtinfo_t::CALL;
|
|
tgt.target = target & ~1;
|
|
}
|
|
break;
|
|
}
|
|
tgt.save_to_idb(*this, node2ea(ndx));
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
const char *tgtinfo_t::get_type_name() const
|
|
{
|
|
switch ( type )
|
|
{
|
|
case tgtinfo_t::CALL: return "CALL";
|
|
case tgtinfo_t::BRANCH: return "BRANCH";
|
|
case tgtinfo_t::IND_CALL: return "INDIRECT CALL";
|
|
case tgtinfo_t::IND_BRANCH: return "INDIRECT BRANCH";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
#define TGTINFO_MAX_SIZE (1 + ea_packed_size)
|
|
void tgtinfo_t::save_to_idb(tms6_t &pm, ea_t ea) const
|
|
{
|
|
uchar buf[TGTINFO_MAX_SIZE];
|
|
uchar *ptr = buf;
|
|
uchar *end = buf + sizeof(buf);
|
|
ptr = pack_db(ptr, end, uchar(type));
|
|
if ( has_target() )
|
|
ptr = pack_ea(ptr, end, ea2node(target));
|
|
pm.helper.supset_ea(ea, buf, ptr - buf);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool tgtinfo_t::restore_from_idb(const tms6_t &pm, ea_t ea)
|
|
{
|
|
uchar buf[TGTINFO_MAX_SIZE];
|
|
ssize_t code = pm.helper.supval_ea(ea, buf, sizeof(buf));
|
|
if ( code < 1 )
|
|
return false;
|
|
memory_deserializer_t mmdsr(buf, code);
|
|
uchar t = mmdsr.unpack_db();
|
|
if ( t > IND_BRANCH )
|
|
return false;
|
|
type = type_t(t);
|
|
if ( has_target() )
|
|
target = node2ea(mmdsr.unpack_ea());
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
// TMS320C6x COFF Assembler
|
|
//-----------------------------------------------------------------------
|
|
static const asm_t dspasm =
|
|
{
|
|
AS_COLON | ASH_HEXF0 | ASD_DECF0 | ASB_BINF0 | ASO_OCTF5,
|
|
0,
|
|
"TMS320C6x COFF Assembler",
|
|
0,
|
|
NULL, // header lines
|
|
NULL, // org
|
|
".end",
|
|
|
|
";", // comment string
|
|
'"', // string delimiter
|
|
'\'', // char delimiter
|
|
"\\\"'", // special symbols in char and string constants
|
|
|
|
".string", // ascii string directive
|
|
".char", // byte directive
|
|
".short", // word directive
|
|
".long", // double words
|
|
NULL, // no qwords
|
|
NULL, // oword (16 bytes)
|
|
NULL, // float (4 bytes)
|
|
NULL, // double (8 bytes)
|
|
NULL, // tbyte (10/12 bytes)
|
|
NULL, // packed decimal real
|
|
NULL, // arrays (#h,#d,#v,#s(...)
|
|
".space %s", // uninited arrays
|
|
".set", // equ
|
|
NULL, // 'seg' prefix (example: push seg seg001)
|
|
"$", // current IP (instruction pointer)
|
|
NULL, // func_header
|
|
NULL, // func_footer
|
|
".def", // "public" name keyword
|
|
NULL, // "weak" name keyword
|
|
".ref", // "extrn" name keyword
|
|
".usect", // "comm" (communal variable)
|
|
NULL, // get_type_name
|
|
".align", // "align" keyword
|
|
'(', ')', // lbrace, rbrace
|
|
NULL, // mod
|
|
"&", // and
|
|
"|", // or
|
|
"^", // xor
|
|
"!", // not
|
|
"<<", // shl
|
|
">>", // shr
|
|
NULL, // sizeof
|
|
};
|
|
|
|
|
|
static const asm_t *const asms[] = { &dspasm, NULL };
|
|
//-----------------------------------------------------------------------
|
|
#define FAMILY "TMS320C6 series:"
|
|
static const char *const shnames[] = { "TMS320C6", NULL };
|
|
static const char *const lnames[] =
|
|
{
|
|
FAMILY"Texas Instruments TMS320C6xxx",
|
|
NULL
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
static const uchar retcode_1[] = { 0x62, 0x63, 0x0C, 0x00 };
|
|
|
|
static const bytes_t retcodes[] =
|
|
{
|
|
{ sizeof(retcode_1), retcode_1 },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
//-----------------------------------------------------------------------
|
|
// Processor Definition
|
|
//-----------------------------------------------------------------------
|
|
processor_t LPH =
|
|
{
|
|
IDP_INTERFACE_VERSION, // version
|
|
PLFM_TMSC6, // id
|
|
// flag
|
|
PR_USE32
|
|
| PR_DEFSEG32
|
|
| PR_DELAYED
|
|
| PR_ALIGN_INSN, // allow align instructions
|
|
// flag2
|
|
0,
|
|
8, // 8 bits in a byte for code segments
|
|
8, // 8 bits in a byte for other segments
|
|
|
|
shnames,
|
|
lnames,
|
|
|
|
asms,
|
|
|
|
notify,
|
|
|
|
RegNames, // Register names
|
|
qnumber(RegNames), // Number of registers
|
|
|
|
rVcs, // first
|
|
rVds, // last
|
|
0, // size of a segment register
|
|
rVcs, rVds,
|
|
|
|
NULL, // No known code start sequences
|
|
retcodes,
|
|
|
|
TMS6_null,
|
|
TMS6_last,
|
|
Instructions, // instruc
|
|
0, // int tbyte_size;
|
|
{ 2, 4, 8, 12 }, // char real_width[4];
|
|
TMS6_null, // Icode of return instruction. It is ok to give any of possible return instructions
|
|
};
|