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

459 lines
12 KiB
C++

/*
* Interactive disassembler (IDA).
* Version 3.05
* Copyright (c) 1990-95 by Ilfak Guilfanov.
* ALL RIGHTS RESERVED.
* FIDO: 2:5020/209
* E-mail: ig@estar.msk.su
*
*/
#include "tms.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_tms320c5_t : public outctx_t
{
out_tms320c5_t(void) = delete; // not used
void set_has_phrase(void) { user_data = (void*)1; }
bool has_phrase() const { return user_data != NULL; }
const tms320c5_t &pm() const { return *static_cast<tms320c5_t *>(procmod); }
public:
bool out_operand(const op_t &x);
void out_insn(void);
void outreg(int r) { out_register(ph.reg_names[r]); }
void OutDecimal(uval_t x);
int outnextar(const op_t &o, bool comma);
bool shouldIndent(void) const;
void outphraseAr(void);
void OutImmVoid(const op_t &x);
};
CASSERT(sizeof(out_tms320c5_t) == sizeof(outctx_t));
DECLARE_OUT_FUNCS_WITHOUT_OUTMNEM(out_tms320c5_t)
//----------------------------------------------------------------------
static const char *const phrases[] =
{
"*", "*-", "*+", "?",
"*br0-", "*0-", "*0+", "*br0+"
};
//----------------------------------------------------------------------
void out_tms320c5_t::OutDecimal(uval_t x)
{
char buf[40];
qsnprintf(buf, sizeof(buf), "%" FMT_EA "u", x);
out_line(buf, COLOR_NUMBER);
}
//----------------------------------------------------------------------
bool is_mpy(const insn_t &insn)
{
switch ( insn.itype )
{
case TMS_mpy: // Multiply
case TMS_mpya: // Multiply and Accumulate Previous Product
case TMS_mpys: // Multiply and Subtract Previous Product
case TMS2_mpy: // Multiply (with T register, store product in P register)
case TMS2_mpya: // Multiply and accumulate previous product
case TMS2_mpyk: // Multiply immediate
case TMS2_mpys: // Multiply and subtract previous product
return true;
}
return false;
}
//----------------------------------------------------------------------
bool out_tms320c5_t::out_operand(const op_t &x)
{
switch ( x.type )
{
case o_reg:
outreg(x.reg);
break;
case o_phrase:
QASSERT(10087, (x.phrase>>4) < qnumber(phrases));
out_line(phrases[x.phrase>>4], COLOR_SYMBOL);
set_has_phrase();
break;
case o_imm:
switch ( x.sib )
{
default:
{
if ( !pm().isC2() )
out_symbol('#');
flags_t saved = F;
if ( !is_defarg(F, x.n)
&& (is_mpy(insn) || is_invsign(insn.ea, F, x.n)) )
{
F |= dec_flag();
}
int outflags = OOFW_16|(is_mpy(insn) ? OOF_SIGNED : 0);
out_value(x, outflags);
F = saved;
}
break;
case 1:
out_value(x, OOF_NUMBER|OOFS_NOSIGN);
break;
case 2:
case 3:
OutDecimal(x.value);
break;
}
break;
case o_near:
if ( insn.itype == TMS_blpd )
out_symbol('#');
// fallthrough
case o_mem:
{
if ( insn.itype == TMS_bldd && x.sib )
out_symbol('#');
ea_t v = map_ea(insn, x, x.type == o_near);
bool rptb_tail = false;
uval_t addr = x.addr;
if ( insn.itype == TMS_rptb && is_tail(get_flags(v)) )
{
// small hack to display end_loop-1 instead of before_end_loop+1
v++;
addr++;
rptb_tail = true;
}
bool ok = out_name_expr(x, v, addr);
if ( !ok )
{
out_value(x, OOF_ADDR|OOF_NUMBER|OOFS_NOSIGN|OOFW_16);
remember_problem(PR_NONAME, insn.ea);
}
else
{
if ( rptb_tail )
{
out_symbol('-');
out_line("1", COLOR_NUMBER);
}
}
}
break;
case o_void:
return 0;
case o_bit:
{
static const char *const bitnames[] =
{
"intm", "ovm", "cnf", "sxm",
"hm", "tc", "xf", "c"
};
out_keyword(bitnames[uchar(x.value)]);
}
break;
case o_cond:
{
int mask = int(x.value>>0) & 0xF;
int cond = int(x.value>>4) & 0xF;
int comma = 1;
out_tagon(COLOR_KEYWORD);
switch ( (mask>>2) & 3 ) // Z L
{
case 0:
comma = 0;
break;
case 1:
out_line((cond>>2)&1 ? "lt" : "gt");
break;
case 2:
out_line((cond>>2)&2 ? "eq" : "neq");
break;
case 3:
switch ( (cond>>2)&3 )
{
case 2: out_line("geq"); break;
case 3: out_line("leq"); break;
}
break;
}
if ( mask & 1 ) // C
{
if ( comma )
out_char(',');
if ( (cond & 1) == 0 )
out_char('n');
out_char('c');
comma = 1;
}
if ( mask & 2 ) // V
{
if ( comma )
out_char(',');
if ( (cond & 2) == 0 )
out_char('n');
out_char('o');
out_char('v');
comma = 1;
}
static const char *const TP[] = { "bio", "tc", "ntc", NULL };
const char *ptr = TP[int(x.value>>8) & 3];
if ( ptr != NULL )
{
if ( comma )
out_char(',');
out_line(ptr);
}
out_tagoff(COLOR_KEYWORD);
}
break;
default:
warning("out: %a: bad optype %d", insn.ea, x.type);
break;
}
return 1;
}
//----------------------------------------------------------------------
int out_tms320c5_t::outnextar(const op_t &o, bool comma)
{
if ( o.type == o_phrase && (o.phrase & 8) != 0 )
{
if ( comma )
{
out_symbol(',');
out_char(' ');
}
outreg(rAr0+(o.phrase&7));
return 1;
}
return 0;
}
//----------------------------------------------------------------------
static int isDelayed(ushort code)
{
// 7D?? BD 0111 1101 1AAA AAAA + 1 Branch unconditional with AR update delayed
// 7E?? CALLD 0111 1110 1AAA AAAA + 1 Call unconditional with AR update delayed
// 7F?? BANZD 0111 1111 1AAA AAAA + 1 Branch AR=0 with AR update delayed
// BE3D CALAD 1011 1110 0011 1101 Call subroutine addressed by ACC delayed
// BE21 BACCD 1011 1110 0010 0001 Branch addressed by ACC delayed
// FF00 RETD 1111 1111 0000 0000 Return, delayed
// F??? CCD 1111 10TP ZLVC ZLVC + 1 Call conditional delayed
// F??? RETCD 1111 11TP ZLVC ZLVC Return conditional delayed
// F??? BCNDD 1111 00TP ZLVC ZLVC + 1 Branch conditional delayed
ushort subcode;
switch ( code>>12 )
{
case 7:
subcode = (code >> 7);
return subcode == 0xFB || subcode == 0xFD || subcode == 0xFF;
case 0xB:
return code == 0xBE21u || code == 0xBE3Du;
case 0xF:
if ( code == 0xFF00 )
return 1;
subcode = (code & 0x0C00);
return subcode != 0x400;
}
return 0;
}
//----------------------------------------------------------------------
ea_t prevInstruction(ea_t ea)
{
ea--;
if ( !is_code(get_flags(ea)) )
ea--;
return ea;
}
//----------------------------------------------------------------------
bool out_tms320c5_t::shouldIndent(void) const
{
if ( pm().isC2() )
return false; // TMS320C2 - no indention
if ( !is_flow(F) )
return false; // no previous instructions
ea_t ea = prevInstruction(insn.ea);
flags_t flags = get_flags(ea);
if ( !is_code(flags) )
return false;
if ( isDelayed((ushort)get_wide_byte(ea)) )
return true;
if ( insn.size == 2 ) // our instruction is long
{
; // nothing to do
}
else
{ // our instruction short
if ( (insn.ea-ea) == 2 ) // prev instruction long
return false; // can't be executed in delayed manner
if ( !is_flow(flags) )
return false; // no prev instr...
ea = prevInstruction(ea);
flags = get_flags(ea);
}
return is_code(flags) && isDelayed((ushort)get_wide_byte(ea));
}
//----------------------------------------------------------------------
void out_tms320c5_t::outphraseAr(void)
{
ea_t ar;
if ( pm().find_ar(insn, &ar) )
{
char buf[MAXSTR];
ea2str(buf, sizeof(buf), ar);
out_printf(COLSTR(" %s(%s)", SCOLOR_AUTOCMT), ash.cmnt, buf);
}
}
//----------------------------------------------------------------------
void out_tms320c5_t::OutImmVoid(const op_t &x)
{
if ( !pm().tmsfunny )
return;
if ( x.type == o_imm )
{
if ( x.value != 0 )
{
int v = int(short(x.value) * 10000L / 0x7FFF);
out_char(' ');
out_tagon(COLOR_AUTOCMT);
out_line(ash.cmnt);
out_char(' ');
if ( v < 0 )
{
out_char('-');
v = -v;
}
char buf[10];
if ( v == 10000 )
qstrncpy(buf, "1.0000", sizeof(buf));
else
qsnprintf(buf, sizeof(buf), "0.%04d", v);
out_line(buf);
out_tagoff(COLOR_AUTOCMT);
}
}
}
//----------------------------------------------------------------------
void out_tms320c5_t::out_insn(void)
{
if ( shouldIndent() )
out_char(' ');
out_mnemonic();
bool comma = insn.Op1.shown() && out_one_operand(0);
if ( insn.Op2.shown() && insn.Op2.type != o_void )
{
if ( comma )
{
out_tagon(COLOR_SYMBOL);
out_char(',');
out_tagoff(COLOR_SYMBOL);
out_char(' ');
}
out_one_operand(1);
}
if ( insn.Op1.type == o_phrase )
if ( outnextar(insn.Op1, comma) )
comma = true;
if ( insn.Op2.type == o_phrase )
outnextar(insn.Op2, comma);
out_immchar_cmts();
if ( has_phrase() )
outphraseAr();
flush_outbuf();
}
//--------------------------------------------------------------------------
void idaapi header(outctx_t &ctx)
{
ctx.gen_header(GH_PRINT_ALL_BUT_BYTESEX);
}
//--------------------------------------------------------------------------
//lint -e{818} seg could be const
void tms320c5_t::segstart(outctx_t &ctx, segment_t *seg) const
{
qstring sname;
get_visible_segm_name(&sname, seg);
ctx.gen_printf(DEFAULT_INDENT, COLSTR(".sect \"%s\"", SCOLOR_ASMDIR), sname.c_str());
if ( (inf_get_outflags() & OFLG_GEN_ORG) != 0 )
{
ea_t org = seg->start_ea - get_segm_base(seg);
if ( org != 0 )
{
char buf[MAX_NUMBUF];
btoa(buf, sizeof(buf), org);
ctx.gen_printf(DEFAULT_INDENT, COLSTR("%s .org %s", SCOLOR_AUTOCMT),
ash.cmnt, buf);
}
}
}
//--------------------------------------------------------------------------
void tms320c5_t::footer(outctx_t &ctx) const
{
if ( ash.end != NULL )
{
ctx.gen_empty_line();
ctx.out_line(ash.end, COLOR_ASMDIR);
qstring name;
if ( get_colored_name(&name, inf_get_start_ea()) > 0 )
{
ctx.out_char(' ');
ctx.out_line(name.begin());
}
ctx.flush_outbuf(DEFAULT_INDENT);
}
else
{
ctx.gen_cmt_line("end of file");
}
}
//--------------------------------------------------------------------------
//lint -e{1764} ctx could be const
void tms320c5_t::tms_assumes(outctx_t &ctx) const
{
ea_t ea = ctx.insn_ea;
segment_t *seg = getseg(ea);
if ( (inf_get_outflags() & OFLG_GEN_ASSUME) == 0 || seg == NULL )
return;
bool seg_started = (ea == seg->start_ea);
if ( seg->type == SEG_XTRN
|| seg->type == SEG_DATA
|| (inf_get_outflags() & OFLG_GEN_ASSUME) == 0 )
{
return;
}
sreg_range_t sra;
if ( !get_sreg_range(&sra, ea, rDP) )
return;
bool show = sra.start_ea == ea;
if ( show )
{
sreg_range_t prev_sra;
if ( get_prev_sreg_range(&prev_sra, ea, rDP) )
show = sra.val != prev_sra.val;
}
if ( seg_started || show )
ctx.gen_printf(DEFAULT_INDENT,
COLSTR("%s --- assume DP %04a", SCOLOR_AUTOCMT),
ash.cmnt, sra.val);
}