rizin/libr/asm/arch/tms320/tms320_dasm.c
2016-04-04 00:17:57 +02:00

1122 lines
28 KiB
C

/*
* TMS320 disassembly engine
*
* Written by Ilya V. Matveychikov <i.matveychikov@milabs.ru>
*
* Distributed under LGPL
*/
#include <stdio.h>
#include <string.h>
/* public headers */
#include <r_util.h>
#include <r_types.h>
// TODO: wtf?
#define ht_(name) r_hashtable_##name
/* private headers */
#include "tms320_p.h"
#include "tms320_dasm.h"
#include "c55x_plus/c55plus.h"
/*
* TMS320 disassembly engine implementation
*/
int run_f_list(tms320_dasm_t * dasm) {
ut32 temp;
insn_flag_t * flag;
if (!dasm->insn->f_list)
return 1;
for (flag = dasm->insn->f_list; !f_list_last(flag); flag++) {
switch (flag->v) {
case TMS320_FLAG_E:
temp = get_bits(dasm->opcode64, flag->f, 1);
set_field_value(dasm, E, temp);
break;
case TMS320_FLAG_R:
temp = get_bits(dasm->opcode64, flag->f, 1);
set_field_value(dasm, R, temp);
break;
case TMS320_FLAG_U:
temp = get_bits(dasm->opcode64, flag->f, 1);
set_field_value(dasm, U, temp);
break;
case TMS320_FLAG_u:
temp = get_bits(dasm->opcode64, flag->f, 1);
set_field_value(dasm, u, temp);
break;
case TMS320_FLAG_g:
temp = get_bits(dasm->opcode64, flag->f, 1);
set_field_value(dasm, g, temp);
break;
case TMS320_FLAG_r:
temp = get_bits(dasm->opcode64, flag->f, 1);
set_field_value(dasm, r, temp);
break;
case TMS320_FLAG_t:
temp = get_bits(dasm->opcode64, flag->f, 1);
set_field_value(dasm, t, temp);
break;
case TMS320_FLAG_k3:
temp = get_bits(dasm->opcode64, flag->f, 3);
set_field_value(dasm, k3, temp);
break;
case TMS320_FLAG_k4:
temp = get_bits(dasm->opcode64, flag->f, 4);
set_field_value(dasm, k4, temp);
break;
case TMS320_FLAG_k5:
temp = get_bits(dasm->opcode64, flag->f, 5);
set_field_value(dasm, k5, temp);
break;
case TMS320_FLAG_k6:
temp = get_bits(dasm->opcode64, flag->f, 6);
set_field_value(dasm, k6, temp);
break;
case TMS320_FLAG_k8:
temp = get_bits(dasm->opcode64, flag->f, 8);
set_field_value(dasm, k8, temp);
break;
case TMS320_FLAG_k12:
temp = get_bits(dasm->opcode64, flag->f, 12);
set_field_value(dasm, k12, temp);
break;
case TMS320_FLAG_k16:
temp = get_bits(dasm->opcode64, flag->f, 16);
set_field_value(dasm, k16, temp);
break;
case TMS320_FLAG_l1:
temp = get_bits(dasm->opcode64, flag->f, 1);
set_field_value(dasm, l1, temp);
break;
case TMS320_FLAG_l3:
temp = get_bits(dasm->opcode64, flag->f, 3);
set_field_value(dasm, l3, temp);
break;
case TMS320_FLAG_l7:
temp = get_bits(dasm->opcode64, flag->f, 7);
set_field_value(dasm, l7, temp);
break;
case TMS320_FLAG_l16:
temp = get_bits(dasm->opcode64, flag->f, 16);
set_field_value(dasm, l16, temp);
break;
case TMS320_FLAG_K8:
temp = get_bits(dasm->opcode64, flag->f, 8);
set_field_value(dasm, K8, temp);
break;
case TMS320_FLAG_K16:
temp = get_bits(dasm->opcode64, flag->f, 16);
set_field_value(dasm, K16, temp);
break;
case TMS320_FLAG_L7:
temp = get_bits(dasm->opcode64, flag->f, 7);
set_field_value(dasm, L7, temp);
break;
case TMS320_FLAG_L8:
temp = get_bits(dasm->opcode64, flag->f, 8);
set_field_value(dasm, L8, temp);
break;
case TMS320_FLAG_L16:
temp = get_bits(dasm->opcode64, flag->f, 16);
set_field_value(dasm, L16, temp);
break;
case TMS320_FLAG_P8:
temp = get_bits(dasm->opcode64, flag->f, 8);
set_field_value(dasm, P8, temp);
break;
case TMS320_FLAG_P24:
temp = get_bits(dasm->opcode64, flag->f, 24);
set_field_value(dasm, P24, temp);
break;
case TMS320_FLAG_D16:
temp = get_bits(dasm->opcode64, flag->f, 16);
set_field_value(dasm, D16, temp);
break;
case TMS320_FLAG_SHFT:
temp = get_bits(dasm->opcode64, flag->f, 4);
set_field_value(dasm, SHFT, temp);
break;
case TMS320_FLAG_SHIFTW:
temp = get_bits(dasm->opcode64, flag->f, 6);
set_field_value(dasm, SHIFTW, temp);
break;
case TMS320_FLAG_CCCCCCC:
temp = get_bits(dasm->opcode64, flag->f, 7);
set_field_value(dasm, CCCCCCC, temp);
break;
case TMS320_FLAG_AAAAAAAI:
temp = get_bits(dasm->opcode64, flag->f, 8);
set_field_value(dasm, AAAAAAAI, temp);
break;
case TMS320_FLAG_uu:
temp = get_bits(dasm->opcode64, flag->f, 2);
set_field_value(dasm, uu, temp);
break;
case TMS320_FLAG_cc:
temp = get_bits(dasm->opcode64, flag->f, 2);
set_field_value(dasm, cc, temp);
break;
case TMS320_FLAG_ss:
temp = get_bits(dasm->opcode64, flag->f, 2);
set_field_value(dasm, ss, temp);
break;
case TMS320_FLAG_dd:
temp = get_bits(dasm->opcode64, flag->f, 2);
set_field_value(dasm, dd, temp);
break;
case TMS320_FLAG_mm:
temp = get_bits(dasm->opcode64, flag->f, 2);
set_field_value(dasm, mm, temp);
break;
case TMS320_FLAG_vv:
temp = get_bits(dasm->opcode64, flag->f, 2);
set_field_value(dasm, vv, temp);
break;
case TMS320_FLAG_tt:
temp = get_bits(dasm->opcode64, flag->f, 2);
set_field_value(dasm, tt, temp);
break;
case TMS320_FLAG_XSSS:
temp = get_bits(dasm->opcode64, flag->f, 4);
set_field_value(dasm, XSSS, temp);
break;
case TMS320_FLAG_XDDD:
temp = get_bits(dasm->opcode64, flag->f, 4);
set_field_value(dasm, XDDD, temp);
break;
case TMS320_FLAG_FSSS:
temp = get_bits(dasm->opcode64, flag->f, 4);
set_field_value(dasm, FSSS, temp);
break;
case TMS320_FLAG_FDDD:
temp = get_bits(dasm->opcode64, flag->f, 4);
set_field_value(dasm, FDDD, temp);
break;
case TMS320_FLAG_XACS:
temp = get_bits(dasm->opcode64, flag->f, 4);
set_field_value(dasm, XACS, temp);
break;
case TMS320_FLAG_XACD:
temp = get_bits(dasm->opcode64, flag->f, 4);
set_field_value(dasm, XACD, temp);
break;
case TMS320_FLAG_SS:
temp = get_bits(dasm->opcode64, flag->f, 2);
if (!field_valid(dasm, SS)) {
set_field_value(dasm, SS, temp);
} else {
set_field_value(dasm, SS2, temp);
}
break;
case TMS320_FLAG_DD:
temp = get_bits(dasm->opcode64, flag->f, 2);
if (!field_valid(dasm, DD)) {
set_field_value(dasm, DD, temp);
} else {
set_field_value(dasm, DD2, temp);
}
break;
case TMS320_FLAG_XXX:
temp = get_bits(dasm->opcode64, flag->f, 3);
set_field_value(dasm, Xmem_reg, temp);
break;
case TMS320_FLAG_MMM:
temp = get_bits(dasm->opcode64, flag->f, 3);
if (!field_valid(dasm, Xmem_mmm)) {
set_field_value(dasm, Xmem_mmm, temp);
} else {
set_field_value(dasm, Ymem_mmm, temp);
}
break;
case TMS320_FLAG_Y:
temp = get_bits(dasm->opcode64, flag->f, 1) << 0;
if (!field_valid(dasm, Ymem_reg)) {
set_field_value(dasm, Ymem_reg, temp);
} else {
field_value(dasm, Ymem_reg) |= temp;
}
break;
case TMS320_FLAG_YY:
temp = get_bits(dasm->opcode64, flag->f, 2) << 1;
if (!field_valid(dasm, Ymem_reg)) {
set_field_value(dasm, Ymem_reg, temp);
} else {
field_value(dasm, Ymem_reg) |= temp;
}
break;
default:
printf("TODO: unknown opcode flag %02x\n", flag->v);
return 0;
}
}
return 1;
}
int run_m_list(tms320_dasm_t * dasm)
{
insn_mask_t * mask;
if (!dasm->insn->m_list)
return 1;
for (mask = dasm->insn->m_list; !m_list_last(mask); mask++) {
/* match bits in range [f, f + n] with mask's value */
if (get_bits(dasm->opcode64, mask->f, mask->n) != mask->v)
return 0;
}
return 1;
}
int vreplace(char * string, const char * token, const char * fmt, va_list args)
{
char data[64];
char * pos;
pos = strstr(string, token);
if (!pos)
return 0;
vsnprintf(data, sizeof(data), fmt, args);
memmove(pos + strlen(data), pos + strlen(token), strlen(pos + strlen(token)) + 1);
memmove(pos, data, strlen(data));
return 1;
}
int replace(char * string, const char * token, const char * fmt, ...)
{
int result;
va_list args;
va_start(args, fmt);
result = vreplace(string, token, fmt, args);
va_end(args);
return result;
}
void substitute(char * string, const char * token, const char * fmt, ...)
{
int result;
va_list args;
do {
va_start(args, fmt);
result = vreplace(string, token, fmt, args);
va_end(args);
} while (result);
}
const char * get_xreg_str(ut8 key, char * str)
{
static const char * table[16] = {
"ac0", "ac1", "ac2", "ac3", "xsp", "xssp", "xdp", "xcdp",
"xar0", "xar1", "xar2", "xar3", "xar4", "xar5", "xar6", "xar7",
};
return table[ key & 15 ];
}
const char * get_freg_str(ut8 key, char * str)
{
static const char * table[16] = {
"ac0", "ac1", "ac2", "ac3", "t0", "t1", "t2", "t3",
"ar0", "ar1", "ar2", "ar3", "ar4", "ar5", "ar6", "ar7",
};
return table[ key & 15 ];
}
const char * get_swap_str(ut8 key, char * str)
{
switch (key) {
case 0: return "swap ac0, ac2";
case 1: return "swap ac1, ac3";
case 4: return "swap t0, t2";
case 5: return "swap t1, t3";
case 8: return "swap ar0, ar2";
case 9: return "swap ar1, ar3";
case 12: return "swap ar4, t0";
case 13: return "swap ar5, t1";
case 14: return "swap ar6, t2";
case 15: return "swap ar7, t3";
case 16: return "swapp ac0, ac2";
case 20: return "swapp t0, t2";
case 24: return "swapp ar0, ar2";
case 28: return "swapp ar4, t0";
case 30: return "swapp ar6, t2";
case 44: return "swap4 ar4, t0";
case 56: return "swap ar0, ar1";
}
return "invalid";
}
const char * get_relop_str(ut8 key, char * str)
{
static const char * table[] = {
"==", "<", ">=", "!="
};
return table[ key & 3 ];
}
const char * get_cond_str(ut8 key, char * str)
{
/* 000 FSSS ... 101 FSSS */
if ((key >> 4) <= 5) {
static const char * op[6] = { "==", "!=", "<", "<=", ">", ">=" };
sprintf(str, "%s %s 0", get_freg_str(key & 15, NULL), op[(key >> 4) & 7]);
return str;
}
/* 110 00SS */
if ((key >> 2) == 0x18) {
sprintf(str, "overflow(ac%d)", key & 3);
return str;
}
/* 111 00SS */
if ((key >> 2) == 0x1C) {
sprintf(str, "!overflow(ac%d)", key & 3);
return str;
}
switch (key) {
case 0x64: return "tc1";
case 0x65: return "tc2";
case 0x66: return "carry";
case 0x74: return "!tc1";
case 0x75: return "!tc2";
case 0x76: return "!carry";
/* "&" operation */
case 0x68: return "tc1 & tc2";
case 0x69: return "tc1 & !tc2";
case 0x6A: return "!tc1 & tc2";
case 0x6B: return "!tc1 & !tc2";
/* "|" operation */
case 0x78: return "tc1 | tc2";
case 0x79: return "tc1 | !tc2";
case 0x7A: return "!tc1 | tc2";
case 0x7B: return "!tc1 | !tc2";
/* "^" operation */
case 0x7C: return "tc1 ^ tc2";
case 0x7D: return "tc1 ^ !tc2";
case 0x7E: return "!tc1 ^ tc2";
case 0x7F: return "!tc1 ^ !tc2";
}
return "invalid";
}
const char * get_v_str(ut8 key, char * str)
{
static const char * table[2] = {
"carry", "tc2",
};
return table[ key & 1 ];
}
const char * get_t_str(ut8 key, char * str)
{
static const char * table[2] = {
"tc1", "tc2",
};
return table[ key & 1 ];
}
const char * get_cmem_str(ut8 key, char * str)
{
static const char * table[4] = {
"*cdp", "*cdp+", "*cdp-", "*(cdp+t0)",
};
return table[ key & 3 ];
}
const char * get_smem_str(ut8 key, char * str)
{
// direct memory
if ((key & 0x01) == 0) {
#ifdef IDA_COMPATIBLE_MODE
sprintf(str, "*sp(#%Xh)", key >> 1);
#else
sprintf(str, "@0x%02X", key >> 1);
#endif
return str;
}
// indirect memory
switch (key) {
case 0x11: return "abs16(k16)";
case 0x31: return "*(k23)";
case 0x51: return "port(k16)";
case 0x71: return "*cdp";
case 0x91: return "*cdp+";
case 0xB1: return "*cdp-";
case 0xD1: return "*cdp(K16)";
case 0xF1: return "*+cdp(K16)";
}
switch (key & 0x1F) {
case 0x01: return "*ARn";
case 0x03: return "*ARn+";
case 0x05: return "*ARn-";
// TODO:
// C54CM:0 => *(ARn + T0)
// C54CM:1 => *(ARn + AR0)
case 0x07: return "*(ARn + t0)";
// TODO:
// C54CM:0 => *(ARn - t0)
// C54CM:1 => *(ARn - AR0)
case 0x09: return "*(ARn - t0)";
// TODO:
// C54CM:0 => *ARn(t0)
// C54CM:1 => *ARn(AR0)
case 0x0B: return "*ARn(t0)";
case 0x0D: return "*ARn(k16)";
case 0x0F: return "*+ARn(k16)";
// TODO:
// ARMS:0 => *(ARn + T1)
// ARMS:1 => *ARn(short(1))
case 0x13: return "*(ARn + t1)";
// TODO:
// ARMS:0 => *(ARn - T1)
// ARMS:1 => *ARn(short(2))
case 0x15: return "*(ARn - t1)";
// TODO:
// ARMS:0 => *ARn(T1)
// ARMS:1 => *ARn(short(3))
case 0x17: return "*ARn(t1)";
// TODO:
// ARMS:0 => *+ARn
// ARMS:1 => *ARn(short(4))
case 0x19: return "*+ARn";
// TODO:
// ARMS:0 => *-ARn
// ARMS:1 => *ARn(short(5))
case 0x1B: return "*-ARn";
// TODO:
// ARMS:0 => *(ARn + t0b)
// ARMS:1 => *ARn(short(6))
case 0x1D: return "*(ARn + t0b)";
// TODO:
// ARMS:0 => *(arn - t0b)
// ARMS:1 => *arn(short(7))
case 0x1F: return "*(ARn - t0b)";
}
return "invalid";
}
const char * get_mmm_str(ut8 key, char * str)
{
switch (key & 7) {
default:
case 0x00: return "*ARn";
case 0x01: return "*ARn+";
case 0x02: return "*ARn-";
// TODO:
// C54CM:0 => *(ARn + T0)
// C54CM:1 => *(ARn + AR0)
case 0x03: return "*(ARn + t0)";
case 0x04: return "*(ARn + t1)";
// TODO:
// C54CM:0 => *(ARn - t0)
// C54CM:1 => *(ARn - AR0)
case 0x05: return "*(ARn - t0)";
case 0x06: return "*(ARn - t1)";
// TODO:
// C54CM:0 => *ARn(T0)
// C54CM:1 => *ARn(AR0)
case 0x07: return "*ARn(t0)";
};
}
/*
* syntax decoders
*/
void decode_bits(tms320_dasm_t * dasm)
{
// rounding
if (field_valid(dasm, R))
substitute(dasm->syntax, "[r]", "%s", field_value(dasm, R) ? "r" : "");
// unsigned
if (field_valid(dasm, u))
substitute(dasm->syntax, "[u]", "%s", field_value(dasm, u) ? "u" : "");
// 40 keyword
if (field_valid(dasm, g))
substitute(dasm->syntax, "[40]", "%s", field_value(dasm, g) ? "40" : "");
// T3 update
if (field_valid(dasm, U))
substitute(dasm->syntax, "[T3 = ]", "%s", field_value(dasm, U) ? "t3=" : "");
}
void decode_braces(tms320_dasm_t * dasm)
{
char * pos;
pos = strstr(dasm->syntax, "[(saturate]");
if (pos) {
replace(pos, "[)", ")[");
replace(dasm->syntax, "[(saturate]", "%s", "(saturate");
}
if (field_valid(dasm, R)) {
pos = strstr(dasm->syntax, "[rnd(]");
if (pos) {
replace(pos, "[)", "%s", field_value(dasm, R) ? ")[" : "[");
replace(dasm->syntax, "[rnd(]", "%s", field_value(dasm, R) ? "rnd(" : "");
}
}
if (field_valid(dasm, u)) {
pos = strstr(dasm->syntax, "[uns(]");
if (pos) {
replace(pos, "[)", "%s", field_value(dasm, u) ? ")[" : "[");
replace(dasm->syntax, "[uns(]", "%s", field_value(dasm, u) ? "uns(" : "");
}
}
if (field_valid(dasm, uu)) {
bool parallel = !!strstr (dasm->syntax, "::");
// first
replace(dasm->syntax, "[uns(]", "%s", field_value(dasm, uu) & 2 ? "uns(" : "");
replace(dasm->syntax, "[)]", "%s", field_value(dasm, uu) & 2 ? ")" : "");
if (parallel) {
replace(dasm->syntax, "[uns(]", "%s", field_value(dasm, uu) & 2 ? "uns(" : "");
replace(dasm->syntax, "[)]", "%s", field_value(dasm, uu) & 2 ? ")" : "");
}
// second
replace(dasm->syntax, "[uns(]", "%s", field_value(dasm, uu) & 1 ? "uns(" : "");
replace(dasm->syntax, "[)]", "%s", field_value(dasm, uu) & 1 ? ")" : "");
if (parallel) {
replace(dasm->syntax, "[uns(]", "%s", field_value(dasm, uu) & 1 ? "uns(" : "");
replace(dasm->syntax, "[)]", "%s", field_value(dasm, uu) & 1 ? ")" : "");
}
}
// remove rudiments
substitute(dasm->syntax, "[]", "%s", "");
}
void decode_constants(tms320_dasm_t * dasm)
{
// signed constant
if (field_valid(dasm, K8))
substitute(dasm->syntax, "K8", "0x%02X", field_value(dasm, K8));
if (field_valid(dasm, K16))
substitute(dasm->syntax, "K16", "0x%04X", be16(field_value(dasm, K16)));
// unsigned constant
if (field_valid(dasm, k4))
substitute(dasm->syntax, "K4", "0x%01X", field_value(dasm, k4));
if (field_valid(dasm, k5))
substitute(dasm->syntax, "k5", "0x%02X", field_value(dasm, k5));
if (field_valid(dasm, k8))
substitute(dasm->syntax, "k8", "0x%02X", field_value(dasm, k8));
if (field_valid(dasm, k12))
substitute(dasm->syntax, "k12", "0x%03X", be16(field_value(dasm, k12)));
if (field_valid(dasm, k16))
substitute(dasm->syntax, "k16", "0x%04X", be16(field_value(dasm, k16)));
if (field_valid(dasm, k4) && field_valid(dasm, k3))
substitute(dasm->syntax, "k7", "0x%02X", (field_value(dasm, k3) << 4) | field_value(dasm, k4));
if (field_valid(dasm, k4) && field_valid(dasm, k5))
substitute(dasm->syntax, "k9", "0x%03X", (field_value(dasm, k5) << 4) | field_value(dasm, k4));
if (field_valid(dasm, k4) && field_valid(dasm, k8))
substitute(dasm->syntax, "k12", "0x%03X", (field_value(dasm, k8) << 4) | field_value(dasm, k4));
// dasm address label
if (field_valid(dasm, D16))
substitute(dasm->syntax, "D16", "0x%04X", be16(field_value(dasm, D16)));
// immediate shift value
if (field_valid(dasm, SHFT))
substitute(dasm->syntax, "#SHFT", "0x%01X", field_value(dasm, SHFT));
if (field_valid(dasm, SHIFTW))
substitute(dasm->syntax, "#SHIFTW", "0x%02X", field_value(dasm, SHIFTW));
}
void decode_addresses(tms320_dasm_t * dasm)
{
// program address label
if (field_valid(dasm, L7))
substitute(dasm->syntax, "L7", "0x%02X", field_value(dasm, L7));
if (field_valid(dasm, L8))
substitute(dasm->syntax, "L8", "0x%02X", field_value(dasm, L8));
if (field_valid(dasm, L16))
substitute(dasm->syntax, "L16", "0x%04X", be16(field_value(dasm, L16)));
// program address label
if (field_valid(dasm, l1) && field_valid(dasm, l3))
substitute(dasm->syntax, "l4", "0x%01X", (field_value(dasm, l3) << 1) | field_value(dasm, l1));
// program memory address
if (field_valid(dasm, l7))
substitute(dasm->syntax, "pmad", "0x%02X", field_value(dasm, l7));
if (field_valid(dasm, l16))
substitute(dasm->syntax, "pmad", "0x%04X", be16(field_value(dasm, l16)));
// program or dasm address label
if (field_valid(dasm, P8))
substitute(dasm->syntax, "P8", "0x%02X", field_value(dasm, P8));
if (field_valid(dasm, P24))
substitute(dasm->syntax, "P24", "0x%06X", be24(field_value(dasm, P24)));
}
void decode_swap(tms320_dasm_t * dasm)
{
char tmp[64];
if (field_valid(dasm, k6))
substitute(dasm->syntax, "SWAP ( )", get_swap_str(field_value(dasm, k6), tmp));
}
void decode_relop(tms320_dasm_t * dasm)
{
if (field_valid(dasm, cc))
substitute(dasm->syntax, "RELOP", get_relop_str(field_value(dasm, cc), NULL));
}
void decode_cond(tms320_dasm_t * dasm)
{
char tmp[64];
if (field_valid(dasm, CCCCCCC))
substitute(dasm->syntax, "cond", "%s", get_cond_str(field_value(dasm, CCCCCCC), tmp));
substitute(dasm->syntax, "[label, ]", "");
}
void decode_registers(tms320_dasm_t * dasm)
{
ut8 code = 0;
// transition register
if (field_valid(dasm, r))
substitute(dasm->syntax, "TRNx", "trn%d", field_value(dasm, r));
// source and destination temporary registers
if (field_valid(dasm, ss))
substitute(dasm->syntax, "Tx", "t%d", field_value(dasm, ss));
if (field_valid(dasm, dd))
substitute(dasm->syntax, "Tx", "t%d", field_value(dasm, dd));
// shifted in/out bit values
if (field_valid(dasm, vv)) {
substitute(dasm->syntax, "BitIn", "%s", get_v_str(field_value(dasm, vv) >> 1, NULL));
substitute(dasm->syntax, "BitOut", "%s", get_v_str(field_value(dasm, vv) >> 0, NULL));
}
// source and destination of CRC instruction
if (field_valid(dasm, t))
substitute(dasm->syntax, "TCx", "%s", get_t_str(field_value(dasm, t), NULL));
if (field_valid(dasm, tt)) {
substitute(dasm->syntax, "TCx", "%s", get_t_str(field_value(dasm, tt) >> 0, NULL));
substitute(dasm->syntax, "TCy", "%s", get_t_str(field_value(dasm, tt) >> 1, NULL));
}
// source or destination accumulator or extended register
if (field_valid(dasm, XSSS)) {
substitute(dasm->syntax, "xsrc", "%s", get_xreg_str(field_value(dasm, XSSS), NULL));
substitute(dasm->syntax, "XAsrc", "%s", get_xreg_str(field_value(dasm, XSSS), NULL));
}
if (field_valid(dasm, XDDD)) {
substitute(dasm->syntax, "xdst", "%s", get_xreg_str(field_value(dasm, XDDD), NULL));
substitute(dasm->syntax, "XAdst", "%s", get_xreg_str(field_value(dasm, XDDD), NULL));
}
// source or destination accumulator, auxiliary or temporary register
if (field_valid(dasm, FSSS) && field_valid(dasm, FDDD)) {
if (field_value(dasm, FSSS) == field_value(dasm, FDDD))
substitute(dasm->syntax, "[src,] dst", "dst");
else
substitute(dasm->syntax, "[src,] dst", "src, dst");
}
if (field_valid(dasm, FSSS) && field_valid(dasm, FDDD)) {
substitute(dasm->syntax, "src1", "%s", get_freg_str(field_value(dasm, FSSS), NULL));
substitute(dasm->syntax, "src2", "%s", get_freg_str(field_value(dasm, FDDD), NULL));
substitute(dasm->syntax, "dst1", "%s", get_freg_str(field_value(dasm, FSSS), NULL));
substitute(dasm->syntax, "dst2", "%s", get_freg_str(field_value(dasm, FDDD), NULL));
}
code &= 0;
code |= field_valid(dasm, FSSS) ? 0x01 : 0x00;
code |= field_valid(dasm, FDDD) ? 0x02 : 0x00;
switch (code) {
case 0x01: // FSSS
substitute(dasm->syntax, "TAx", "%s", get_freg_str(field_value(dasm, FSSS), NULL));
break;
case 0x02: // FDDD
substitute(dasm->syntax, "TAx", "%s", get_freg_str(field_value(dasm, FDDD), NULL));
substitute(dasm->syntax, "TAy", "%s", get_freg_str(field_value(dasm, FDDD), NULL));
break;
case 0x03: // FSSS FDDD
substitute(dasm->syntax, "TAx", "%s", get_freg_str(field_value(dasm, FSSS), NULL));
substitute(dasm->syntax, "TAy", "%s", get_freg_str(field_value(dasm, FDDD), NULL));
break;
}
if (field_valid(dasm, FSSS)) {
substitute(dasm->syntax, "src", "%s", get_freg_str(field_value(dasm, FSSS), NULL));
}
if (field_valid(dasm, FDDD)) {
substitute(dasm->syntax, "dst", "%s", get_freg_str(field_value(dasm, FDDD), NULL));
}
if (field_valid(dasm, XACS))
substitute(dasm->syntax, "XACsrc", "%s", get_xreg_str(field_value(dasm, XACS), NULL));
if (field_valid(dasm, XACD))
substitute(dasm->syntax, "XACdst", "%s", get_xreg_str(field_value(dasm, XACD), NULL));
// source and destination accumulator registers
code &= 0;
code |= field_valid(dasm, SS) ? 0x01 : 0x00;
code |= field_valid(dasm, SS2) ? 0x02 : 0x00;
code |= field_valid(dasm, DD) ? 0x10 : 0x00;
code |= field_valid(dasm, DD2) ? 0x20 : 0x00;
switch (code) {
case 0x01: // SS
substitute(dasm->syntax, "ACx", "ac%d", field_value(dasm, SS));
break;
case 0x03: // SSSS
substitute(dasm->syntax, "ACx", "ac%d", field_value(dasm, SS));
substitute(dasm->syntax, "ACy", "ac%d", field_value(dasm, SS2));
break;
case 0x11: // SS DD
if (field_value(dasm, SS) == field_value(dasm, DD)) {
substitute(dasm->syntax, "[, ACy]", "");
substitute(dasm->syntax, "[ACx,] ACy", "ACy");
} else {
substitute(dasm->syntax, "[, ACy]", ", ACy");
substitute(dasm->syntax, "[ACx,] ACy", "ACx, ACy");
}
substitute(dasm->syntax, "ACx", "ac%d", field_value(dasm, SS));
substitute(dasm->syntax, "ACy", "ac%d", field_value(dasm, DD));
break;
case 0x33: // SSSS DDDD
substitute(dasm->syntax, "ACx", "ac%d", field_value(dasm, SS));
substitute(dasm->syntax, "ACy", "ac%d", field_value(dasm, SS2));
substitute(dasm->syntax, "ACz", "ac%d", field_value(dasm, DD));
substitute(dasm->syntax, "ACw", "ac%d", field_value(dasm, DD2));
break;
case 0x10: // DD
substitute(dasm->syntax, "ACx", "ac%d", field_value(dasm, DD));
break;
case 0x30: // DDDD
substitute(dasm->syntax, "ACx", "ac%d", field_value(dasm, DD));
substitute(dasm->syntax, "ACy", "ac%d", field_value(dasm, DD2));
break;
}
}
void decode_addressing_modes(tms320_dasm_t * dasm)
{
// Cmem
if (field_valid(dasm, mm))
substitute(dasm->syntax, "Cmem", "%s", get_cmem_str(field_value(dasm, mm), NULL));
// Xmem and Ymem
if (field_valid(dasm, Xmem_reg) && field_valid(dasm, Xmem_mmm)) {
substitute(dasm->syntax, "Xmem", "%s", get_mmm_str(field_value(dasm, Xmem_mmm), NULL));
substitute(dasm->syntax, "ARn", "ar%d", field_value(dasm, Xmem_reg));
}
if (field_valid(dasm, Ymem_reg) && field_valid(dasm, Ymem_mmm)) {
substitute(dasm->syntax, "Ymem", "%s", get_mmm_str(field_value(dasm, Ymem_mmm), NULL));
substitute(dasm->syntax, "ARn", "ar%d", field_value(dasm, Ymem_reg));
}
// Lmem and Smem
if (field_valid(dasm, AAAAAAAI)) {
char str[64], tmp[64];
snprintf(tmp, sizeof(tmp), "%s", get_smem_str(field_value(dasm, AAAAAAAI), str));
if (field_value(dasm, AAAAAAAI) & 1) {
if (strstr(tmp, "k16")) {
substitute(tmp, "k16", "0x%04X", be16(*(ut16 *)(&dasm->stream + dasm->length)));
dasm->length += 2;
} else if (strstr(tmp, "k23")) {
substitute(tmp, "k23", "0x%06X", be24(*(ut32 *)(&dasm->stream + dasm->length) & 0x7FFFFF));
dasm->length += 3;
} else if (strstr(tmp, "K16")) {
substitute(tmp, "K16", "0x%04X", be16(*(ut16 *)(&dasm->stream + dasm->length)));
dasm->length += 2;
}
substitute(tmp, "ARn", "ar%d", field_value(dasm, AAAAAAAI) >> 5);
}
substitute(dasm->syntax, "Smem", "%s", tmp);
substitute(dasm->syntax, "Lmem", "%s", tmp);
}
}
void decode_qualifiers(tms320_dasm_t * dasm)
{
switch (dasm->stream[dasm->length]) {
case 0x98:
// 1001 1000 - mmap
break;
case 0x99:
// 1001 1001 - port(Smem)
break;
case 0x9a:
// 1001 1010 - port(Smem)
break;
case 0x9c:
// 1001 1100 - <insn>.LR
set_field_value(dasm, q_lr, 1);
break;
case 0x9d:
// 1001 1101 - <insn>.CR
set_field_value(dasm, q_cr, 1);
break;
}
}
static insn_item_t * finalize(tms320_dasm_t * dasm)
{
// remove odd spaces
substitute(dasm->syntax, " ", "%s", " ");
// add some qualifiers
if (field_value(dasm, q_lr))
replace(dasm->syntax, " ", ".lr ");
if (field_value(dasm, q_cr))
replace(dasm->syntax, " ", ".cr ");
return dasm->insn;
}
insn_item_t * decode_insn(tms320_dasm_t * dasm)
{
dasm->length = dasm->head->size;
snprintf(dasm->syntax, sizeof(dasm->syntax), \
field_valid(dasm, E) && field_value(dasm, E) ? "|| %s" : "%s", dasm->insn->syntax);
decode_bits(dasm);
decode_braces(dasm);
decode_qualifiers(dasm);
decode_constants(dasm);
decode_addresses(dasm);
decode_swap(dasm);
decode_relop(dasm);
decode_cond(dasm);
decode_registers(dasm);
decode_addressing_modes(dasm);
return finalize(dasm);
}
insn_item_t * decode_insn_head(tms320_dasm_t * dasm)
{
run_f_list(dasm);
if (dasm->insn->i_list) {
dasm->insn = dasm->insn->i_list;
while (!i_list_last(dasm->insn)) {
if (run_m_list(dasm) && run_f_list(dasm))
break;
dasm->insn++;
}
}
if (!i_list_last(dasm->insn))
return decode_insn(dasm);
return NULL;
}
static ut8 c55x_e_list[] = {
0xF8, 0x60, /* 0110 0lll */
0xF0, 0xA0, /* 1010 FDDD */
0xFC, 0xB0, /* 1011 00DD */
0xF0, 0xC0, /* 1100 FSSS */
0xFC, 0xBC, /* 1011 11SS */
0x00, 0x00,
};
insn_head_t * lookup_insn_head(tms320_dasm_t * dasm)
{
ut8 * e_list = NULL;
/* handle some exceptions */
if (tms320_f_get_cpu(dasm) == TMS320_F_CPU_C55X)
e_list = c55x_e_list;
while (e_list && (e_list[0] && e_list[1])) {
if ((dasm->opcode & e_list[0]) == e_list[1]) {
dasm->head = ht_(lookup)(dasm->map, e_list[1]);
break;
}
e_list += 2;
}
if (!dasm->head) {
dasm->head = ht_(lookup)(dasm->map, dasm->opcode);
if (!dasm->head)
dasm->head = ht_(lookup)(dasm->map, dasm->opcode & 0xfe);
}
dasm->insn = dasm->head ? &dasm->head->insn : NULL;
return dasm->head;
}
static void init_dasm(tms320_dasm_t * dasm, const ut8 * stream, int len)
{
strcpy(dasm->syntax, "invalid");
memcpy(dasm->stream, stream, min(sizeof(dasm->stream), len));
dasm->status = 0;
dasm->length = 0;
memset(&dasm->f, 0, sizeof(dasm->f));
dasm->head = NULL;
dasm->insn = NULL;
}
static int full_insn_size(tms320_dasm_t * dasm)
{
int qualifier_size = 0;
if (field_value(dasm, q_cr))
qualifier_size = 1;
if (field_value(dasm, q_lr))
qualifier_size = 1;
return dasm->length + qualifier_size;
}
/*
* TMS320 disassembly engine public interface
*/
int tms320_dasm(tms320_dasm_t * dasm, const ut8 * stream, int len)
{
init_dasm(dasm, stream, len);
if (tms320_f_get_cpu(dasm) != TMS320_F_CPU_C55X_PLUS) {
if (lookup_insn_head(dasm) && decode_insn_head(dasm)) {
if (dasm->length > len)
dasm->status |= TMS320_S_INVAL;
}
} else {
c55x_plus_disassemble(dasm, stream, len);
}
if (strstr(dasm->syntax, "invalid"))
dasm->status |= TMS320_S_INVAL;
if (dasm->status & TMS320_S_INVAL)
strcpy(dasm->syntax, "invalid"), dasm->length = 1;
return full_insn_size(dasm);
}
static insn_head_t c55x_list[] = {
# include "c55x/table.h"
};
int tms320_dasm_init(tms320_dasm_t * dasm) {
int i = 0;
dasm->map = ht_(new)();
for (i = 0; i < ARRAY_SIZE(c55x_list); i++)
ht_(insert)(dasm->map, c55x_list[i].byte, &c55x_list[i]);
tms320_f_set_cpu(dasm, TMS320_F_CPU_C55X);
return 0;
}
int tms320_dasm_fini(tms320_dasm_t * dasm) {
if (dasm) {
if (dasm->map)
ht_(free)(dasm->map);
/* avoid double free */
memset (dasm, 0, sizeof (tms320_dasm_t));
}
return 0;
}