376 lines
9.8 KiB
C++
376 lines
9.8 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"
|
|
|
|
//------------------------------------------------------------------------
|
|
static void set_immd_bit(const insn_t &insn, int n, flags_t F)
|
|
{
|
|
set_immd(insn.ea);
|
|
if ( is_defarg(F, n) )
|
|
return;
|
|
switch ( insn.itype )
|
|
{
|
|
case TMS6_mvk:
|
|
if ( is_mvk_scst16_form(insn.ea) )
|
|
{
|
|
op_hex(insn.ea, n);
|
|
break;
|
|
}
|
|
// fallthrough for scst5 form
|
|
case TMS6_addk:
|
|
case TMS6_and: // Rd = Op1 & Op2
|
|
case TMS6_xor: // Rd = Op1 ^ Op2
|
|
case TMS6_or: // Rd = Op2 | Op1
|
|
case TMS6_cmpeq:
|
|
case TMS6_cmpgt:
|
|
case TMS6_cmplt:
|
|
case TMS6_mpy:
|
|
case TMS6_mpyi:
|
|
case TMS6_mpyid:
|
|
case TMS6_mpysu:
|
|
case TMS6_sadd:
|
|
case TMS6_ssub:
|
|
case TMS6_sub:
|
|
case TMS6_set: // Rd = Op1 & ~Op2
|
|
case TMS6_clr: // Rd = Op1 & ~Op2
|
|
case TMS6_ext: // Rd = Op1 & ~Op2
|
|
case TMS6_extu: // Rd = Op1 & ~Op2
|
|
op_dec(insn.ea, n);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static void handle_operand(const insn_t &insn, const op_t &x, flags_t F, bool isload)
|
|
{
|
|
switch ( x.type )
|
|
{
|
|
case o_regpair:
|
|
case o_reg:
|
|
case o_phrase:
|
|
case o_spmask:
|
|
case o_stgcyc:
|
|
break;
|
|
case o_imm:
|
|
if ( !isload )
|
|
goto badTouch;
|
|
/* no break */
|
|
case o_displ:
|
|
set_immd_bit(insn, x.n, F);
|
|
if ( op_adds_xrefs(F, x.n) )
|
|
{
|
|
int outf = x.type != o_imm ? OOF_ADDR : 0;
|
|
if ( x.dtype == dt_word )
|
|
outf |= OOF_SIGNED;
|
|
insn.add_off_drefs(x, dr_O, outf);
|
|
}
|
|
break;
|
|
case o_near:
|
|
{
|
|
ea_t ea = to_ea(insn.cs, x.addr);
|
|
ea_t ref = find_first_insn_in_packet(ea);
|
|
insn.add_cref(ref, x.offb, fl_JN);
|
|
}
|
|
break;
|
|
default:
|
|
badTouch:
|
|
INTERR(10380);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
ea_t find_first_insn_in_packet(ea_t ea)
|
|
{
|
|
if ( !is_spec_ea(ea) )
|
|
{
|
|
while ( (ea & 0x1F) != 0 )
|
|
{
|
|
ea_t ea2 = prev_not_tail(ea);
|
|
if ( ea2 == BADADDR
|
|
|| !is_code(get_flags(ea2))
|
|
|| (get_dword(ea2) & BIT0) == 0 )
|
|
{
|
|
break;
|
|
}
|
|
ea = ea2;
|
|
}
|
|
}
|
|
return ea;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline bool is_tms6_nop(uint32 code)
|
|
{
|
|
return (code & 0x21FFEL) == 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline bool is_tms6_bnop(uint32 code)
|
|
{
|
|
return (code & 0x00001FFC) == 0x00000120 // Branch Using a Displacement With NOP
|
|
|| (code & 0x0F830FFE) == 0x00800362; // Branch Using a Register With NOP
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static int get_delay(uint32 code)
|
|
{
|
|
if ( is_tms6_nop(code) ) // NOP
|
|
return int((code >> 13) & 0xF) + 1;
|
|
if ( is_tms6_bnop(code) ) // BNOP
|
|
return int((code >> 13) & 0x7);
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
struct call_info_t
|
|
{
|
|
ea_t mvk;
|
|
ea_t mvkh;
|
|
uint32 next;
|
|
int reg;
|
|
call_info_t(ea_t n) : mvk(BADADDR), mvkh(BADADDR), next(n), reg(rB3) {}
|
|
bool call_is_present(void) const { return mvk != BADADDR && mvkh != BADADDR; }
|
|
void test(ea_t ea, uint32 code);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
inline ushort get_mvk_op(uint32 code) { return ushort(code >> 7); }
|
|
|
|
void call_info_t::test(ea_t ea, uint32 code)
|
|
{
|
|
if ( (code & 0xF000007CL) == 0x28 && mvk == BADADDR )
|
|
{ // unconditional MVK.S
|
|
int mvk_reg = int(code >> 23) & 0x1F;
|
|
if ( code & BIT1 )
|
|
mvk_reg += rB0;
|
|
if ( (reg == -1 || reg == mvk_reg) && ushort(next) == get_mvk_op(code) )
|
|
{
|
|
reg = mvk_reg;
|
|
mvk = ea;
|
|
}
|
|
}
|
|
else if ( (code & 0xF000007CL) == 0x68 && mvkh == BADADDR )
|
|
{ // unconditional MVKH.S
|
|
int mvk_reg = int(code >> 23) & 0x1F;
|
|
if ( code & BIT1 )
|
|
mvk_reg += rB0;
|
|
if ( (reg == -1 || reg == mvk_reg) && ushort(next>>16) == get_mvk_op(code) )
|
|
{
|
|
reg = mvk_reg;
|
|
mvkh = ea;
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static int calc_packet_delay(ea_t ea, call_info_t *ci)
|
|
{
|
|
int delay = 1;
|
|
while ( true )
|
|
{
|
|
uint32 code = get_dword(ea);
|
|
int d2 = get_delay(code);
|
|
if ( d2 > delay )
|
|
delay = d2;
|
|
ci->test(ea, code);
|
|
if ( (code & BIT0) == 0 )
|
|
break;
|
|
ea += 4;
|
|
if ( !is_code(get_flags(ea)) )
|
|
break;
|
|
}
|
|
return delay;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static ea_t find_prev_packet(ea_t ea)
|
|
{
|
|
ea_t res = BADADDR;
|
|
while ( 1 )
|
|
{
|
|
ea_t ea2 = prev_not_tail(res != BADADDR ? res : ea);
|
|
if ( ea2 == BADADDR )
|
|
break;
|
|
if ( !is_code(get_flags(ea2)) )
|
|
break;
|
|
res = ea2;
|
|
if ( (get_dword(ea2) & BIT0) == 0 )
|
|
break;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static ea_t get_branch_ea(ea_t ea)
|
|
{
|
|
while ( 1 )
|
|
{
|
|
uint32 code = get_dword(ea);
|
|
if ( (code >> 28) == cAL )
|
|
{
|
|
switch ( (code >> 2) & 0x1F )
|
|
{
|
|
case 0x04: // bcond()
|
|
return ea;
|
|
case 0x08: // S unit
|
|
case 0x18:
|
|
{
|
|
int opcode = int(code >> 6) & 0x3F;
|
|
switch ( opcode )
|
|
{
|
|
case 0: // bdec/bpos
|
|
case 3: // b irp
|
|
case 4: // bnop
|
|
case 13: // b
|
|
return ea;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if ( (code & BIT0) == 0 )
|
|
break;
|
|
ea += 4;
|
|
if ( !is_code(get_flags(ea)) )
|
|
break;
|
|
}
|
|
return BADADDR;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int tms6_t::emu(const insn_t &insn)
|
|
{
|
|
uint32 Feature = insn.get_canon_feature(ph);
|
|
flow = ((Feature & CF_STOP) == 0);
|
|
|
|
if ( segtype(insn.ea) == SEG_XTRN )
|
|
{
|
|
flow = false;
|
|
}
|
|
else if ( (insn.cflags & aux_para) == 0 ) // the last instruction of packet
|
|
{
|
|
// From spru732j.pdf:
|
|
// Although branch instructions take one execute phase, there are five
|
|
// delay slots between the execution of the branch and execution of the
|
|
// target code.
|
|
|
|
// We look backwards for five delay slots to check for an unconditionnal
|
|
// branch instruction.
|
|
|
|
ea_t ea = find_first_insn_in_packet(insn.ea);
|
|
int delay = 0;
|
|
call_info_t ci(insn.ea+insn.size);
|
|
while ( 1 )
|
|
{
|
|
// If there are any crefs to this address, we cannot guarantee that
|
|
// the branch instruction really got executed.
|
|
if ( has_xref(get_flags(ea)) )
|
|
break;
|
|
|
|
// Increment delay count for this packet.
|
|
delay += calc_packet_delay(ea, &ci);
|
|
if ( delay > 5 )
|
|
break;
|
|
|
|
// Unless we have a bnop instruction, seek to the previous packet.
|
|
bool is_bnop = is_tms6_bnop(get_dword(ea));
|
|
if ( !is_bnop )
|
|
{
|
|
ea = find_prev_packet(ea);
|
|
if ( ea == BADADDR )
|
|
break;
|
|
ea = find_first_insn_in_packet(ea);
|
|
}
|
|
|
|
ea_t brea;
|
|
if ( delay == 5 && (brea=get_branch_ea(ea)) != BADADDR )
|
|
{
|
|
// We seeked to the previous packet and it was a bnop. The check
|
|
// for delay == 5 is no longer correct, since we did not take into
|
|
// account the delays of the bnop instruction itself.
|
|
if ( is_tms6_bnop(get_dword(ea)) && !is_bnop )
|
|
break;
|
|
|
|
insn_t brins;
|
|
calc_packet_delay(ea, &ci); // just to test for MVK/MVKH
|
|
bool iscall = ci.call_is_present();
|
|
decode_insn(&brins, brea);
|
|
tgtinfo_t tgt;
|
|
if ( brins.Op1.type == o_near )
|
|
{
|
|
ea_t target = to_ea(brins.cs, brins.Op1.addr);
|
|
if ( iscall )
|
|
{
|
|
target = find_first_insn_in_packet(target);
|
|
brins.add_cref(target, brins.Op1.offb, fl_CN);
|
|
if ( !func_does_return(target) )
|
|
flow = false;
|
|
}
|
|
tgt.type = iscall ? tgtinfo_t::CALL : tgtinfo_t::BRANCH;
|
|
tgt.target = target;
|
|
}
|
|
else
|
|
{
|
|
tgt.type = iscall ? tgtinfo_t::IND_CALL : tgtinfo_t::IND_BRANCH;
|
|
}
|
|
if ( !iscall )
|
|
flow = false;
|
|
tgt.save_to_idb(*this, insn.ea);
|
|
if ( iscall )
|
|
{
|
|
if ( !is_off0(get_flags(ci.mvk)) )
|
|
op_offset(ci.mvk, 0, REF_LOW16, ci.next, brins.cs, 0);
|
|
if ( !is_off0(get_flags(ci.mvkh)) )
|
|
op_offset(ci.mvkh, 0, REF_HIGH16, ci.next, brins.cs, 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// We don't check past one bnop instruction.
|
|
if ( is_bnop )
|
|
break;
|
|
}
|
|
}
|
|
|
|
flags_t F = get_flags(insn.ea);
|
|
if ( Feature & CF_USE1 ) handle_operand(insn, insn.Op1, F, true);
|
|
if ( Feature & CF_USE2 ) handle_operand(insn, insn.Op2, F, true);
|
|
if ( Feature & CF_USE3 ) handle_operand(insn, insn.Op3, F, true);
|
|
|
|
if ( Feature & CF_CHG1 ) handle_operand(insn, insn.Op1, F, false);
|
|
if ( Feature & CF_CHG2 ) handle_operand(insn, insn.Op2, F, false);
|
|
if ( Feature & CF_CHG3 ) handle_operand(insn, insn.Op3, F, false);
|
|
|
|
if ( flow )
|
|
add_cref(insn.ea, insn.ea + insn.size, fl_F);
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int idaapi is_align_insn(ea_t ea)
|
|
{
|
|
insn_t insn;
|
|
decode_insn(&insn, ea);
|
|
switch ( insn.itype )
|
|
{
|
|
case TMS6_mv:
|
|
if ( insn.Op1.reg == insn.Op2.reg )
|
|
break;
|
|
default:
|
|
return 0;
|
|
case TMS6_nop:
|
|
break;
|
|
}
|
|
return insn.size;
|
|
}
|