506 lines
12 KiB
C++
506 lines
12 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"
|
|
|
|
// simple wrapper class for syntactic sugar of member functions
|
|
// this class may have only simple member functions.
|
|
// virtual functions and data fields are forbidden, otherwise the class
|
|
// layout may change
|
|
class out_tms320c6_t : public outctx_t
|
|
{
|
|
out_tms320c6_t(void) = delete; // not used
|
|
public:
|
|
bool out_operand(const op_t &x);
|
|
void out_insn(void);
|
|
void outreg(int r) { out_register(ph.reg_names[r]); }
|
|
void out_pre_mode(int mode);
|
|
void out_post_mode(int mode);
|
|
void print_stg_cyc(ea_t ea, int stgcyc);
|
|
bool tms6_out_name_expr(const op_t &x, uval_t opval);
|
|
};
|
|
CASSERT(sizeof(out_tms320c6_t) == sizeof(outctx_t));
|
|
|
|
DECLARE_OUT_FUNCS_WITHOUT_OUTMNEM(out_tms320c6_t)
|
|
|
|
//----------------------------------------------------------------------
|
|
static bool is_first_insn_in_exec_packet(ea_t ea)
|
|
{
|
|
// if ( (ea & 0x1F) == 0 )
|
|
// return 1;
|
|
ea = prev_not_tail(ea);
|
|
return ea == BADADDR
|
|
|| !is_code(get_flags(ea))
|
|
|| (get_dword(ea) & BIT0) == 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static bool prev_complex(const insn_t &insn)
|
|
{
|
|
ea_t ea = prev_not_tail(insn.ea);
|
|
if ( ea == BADADDR || !is_code(get_flags(ea)) )
|
|
return 0;
|
|
return !is_first_insn_in_exec_packet(ea);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void out_tms320c6_t::out_pre_mode(int mode)
|
|
{
|
|
out_symbol('*');
|
|
switch ( mode )
|
|
{
|
|
case 0x08: // 1000 *--R[cst]
|
|
case 0x0C: // 1100 *--Rb[Ro]
|
|
out_symbol('-');
|
|
// fallthrough
|
|
case 0x00: // 0000 *-R[cst]
|
|
case 0x04: // 0100 *-Rb[Ro]
|
|
out_symbol('-');
|
|
break;
|
|
case 0x09: // 1001 *++R[cst]
|
|
case 0x0D: // 1101 *++Rb[Ro]
|
|
out_symbol('+');
|
|
out_symbol('+');
|
|
break;
|
|
case 0x01: // 0001 *+R[cst]
|
|
case 0x05: // 0101 *+Rb[Ro]
|
|
// out_symbol('+');
|
|
break;
|
|
case 0x0A: // 1010 *R--[cst]
|
|
case 0x0B: // 1011 *R++[cst]
|
|
case 0x0E: // 1110 *Rb--[Ro]
|
|
case 0x0F: // 1111 *Rb++[Ro]
|
|
break;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void out_tms320c6_t::out_post_mode(int mode)
|
|
{
|
|
switch ( mode )
|
|
{
|
|
case 0x08: // 1000 *--R[cst]
|
|
case 0x0C: // 1100 *--Rb[Ro]
|
|
case 0x00: // 0000 *-R[cst]
|
|
case 0x04: // 0100 *-Rb[Ro]
|
|
case 0x09: // 1001 *++R[cst]
|
|
case 0x0D: // 1101 *++Rb[Ro]
|
|
case 0x01: // 0001 *+R[cst]
|
|
case 0x05: // 0101 *+Rb[Ro]
|
|
break;
|
|
case 0x0A: // 1010 *R--[cst]
|
|
case 0x0E: // 1110 *Rb--[Ro]
|
|
out_symbol('-');
|
|
out_symbol('-');
|
|
break;
|
|
case 0x0B: // 1011 *R++[cst]
|
|
case 0x0F: // 1111 *Rb++[Ro]
|
|
out_symbol('+');
|
|
out_symbol('+');
|
|
break;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
struct ii_info_t
|
|
{
|
|
char ii;
|
|
char cyc;
|
|
};
|
|
|
|
static const ii_info_t ii_info[] =
|
|
{
|
|
{ 1, 0 },
|
|
{ 2, 1 },
|
|
{ 4, 2 },
|
|
{ 8, 3 },
|
|
{ 14, 4 },
|
|
};
|
|
|
|
void out_tms320c6_t::print_stg_cyc(ea_t ea, int stgcyc)
|
|
{
|
|
int ii = 1;
|
|
insn_t prev;
|
|
for ( int i=0; i < 14 && decode_prev_insn(&prev, ea) != BADADDR; i++ )
|
|
{
|
|
if ( prev.itype == TMS6_sploop
|
|
|| prev.itype == TMS6_sploopd
|
|
|| prev.itype == TMS6_sploopw )
|
|
{
|
|
ii = prev.Op1.value;
|
|
break;
|
|
}
|
|
ea = prev.ea;
|
|
}
|
|
for ( int i=0; i < qnumber(ii_info); i++ )
|
|
{
|
|
if ( ii_info[i].ii >= ii )
|
|
{
|
|
int cyc = ii_info[i].cyc;
|
|
int stg = 0;
|
|
int stgbits = 6 - cyc;
|
|
int bit = 1 << cyc;
|
|
for ( int j=0; j < stgbits; j++, bit<<=1 )
|
|
{
|
|
stg <<= 1;
|
|
if ( stgcyc & bit )
|
|
stg |= 1;
|
|
}
|
|
cyc = stgcyc & ((1<<cyc)-1);
|
|
out_long(stg, 10);
|
|
out_symbol(',');
|
|
out_long(cyc, 10);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool out_tms320c6_t::tms6_out_name_expr(const op_t &x, uval_t opval)
|
|
{
|
|
ea_t ea = to_ea(insn.cs, opval);
|
|
ea_t safe = find_first_insn_in_packet(ea);
|
|
adiff_t delta = ea - safe;
|
|
if ( !out_name_expr(x, safe, opval - delta) )
|
|
return false;
|
|
if ( delta > 0 )
|
|
{
|
|
out_symbol('+');
|
|
out_long(delta, 16);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool out_tms320c6_t::out_operand(const op_t &x)
|
|
{
|
|
switch ( x.type )
|
|
{
|
|
case o_void:
|
|
return 0;
|
|
|
|
case o_reg:
|
|
outreg(x.reg);
|
|
break;
|
|
|
|
case o_regpair:
|
|
outreg(x.reg + 1);
|
|
out_symbol(':');
|
|
outreg(x.reg);
|
|
break;
|
|
|
|
case o_imm:
|
|
{
|
|
uchar sign = insn.itype == TMS6_mvkh
|
|
|| insn.itype == TMS6_mvklh
|
|
|| (insn.itype == TMS6_mvk && is_mvk_scst16_form(insn.ea))
|
|
? 0
|
|
: OOF_SIGNED;
|
|
out_value(x, OOFS_IFSIGN|OOFW_IMM|sign);
|
|
break;
|
|
}
|
|
|
|
case o_stgcyc:
|
|
print_stg_cyc(insn.ea, x.value);
|
|
break;
|
|
|
|
case o_near:
|
|
if ( !tms6_out_name_expr(x, x.addr) )
|
|
{
|
|
out_tagon(COLOR_ERROR);
|
|
out_btoa(x.addr, 16);
|
|
out_tagoff(COLOR_ERROR);
|
|
remember_problem(PR_NONAME, insn.ea);
|
|
}
|
|
break;
|
|
|
|
case o_phrase:
|
|
out_pre_mode(x.mode);
|
|
outreg(x.reg);
|
|
out_post_mode(x.mode);
|
|
out_symbol('[');
|
|
outreg(x.secreg);
|
|
out_symbol(']');
|
|
break;
|
|
|
|
case o_displ:
|
|
out_pre_mode(x.mode);
|
|
outreg(x.reg);
|
|
out_post_mode(x.mode);
|
|
{
|
|
if ( x.addr != 0 || is_off(F, x.n) )
|
|
{
|
|
if ( is_off(F, x.n) )
|
|
{
|
|
out_symbol('(');
|
|
out_value(x, OOF_ADDR|OOFS_IFSIGN|OOFW_IMM|OOF_SIGNED|OOFW_32);
|
|
out_symbol(')');
|
|
}
|
|
else
|
|
{
|
|
out_symbol('[');
|
|
out_value(x, OOF_ADDR|OOFS_IFSIGN|OOFW_IMM|OOF_SIGNED|OOFW_32);
|
|
out_symbol(']');
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case o_spmask:
|
|
{
|
|
static const char units[] = "LLSSDDMM";
|
|
uchar mask = x.reg;
|
|
bool need_comma = false;
|
|
for ( int i=0; i < 8; i++, mask>>=1 )
|
|
{
|
|
if ( mask & 1 )
|
|
{
|
|
if ( need_comma )
|
|
out_symbol(',');
|
|
out_tagon(COLOR_KEYWORD);
|
|
out_char(units[i]);
|
|
out_char('1'+(i&1));
|
|
out_tagoff(COLOR_KEYWORD);
|
|
need_comma = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
warning("out: %a: bad optype %d", insn.ea, x.type);
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void out_tms320c6_t::out_insn(void)
|
|
{
|
|
//
|
|
// Parallel instructions
|
|
//
|
|
ea_t ea = insn.ea;
|
|
if ( !is_first_insn_in_exec_packet(ea) )
|
|
{
|
|
out_symbol('|');
|
|
out_symbol('|');
|
|
}
|
|
else
|
|
{
|
|
if ( !has_any_name(F)
|
|
&& (prev_complex(insn) || insn.cflags & aux_para) )
|
|
{
|
|
gen_empty_line();
|
|
}
|
|
out_char(' ');
|
|
out_char(' ');
|
|
}
|
|
|
|
//
|
|
// Condition code
|
|
//
|
|
static const char *const conds[] =
|
|
{
|
|
" ", " ", "[B0] ", "[!B0]",
|
|
"[B1] ", "[!B1]", "[B2] ", "[!B2]",
|
|
"[A1] ", "[!A1]", "[A2] ", "[!A2]",
|
|
"[A0] ", "[!A0]", " ", " "
|
|
};
|
|
out_keyword(conds[insn.cond]);
|
|
out_char(' ');
|
|
|
|
//
|
|
// Instruction name
|
|
//
|
|
out_mnemonic();
|
|
//
|
|
// Functional unit
|
|
//
|
|
static const char *const units[] =
|
|
{
|
|
NULL,
|
|
".L1", ".L2",
|
|
".S1", ".S2",
|
|
".M1", ".M2",
|
|
".D1", ".D2",
|
|
};
|
|
if ( insn.funit != FU_NONE )
|
|
out_keyword(units[uchar(insn.funit)]);
|
|
else
|
|
out_line(" ");
|
|
if ( insn.cflags & aux_xp )
|
|
out_keyword("X");
|
|
else
|
|
out_char(' ');
|
|
out_line(" ");
|
|
|
|
//
|
|
// Operands
|
|
//
|
|
if ( (insn.cflags & aux_src2) != 0 )
|
|
{
|
|
outreg(insn.Op1.src2);
|
|
out_symbol(',');
|
|
out_char(' ');
|
|
}
|
|
|
|
if ( insn.Op1.shown() )
|
|
out_one_operand(0);
|
|
|
|
if ( insn.Op2.type != o_void && insn.Op2.shown() )
|
|
{
|
|
out_symbol(',');
|
|
out_char(' ');
|
|
out_one_operand(1);
|
|
}
|
|
|
|
|
|
if ( insn.Op3.type != o_void && insn.Op3.shown() )
|
|
{
|
|
out_symbol(',');
|
|
out_char(' ');
|
|
out_one_operand(2);
|
|
}
|
|
|
|
out_immchar_cmts();
|
|
|
|
int indent = inf_get_indent() - 8; // reserve space for conditions
|
|
if ( indent <= 1 ) // too little space?
|
|
indent = 2; // pass -2, which means one space
|
|
// (-1 would mean 'use DEFAULT_INDENT')
|
|
flush_outbuf(-indent); // negative value means 'print opcodes here'
|
|
|
|
if ( (insn.cflags & aux_para) == 0 )
|
|
{
|
|
tms6_t &pm = *static_cast<tms6_t *>(procmod);
|
|
tgtinfo_t tgt;
|
|
if ( tgt.restore_from_idb(pm, ea) )
|
|
{
|
|
qstring buf = tgt.get_type_name();
|
|
if ( tgt.has_target() )
|
|
{
|
|
qstring name = get_colored_name(tgt.target);
|
|
buf.append(" ");
|
|
buf.append(name);
|
|
}
|
|
gen_printf(DEFAULT_INDENT,
|
|
COLSTR("; %s OCCURS", SCOLOR_AUTOCMT),
|
|
buf.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
//lint -e{818} seg could be const
|
|
void idaapi segstart(outctx_t &ctx, segment_t *seg)
|
|
{
|
|
if ( is_spec_segm(seg->type) )
|
|
return;
|
|
|
|
qstring sname;
|
|
get_segm_name(&sname, seg);
|
|
|
|
if ( sname == ".bss" )
|
|
return;
|
|
if ( sname == ".text" || sname == ".data" )
|
|
{
|
|
ctx.gen_printf(DEFAULT_INDENT, COLSTR("%s", SCOLOR_ASMDIR), sname.c_str());
|
|
}
|
|
else
|
|
{
|
|
validate_name(&sname, VNT_IDENT);
|
|
ctx.gen_printf(DEFAULT_INDENT, COLSTR(".sect \"%s\"", SCOLOR_ASMDIR), sname.c_str());
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void idaapi segend(outctx_t &, segment_t *)
|
|
{
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void idaapi header(outctx_t &ctx)
|
|
{
|
|
ctx.gen_header(GH_PRINT_ALL);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void tms6_t::footer(outctx_t &ctx) const
|
|
{
|
|
qstring nbuf = get_colored_name(inf_get_start_ea());
|
|
const char *name = nbuf.c_str();
|
|
const char *end = ash.end;
|
|
if ( end == NULL )
|
|
ctx.gen_printf(DEFAULT_INDENT, COLSTR("%s end %s", SCOLOR_AUTOCMT), ash.cmnt, name);
|
|
else
|
|
ctx.gen_printf(DEFAULT_INDENT, COLSTR("%s", SCOLOR_ASMDIR)
|
|
" "
|
|
COLSTR("%s %s", SCOLOR_AUTOCMT), ash.end, ash.cmnt, name);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void idaapi data(outctx_t &ctx, bool analyze_only)
|
|
{
|
|
ea_t ea = ctx.insn_ea;
|
|
segment_t *s = getseg(ea);
|
|
if ( s != NULL )
|
|
{
|
|
qstring sname;
|
|
if ( get_segm_name(&sname, s) > 0 && sname == ".bss" )
|
|
{
|
|
qstring name;
|
|
if ( get_colored_name(&name, ea) <= 0 )
|
|
name.sprnt(COLSTR("bss_dummy_name_%a", SCOLOR_UNKNAME), ea);
|
|
char num[MAX_NUMBUF];
|
|
btoa(num, sizeof(num), get_item_size(ea), get_radix(ctx.F, 0));
|
|
ctx.ctxflags |= CTXF_LABEL_OK;
|
|
ctx.gen_printf(-1,
|
|
COLSTR(".bss", SCOLOR_KEYWORD)
|
|
" %s, "
|
|
COLSTR("%s", SCOLOR_DNUM),
|
|
name.begin(),
|
|
num);
|
|
return;
|
|
}
|
|
}
|
|
ctx.out_data(analyze_only);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
//lint -e{1764} ctx could be const
|
|
bool tms6_t::outspec(outctx_t &ctx, uchar stype) const
|
|
{
|
|
ea_t ea = ctx.insn_ea;
|
|
qstring nbuf;
|
|
if ( get_colored_name(&nbuf, ea) <= 0 )
|
|
return false;
|
|
const char *name = nbuf.begin();
|
|
char buf[MAX_NUMBUF];
|
|
switch ( stype )
|
|
{
|
|
case SEG_XTRN:
|
|
return ctx.gen_printf(-1, COLSTR("%s %s", SCOLOR_ASMDIR), ash.a_extrn,name);
|
|
case SEG_ABSSYM:
|
|
// i don't know how to declare absolute symbols.
|
|
// perhaps, like this?
|
|
btoa(buf, sizeof(buf), get_dword(ea));
|
|
return ctx.gen_printf(-1, COLSTR("%s = %s", SCOLOR_ASMDIR), name, buf);
|
|
case SEG_COMM:
|
|
btoa(buf, sizeof(buf), get_dword(ea));
|
|
ctx.gen_printf(-1,
|
|
COLSTR("%s \"%s\", %s", SCOLOR_ASMDIR),
|
|
ash.a_comdef, name, buf);
|
|
}
|
|
return false;
|
|
}
|