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

1578 lines
63 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
*
*/
// SWAP4 is not disassembled!
#include "tms6.hpp"
//lint -estring(958,member) padding is required
//lint -e754 local struct member is not referenced
//--------------------------------------------------------------------------
struct tmsinsn_t
{
uint16 itype;
uchar src1;
uchar src2;
uchar dst;
};
// operand types
#define t_none 0
#define t_sint 1
#define t_xsint 2
#define t_uint 3
#define t_xuint 4
#define t_slong 5
#define t_xslong 6
#define t_ulong 7
#define t_xulong 8
#define t_scst5 9
#define t_ucst5 10
#define t_slsb16 11
#define t_xslsb16 12
#define t_ulsb16 13
#define t_xulsb16 14
#define t_smsb16 15
#define t_xsmsb16 16
#define t_umsb16 17
#define t_xumsb16 18
#define t_irp 19
#define t_cregr 20
#define t_cregw 21
#define t_ucst1 22
#define t_dp 23
#define t_xdp 24
#define t_sp 25
#define t_xsp 26
#define t_ucst15 27
#define t_scst7 28
#define t_ucst3 29
#define t_b14 30
#define t_dint 31
#define t_i2 32
#define t_xi2 33
#define t_i4 34
#define t_xi4 35
#define t_s2 36
#define t_xs2 37
#define t_u2 38
#define t_xu2 39
#define t_s4 40
#define t_xs4 41
#define t_u4 42
#define t_xu4 43
#define t_scst10 44
#define t_scst12 45
#define t_scst21 46
#define t_a3 47 // a3 or b3
#define t_bv2 48 // 2 bits
#define t_bv4 49 // 4 bits
#define t_ds2 50
#define t_sllong 51
#define t_ullong 52
#define t_dws4 53
#define t_dwu4 54
//--------------------------------------------------------------------------
static void swap_op1_and_op2(insn_t &insn)
{
if ( (insn.cflags & aux_pseudo) == 0 )
{
op_t tmp = insn.Op1;
insn.Op1 = insn.Op2;
insn.Op2 = tmp;
insn.Op1.n = 0;
insn.Op2.n = 1;
}
}
//--------------------------------------------------------------------------
static void swap_op2_and_op3(insn_t &insn)
{
if ( (insn.cflags & aux_pseudo) == 0 )
{
op_t tmp = insn.Op3;
insn.Op3 = insn.Op2;
insn.Op2 = tmp;
insn.Op2.n = 1;
insn.Op3.n = 2;
}
}
//--------------------------------------------------------------------------
inline int op_spmask(const insn_t &insn, op_t &x, uint32 code)
{
x.type = o_spmask;
x.dtype = dt_dword;
x.reg = (code >> 18) & 0xFF;
return insn.size;
}
//--------------------------------------------------------------------------
inline void op_reg(op_t &x, int reg)
{
x.type = o_reg;
x.dtype = dt_dword;
x.reg = reg;
}
//--------------------------------------------------------------------------
inline void op_ucst15(op_t &x, uint32 code)
{
x.type = o_imm;
x.dtype = dt_dword;
x.value = (code >> 8) & 0x7FFF;
}
//--------------------------------------------------------------------------
inline bool second_unit(const insn_t &insn)
{
return insn.funit == FU_L2
|| insn.funit == FU_S2
|| insn.funit == FU_M2
|| insn.funit == FU_D2;
}
//--------------------------------------------------------------------------
static uchar make_reg(const insn_t &insn, int32 v, bool isother)
{
if ( second_unit(insn) == isother )
return uchar(v);
else
return uchar((v) + rB0);
}
//--------------------------------------------------------------------------
inline void op_imm(op_t &x, uval_t val)
{
x.type = o_imm;
x.dtype = dt_dword;
x.value = val;
}
//--------------------------------------------------------------------------
// bcb __ea64__ fails with backend error if this function is declared inline
static void op_near(
const insn_t &insn,
op_t &x,
uint32 code,
int shift,
uval_t mask)
{
x.type = o_near;
x.dtype = dt_code;
sval_t cst = (code >> shift) & mask;
int signbit = (mask + 1) >> 1;
if ( cst & signbit )
cst |= ~mask; // extend sign
cst <<= 2;
x.addr = (insn.ip & ~0x1F) + cst;
}
//--------------------------------------------------------------------------
struct tms_reginfo_t
{
int mask;
int idx;
int reg;
};
static const tms_reginfo_t ctrls[] =
{
{ 0x21F, 0x00, rAMR }, // Addressing mode register
{ 0x21F, 0x01, rCSR }, // Control status register
// { 0x21F, 0x02, rIFR }, // Interrupt flag register
{ 0x21F, 0x02, rISR }, // Interrupt set register
{ 0x21F, 0x03, rICR }, // Interrupt clear register
{ 0x21F, 0x04, rIER }, // Interrupt enable register
{ 0x21F, 0x05, rISTP }, // Interrupt service table pointer register
{ 0x21F, 0x06, rIRP }, // Interrupt return pointer register
{ 0x21F, 0x07, rNRP }, // Nonmaskable interrupt or exception return pointer
{ 0x3FF, 0x0A, rTSCL }, // Time-stamp counter (low 32 bits) register
{ 0x3FF, 0x0B, rTSCH }, // Time-stamp counter (high 32 bits) register
{ 0x3FF, 0x0D, rILC }, // Inner loop count register
{ 0x3FF, 0x0E, rRILC }, // Reload inner loop count register
{ 0x3FF, 0x0F, rREP }, // Restricted entry point address register
{ 0x3FF, 0x10, rPCE1 }, // Program counter, E1 phase
{ 0x3FF, 0x11, rDNUM }, // DSP core number register
{ 0x3FF, 0x12, rFADCR }, // Floating-point adder configuration register
{ 0x3FF, 0x13, rFAUCR }, // Floating-point auxiliary configuration register
{ 0x3FF, 0x14, rFMCR }, // Floating-point multiplier configuration register
{ 0x3FF, 0x15, rSSR }, // Saturation status register
{ 0x3FF, 0x16, rGPLYA }, // GMPY A-side polynomial register
{ 0x3FF, 0x17, rGPLYB }, // GMPY B-side polynomial register
{ 0x3FF, 0x18, rGFPGFR }, // Galois field multiply control register
{ 0x3FF, 0x1A, rTSR }, // Task state register
{ 0x3FF, 0x1B, rITSR }, // Interrupt task state register
{ 0x3FF, 0x1C, rNTSR }, // NMI/Exception task state register
{ 0x3FF, 0x1D, rECR }, // Exception clear register
// { 0x3FF, 0x1D, rEFR }, // Exception flag register
{ 0x3FF, 0x1F, rIERR }, // Internal exception report register
};
static int find_crreg(int idx)
{
for ( int i=0; i < qnumber(ctrls); i++ )
if ( ctrls[i].idx == (idx & ctrls[i].mask) )
return ctrls[i].reg;
return -1;
}
//--------------------------------------------------------------------------
static int make_op(
const insn_t &insn,
op_t &x,
uint32 code,
uchar optype,
int32 v,
bool isother)
{
switch ( optype )
{
case t_none:
break;
case t_s2:
case t_u2:
case t_i2:
case t_i4:
case t_s4:
case t_u4:
case t_ds2:
case t_sint:
case t_uint:
case t_bv2:
case t_bv4:
isother = false;
// no break
case t_xs2:
case t_xu2:
case t_xi2:
case t_xi4:
case t_xu4:
case t_xs4:
case t_xsint:
case t_xuint:
x.type = o_reg;
x.dtype = dt_dword;
x.reg = make_reg(insn, v, isother);
break;
case t_slsb16:
case t_ulsb16:
case t_smsb16:
case t_umsb16:
isother = false;
// no break
case t_xslsb16:
case t_xulsb16:
case t_xsmsb16:
case t_xumsb16:
x.type = o_reg;
x.dtype = dt_word;
x.reg = make_reg(insn, v, isother);
break;
case t_dint:
case t_slong:
case t_ulong:
case t_sllong:
case t_ullong:
case t_dws4:
case t_dwu4:
isother = false;
// no break
case t_xslong:
case t_xulong:
x.type = o_regpair;
x.dtype = dt_qword;
x.reg = make_reg(insn, v, isother);
break;
case t_sp:
isother = false;
// no break
case t_xsp:
x.type = o_reg;
x.dtype = dt_float;
x.reg = make_reg(insn, v, isother);
break;
case t_dp:
isother = false;
// no break
case t_xdp:
x.type = o_regpair;
x.dtype = dt_double;
x.reg = make_reg(insn, v & ~1, isother);
break;
case t_ucst1:
op_imm(x, v & 1);
break;
case t_scst5:
if ( v & 0x10 )
v |= ~0x1FL; // extend sign
/* fall thru */
case t_ucst5:
op_imm(x, v);
break;
case t_ucst15:
op_imm(x, (code >> 8) & 0x7FFF);
break;
case t_ucst3:
op_imm(x, (code >> 13) & 7);
break;
case t_scst7:
op_near(insn, x, code, 16, 0x7F);
break;
case t_scst10:
op_near(insn, x, code, 13, 0x3FF);
break;
case t_scst12:
op_near(insn, x, code, 16, 0xFFF);
break;
case t_scst21:
op_near(insn, x, code, 7, 0x1FFFFF);
break;
case t_irp:
x.type = o_reg;
x.dtype = dt_word;
if ( v == 6 )
x.reg = rIRP;
else if ( v == 7 )
x.reg = rNRP;
else
return 0;
break;
case t_cregr: // read control reg
{
int idx = (code >> 18) & 0x1F;
int reg = find_crreg(idx);
if ( reg == -1 )
return 0;
if ( reg == rISR )
reg = rIFR;
if ( reg == rECR )
reg = rEFR;
op_reg(x, reg);
}
break;
case t_cregw:
{
int idx = (code >> 23) & 0x1F;
int reg = find_crreg(idx);
if ( reg == -1 )
return 0;
op_reg(x, reg);
}
break;
case t_b14:
op_reg(x, rB14 + ((code >> 7) & 1));
break;
case t_a3:
op_reg(x, make_reg(insn, rA3, isother));
break;
default:
INTERR(257);
}
return true;
}
//--------------------------------------------------------------------------
static void make_pseudo(insn_t &insn)
{
switch ( insn.itype )
{
case TMS6_add:
case TMS6_or:
if ( insn.Op1.type == o_imm && insn.Op1.value == 0 )
{
insn.itype = TMS6_mv;
SHIFT_OPS:
insn.Op1 = insn.Op2;
insn.Op2 = insn.Op3;
insn.Op1.n = 0;
insn.Op2.n = 1;
insn.Op3.type = o_void;
insn.cflags |= aux_pseudo;
}
break;
case TMS6_sub:
if ( insn.Op1.type == o_imm
&& insn.Op1.value == 0
&& insn.funit != FU_D1
&& insn.funit != FU_D2 )
{
insn.itype = TMS6_neg;
goto SHIFT_OPS;
}
if ( insn.Op1.type == o_reg
&& insn.Op2.type == o_reg
&& insn.Op3.type == o_reg
&& insn.Op1.reg == insn.Op2.reg )
{
insn.itype = TMS6_zero;
insn.Op1.reg = insn.Op3.reg;
insn.Op2.type = o_void;
insn.Op3.type = o_void;
insn.cflags |= aux_pseudo;
}
break;
case TMS6_xor:
if ( insn.Op1.type == o_imm && insn.Op1.value == uval_t(-1) )
{
insn.itype = TMS6_not;
goto SHIFT_OPS;
}
break;
case TMS6_packlh2:
if ( insn.Op1.type == o_reg
&& insn.Op2.type == o_reg
&& insn.Op1.reg == insn.Op2.reg )
{
insn.itype = TMS6_swap2;
swap_op2_and_op3(insn);
insn.Op3.type = o_void;
insn.cflags |= aux_pseudo;
}
break;
}
}
//--------------------------------------------------------------------------
static int table_insns(
insn_t &insn,
uint32 code,
const tmsinsn_t *tinsn,
bool isother)
{
// +------------------------------------------...
// |31 29|28|27 23|22 18|17 13|...
// | creg |z | dst | src2 | src1/cst |...
// +------------------------------------------...
if ( tinsn->itype == TMS6_null )
return 0;
insn.itype = tinsn->itype;
if ( isother )
insn.cflags |= aux_xp; // xpath is used
op_t *xptr = &insn.Op1;
if ( !make_op(insn, *xptr, code, tinsn->src1, (code >> 13) & 0x1F, isother) )
return 0;
if ( xptr->type != o_void )
xptr++;
if ( !make_op(insn, *xptr, code, tinsn->src2, (code >> 18) & 0x1F, isother) )
return 0;
if ( xptr->type != o_void )
xptr++;
if ( !make_op(insn, *xptr, code, tinsn->dst, (code >> 23) & 0x1F, isother) )
return 0;
make_pseudo(insn);
return insn.size;
}
//--------------------------------------------------------------------------
// L UNIT OPERATIONS
//--------------------------------------------------------------------------
static const tmsinsn_t lops[128] =
{ // bits 11..5
{ TMS6_pack2, t_i2, t_xi2, t_i2 }, // 000 0000
{ TMS6_dptrunc,t_none, t_dp, t_sint }, // 000 0001
{ TMS6_add, t_scst5, t_xsint, t_sint }, // 000 0010
{ TMS6_add, t_sint, t_xsint, t_sint }, // 000 0011
{ TMS6_sub2, t_i2, t_xi2, t_i2 }, // 000 0100
{ TMS6_add2, t_i2, t_xi2, t_i2 }, // 000 0101
{ TMS6_sub, t_scst5, t_xsint, t_sint }, // 000 0110
{ TMS6_sub, t_sint, t_xsint, t_sint }, // 000 0111
{ TMS6_dpint, t_none, t_dp, t_sint }, // 000 1000
{ TMS6_dpsp, t_none, t_dp, t_sp }, // 000 1001
{ TMS6_spint, t_none, t_sp, t_sint }, // 000 1010
{ TMS6_sptrunc,t_none, t_xsp, t_sint }, // 000 1011
{ TMS6_null, t_none, t_none, t_none }, // 000 1100
{ TMS6_null, t_none, t_none, t_none }, // 000 1101
{ TMS6_ssub, t_scst5, t_xsint, t_sint }, // 000 1110
{ TMS6_ssub, t_sint, t_xsint, t_sint }, // 000 1111
{ TMS6_addsp, t_sp, t_xsp, t_sp }, // 001 0000
{ TMS6_subsp, t_sp, t_xsp, t_sp }, // 001 0001
{ TMS6_sadd, t_scst5, t_xsint, t_sint }, // 001 0010
{ TMS6_sadd, t_sint, t_xsint, t_sint }, // 001 0011
{ TMS6_null, t_none, t_none, t_none }, // 001 0100
{ TMS6_subsp, t_xsp, t_sp, t_sp }, // 001 0101
{ TMS6_null, t_none, t_none, t_none }, // 001 0110
{ TMS6_sub, t_xsint, t_sint, t_sint }, // 001 0111
{ TMS6_adddp, t_dp, t_xdp, t_dp }, // 001 1000
{ TMS6_subdp, t_dp, t_xdp, t_dp }, // 001 1001
{ TMS6_null, t_none, t_xsint, t_sint }, // 001 1010 * why parse args?
{ TMS6_packlh2,t_i2, t_xi2, t_i2 }, // 001 1011 *
{ TMS6_packhl2,t_i2, t_xi2, t_i2 }, // 001 1100
{ TMS6_subdp, t_xdp, t_dp, t_dp }, // 001 1101
{ TMS6_packh2, t_i2, t_xi2, t_i2 }, // 001 1110
{ TMS6_ssub, t_xsint, t_sint, t_sint }, // 001 1111
{ TMS6_add, t_scst5, t_slong, t_slong }, // 010 0000
{ TMS6_add, t_xsint, t_slong, t_slong }, // 010 0001
{ TMS6_null, t_none, t_none, t_none }, // 010 0010
{ TMS6_add, t_sint, t_xsint, t_slong }, // 010 0011
{ TMS6_sub, t_scst5, t_slong, t_slong }, // 010 0100
{ TMS6_null, t_none, t_none, t_none }, // 010 0101
{ TMS6_null, t_none, t_none, t_none }, // 010 0110
{ TMS6_sub, t_sint, t_xsint, t_slong }, // 010 0111
{ TMS6_null, t_none, t_none, t_none }, // 010 1000
{ TMS6_addu, t_xuint, t_ulong, t_ulong }, // 010 1001
{ TMS6_null, t_none, t_none, t_none }, // 010 1010
{ TMS6_addu, t_uint, t_xuint, t_ulong }, // 010 1011
{ TMS6_ssub, t_scst5, t_slong, t_slong }, // 010 1100
{ TMS6_null, t_none, t_none, t_none }, // 010 1101
{ TMS6_null, t_none, t_none, t_none }, // 010 1110
{ TMS6_subu, t_uint, t_xuint, t_ulong }, // 010 1111
{ TMS6_sadd, t_scst5, t_slong, t_slong }, // 011 0000
{ TMS6_sadd, t_xsint, t_slong, t_slong }, // 011 0001
{ TMS6_null, t_none, t_none, t_none }, // 011 0010
{ TMS6_null, t_none, t_none, t_none }, // 011 0011
{ TMS6_null, t_none, t_none, t_none }, // 011 0100
{ TMS6_null, t_none, t_none, t_none }, // 011 0101
{ TMS6_null, t_none, t_none, t_none }, // 011 0110
{ TMS6_sub, t_xsint, t_sint, t_slong }, // 011 0111
{ TMS6_abs, t_none, t_slong, t_slong }, // 011 1000
{ TMS6_intdp, t_none, t_xsint, t_dp }, // 011 1001
{ TMS6_null, t_none, t_none, t_none }, // 011 1010
{ TMS6_intdpu, t_none, t_xuint, t_dp }, // 011 1011
{ TMS6_null, t_none, t_none, t_none }, // 011 1100
{ TMS6_null, t_none, t_none, t_none }, // 011 1101
{ TMS6_null, t_none, t_none, t_none }, // 011 1110
{ TMS6_subu, t_xuint, t_uint, t_ulong }, // 011 1111
{ TMS6_sat, t_none, t_slong, t_sint }, // 100 0000
{ TMS6_min2, t_s2, t_xs2, t_s2 }, // 100 0001
{ TMS6_max2, t_s2, t_xs2, t_s2 }, // 100 0010
{ TMS6_maxu4, t_u4, t_xu4, t_u4 }, // 100 0011
{ TMS6_cmpgt, t_scst5, t_slong, t_uint }, // 100 0100
{ TMS6_cmpgt, t_xsint, t_slong, t_uint }, // 100 0101
{ TMS6_cmpgt, t_scst5, t_xsint, t_uint }, // 100 0110
{ TMS6_cmpgt, t_sint, t_xsint, t_uint }, // 100 0111
{ TMS6_minu4, t_u4, t_xu4, t_u4 }, // 100 1000
{ TMS6_intspu, t_none, t_xuint, t_sp }, // 100 1010
{ TMS6_intsp, t_none, t_xsint, t_sp }, // 100 1010
{ TMS6_subc, t_uint, t_xuint, t_uint }, // 100 1011
// 1: on the C62x and C67x, op1 is ucst4 (the TI docs are misleading); on the
// C64x/C64x+ and C66x, op1 is ucst5; since the MSB should always be clear
// on the former we can treat it as a 5-bit field
{ TMS6_cmpgtu, t_ucst5, t_ulong, t_uint }, // 100 1100 // see [1]
{ TMS6_cmpgtu, t_xuint, t_ulong, t_uint }, // 100 1101
{ TMS6_cmpgtu, t_ucst5, t_xuint, t_uint }, // 100 1110 // see [1]
{ TMS6_cmpgtu, t_uint, t_xuint, t_uint }, // 100 1111
{ TMS6_cmpeq, t_scst5, t_slong, t_uint }, // 101 0000
{ TMS6_cmpeq, t_xsint, t_slong, t_uint }, // 101 0001
{ TMS6_cmpeq, t_scst5, t_xsint, t_uint }, // 101 0010
{ TMS6_cmpeq, t_sint, t_xsint, t_uint }, // 101 0011
{ TMS6_cmplt, t_scst5, t_slong, t_uint }, // 101 0100
{ TMS6_cmplt, t_xsint, t_slong, t_uint }, // 101 0101
{ TMS6_cmplt, t_scst5, t_xsint, t_uint }, // 101 0110
{ TMS6_cmplt, t_sint, t_xsint, t_uint }, // 101 0111
{ TMS6_null, t_none, t_none, t_none }, // 101 1000
{ TMS6_null, t_none, t_none, t_none }, // 101 1001
{ TMS6_subabs4,t_u4, t_xu4, t_u4 }, // 101 1010
{ TMS6_null, t_none, t_none, t_none }, // 101 1011
{ TMS6_cmpltu, t_ucst5, t_ulong, t_uint }, // 101 1100 // see [1]
{ TMS6_cmpltu, t_xuint, t_ulong, t_uint }, // 101 1101
{ TMS6_cmpltu, t_ucst5, t_xuint, t_uint }, // 101 1110 // see [1]
{ TMS6_cmpltu, t_uint, t_xuint, t_uint }, // 101 1111
{ TMS6_norm, t_none, t_slong, t_uint }, // 110 0000
{ TMS6_shlmb, t_u4, t_xu4, t_u4 }, // 110 0001
{ TMS6_shrmb, t_u4, t_xu4, t_u4 }, // 110 0010
{ TMS6_norm, t_none, t_xsint, t_uint }, // 110 0011
{ TMS6_ssub2, t_s2, t_xs2, t_s2 }, // 110 0100
{ TMS6_add4, t_i4, t_xi4, t_i4 }, // 110 0101
{ TMS6_sub4, t_i4, t_xi4, t_i4 }, // 110 0101
{ TMS6_null, t_none, t_none, t_none }, // 110 0111
{ TMS6_packl4, t_i4, t_xi4, t_i4 }, // 110 1000
{ TMS6_packh4, t_i4, t_xi4, t_i4 }, // 110 1001
// op1 is actually scst5, but because only the LSB is meaningful, we decode
// it as ucst1 so that the result would be correct for the assembler
{ TMS6_lmbd, t_ucst1, t_xuint, t_uint }, // 110 1010
{ TMS6_lmbd, t_uint, t_xuint, t_uint }, // 110 1011
{ TMS6_null, t_none, t_none, t_none }, // 110 1100
{ TMS6_null, t_none, t_none, t_none }, // 110 1101
{ TMS6_xor, t_scst5, t_xuint, t_uint }, // 110 1110
{ TMS6_xor, t_uint, t_xuint, t_uint }, // 110 1111
{ TMS6_addsp, t_sp, t_xsp, t_sp }, // 111 0000
{ TMS6_subsp, t_sp, t_xsp, t_sp }, // 111 0001
{ TMS6_adddp, t_dp, t_xdp, t_dp }, // 111 0010
{ TMS6_subdp, t_dp, t_xdp, t_dp }, // 111 0011
{ TMS6_null, t_none, t_none, t_none }, // 111 0100
{ TMS6_subsp, t_xsp, t_sp, t_sp }, // 111 0101
{ TMS6_null, t_none, t_none, t_none }, // 111 0110
{ TMS6_subdp, t_xdp, t_dp, t_dp }, // 111 0111
{ TMS6_null, t_none, t_none, t_none }, // 111 1000
{ TMS6_null, t_none, t_none, t_none }, // 111 1001
{ TMS6_and, t_scst5, t_xuint, t_uint }, // 111 1010
{ TMS6_and, t_uint, t_xuint, t_uint }, // 111 1011
{ TMS6_andn, t_uint, t_xuint, t_uint }, // 111 1100
{ TMS6_null, t_none, t_none, t_none }, // 111 1101
{ TMS6_or, t_scst5, t_xuint, t_uint }, // 111 1110
{ TMS6_or, t_uint, t_xuint, t_uint }, // 111 1111
};
static const tmsinsn_t esc1A[32] =
{
{ TMS6_abs, t_none, t_xsint, t_sint }, // 0 0000
{ TMS6_swap4, t_none, t_xu4, t_u4 }, // 0 0001
{ TMS6_unpklu4,t_none, t_xsint, t_sint }, // 0 0010
{ TMS6_unpkhu4,t_none, t_xsint, t_sint }, // 0 0011
{ TMS6_abs2, t_none, t_xs2, t_s2 }, // 0 0100
{ TMS6_mvk, t_none, t_scst5, t_sint }, // 0 0101
{ TMS6_null, t_none, t_none, t_none }, // 0 0110
{ TMS6_null, t_none, t_none, t_none }, // 0 0111
{ TMS6_null, t_none, t_none, t_none }, // 0 1000
{ TMS6_null, t_none, t_none, t_none }, // 0 1001
{ TMS6_null, t_none, t_none, t_none }, // 0 1010
{ TMS6_null, t_none, t_none, t_none }, // 0 1011
{ TMS6_null, t_none, t_none, t_none }, // 0 1100
{ TMS6_null, t_none, t_none, t_none }, // 0 1101
{ TMS6_null, t_none, t_none, t_none }, // 0 1110
{ TMS6_null, t_none, t_none, t_none }, // 0 1111
{ TMS6_null, t_none, t_none, t_none }, // 1 0000
{ TMS6_null, t_none, t_none, t_none }, // 1 0001
{ TMS6_null, t_none, t_none, t_none }, // 1 0010
{ TMS6_null, t_none, t_none, t_none }, // 1 0011
{ TMS6_null, t_none, t_none, t_none }, // 1 0100
{ TMS6_null, t_none, t_none, t_none }, // 1 0101
{ TMS6_null, t_none, t_none, t_none }, // 1 0110
{ TMS6_null, t_none, t_none, t_none }, // 1 0111
{ TMS6_null, t_none, t_none, t_none }, // 1 1000
{ TMS6_null, t_none, t_none, t_none }, // 1 1001
{ TMS6_null, t_none, t_none, t_none }, // 1 1010
{ TMS6_null, t_none, t_none, t_none }, // 1 1011
{ TMS6_null, t_none, t_none, t_none }, // 1 1100
{ TMS6_null, t_none, t_none, t_none }, // 1 1101
{ TMS6_null, t_none, t_none, t_none }, // 1 1110
{ TMS6_null, t_none, t_none, t_none }, // 1 1111
};
static int l_ops(insn_t &insn, uint32 code)
{
// +--------------------------------------------------------------+
// |31 29|28|27 23|22 18|17 13|12|11 5|4|3|2|1|0|
// | creg |z | dst | src2 | src1/cst |x | op |1|1|0|s|p|
// +--------------------------------------------------------------+
int opcode = (code >> 5) & 0x7F;
const tmsinsn_t *table = lops;
switch ( opcode )
{
case 0x1A:
opcode = (code >> 13) & 0x1F;
table = esc1A;
break;
case 0x70: // addsp
case 0x71: // subsp
case 0x72: // adddp
case 0x73: // subdp
insn.funit += 2; // move from L to S unit
break;
}
return table_insns(insn, code, table + opcode, (code & BIT12) != 0);
}
//--------------------------------------------------------------------------
// M UNIT OPERATIONS
//--------------------------------------------------------------------------
static const tmsinsn_t mops[32] =
{ // bits 11..7
{ TMS6_null, t_none, t_none, t_none }, // 0 0000
{ TMS6_mpyh, t_smsb16, t_xsmsb16, t_sint }, // 0 0001
{ TMS6_smpyh, t_smsb16, t_xsmsb16, t_sint }, // 0 0010
{ TMS6_mpyhsu, t_smsb16, t_xumsb16, t_sint }, // 0 0011
{ TMS6_mpyi, t_sint, t_xsint, t_sint }, // 0 0100
{ TMS6_mpyhus, t_umsb16, t_xsmsb16, t_sint }, // 0 0101
{ TMS6_mpyi, t_scst5, t_xsint, t_sint }, // 0 0110
{ TMS6_mpyhu, t_umsb16, t_xumsb16, t_uint }, // 0 0111
{ TMS6_mpyid, t_sint, t_xsint, t_dint }, // 0 1000
{ TMS6_mpyhl, t_smsb16, t_xslsb16, t_sint }, // 0 1001
{ TMS6_smpyhl, t_smsb16, t_xslsb16, t_sint }, // 0 1010
{ TMS6_mpyhslu, t_smsb16, t_xulsb16, t_sint }, // 0 1011
{ TMS6_mpyid, t_scst5, t_xsint, t_dint }, // 0 1100
{ TMS6_mpyhuls, t_umsb16, t_xslsb16, t_sint }, // 0 1101
{ TMS6_mpydp, t_dp, t_dp, t_dp }, // 0 1110
{ TMS6_mpyhlu, t_umsb16, t_xulsb16, t_uint }, // 0 1111
{ TMS6_mpy32, t_sint, t_xsint, t_sint }, // 1 0000
{ TMS6_mpylh, t_slsb16, t_xsmsb16, t_sint }, // 1 0001
{ TMS6_smpylh, t_slsb16, t_xsmsb16, t_sint }, // 1 0010
{ TMS6_mpylshu, t_slsb16, t_xumsb16, t_sint }, // 1 0011
{ TMS6_mpy32, t_sint, t_xsint, t_dint }, // 1 0100
{ TMS6_mpyluhs, t_ulsb16, t_xsmsb16, t_sint }, // 1 0101
{ TMS6_mpy32su, t_sint, t_xuint, t_dint }, // 1 0000
{ TMS6_mpylhu, t_ulsb16, t_xumsb16, t_uint }, // 1 0111
{ TMS6_mpy, t_scst5, t_xslsb16, t_sint }, // 1 1000
{ TMS6_mpy, t_slsb16, t_xslsb16, t_sint }, // 1 1001
{ TMS6_smpy, t_slsb16, t_xslsb16, t_sint }, // 1 1010
{ TMS6_mpysu, t_slsb16, t_xulsb16, t_sint }, // 1 1011
{ TMS6_mpysp, t_sp, t_xsp, t_sp }, // 1 1100
{ TMS6_mpyus, t_ulsb16, t_xslsb16, t_sint }, // 1 1101
{ TMS6_mpysu, t_scst5, t_xulsb16, t_sint }, // 1 1110
{ TMS6_mpyu, t_ulsb16, t_xulsb16, t_uint }, // 1 1111
};
inline int m_ops(insn_t &insn, uint32 code)
{
// +------------------------------------------------------------------+
// |31 29|28|27 23|22 18|17 13|12|11 7|6|5|4|3|2|1|0|
// | creg |z | dst | src2 | src1/cst |x | op |0|0|0|0|0|s|p|
// +------------------------------------------------------------------+
return table_insns(insn, code, mops + ((code >> 7) & 0x1F), (code & BIT12) != 0);
}
//--------------------------------------------------------------------------
// D UNIT OPERATIONS
//--------------------------------------------------------------------------
static const tmsinsn_t dops[] =
{ // bits 12..7
{ TMS6_add, t_sint, t_sint, t_sint }, // 01 0000
{ TMS6_sub, t_sint, t_sint, t_sint }, // 01 0001
{ TMS6_add, t_ucst5, t_sint, t_sint }, // 01 0010
{ TMS6_sub, t_ucst5, t_sint, t_sint }, // 01 0011
{ TMS6_null, t_none, t_none, t_none }, // 01 0100
{ TMS6_null, t_none, t_none, t_none }, // 01 0101
{ TMS6_null, t_none, t_none, t_none }, // 01 0110
{ TMS6_null, t_none, t_none, t_none }, // 01 0111
{ TMS6_null, t_none, t_none, t_none }, // 01 1000
{ TMS6_null, t_none, t_none, t_none }, // 01 1001
{ TMS6_null, t_none, t_none, t_none }, // 01 1010
{ TMS6_null, t_none, t_none, t_none }, // 01 1011
{ TMS6_null, t_none, t_none, t_none }, // 01 1100
{ TMS6_null, t_none, t_none, t_none }, // 01 1101
{ TMS6_null, t_none, t_none, t_none }, // 01 1110
{ TMS6_null, t_none, t_none, t_none }, // 01 1111
{ TMS6_null, t_none, t_none, t_none }, // 10 0000
{ TMS6_null, t_none, t_none, t_none }, // 10 0001
{ TMS6_null, t_none, t_none, t_none }, // 10 0010
{ TMS6_null, t_none, t_none, t_none }, // 10 0011
{ TMS6_null, t_none, t_none, t_none }, // 10 0100
{ TMS6_null, t_none, t_none, t_none }, // 10 0101
{ TMS6_null, t_none, t_none, t_none }, // 10 0110
{ TMS6_null, t_none, t_none, t_none }, // 10 0111
{ TMS6_null, t_none, t_none, t_none }, // 10 1000
{ TMS6_null, t_none, t_none, t_none }, // 10 1001
{ TMS6_null, t_none, t_none, t_none }, // 10 1010
{ TMS6_null, t_none, t_none, t_none }, // 10 1011
{ TMS6_null, t_none, t_none, t_none }, // 10 1100
{ TMS6_null, t_none, t_none, t_none }, // 10 1101
{ TMS6_null, t_none, t_none, t_none }, // 10 1110
{ TMS6_null, t_none, t_none, t_none }, // 10 1111
{ TMS6_addab, t_sint, t_sint, t_sint }, // 11 0000
{ TMS6_subab, t_sint, t_sint, t_sint }, // 11 0001
{ TMS6_addab, t_ucst5, t_sint, t_sint }, // 11 0010
{ TMS6_subab, t_ucst5, t_sint, t_sint }, // 11 0011
{ TMS6_addah, t_sint, t_sint, t_sint }, // 11 0100
{ TMS6_subah, t_sint, t_sint, t_sint }, // 11 0101
{ TMS6_addah, t_ucst5, t_sint, t_sint }, // 11 0110
{ TMS6_subah, t_ucst5, t_sint, t_sint }, // 11 0111
{ TMS6_addaw, t_sint, t_sint, t_sint }, // 11 1000
{ TMS6_subaw, t_sint, t_sint, t_sint }, // 11 1001
{ TMS6_addaw, t_ucst5, t_sint, t_sint }, // 11 1010
{ TMS6_subaw, t_ucst5, t_sint, t_sint }, // 11 1011
{ TMS6_addad, t_sint, t_sint, t_sint }, // 11 1100
{ TMS6_addad, t_ucst5, t_sint, t_sint }, // 11 1101
{ TMS6_null, t_none, t_none, t_none }, // 11 1110
{ TMS6_null, t_none, t_none, t_none }, // 11 1111
};
static int d_ops(insn_t &insn, uint32 code)
{
// +--------------------------------------------------------------+
// |31 29|28|27 23|22 18|17 13|12 7|6|5|4|3|2|1|0|
// | creg |z | dst | src2 | src1/cst | op |1|0|0|0|0|s|p|
// +--------------------------------------------------------------+
int opcode = (code >> 7) & 0x3F;
int res = 0;
if ( opcode == 0 )
{
static const tmsinsn_t mvk = { TMS6_mvk, t_scst5, t_none, t_sint };
res = table_insns(insn, code, &mvk, 0);
}
else if ( opcode >= 0x10 )
{
res = table_insns(insn, code, dops + (opcode - 0x10), 0);
if ( res != 0 )
swap_op1_and_op2(insn);
}
return res;
}
//--------------------------------------------------------------------------
// D UNIT OPERATIONS WITH CROSSPATH
//--------------------------------------------------------------------------
static const tmsinsn_t dxops[32] =
{ // bits 11..7
{ TMS6_mpy2, t_s2, t_xs2, t_ullong }, // 0 0000
{ TMS6_dotpsu4, t_s4, t_xu4, t_uint }, // 0 0001
{ TMS6_mpyu4, t_u4, t_xu4, t_dwu4 }, // 0 0010
{ TMS6_dotpu4, t_s4, t_xu4, t_uint }, // 0 0011
{ TMS6_null, t_none, t_none, t_none }, // 0 0100
{ TMS6_null, t_none, t_none, t_none }, // 0 0101
{ TMS6_dotp2, t_s2, t_xs2, t_sint }, // 0 0110
{ TMS6_mpylir, t_sint, t_xsint, t_sint }, // 0 0111
{ TMS6_mpyhir, t_sint, t_xsint, t_sint }, // 0 1000
{ TMS6_avgu4, t_u4, t_xu4, t_u4 }, // 0 1001
{ TMS6_mpyhi, t_sint, t_xsint, t_sllong }, // 0 1010
// TI docs say mpyspdp operands are "sp, xsp, sp", but the TI assembler wants
// regpairs for operands 2 and 3, so the docs must be wrong
{ TMS6_mpyspdp, t_sp, t_xdp, t_dp }, // 0 1011
{ TMS6_mpy32u, t_uint, t_xuint, t_dint }, // 0 1100
{ TMS6_sshvr, t_sint, t_xsint, t_sint }, // 0 1101
{ TMS6_sshvl, t_sint, t_xsint, t_sint }, // 0 1110
{ TMS6_rotl, t_ucst5, t_xuint, t_uint }, // 0 1111
{ TMS6_andn, t_uint, t_xuint, t_uint }, // 1 0000
{ TMS6_or, t_uint, t_xuint, t_uint }, // 1 0001
{ TMS6_add2, t_i2, t_xi2, t_i2 }, // 1 0010
{ TMS6_and, t_uint, t_xuint, t_uint }, // 1 0011
{ TMS6_null, t_none, t_none, t_none }, // 1 0100
{ TMS6_add, t_sint, t_xsint, t_sint }, // 1 0101
{ TMS6_sub, t_sint, t_xsint, t_sint }, // 1 0110
{ TMS6_xor, t_uint, t_xuint, t_uint }, // 1 0111
{ TMS6_sadd2, t_s2, t_xs2, t_s2 }, // 1 1000
{ TMS6_spack2, t_sint, t_xsint, t_s2 }, // 1 1001
{ TMS6_spacku4, t_s2, t_xs2, t_u4 }, // 1 1010
{ TMS6_andn, t_uint, t_xuint, t_uint }, // 1 1011
{ TMS6_shru2, t_uint, t_xu2, t_u2 }, // 1 1011
{ TMS6_shrmb, t_u4, t_xu4, t_u4 }, // 1 1101
{ TMS6_min2, t_s2, t_xs2, t_s2 }, // 1 1110
{ TMS6_null, t_none, t_none, t_none }, // 1 1111
};
static int handle_dx(insn_t &insn, const tmsinsn_t *table, uint32 code)
{
int opcode = (code >> 7) & 0x1F;
if ( opcode < 0x10 )
insn.funit -= 2; // D -> M
else if ( opcode >= 0x18 )
insn.funit -= 4; // D -> S
int size = table_insns(insn, code, table + opcode, (code & BIT12) != 0);
if ( size > 0 )
{
switch ( insn.itype )
{
case TMS6_rotl:
case TMS6_sshvl:
case TMS6_sshvr:
case TMS6_shru2:
swap_op1_and_op2(insn);
break;
}
}
return size;
}
inline int dx_ops(insn_t &insn, uint32 code)
{
// +-----------------------------------------------------------------+
// |31 29|28|27 23|22 18|17 13|12|11 7|6|5|4|3|2|1|0|
// | creg |z | dst | src2 | src1/cst |x | op |0|1|1|0|0|s|p|
// +-----------------------------------------------------------------+
return handle_dx(insn, dxops, code);
}
//--------------------------------------------------------------------------
// D UNIT OPERATIONS WITH CONSTANT CROSSPATH
//--------------------------------------------------------------------------
#define BITGRP uint16(-1)
static const tmsinsn_t dxcops[32] =
{ // bits 11..7
{ TMS6_smpy2, t_s2, t_xs2, t_ullong }, // 0 0000
{ BITGRP, t_none, t_xu4, t_u4 }, // 0 0001
{ TMS6_mpysu4, t_s4, t_xu4, t_dws4 }, // 0 0010
{ TMS6_dotpnrsu2,t_s2, t_xu2, t_sint }, // 0 0011
{ TMS6_dotpn2, t_s2, t_xs2, t_sint }, // 0 0100
{ TMS6_dotp2, t_s2, t_xs2, t_sllong }, // 0 0101
{ TMS6_dotprsu2, t_s2, t_xu2, t_sint }, // 0 0110
{ TMS6_null, t_none, t_none, t_none }, // 0 0111
{ TMS6_gmpy4, t_u4, t_xu4, t_u4, }, // 0 1000
{ TMS6_avg2, t_s2, t_xs2, t_s2 }, // 0 1001
{ TMS6_mpyli, t_sint, t_xsint, t_sllong }, // 0 1010
// TI docs say mpysp2dp operands are "sp, xsp, sp", but the TI assembler
// wants a regpair for operand 3, so the docs must be wrong
{ TMS6_mpysp2dp, t_sp, t_xsp, t_dp }, // 0 1011
{ TMS6_mpy32us, t_uint, t_xsint, t_dint }, // 0 1100
{ TMS6_null, t_none, t_none, t_none }, // 0 1101
{ TMS6_rotl, t_uint, t_xuint, t_uint }, // 0 1110
{ TMS6_null, t_none, t_none, t_none }, // 0 1111
{ TMS6_null, t_none, t_none, t_none }, // 1 0000
{ TMS6_or, t_scst5, t_xuint, t_uint }, // 1 0001
{ TMS6_sub2, t_i2, t_xi2, t_i2 }, // 1 0010
{ TMS6_and, t_scst5, t_xuint, t_uint }, // 1 0011
{ TMS6_null, t_none, t_none, t_none }, // 1 0100
{ TMS6_add, t_scst5, t_xsint, t_sint }, // 1 0101
{ TMS6_null, t_none, t_none, t_none }, // 1 0110
{ TMS6_xor, t_scst5, t_xuint, t_uint }, // 1 0111
{ TMS6_saddus2, t_u2, t_xs2, t_u2 }, // 1 1000
{ TMS6_saddu4, t_u4, t_xu4, t_u4 }, // 1 1001
{ TMS6_sub, t_sint, t_xsint, t_sint }, // 1 1010
{ TMS6_shr2, t_uint, t_xs2, t_s2 }, // 1 1011
{ TMS6_shlmb, t_u4, t_xu4, t_u4 }, // 1 1100
{ TMS6_dmv, t_sint, t_xsint, t_dint }, // 1 1101
{ TMS6_max2, t_s2, t_xs2, t_s2 }, // 1 1110
{ TMS6_pack2, t_i2, t_xi2, t_i2 }, // 1 1111
};
static const uint16 bititypes[32] =
{
TMS6_null, TMS6_null, TMS6_null, TMS6_null,
TMS6_null, TMS6_null, TMS6_null, TMS6_null,
TMS6_null, TMS6_null, TMS6_null, TMS6_null,
TMS6_null, TMS6_null, TMS6_null, TMS6_null,
TMS6_null, TMS6_null, TMS6_null, TMS6_null,
TMS6_null, TMS6_null, TMS6_null, TMS6_null,
TMS6_xpnd4, TMS6_xpnd2, TMS6_mvd, TMS6_null,
TMS6_shfl, TMS6_deal, TMS6_bitc4, TMS6_bitr,
};
static int dxc_ops(insn_t &insn, uint32 code)
{
// +-----------------------------------------------------------------+
// |31 29|28|27 23|22 18|17 13|12|11 7|6|5|4|3|2|1|0|
// | creg |z | dst | src2 | src1/cst |x | op |1|1|1|0|0|s|p|
// +-----------------------------------------------------------------+
int size = handle_dx(insn, dxcops, code);
if ( size > 0 )
{
switch ( insn.itype )
{
case BITGRP:
insn.itype = bititypes[(code >>13) & 0x1F];
if ( insn.itype == TMS6_null )
return 0;
break;
case TMS6_shr2:
swap_op1_and_op2(insn);
break;
}
}
return size;
}
//--------------------------------------------------------------------------
// LOAD/STORE WITH 15-BIT OFFSET (ON D2 UNIT)
//--------------------------------------------------------------------------
struct tms_ldinfo_t
{
uchar itype;
uchar dtype;
uchar shift;
};
static const tms_ldinfo_t ldinfo[] =
{
{ TMS6_ldhu, dt_word, 1 }, // 0000
{ TMS6_ldbu, dt_byte, 0 }, // 0001
{ TMS6_ldb, dt_byte, 0 }, // 0010
{ TMS6_stb, dt_byte, 0 }, // 0011
{ TMS6_ldh, dt_word, 1 }, // 0100
{ TMS6_sth, dt_word, 1 }, // 0101
{ TMS6_ldw, dt_dword, 2 }, // 0110
{ TMS6_stw, dt_dword, 2 }, // 0111
{ TMS6_null, 0, 0 }, // 1000
{ TMS6_null, 0, 0 }, // 1001
{ TMS6_ldndw, dt_qword, 3 }, // 1010
{ TMS6_ldnw, dt_dword, 2 }, // 1011
{ TMS6_stdw, dt_qword, 3 }, // 1100
{ TMS6_stnw, dt_dword, 2 }, // 1101
{ TMS6_lddw, dt_qword, 3 }, // 1110
{ TMS6_stndw, dt_qword, 3 }, // 1111
};
static int ld_common(insn_t &insn, uint32 code, bool use_bit8)
{
int idx = (code >> 4) & 7;
if ( use_bit8 )
idx |= (code & BIT8) >> 5;
const tms_ldinfo_t *ld = &ldinfo[idx];
insn.itype = ld->itype;
if ( insn.itype == TMS6_null )
return -1;
insn.Op2.type = o_reg;
insn.Op2.dtype = dt_dword;
insn.Op2.reg = (code >> 23) & 0x1F;
if ( code & BIT1 )
insn.Op2.reg += rB0;
insn.Op1.dtype = ld->dtype;
if ( ld->shift == 3 )
{
insn.Op2.reg &= ~1;
insn.Op2.type = o_regpair;
if ( (code & BIT23) == 0 )
if ( insn.itype == TMS6_ldndw || insn.itype == TMS6_stndw )
return 1; // no scaling
}
return ld->shift;
}
static bool is_store_insn(ushort itype)
{
switch ( itype )
{
case TMS6_stb:
case TMS6_sth:
case TMS6_stw:
case TMS6_stdw:
case TMS6_stnw:
case TMS6_stndw:
return true;
default:
return false;
}
}
static int ld15(insn_t &insn, uint32 code)
{
int shift = ld_common(insn, code, false);
if ( shift == -1 )
return 0;
insn.Op1.type = o_displ;
insn.Op1.mode = 5; // *+R[cst]
insn.Op1.reg = code & BIT7 ? rB15 : rB14;
insn.Op1.addr = (code >> 8) & 0x7FFF;
bool is_store = is_store_insn(insn.itype);
if ( is_off(get_flags(insn.ea), is_store) )
insn.Op1.addr <<= shift;
if ( is_store )
swap_op1_and_op2(insn);
return insn.size;
}
//--------------------------------------------------------------------------
// LOAD/STORE BASER+OFFSETR/CONST (ON D UNITS)
//--------------------------------------------------------------------------
static int ldbase(insn_t &insn, uint32 code)
{
// +------------------------------------------------------------------------+
// |31 29|28|27 23|22 18|17 13|12 9|8|7|6 4|3|2|1|0|
// | creg |z | dst | baseR | offsetR/ucst5 | mode |r|y| ld/st |0|1|s|p|
// +------------------------------------------------------------------------+
int shift = ld_common(insn, code, true);
if ( shift == -1 )
return 0;
insn.Op1.mode = (code >> 9) & 0xF;
bool is_store = is_store_insn(insn.itype);
switch ( insn.Op1.mode )
{
case 0x02: // 0010
case 0x03: // 0011
case 0x06: // 0110
case 0x07: // 0111
return 0;
case 0x00: // 0000 *-R[cst]
case 0x01: // 0001 *+R[cst]
case 0x08: // 1000 *--R[cst]
case 0x09: // 1001 *++R[cst]
case 0x0A: // 1010 *R--[cst]
case 0x0B: // 1011 *R++[cst]
insn.Op1.type = o_displ;
insn.Op1.addr = (code >> 13) & 0x1F;
if ( is_off(get_flags(insn.ea), is_store) )
insn.Op1.addr <<= shift;
break;
case 0x04: // 0100 *-Rb[Ro]
case 0x05: // 0101 *+Rb[Ro]
case 0x0C: // 1100 *--Rb[Ro]
case 0x0D: // 1101 *++Rb[Ro]
case 0x0E: // 1110 *Rb--[Ro]
case 0x0F: // 1111 *Rb++[Ro]
insn.Op1.type = o_phrase;
insn.Op1.secreg = make_reg(insn, (code >> 13) & 0x1F, 0);
break;
}
insn.Op1.reg = make_reg(insn, (code >> 18) & 0x1F, 0);
if ( is_store )
swap_op1_and_op2(insn);
return insn.size;
}
//--------------------------------------------------------------------------
// S UNIT OPERATIONS
//--------------------------------------------------------------------------
static const tmsinsn_t sops[64] =
{ // bits 11..6
{ TMS6_bdec, t_scst10, t_none, t_uint }, // 00 0000
{ TMS6_add2, t_i2, t_xi2, t_i2 }, // 00 0001
{ TMS6_spdp, t_none, t_xsp, t_dp }, // 00 0010
{ TMS6_b, t_none, t_irp, t_none }, // 00 0011
{ TMS6_bnop, t_none, t_scst12, t_ucst3 }, // 00 0100
{ TMS6_addkpc, t_scst7, t_ucst3, t_uint }, // 00 0101
{ TMS6_add, t_scst5, t_xsint, t_sint }, // 00 0110
{ TMS6_add, t_sint, t_xsint, t_sint }, // 00 0111
{ TMS6_packhl2,t_i2, t_xi2, t_i2 }, // 00 1000
{ TMS6_packh2, t_i2, t_xi2, t_i2 }, // 00 1000
{ TMS6_xor, t_scst5, t_xuint, t_uint }, // 00 1010
{ TMS6_xor, t_uint, t_xuint, t_uint }, // 00 1011
{ TMS6_null, t_none, t_none, t_none }, // 00 1100
{ TMS6_b, t_none, t_xuint, t_none }, // 00 1101
{ TMS6_mvc, t_none, t_xuint, t_cregw }, // 00 1110
{ TMS6_mvc, t_none, t_cregr, t_uint }, // 00 1111
{ TMS6_packlh2,t_i2, t_xi2, t_i2 }, // 01 0000
{ TMS6_sub2, t_sint, t_xsint, t_sint }, // 01 0001
{ TMS6_shl, t_ucst5, t_xsint, t_slong }, // 01 0010
{ TMS6_shl, t_uint, t_xsint, t_slong }, // 01 0011
{ TMS6_cmpgt2, t_s2, t_xs2, t_bv2 }, // 01 0100
{ TMS6_cmpgtu4,t_u4, t_xu4, t_bv4 }, // 01 0101
{ TMS6_sub, t_scst5, t_xsint, t_sint }, // 01 0110
{ TMS6_sub, t_sint, t_xsint, t_sint }, // 01 0111
{ TMS6_shr2, t_ucst5, t_xs2, t_s2 }, // 01 1000
{ TMS6_shru2, t_ucst5, t_xu2, t_u2 }, // 01 1001
{ TMS6_or, t_scst5, t_xuint, t_uint }, // 01 1010
{ TMS6_or, t_uint, t_xuint, t_uint }, // 01 1011
{ TMS6_cmpeq4, t_s4, t_xs4, t_bv4 }, // 01 1100
{ TMS6_cmpeq2, t_s2, t_xs2, t_bv2 }, // 01 1101
{ TMS6_and, t_scst5, t_xuint, t_uint }, // 01 1110
{ TMS6_and, t_uint, t_xuint, t_uint }, // 01 1111
{ TMS6_sadd, t_sint, t_xsint, t_sint }, // 10 0000
{ TMS6_null, t_none, t_none, t_none }, // 10 0001
{ TMS6_sshl, t_ucst5, t_xsint, t_sint }, // 10 0010
{ TMS6_sshl, t_uint, t_xsint, t_sint }, // 10 0011
{ TMS6_shru, t_ucst5, t_ulong, t_ulong }, // 10 0100
{ TMS6_shru, t_uint, t_ulong, t_ulong }, // 10 0101
{ TMS6_shru, t_ucst5, t_xuint, t_uint }, // 10 0110
{ TMS6_shru, t_uint, t_xuint, t_uint }, // 10 0111
{ TMS6_cmpeqdp,t_dp, t_xdp, t_sint }, // 10 1000
{ TMS6_cmpgtdp,t_dp, t_xdp, t_sint }, // 10 1001
{ TMS6_cmpltdp,t_dp, t_xdp, t_sint }, // 10 1010
{ TMS6_extu, t_uint, t_xuint, t_uint }, // 10 1011
{ TMS6_absdp, t_dp, t_none, t_dp }, // 10 1100
{ TMS6_rcpdp, t_dp, t_none, t_dp }, // 10 1101
{ TMS6_rsqrdp, t_dp, t_none, t_dp }, // 10 1110
{ TMS6_ext, t_uint, t_xsint, t_sint }, // 10 1111
{ TMS6_shl, t_ucst5, t_slong, t_slong }, // 11 0000
{ TMS6_shl, t_uint, t_slong, t_slong }, // 11 0001
{ TMS6_shl, t_ucst5, t_xsint, t_sint }, // 11 0010
{ TMS6_shl, t_uint, t_xsint, t_sint }, // 11 0011
{ TMS6_shr, t_ucst5, t_slong, t_slong }, // 11 0100
{ TMS6_shr, t_uint, t_slong, t_slong }, // 11 0101
{ TMS6_shr, t_ucst5, t_xsint, t_sint }, // 11 0110
{ TMS6_shr, t_uint, t_xsint, t_sint }, // 11 0111
{ TMS6_cmpeqsp,t_sp, t_xsp, t_sint }, // 11 1000
{ TMS6_cmpgtsp,t_sp, t_xsp, t_sint }, // 11 1001
{ TMS6_cmpltsp,t_sp, t_xsp, t_sint }, // 11 1010
{ TMS6_set, t_uint, t_xuint, t_uint }, // 11 1011
{ TMS6_abssp, t_none, t_xsp, t_sp }, // 11 1100
{ TMS6_rcpsp, t_none, t_xsp, t_sp }, // 11 1101
{ TMS6_rsqrsp, t_none, t_xsp, t_sp }, // 11 1110
{ TMS6_clr, t_uint, t_xuint, t_uint }, // 11 1111
};
static int s_ops(insn_t &insn, uint32 code)
{
// +----------------------------------------------------------------+
// |31 29|28|27 23|22 18|17 13|12|11 6|5|4|3|2|1|0|
// | creg |z | dst | src2 | src1/cst |x | op |1|0|0|0|s|p|
// +----------------------------------------------------------------+
int opcode = (code >> 6) & 0x3F;
if ( !table_insns(insn, code, sops + opcode, (code & BIT12) != 0) )
return 0;
switch ( insn.itype )
{
case TMS6_mvc:
insn.cflags &= ~aux_xp; // XPATH should not be displayed
// (assembler does not like it)
if ( insn.funit != FU_S2 )
return 0;
break;
case TMS6_b:
if ( insn.funit != FU_S2 )
return 0;
if ( opcode != 3 ) // b irp
{
switch ( (code >> 23) & 0x1F )
{
case 0: // b
break;
case 1: // bnop
insn.itype = TMS6_bnop;
make_op(insn, insn.Op2, code, t_ucst3, (code >> 13) & 0x1F, false);
break;
default:
return 0;
}
}
break;
case TMS6_bdec:
insn.cflags &= ~aux_xp; // XPATH should not be displayed
if ( (code & BIT12) == 0 )
insn.itype = TMS6_bpos;
break;
case TMS6_extu:
case TMS6_ext:
case TMS6_set:
case TMS6_clr:
insn.cflags &= ~aux_xp; // XPATH should not be displayed
// (assembler does not like it)
/* fall thru */
case TMS6_shl:
case TMS6_sshl:
case TMS6_shr:
case TMS6_shru:
case TMS6_shr2:
case TMS6_shru2:
swap_op1_and_op2(insn);
break;
case TMS6_addkpc:
swap_op2_and_op3(insn);
break;
}
return insn.size;
}
//--------------------------------------------------------------------------
// ADDK ON S UNITS
//--------------------------------------------------------------------------
static int addk(insn_t &insn, uint32 code)
{
// +-----------------------------------------------------+
// |31 29|28|27 23|22 7|6|5|4|3|2|1|0|
// | creg |z | dst | cst |1|0|1|0|0|s|p|
// +-----------------------------------------------------+
insn.itype = TMS6_addk;
insn.Op1.type = o_imm;
insn.Op1.dtype = dt_word;
insn.Op1.value = short(code >> 7);
insn.Op2.type = o_reg;
insn.Op2.dtype = dt_dword;
insn.Op2.reg = make_reg(insn, (code >> 23) & 0x1F, 0);
return insn.size;
}
//--------------------------------------------------------------------------
// FIELD OPERATIONS (IMMEDIATE FORMS) ON S UNITS
//--------------------------------------------------------------------------
static int field_ops(insn_t &insn, uint32 code)
{
// +---------------------------------------------------------------+
// |31 29|28|27 23|22 18|17 13|12 8|7 6|5|4|3|2|1|0|
// | creg |z | dst | src2 | csta | cstb | op |0|0|1|0|s|p|
// +---------------------------------------------------------------+
static const uint16 itypes[] =
{
TMS6_extu, // 00
TMS6_ext, // 01
TMS6_set, // 10
TMS6_clr, // 11
};
insn.itype = itypes[(code >> 6) & 3];
insn.Op1.type = o_imm;
insn.Op1.value = (code >> 13) & 0x1F;
insn.Op2.type = o_imm;
insn.Op2.value = (code >> 8) & 0x1F;
insn.Op3.type = o_reg;
insn.Op3.reg = make_reg(insn, (code >> 23) & 0x1F, 0);
insn.Op1.src2 = make_reg(insn, (code >> 18) & 0x1F, 0);
insn.cflags |= aux_src2;
return insn.size;
}
//--------------------------------------------------------------------------
// MVK AND MVKH ON S UNITS
//--------------------------------------------------------------------------
static int mvk(insn_t &insn, uint32 code)
{
// +-----------------------------------------------------+
// |31 29|28|27 23|22 7|6|5|4|3|2|1|0|
// | creg |z | dst | cst |x|1|0|1|0|s|p|
// +-----------------------------------------------------+
insn.itype = code & BIT6 ? TMS6_mvkh : TMS6_mvk;
insn.Op1.type = o_imm;
insn.Op1.dtype = dt_dword;
insn.Op1.value = int16(code >> 7);
if ( insn.itype == TMS6_mvkh )
// we cannot use <<= 16 because bcb6 generates wrong code for __EA64__
insn.Op1.value = uint32(insn.Op1.value << 16);
insn.Op2.type = o_reg;
insn.Op2.dtype = dt_word;
insn.Op2.reg = make_reg(insn, (code >> 23) & 0x1F, 0);
return insn.size;
}
//--------------------------------------------------------------------------
// BCOND DISP ON S UNITS
//--------------------------------------------------------------------------
static int bcond(insn_t &insn, uint32 code)
{
// +--------------------------------------------+
// |31 29|28|27 7|6|5|4|3|2|1|0|
// | creg |z | cst |0|0|1|0|0|s|p|
// +--------------------------------------------+
insn.itype = TMS6_b;
op_near(insn, insn.Op1, code, 7, 0x1FFFFF);
return insn.size;
}
//--------------------------------------------------------------------------
// INSTRUCTIONS THAT CANNOT BE PREDICATED
//--------------------------------------------------------------------------
struct tmsinsn_indexed_t
{
uint16 itype;
uchar src1;
uchar src2;
uchar dst;
uint32 index;
uint32 mask;
funit_t unit;
};
static const tmsinsn_indexed_t nopreds[] =
{ // bits 11..2
{ TMS6_callp, t_scst21, t_a3, t_none, 0x004, 0x01F, FU_S1 },
{ TMS6_addab, t_b14, t_ucst15, t_uint, 0x00F, 0x01F, FU_D1 },
{ TMS6_addad, t_b14, t_ucst15, t_uint, 0x010, 0x01F, FU_D1 },
{ TMS6_addah, t_b14, t_ucst15, t_uint, 0x017, 0x01F, FU_D1 },
{ TMS6_addaw, t_b14, t_ucst15, t_uint, 0x01F, 0x01F, FU_D1 },
{ TMS6_addsub, t_sint, t_xsint, t_dint, 0x066, 0x3FF, FU_L1 },
{ TMS6_saddsub, t_sint, t_xsint, t_dint, 0x076, 0x3FF, FU_L1 },
{ TMS6_dpack2, t_sint, t_xsint, t_dint, 0x1A6, 0x3FF, FU_L1 },
{ TMS6_shfl3, t_sint, t_xsint, t_dint, 0x1B6, 0x3FF, FU_L1 },
{ TMS6_addsub2, t_sint, t_xsint, t_dint, 0x06E, 0x3FF, FU_L1 },
{ TMS6_saddsub2, t_sint, t_xsint, t_dint, 0x07E, 0x3FF, FU_L1 },
{ TMS6_dpackx2, t_sint, t_xsint, t_dint, 0x19E, 0x3FF, FU_L1 },
{ TMS6_cmpy, t_s2, t_xs2, t_dint, 0x0AC, 0x3FF, FU_M1 },
{ TMS6_cmpyr, t_s2, t_xs2, t_s2, 0x0BC, 0x3FF, FU_M1 },
{ TMS6_cmpyr1, t_s2, t_xs2, t_s2, 0x0CC, 0x3FF, FU_M1 },
{ TMS6_mpy2ir, t_sint, t_xsint, t_dint, 0x0FC, 0x3FF, FU_M1 },
{ TMS6_ddotpl2r, t_dint, t_xs2, t_s2, 0x14C, 0x3FF, FU_M1 },
{ TMS6_ddotph2r, t_dint, t_xs2, t_s2, 0x15C, 0x3FF, FU_M1 },
{ TMS6_ddotpl2, t_dint, t_xs2, t_dint, 0x16C, 0x3FF, FU_M1 },
{ TMS6_ddotph2, t_dint, t_xs2, t_dint, 0x17C, 0x3FF, FU_M1 },
{ TMS6_ddotp4, t_ds2, t_xs2, t_dint, 0x18C, 0x3FF, FU_M1 },
{ TMS6_smpy32, t_sint, t_xsint, t_sint, 0x19C, 0x3FF, FU_M1 },
{ TMS6_xormpy, t_uint, t_xuint, t_uint, 0x1BC, 0x3FF, FU_M1 },
{ TMS6_gmpy, t_uint, t_xuint, t_uint, 0x1FC, 0x3FF, FU_M1 },
{ TMS6_rpack2, t_sint, t_xsint, t_s2, 0x3BC, 0x3FF, FU_S1 },
{ TMS6_swe, t_none, t_none, t_none, 0x0000000, 0x3FFFFFF, FU_NONE },
{ TMS6_dint, t_none, t_none, t_none, 0x0001000, 0x3FFFFFF, FU_NONE },
{ TMS6_swenr, t_none, t_none, t_none, 0x0000800, 0x3FFFFFF, FU_NONE },
{ TMS6_rint, t_none, t_none, t_none, 0x0001800, 0x3FFFFFF, FU_NONE },
};
static int nopred(insn_t &insn, uint32 code)
{
int idx = (code >> 2) & 0x3FFFFFF;
const tmsinsn_indexed_t *p = nopreds;
for ( int i=0; i < qnumber(nopreds); i++, p++ )
{
if ( p->index == (idx & p->mask) )
{
insn.funit = p->unit + ((code & BIT1) >> 1);
bool other = (code & BIT1) != 0;
if ( p->unit == FU_M1 || p->unit == FU_L1 )
other = (code & BIT12) != 0;
int size = table_insns(insn, code, (tmsinsn_t *)p, other);
if ( p->src1 == t_b14 )
insn.cflags &= ~aux_xp;
return size;
}
}
return 0;
}
//--------------------------------------------------------------------------
int idaapi ana(insn_t *_insn)
{
insn_t &insn = *_insn;
if ( insn.ip & 3 )
return 0; // alignment error
uint32 code = insn.get_next_dword();
if ( code & BIT0 )
insn.cflags |= aux_para; // parallel execution with the next insn
insn.cond = code >> 28;
switch ( insn.cond )
{
case 0x0: // 0000 unconditional
case 0x2: // 0010 B0
case 0x3: // 0011 !B0
case 0x4: // 0100 B1
case 0x5: // 0101 !B1
case 0x6: // 0110 B2
case 0x7: // 0111 !B2
case 0x8: // 1000 A1
case 0x9: // 1001 !A1
case 0xA: // 1010 A2
case 0xB: // 1011 !A2
case 0xC: // 1100 A0
case 0xD: // 1101 !A0
break;
case 0xE: // 1110 reserved
case 0xF: // 1111 reserved
return 0;
case 0x1: // 0001 no predicate
insn.cond = 0;
return nopred(insn, code);
}
switch ( (code >> 2) & 0x1F )
{
//
// Operations on L units
//
case 0x06: // 00110
case 0x0E: // 01110
case 0x16: // 10110
case 0x1E: // 11110
insn.funit = code & BIT1 ? FU_L2 : FU_L1;
return l_ops(insn, code);
//
// Operations on M units
//
case 0x00: // 00000
if ( (code & 0x3FFFCL) == 0x1E000L )
{
insn.itype = TMS6_idle;
return insn.size;
}
if ( (code & 0x21FFEL) == 0 )
{
insn.Op1.type = o_imm;
insn.Op1.dtype = dt_dword;
insn.Op1.value = ((code >> 13) & 0xF) + 1;
if ( insn.Op1.value > 9 )
return 0;
if ( insn.Op1.value == 1 )
insn.Op1.clr_shown();
insn.itype = TMS6_nop;
return insn.size;
}
if ( (code & 0x0C03FFFC) == 0x32000 )
{
insn.itype = TMS6_spmaskr;
return op_spmask(insn, insn.Op1, code);
}
if ( (code & 0x0C03FFFC) == 0x30000 )
{
insn.itype = TMS6_spmask;
return op_spmask(insn, insn.Op1, code);
}
if ( (code & 0x371FFC) == 0x030000 )
{
static const uint16 itypes[] =
{
TMS6_null, TMS6_null, TMS6_spkernel, TMS6_spkernelr,
TMS6_sploop, TMS6_sploopd, TMS6_null, TMS6_sploopw,
};
int idx = (code >> 13) & 7;
insn.itype = itypes[idx];
switch ( idx )
{
default:
return 0;
case 2: // spkernel
insn.Op1.type = o_stgcyc;
insn.Op1.dtype = dt_dword;
insn.Op1.value = ((code >> 22) & 0x3F);
break;
case 3: // spkernelr
break;
case 4: // sploop
case 5: // sploopd
case 7: // sploopw
insn.Op1.type = o_imm;
insn.Op1.dtype = dt_dword;
insn.Op1.value = ((code >> 23) & 0x1F) + 1;
break;
}
return insn.size;
}
insn.funit = code & BIT1 ? FU_M2 : FU_M1;
return m_ops(insn, code);
//
// Operations on D units
//
case 0x10: // 10000
insn.funit = code & BIT1 ? FU_D2 : FU_D1;
return d_ops(insn, code);
//
// Operations on D units (with cross path)
//
case 0x0C: // 01100
insn.funit = code & BIT1 ? FU_D2 : FU_D1;
return dx_ops(insn, code);
//
// Operations on D units (cross path used with a constant)
//
case 0x1C: // 11100
insn.funit = code & BIT1 ? FU_D2 : FU_D1;
return dxc_ops(insn, code);
//
// Load/store with 15-bit offset (on D2 unit)
//
case 0x03: // 00011
case 0x07: // 00111
case 0x0B: // 01011
case 0x0F: // 01111
case 0x13: // 10011
case 0x17: // 10111
case 0x1B: // 11011
case 0x1F: // 11111
insn.funit = FU_D2;
return ld15(insn, code);
//
// Load/store baseR+offsetR/const (on D units)
//
case 0x01: // 00001
case 0x05: // 00101
case 0x09: // 01001
case 0x0D: // 01101
case 0x11: // 10001
case 0x15: // 10101
case 0x19: // 11001
case 0x1D: // 11101
insn.funit = code & BIT7 ? FU_D2 : FU_D1;
return ldbase(insn, code);
//
// Operations on S units
//
case 0x08: // 01000
case 0x18: // 11000
insn.funit = code & BIT1 ? FU_S2 : FU_S1;
return s_ops(insn, code);
//
// ADDK on S units
//
case 0x14: // 10100
insn.funit = code & BIT1 ? FU_S2 : FU_S1;
return addk(insn, code);
//
// Field operations (immediate forms) on S units
//
case 0x02: // 00010
case 0x12: // 10010
insn.funit = code & BIT1 ? FU_S2 : FU_S1;
return field_ops(insn, code);
//
// MVK and MVKH on S units
//
case 0x0A: // 01010
case 0x1A: // 11010
insn.funit = code & BIT1 ? FU_S2 : FU_S1;
return mvk(insn, code);
//
// Bcond disp on S units
//
case 0x04: // 00100
insn.funit = code & BIT1 ? FU_S2 : FU_S1;
return bcond(insn, code);
}
return 0;
}