796 lines
31 KiB
C++
796 lines
31 KiB
C++
|
|
#include "fr.hpp"
|
|
|
|
// distinct sizes :
|
|
//lint -esym(749, S_11) not referenced
|
|
enum
|
|
{
|
|
S_0,
|
|
S_4, // 4 bits
|
|
S_5, // 5 bits
|
|
S_8, // 8 bits
|
|
S_11, // 11 bits
|
|
S_12, // 12 bits
|
|
S_16 // 16 bits
|
|
};
|
|
|
|
// bits numbers for sizes :
|
|
const int bits[] =
|
|
{
|
|
0,
|
|
4,
|
|
5,
|
|
8,
|
|
11,
|
|
12,
|
|
16
|
|
};
|
|
|
|
// masks for sizes :
|
|
const int masks[] =
|
|
{
|
|
0x0000,
|
|
0x000F,
|
|
0x001F,
|
|
0x00FF,
|
|
0x07FF,
|
|
0x0FFF,
|
|
0xFFFF
|
|
};
|
|
|
|
const char dtypes[] =
|
|
{
|
|
0,
|
|
dt_byte,
|
|
dt_byte,
|
|
dt_byte,
|
|
dt_word,
|
|
dt_word,
|
|
dt_word
|
|
};
|
|
|
|
// distinct operands :
|
|
enum
|
|
{
|
|
O_null, // null opcode
|
|
O_gr, // general register Ri
|
|
O_gri, // general register indirect @Ri
|
|
O_grip, // general register indirect post-increment @Ri+
|
|
O_r13_gr_i, // indirect r13 + general register @(R13, Ri)
|
|
O_r14_imm8_i, // indirect r14 + 8 bits immediate value @(R14, imm)
|
|
O_r15_imm4_i, // indirect r15 + 4 bits immediate value @(R15, imm)
|
|
O_r15ip, // indirect r15 post-increment @R15+
|
|
O_r15im, // indirect r15 pre-decrement @-R15
|
|
O_r13, // register r13 R13
|
|
O_r13ip, // indirect r13 post-increment @R13+
|
|
O_dr, // dedicated register Rs
|
|
O_ps, // program status register (PS) PS
|
|
O_imm, // immediate value #i
|
|
O_diri, // indirect direct value @i
|
|
O_rel, // relative value label5
|
|
O_reglist // register list (R0, R1, R2, ...)
|
|
};
|
|
|
|
static int invert_word(int word)
|
|
{
|
|
int new_word = 0;
|
|
|
|
new_word |= (word & 0x000F) >> 0;
|
|
new_word <<= 4;
|
|
new_word |= (word & 0x00F0) >> 4;
|
|
new_word <<= 4;
|
|
new_word |= (word & 0x0F00) >> 8;
|
|
new_word <<= 4;
|
|
new_word |= (word & 0xF000) >> 12;
|
|
|
|
return new_word;
|
|
}
|
|
|
|
// structure of an opcode :
|
|
struct opcode_t
|
|
{
|
|
int itype;
|
|
int opcode;
|
|
int opcode_size;
|
|
|
|
int op1;
|
|
int op1_size;
|
|
int op2;
|
|
int op2_size;
|
|
|
|
#define I_SWAPOPS 0x00000100 // swap operands
|
|
#define I_DSHOT 0x00000200 // delay shot
|
|
#define I_ADDR_R OP_ADDR_R
|
|
#define I_ADDR_W OP_ADDR_W
|
|
#define I_IMM_2 0x00001000 // imm = imm * 2
|
|
#define I_IMM_4 0x00002000 // imm = imm * 4
|
|
int flags;
|
|
|
|
inline bool swap_ops(void) const { return (flags & I_SWAPOPS) != 0; }
|
|
inline bool delay_shot(void) const { return (flags & I_DSHOT) != 0; }
|
|
inline bool implied(void) const { return op1 == O_null && op2 == O_null; }
|
|
|
|
int size(void) const
|
|
{
|
|
int n = bits[opcode_size];
|
|
if ( op1 != O_null )
|
|
n += bits[op1_size];
|
|
if ( op2 != O_null )
|
|
n += bits[op2_size];
|
|
return n;
|
|
}
|
|
|
|
static void check(void);
|
|
static const struct opcode_t *find(insn_t &insn, int *_data);
|
|
};
|
|
|
|
// FR opcodes :
|
|
static const struct opcode_t opcodes[] =
|
|
{
|
|
{ fr_add, 0xA6, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_add, 0xA4, S_8, O_imm, S_4, O_gr, S_4, 0 },
|
|
{ fr_add2, 0xA5, S_8, O_imm, S_4, O_gr, S_4, 0 },
|
|
{ fr_addc, 0xA7, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_addn, 0xA2, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_addn, 0xA0, S_8, O_imm, S_4, O_gr, S_4, 0 },
|
|
{ fr_addn2, 0xA1, S_8, O_imm, S_4, O_gr, S_4, 0 },
|
|
{ fr_sub, 0xAC, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_subc, 0xAD, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_subn, 0xAE, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_cmp, 0xAA, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_cmp, 0xA8, S_8, O_imm, S_4, O_gr, S_4, 0 },
|
|
{ fr_cmp2, 0xA9, S_8, O_imm, S_4, O_gr, S_4, 0 },
|
|
{ fr_and, 0x82, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_and, 0x84, S_8, O_gr, S_4, O_gri, S_4, 0 },
|
|
{ fr_andh, 0x85, S_8, O_gr, S_4, O_gri, S_4, 0 },
|
|
{ fr_andb, 0x86, S_8, O_gr, S_4, O_gri, S_4, 0 },
|
|
{ fr_or, 0x92, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_or, 0x94, S_8, O_gr, S_4, O_gri, S_4, 0 },
|
|
{ fr_orh, 0x95, S_8, O_gr, S_4, O_gri, S_4, 0 },
|
|
{ fr_orb, 0x96, S_8, O_gr, S_4, O_gri, S_4, 0 },
|
|
{ fr_eor, 0x9A, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_eor, 0x9C, S_8, O_gr, S_4, O_gri, S_4, 0 },
|
|
{ fr_eorh, 0x9D, S_8, O_gr, S_4, O_gri, S_4, 0 },
|
|
{ fr_eorb, 0x9E, S_8, O_gr, S_4, O_gri, S_4, 0 },
|
|
{ fr_bandl, 0x80, S_8, O_imm, S_4, O_gri, S_4, 0 },
|
|
{ fr_bandh, 0x81, S_8, O_imm, S_4, O_gri, S_4, 0 },
|
|
{ fr_borl, 0x90, S_8, O_imm, S_4, O_gri, S_4, 0 },
|
|
{ fr_borh, 0x91, S_8, O_imm, S_4, O_gri, S_4, 0 },
|
|
{ fr_beorl, 0x98, S_8, O_imm, S_4, O_gri, S_4, 0 },
|
|
{ fr_beorh, 0x99, S_8, O_imm, S_4, O_gri, S_4, 0 },
|
|
{ fr_btstl, 0x88, S_8, O_imm, S_4, O_gri, S_4, 0 },
|
|
{ fr_btsth, 0x89, S_8, O_imm, S_4, O_gri, S_4, 0 },
|
|
{ fr_mul, 0xAF, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_mulu, 0xAB, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_mulh, 0xBF, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_muluh, 0xBB, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_div0s, 0x974, S_12, O_gr, S_4, O_null, 0, 0 },
|
|
{ fr_div0u, 0x975, S_12, O_gr, S_4, O_null, 0, 0 },
|
|
{ fr_div1, 0x976, S_12, O_gr, S_4, O_null, 0, 0 },
|
|
{ fr_div2, 0x977, S_12, O_gr, S_4, O_null, 0, 0 },
|
|
{ fr_div3, 0x9F60, S_16, O_null, 0, O_null, 0, 0 },
|
|
{ fr_div4s, 0x9F70, S_16, O_null, 0, O_null, 0, 0 },
|
|
{ fr_lsl, 0xB6, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_lsl, 0xB4, S_8, O_imm, S_4, O_gr, S_4, 0 },
|
|
{ fr_lsl2, 0xB5, S_8, O_imm, S_4, O_gr, S_4, 0 },
|
|
{ fr_lsr, 0xB2, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_lsr, 0xB0, S_8, O_imm, S_4, O_gr, S_4, 0 },
|
|
{ fr_lsr2, 0xB1, S_8, O_imm, S_4, O_gr, S_4, 0 },
|
|
{ fr_asr, 0xBA, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_asr, 0xB8, S_8, O_imm, S_4, O_gr, S_4, 0 },
|
|
{ fr_asr2, 0xB9, S_8, O_imm, S_4, O_gr, S_4, 0 },
|
|
// fr_ldi_32 not here (considered as special)
|
|
// fr_ldi_20 not here (considered as special)
|
|
{ fr_ldi_8, 0x0C, S_4, O_imm, S_8, O_gr, S_4, 0 },
|
|
{ fr_ld, 0x04, S_8, O_gri, S_4, O_gr, S_4, 0 },
|
|
{ fr_ld, 0x00, S_8, O_r13_gr_i, S_4, O_gr, S_4, 0 },
|
|
{ fr_ld, 0x02, S_4, O_r14_imm8_i, S_8, O_gr, S_4, I_IMM_4 },
|
|
{ fr_ld, 0x03, S_8, O_r15_imm4_i, S_4, O_gr, S_4, I_IMM_4 },
|
|
{ fr_ld, 0x70, S_12, O_r15ip, S_0, O_gr, S_4, 0 },
|
|
{ fr_ld, 0x78, S_12, O_r15ip, S_0, O_dr, S_4, 0 },
|
|
{ fr_ld, 0x790, S_16, O_r15ip, S_0, O_ps, S_0, 0 },
|
|
{ fr_lduh, 0x05, S_8, O_gri, S_4, O_gr, S_4, 0 },
|
|
{ fr_lduh, 0x01, S_8, O_r13_gr_i, S_4, O_gr, S_4, 0 },
|
|
{ fr_lduh, 0x04, S_4, O_r14_imm8_i, S_8, O_gr, S_4, I_IMM_2 },
|
|
{ fr_ldub, 0x06, S_8, O_gri, S_4, O_gr, S_4, 0 },
|
|
{ fr_ldub, 0x02, S_8, O_r13_gr_i, S_4, O_gr, S_4, 0 },
|
|
{ fr_ldub, 0x06, S_4, O_r14_imm8_i, S_8, O_gr, S_4, 0 },
|
|
{ fr_st, 0x14, S_8, O_gri, S_4, O_gr, S_4, I_SWAPOPS },
|
|
{ fr_st, 0x10, S_8, O_r13_gr_i, S_4, O_gr, S_4, I_SWAPOPS },
|
|
{ fr_st, 0x03, S_4, O_r14_imm8_i, S_8, O_gr, S_4, I_SWAPOPS|I_IMM_4 },
|
|
{ fr_st, 0x13, S_8, O_r15_imm4_i, S_4, O_gr, S_4, I_SWAPOPS|I_IMM_4 },
|
|
{ fr_st, 0x170, S_12, O_gr, S_4, O_r15im, S_0, 0 },
|
|
{ fr_st, 0x178, S_12, O_dr, S_4, O_r15im, S_0, 0 },
|
|
{ fr_st, 0x1790, S_16, O_ps, S_0, O_r15im, S_0, 0 },
|
|
{ fr_sth, 0x15, S_8, O_gri, S_4, O_gr, S_4, I_SWAPOPS },
|
|
{ fr_sth, 0x11, S_8, O_r13_gr_i, S_4, O_gr, S_4, I_SWAPOPS },
|
|
{ fr_sth, 0x05, S_4, O_r14_imm8_i, S_8, O_gr, S_4, I_SWAPOPS|I_IMM_2 },
|
|
{ fr_stb, 0x16, S_8, O_gri, S_4, O_gr, S_4, I_SWAPOPS },
|
|
{ fr_stb, 0x12, S_8, O_r13_gr_i, S_4, O_gr, S_4, I_SWAPOPS },
|
|
{ fr_stb, 0x07, S_4, O_r14_imm8_i, S_8, O_gr, S_4, I_SWAPOPS },
|
|
{ fr_mov, 0x8B, S_8, O_gr, S_4, O_gr, S_4, 0 },
|
|
{ fr_mov, 0xB7, S_8, O_dr, S_4, O_gr, S_4, 0 },
|
|
{ fr_mov, 0x171, S_12, O_ps, S_0, O_gr, S_4, 0 },
|
|
{ fr_mov, 0xB3, S_8, O_dr, S_4, O_gr, S_4, I_SWAPOPS },
|
|
{ fr_mov, 0x71, S_12, O_gr, S_4, O_ps, S_0, 0 },
|
|
{ fr_jmp, 0x970, S_12, O_gri, S_4, O_null, 0, 0 },
|
|
{ fr_call, 0x971, S_12, O_gri, S_4, O_null, 0, 0 },
|
|
{ fr_ret, 0x9720, S_16, O_null, 0, O_null, 0, 0 },
|
|
{ fr_int, 0x1F, S_8, O_imm, S_8, O_null, 0, 0 },
|
|
{ fr_inte, 0x9F30, S_16, O_null, 0, O_null, 0, 0 },
|
|
{ fr_reti, 0x9730, S_16, O_null, 0, O_null, 0, 0 },
|
|
{ fr_bra, 0xE0, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_bno, 0xE1, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_beq, 0xE2, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_bne, 0xE3, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_bc, 0xE4, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_bnc, 0xE5, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_bn, 0xE6, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_bp, 0xE7, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_bv, 0xE8, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_bnv, 0xE9, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_blt, 0xEA, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_bge, 0xEB, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_ble, 0xEC, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_bgt, 0xED, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_bls, 0xEE, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_bhi, 0xEF, S_8, O_rel, S_8, O_null, 0, 0 },
|
|
{ fr_jmp, 0x9F0, S_12, O_gri, S_4, O_null, 0, I_DSHOT },
|
|
{ fr_call, 0x9F1, S_12, O_gri, S_4, O_null, 0, I_DSHOT },
|
|
{ fr_ret, 0x9F20, S_16, O_null, 0, O_null, 0, I_DSHOT },
|
|
{ fr_bra, 0xF0, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_bno, 0xF1, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_beq, 0xF2, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_bne, 0xF3, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_bc, 0xF4, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_bnc, 0xF5, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_bn, 0xF6, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_bp, 0xF7, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_bv, 0xF8, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_bnv, 0xF9, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_blt, 0xFA, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_bge, 0xFB, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_ble, 0xFC, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_bgt, 0xFD, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_bls, 0xFE, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_bhi, 0xFF, S_8, O_rel, S_8, O_null, 0, I_DSHOT },
|
|
{ fr_dmov, 0x08, S_8, O_diri, S_8, O_r13, S_0, I_ADDR_R },
|
|
{ fr_dmov, 0x18, S_8, O_r13, S_0, O_diri, S_8, I_ADDR_W },
|
|
{ fr_dmov, 0x0C, S_8, O_diri, S_8, O_r13ip, S_0, I_ADDR_R },
|
|
{ fr_dmov, 0x1C, S_8, O_r13ip, S_0, O_diri, S_8, I_ADDR_W },
|
|
{ fr_dmov, 0x0B, S_8, O_diri, S_8, O_r15im, S_0, I_ADDR_R },
|
|
{ fr_dmov, 0x1B, S_8, O_r15ip, S_0, O_diri, S_8, I_ADDR_W },
|
|
{ fr_dmovh, 0x09, S_8, O_diri, S_8, O_r13, S_0, I_ADDR_R },
|
|
{ fr_dmovh, 0x19, S_8, O_r13, S_0, O_diri, S_8, I_ADDR_W },
|
|
{ fr_dmovh, 0x0D, S_8, O_diri, S_8, O_r13ip, S_0, I_ADDR_R },
|
|
{ fr_dmovh, 0x1D, S_8, O_r13ip, S_0, O_diri, S_8, I_ADDR_W },
|
|
{ fr_dmovb, 0x0A, S_8, O_diri, S_8, O_r13, S_0, I_ADDR_R },
|
|
{ fr_dmovb, 0x1A, S_8, O_r13, S_0, O_diri, S_8, I_ADDR_W },
|
|
{ fr_dmovb, 0x0E, S_8, O_diri, S_8, O_r13ip, S_0, I_ADDR_R },
|
|
{ fr_dmovb, 0x1E, S_8, O_r13ip, S_0, O_diri, S_8, I_ADDR_W },
|
|
{ fr_ldres, 0xBC, S_8, O_imm, S_4, O_grip, S_4, I_SWAPOPS },
|
|
{ fr_stres, 0xBD, S_8, O_imm, S_4, O_grip, S_4, 0 },
|
|
// fr_copop not here (considered as special)
|
|
// fr_copld not here (considered as special)
|
|
// fr_copst not here (considered as special)
|
|
// fr_copsv not here (considered as special)
|
|
{ fr_nop, 0x9FA0, S_16, O_null, 0, O_null, 0, 0 },
|
|
{ fr_andccr, 0x83, S_8, O_imm, S_8, O_null, 0, 0 },
|
|
{ fr_orccr, 0x93, S_8, O_imm, S_8, O_null, 0, 0 },
|
|
{ fr_stilm, 0x87, S_8, O_imm, S_8, O_null, 0, 0 },
|
|
{ fr_addsp, 0xA3, S_8, O_imm, S_8, O_null, 0, 0 },
|
|
{ fr_extsb, 0x978, S_12, O_gr, S_4, O_null, 0, 0 },
|
|
{ fr_extub, 0x979, S_12, O_gr, S_4, O_null, 0, 0 },
|
|
{ fr_extsh, 0x97A, S_12, O_gr, S_4, O_null, 0, 0 },
|
|
{ fr_extuh, 0x97B, S_12, O_gr, S_4, O_null, 0, 0 },
|
|
{ fr_ldm0, 0x8C, S_8, O_reglist, S_8, O_null, 0, 0 },
|
|
{ fr_ldm1, 0x8D, S_8, O_reglist, S_8, O_null, 0, 0 },
|
|
{ fr_stm0, 0x8E, S_8, O_reglist, S_8, O_null, 0, 0 },
|
|
{ fr_stm1, 0x8F, S_8, O_reglist, S_8, O_null, 0, 0 },
|
|
{ fr_enter, 0x0F, S_8, O_imm, S_8, O_null, 0, I_IMM_4 },
|
|
{ fr_leave, 0x9F90, S_16, O_null, 0, O_null, 0, 0 },
|
|
{ fr_xchb, 0x8A, S_8, O_gri, S_4, O_gr, S_4, 0 }
|
|
};
|
|
|
|
void opcode_t::check(void)
|
|
{
|
|
for ( int i = 0; i < qnumber(opcodes); i++ )
|
|
{
|
|
int n = opcodes[i].size();
|
|
// if ( n != 16 && n != 32 )
|
|
// msg("instruction n%d (%d) : size %d\n", i, opcodes[i].insn, n);
|
|
QASSERT(10001, n == 16 || n == 32);
|
|
}
|
|
}
|
|
|
|
const struct opcode_t * opcode_t::find(insn_t &insn, int *_data)
|
|
{
|
|
QASSERT(10002, _data != NULL);
|
|
|
|
int data = (*_data << 8) | get_byte(insn.ip + insn.size);
|
|
for ( int i = 0; i < qnumber(opcodes); i++ )
|
|
{
|
|
int mask;
|
|
int shift;
|
|
switch ( opcodes[i].opcode_size )
|
|
{
|
|
case S_4: mask = 0xF000; shift = 12; break;
|
|
case S_5: mask = 0xF100; shift = 11; break;
|
|
case S_8: mask = 0xFF00; shift = 8; break;
|
|
case S_12: mask = 0xFFF0; shift = 4; break;
|
|
case S_16: mask = 0xFFFF; shift = 0; break;
|
|
default: INTERR(10012);
|
|
}
|
|
if ( ((data & mask) >> shift) != opcodes[i].opcode )
|
|
continue;
|
|
|
|
insn.size++;
|
|
*_data = invert_word(data);
|
|
return &opcodes[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// get general register.
|
|
static int get_gr(const int num)
|
|
{
|
|
QASSERT(10003, num >= 0 && num <= 15);
|
|
return num;
|
|
}
|
|
|
|
// get coprocessor register.
|
|
static int get_cr(const int num)
|
|
{
|
|
QASSERT(10004, num >= 0 && num <= 15);
|
|
return num + 16;
|
|
}
|
|
|
|
// get dedicated register.
|
|
static int get_dr(int num)
|
|
{
|
|
static const int drs[] =
|
|
{
|
|
rTBR,
|
|
rRP,
|
|
rSSP,
|
|
rUSP,
|
|
rMDH,
|
|
rMDL,
|
|
rReserved6,
|
|
rReserved7,
|
|
rReserved8,
|
|
rReserved9,
|
|
rReserved10,
|
|
rReserved11,
|
|
rReserved12,
|
|
rReserved13,
|
|
rReserved14,
|
|
rReserved15
|
|
};
|
|
QASSERT(10005, num >= 0 && num <= 15);
|
|
return drs[num];
|
|
}
|
|
|
|
// fill an operand as a register.
|
|
static void set_reg(op_t &op, int reg, char d_typ)
|
|
{
|
|
op.type = o_reg;
|
|
op.reg = (uint16)reg;
|
|
op.dtype = d_typ;
|
|
}
|
|
|
|
// fill an operand as an immediate value.
|
|
static void set_imm(op_t &op, int imm, char d_typ)
|
|
{
|
|
op.type = o_imm;
|
|
switch ( d_typ )
|
|
{
|
|
case dt_byte: op.value = (char) imm; break;
|
|
case dt_word: op.value = (short) imm; break;
|
|
case dt_dword: op.value = imm; break;
|
|
default: INTERR(10013);
|
|
}
|
|
op.dtype = d_typ;
|
|
}
|
|
|
|
// fill an operand as a phrase.
|
|
static void set_phrase(op_t &op, int type, int val, char d_typ)
|
|
{
|
|
switch ( type )
|
|
{
|
|
case fIGR: // indirect general register
|
|
case fIGRP: // indirect general register with post-increment
|
|
case fIGRM: // indirect general register with pre-decrement
|
|
case fR13RI: // indirect displacement between R13 and a general register
|
|
op.reg = (uint16)val;
|
|
break;
|
|
|
|
case fIRA: // indirect relative address
|
|
op.addr = val;
|
|
break;
|
|
|
|
default:
|
|
INTERR(10014);
|
|
}
|
|
op.type = o_phrase;
|
|
op.specflag2 = (char)type;
|
|
op.dtype = d_typ;
|
|
}
|
|
|
|
// fill an operand as a relative address.
|
|
static void set_rel(const insn_t &insn, op_t &op, int addr, char d_typ)
|
|
{
|
|
op.type = o_near;
|
|
int raddr;
|
|
switch ( d_typ ) /* ugly but functional */
|
|
{
|
|
case dt_byte:
|
|
raddr = ((signed char) addr);
|
|
break;
|
|
|
|
case dt_word:
|
|
raddr = ((signed short) addr);
|
|
break;
|
|
|
|
default:
|
|
INTERR(10015);
|
|
}
|
|
op.addr = insn.ip + 2 + (raddr * 2); //-V614 uninitialized variable 'raddr'
|
|
#if defined(__DEBUG__)
|
|
msg("0x%a = 0x%a + 2 + ((signed) 0x%X) * 2)\n", op.addr, insn.ip, addr);
|
|
#endif /* __DEBUG__ */
|
|
op.dtype = dt_code;
|
|
}
|
|
|
|
// fill an operand as a reglist
|
|
static void set_reglist(op_t &op, int list)
|
|
{
|
|
op.type = o_reglist;
|
|
op.value = list;
|
|
op.dtype = dt_byte; // list is coded in a byte
|
|
}
|
|
|
|
static void set_displ(op_t &op, int reg, int imm, int flag, int local_flag)
|
|
{
|
|
op.type = o_displ;
|
|
if ( reg != -1 )
|
|
op.reg = (uint16)get_gr(reg);
|
|
if ( imm != -1 )
|
|
{
|
|
int mul = 1;
|
|
if ( local_flag & I_IMM_2 )
|
|
mul = 2;
|
|
if ( local_flag & I_IMM_4 )
|
|
mul = 4;
|
|
op.value = ((unsigned) imm) * mul;
|
|
}
|
|
op.dtype = dt_byte;
|
|
op.specflag1 |= flag;
|
|
}
|
|
|
|
// swap 2 opcodes (o1 <=> o2).
|
|
static void swap_ops(op_t &o1, op_t &o2)
|
|
{
|
|
QASSERT(10006, o1.type != o_void && o2.type != o_void);
|
|
op_t tmp = o1;
|
|
o1 = o2;
|
|
o2 = tmp;
|
|
}
|
|
|
|
static void adjust_data(int size, int *data)
|
|
{
|
|
QASSERT(10007, data != NULL);
|
|
int new_data = *data >> bits[size];
|
|
*data = new_data;
|
|
}
|
|
|
|
/*
|
|
static void prepare_data(int size, int *data) {
|
|
QASSERT(10008, data != NULL);
|
|
int new_data = 0;
|
|
switch ( size ) {
|
|
case S_0:
|
|
case S_4:
|
|
new_data = *data;
|
|
break;
|
|
|
|
case S_5:
|
|
case S_8:
|
|
new_data |= (*data & 0x00F0) >> 4;
|
|
new_data |= (*data & 0x000F) << 4;
|
|
break;
|
|
|
|
case S_11:
|
|
case S_12:
|
|
new_data |= (*data & 0x0F00) >> 8;
|
|
new_data |= (*data & 0x00F0) >> 0;
|
|
new_data |= (*data & 0x000F) << 8;
|
|
break;
|
|
|
|
case S_16:
|
|
new_data |= (*data & 0xF000) >> 12;
|
|
new_data |= (*data & 0x0F00) >> 4;
|
|
new_data |= (*data & 0x00F0) << 4;
|
|
new_data |= (*data & 0x000F) << 12;
|
|
break;
|
|
}
|
|
*data = new_data;
|
|
}*/
|
|
|
|
#define SWAP_IF_BYTE(data) \
|
|
do \
|
|
{ \
|
|
if ( operand_size == S_8 ) \
|
|
{ \
|
|
int h = (data & 0x0F) << 4; \
|
|
int l = (data & 0xF0) >> 4; \
|
|
data = h | l; \
|
|
} \
|
|
} \
|
|
while ( 0 )
|
|
|
|
//
|
|
// defines some shortcuts.
|
|
//
|
|
|
|
#define __set_gr(op, reg) set_reg(op, reg, dt_byte)
|
|
#define set_gr(op, reg) __set_gr(op, get_gr(reg))
|
|
#define __set_dr(op, reg) set_reg(op, reg, dt_word)
|
|
#define set_dr(op, reg) __set_dr(op, get_dr(reg))
|
|
#define __set_cr(op, reg) set_reg(op, reg, dt_word)
|
|
#define set_cr(op, reg) __set_cr(op, get_cr(reg))
|
|
|
|
#define set_gri(insn, op, reg) set_phrase(op, fIGR, get_gr(reg), dt_byte)
|
|
#define set_grip(insn, op, reg) set_phrase(op, fIGRP, get_gr(reg), dt_byte)
|
|
#define set_grim(insn, op, reg) set_phrase(op, fIGRM, get_gr(reg), dt_byte)
|
|
#define set_diri(insn, op, addr) set_phrase(op, fIRA, addr, dt_word)
|
|
#define set_r13_gr_i(insn, op, reg) set_phrase(op, fR13RI, get_gr(reg), dt_byte)
|
|
#define fill_op1(insn, data, opc) fill_op(insn, data, insn.Op1, opc->op1, opc->op1_size, opc->flags)
|
|
#define fill_op2(insn, data, opc) fill_op(insn, data, insn.Op2, opc->op2, opc->op2_size, opc->flags)
|
|
//#define set_displ_gr(op, gr, f1) set_displ(op, gr, -1, f1, 0)
|
|
#define set_displ_imm(op, imm, f1, f2) set_displ(op, -1, imm, f1, f2)
|
|
|
|
static void fill_op(const insn_t &insn, int data, op_t &op, int operand, int operand_size, int flags)
|
|
{
|
|
data &= masks[operand_size];
|
|
//prepare_data(operand_size, &data);
|
|
switch ( operand )
|
|
{
|
|
case O_gr: // general register Ri
|
|
QASSERT(10009, operand_size == S_4);
|
|
set_gr(op, data);
|
|
break;
|
|
|
|
case O_gri: // general register indirect @Ri
|
|
QASSERT(10010, operand_size == S_4);
|
|
set_gri(insn, op, data);
|
|
break;
|
|
|
|
case O_grip: // general register indirect @Ri
|
|
QASSERT(10011, operand_size == S_4);
|
|
set_grip(insn, op, data);
|
|
break;
|
|
|
|
case O_r13_gr_i: // indirect r13 + general register @(R13, Ri)
|
|
set_r13_gr_i(insn, op, data);
|
|
break;
|
|
|
|
case O_r14_imm8_i: // indirect r14 + 8 bits immediate value @(R14, imm)
|
|
SWAP_IF_BYTE(data);
|
|
set_displ_imm(op, data, OP_DISPL_IMM_R14, flags);
|
|
break;
|
|
|
|
case O_r15_imm4_i: // indirect r15 + 4 bits immediate value @(R15, imm)
|
|
SWAP_IF_BYTE(data);
|
|
set_displ_imm(op, data, OP_DISPL_IMM_R15, flags);
|
|
break;
|
|
|
|
case O_r15ip: // indirect r15 post-increment @R15+
|
|
set_grip(insn, op, rR15);
|
|
break;
|
|
|
|
case O_r15im: // indirect r15 pre-decrement @-R15
|
|
set_grim(insn, op, rR15);
|
|
break;
|
|
|
|
case O_r13: // register r13 R13
|
|
__set_gr(op, rR13);
|
|
break;
|
|
|
|
case O_r13ip: // indirect r13 post-increment @R13+
|
|
set_grip(insn, op, rR13);
|
|
break;
|
|
|
|
case O_dr: // dedicated register Rs
|
|
set_dr(op, data);
|
|
break;
|
|
|
|
case O_ps: // program status register (PS) PS
|
|
__set_dr(op, rPS);
|
|
break;
|
|
|
|
case O_imm: // immediate value #i
|
|
SWAP_IF_BYTE(data);
|
|
if ( insn.itype == fr_enter )
|
|
data *= 4;
|
|
if ( insn.itype == fr_addsp )
|
|
data = ((signed) data) * 4;
|
|
set_imm(op, data, dtypes[operand_size]);
|
|
break;
|
|
|
|
case O_diri: // indirect direct value @i
|
|
SWAP_IF_BYTE(data);
|
|
if ( insn.itype == fr_dmov )
|
|
data *= 4;
|
|
if ( insn.itype == fr_dmovh )
|
|
data *= 2;
|
|
set_diri(insn, op, data);
|
|
op.specflag1 |= flags;
|
|
break;
|
|
|
|
case O_rel: // relative value label5
|
|
SWAP_IF_BYTE(data);
|
|
set_rel(insn, op, data, dtypes[operand_size]);
|
|
break;
|
|
|
|
case O_reglist: // register list (R0, R1, R2, ...)
|
|
SWAP_IF_BYTE(data);
|
|
set_reglist(op, data);
|
|
break;
|
|
|
|
case O_null: // null opcode
|
|
INTERR(10016);
|
|
}
|
|
}
|
|
|
|
// analyze a "common" instruction (those which are listed in the opcodes[] array).
|
|
static bool ana_common(insn_t &insn, int data)
|
|
{
|
|
const struct opcode_t *op = opcode_t::find(insn, &data);
|
|
if ( op == NULL )
|
|
return false;
|
|
|
|
// fill instruction type
|
|
insn.itype = (uint16)op->itype;
|
|
|
|
// if instruction is implied, our job is finished!
|
|
if ( op->implied() )
|
|
goto ana_finished;
|
|
|
|
adjust_data(op->opcode_size, &data);
|
|
|
|
// fill operand 1
|
|
if ( op->op1 != O_null )
|
|
{
|
|
fill_op1(insn, data, op);
|
|
adjust_data(op->op1_size, &data);
|
|
}
|
|
|
|
// fill operand 2
|
|
if ( op->op2 != O_null )
|
|
{
|
|
fill_op2(insn, data, op);
|
|
adjust_data(op->op2_size, &data);
|
|
}
|
|
|
|
// swap opcodes if needed
|
|
if ( op->swap_ops() )
|
|
swap_ops(insn.Op1, insn.Op2);
|
|
|
|
ana_finished:
|
|
insn.auxpref = 0;
|
|
|
|
// is insn delay shot ?
|
|
if ( op->delay_shot() )
|
|
insn.auxpref |= INSN_DELAY_SHOT;
|
|
|
|
return true;
|
|
}
|
|
|
|
// analyze a "special" instruction (those which are NOT listed in the opcodes[] array).
|
|
static bool ana_special(insn_t &insn, int data)
|
|
{
|
|
// detect ldi:20 instructions
|
|
if ( data == 0x9B )
|
|
{
|
|
insn.itype = fr_ldi_20;
|
|
data = (data << 8) | insn.get_next_byte();
|
|
set_gr(insn.Op2, data & 0x000F);
|
|
set_imm(insn.Op1, insn.get_next_word() | ((data & 0x00F0) << 12), dt_dword);
|
|
return true;
|
|
}
|
|
|
|
data = (data << 8) | get_byte(insn.ea + insn.size);
|
|
|
|
// detect ldi:32 instructions
|
|
if ( (data & 0xFFF0) == 0x9F80 )
|
|
{
|
|
insn.size++;
|
|
insn.itype = fr_ldi_32;
|
|
set_gr(insn.Op2, data & 0x000F);
|
|
set_imm(insn.Op1, insn.get_next_dword(), dt_dword);
|
|
return true;
|
|
}
|
|
|
|
// detect call [rel] instructions
|
|
int tmp = (data & 0xF800) >> 11;
|
|
if ( tmp == 0x1A || tmp == 0x1B )
|
|
{
|
|
insn.itype = fr_call;
|
|
insn.size++;
|
|
// extend sign
|
|
if ( data & 0x400 )
|
|
data |= ~0x07FF;
|
|
else
|
|
data &= 0x07FF;
|
|
set_rel(insn, insn.Op1, data, dt_word);
|
|
if ( tmp == 0x1B )
|
|
insn.auxpref |= INSN_DELAY_SHOT;
|
|
return true;
|
|
}
|
|
|
|
// detect copop/copld/copst/copsv instructions
|
|
if ( ((data & 0xFF00) >> 8) == 0x9F )
|
|
{
|
|
int word = get_word(insn.ea + insn.size + 1);
|
|
insn.itype = fr_null;
|
|
switch ( (data & 0x00F0) >> 4 )
|
|
{
|
|
// copop
|
|
case 0xC:
|
|
insn.itype = fr_copop;
|
|
set_cr(insn.Op3, (word & 0x00F0) >> 4);
|
|
set_cr(insn.Op4, word & 0x000F);
|
|
break;
|
|
|
|
// copld
|
|
case 0xD:
|
|
insn.itype = fr_copld;
|
|
set_gr(insn.Op3, (word & 0x00F0) >> 4);
|
|
set_cr(insn.Op4, word & 0x000F);
|
|
break;
|
|
|
|
// copst
|
|
case 0xE:
|
|
insn.itype = fr_copst;
|
|
set_cr(insn.Op3, (word & 0x00F0) >> 4);
|
|
set_gr(insn.Op4, word & 0x000F);
|
|
break;
|
|
|
|
// copsv
|
|
case 0xF:
|
|
insn.itype = fr_copsv;
|
|
set_cr(insn.Op3, (word & 0x00F0) >> 4);
|
|
set_gr(insn.Op4, word & 0x000F);
|
|
break;
|
|
}
|
|
if ( insn.itype != fr_null )
|
|
{
|
|
set_imm(insn.Op1, data & 0x000F, dt_byte);
|
|
set_imm(insn.Op2, (word & 0xFF00) >> 8, dt_byte);
|
|
insn.size += 3;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// analyze an instruction.
|
|
int idaapi ana(insn_t *_insn)
|
|
{
|
|
insn_t &insn = *_insn;
|
|
|
|
#if defined(__DEBUG__)
|
|
opcode_t::check();
|
|
#endif /* __DEBUG__ */
|
|
|
|
int byte = insn.get_next_byte();
|
|
|
|
bool ok = ana_special(insn, byte);
|
|
if ( !ok )
|
|
ok = ana_common(insn, byte);
|
|
|
|
return ok ? insn.size : 0;
|
|
}
|