Files
sigmaker-ida/idasdk76/module/tms320c6/emu.cpp
2021-10-31 21:20:46 +02:00

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;
}