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

494 lines
17 KiB
C++

#include "m740.hpp"
// 740 addressing modes :
enum m740_addr_mode_t ENUM_SIZE(uint8)
{
A_IMM, // immediate
A_ACC, // accumulator
A_ZP, // zero page
A_ZPX, // zero page X
A_ZPY, // zero page Y
A_ABS, // absolute
A_ABSX, // absolute X
A_ABSY, // absolute Y
A_IMPL, // implied
A_REL, // relative
A_INDX, // indirect X
A_INDY, // indirect Y
A_INDABS, // indirect absolute
A_ZPIND, // zero page indirect
A_SP, // special page
A_ZPB, // zero page bit
A_ACCB, // accumulator bit
A_ACCBREL, // accumulator bit relative
A_ZPBREL // zero page bit relative
};
//lint -e{958} padding needed
struct opcode
{
uint16 insn; // instruction ID
uchar code; // operation code, one byte
m740_addr_mode_t addr; // addressing mode
};
// 740 operation codes :
static const struct opcode opcodes[] =
{
{ m740_adc, 0x69, A_IMM },
{ m740_adc, 0x65, A_ZP },
{ m740_adc, 0x75, A_ZPX },
{ m740_adc, 0x6D, A_ABS },
{ m740_adc, 0x7D, A_ABSX },
{ m740_adc, 0x79, A_ABSY },
{ m740_adc, 0x61, A_INDX },
{ m740_adc, 0x71, A_INDY },
{ m740_and, 0x29, A_IMM },
{ m740_and, 0x25, A_ZP },
{ m740_and, 0x35, A_ZPX },
{ m740_and, 0x2D, A_ABS },
{ m740_and, 0x3D, A_ABSX },
{ m740_and, 0x39, A_ABSY },
{ m740_and, 0x21, A_INDX },
{ m740_and, 0x31, A_INDY },
{ m740_asl, 0x0A, A_ACC },
{ m740_asl, 0x06, A_ZP },
{ m740_asl, 0x16, A_ZPX },
{ m740_asl, 0x0E, A_ABS },
{ m740_asl, 0x1E, A_ABSX },
{ m740_bcc, 0x90, A_REL },
{ m740_bcs, 0xB0, A_REL },
{ m740_beq, 0xF0, A_REL },
{ m740_bit, 0x24, A_ZP },
{ m740_bit, 0x2C, A_ABS },
{ m740_bmi, 0x30, A_REL },
{ m740_bne, 0xD0, A_REL },
{ m740_bpl, 0x10, A_REL },
{ m740_bra, 0x80, A_REL },
{ m740_brk, 0x00, A_IMPL },
{ m740_bvc, 0x50, A_REL },
{ m740_bvs, 0x70, A_REL },
{ m740_clc, 0x18, A_IMPL },
{ m740_cld, 0xD8, A_IMPL },
{ m740_cli, 0x58, A_IMPL },
{ m740_clt, 0x12, A_IMPL },
{ m740_clv, 0xB8, A_IMPL },
{ m740_cmp, 0xC9, A_IMM },
{ m740_cmp, 0xC5, A_ZP },
{ m740_cmp, 0xD5, A_ZPX },
{ m740_cmp, 0xCD, A_ABS },
{ m740_cmp, 0xDD, A_ABSX },
{ m740_cmp, 0xD9, A_ABSY },
{ m740_cmp, 0xC1, A_INDX },
{ m740_cmp, 0xD1, A_INDY },
{ m740_com, 0x44, A_ZP },
{ m740_cpx, 0xE0, A_IMM },
{ m740_cpx, 0xE4, A_ZP },
{ m740_cpx, 0xEC, A_ABS },
{ m740_cpy, 0xC0, A_IMM },
{ m740_cpy, 0xC4, A_ZP },
{ m740_cpy, 0xCC, A_ABS },
{ m740_dec, 0x1A, A_ACC },
{ m740_dec, 0xC6, A_ZP },
{ m740_dec, 0xD6, A_ZPX },
{ m740_dec, 0xCE, A_ABS },
{ m740_dec, 0xDE, A_ABSX },
{ m740_dex, 0xCA, A_IMPL },
{ m740_dey, 0x88, A_IMPL },
{ m740_div, 0xE2, A_ZPX },
{ m740_eor, 0x49, A_IMM },
{ m740_eor, 0x45, A_ZP },
{ m740_eor, 0x55, A_ZPX },
{ m740_eor, 0x4D, A_ABS },
{ m740_eor, 0x5D, A_ABSX },
{ m740_eor, 0x59, A_ABSY },
{ m740_eor, 0x41, A_INDX },
{ m740_eor, 0x51, A_INDY },
{ m740_inc, 0x3A, A_ACC },
{ m740_inc, 0xE6, A_ZP },
{ m740_inc, 0xF6, A_ZPX },
{ m740_inc, 0xEE, A_ABS },
{ m740_inc, 0xFE, A_ABSX },
{ m740_inx, 0xE8, A_IMPL },
{ m740_iny, 0xC8, A_IMPL },
{ m740_jmp, 0x4C, A_ABS },
{ m740_jmp, 0x6C, A_INDABS },
{ m740_jmp, 0xB2, A_ZPIND },
{ m740_jsr, 0x20, A_ABS },
{ m740_jsr, 0x22, A_SP },
{ m740_jsr, 0x02, A_ZPIND },
{ m740_lda, 0xA9, A_IMM },
{ m740_lda, 0xA5, A_ZP },
{ m740_lda, 0xB5, A_ZPX },
{ m740_lda, 0xAD, A_ABS },
{ m740_lda, 0xBD, A_ABSX },
{ m740_lda, 0xB9, A_ABSY },
{ m740_lda, 0xA1, A_INDX },
{ m740_lda, 0xB1, A_INDY },
{ m740_ldm, 0x3C, A_ZP },
{ m740_ldx, 0xA2, A_IMM },
{ m740_ldx, 0xA6, A_ZP },
{ m740_ldx, 0xB6, A_ZPY },
{ m740_ldx, 0xAE, A_ABS },
{ m740_ldx, 0xBE, A_ABSY },
{ m740_ldy, 0xA0, A_IMM },
{ m740_ldy, 0xA4, A_ZP },
{ m740_ldy, 0xB4, A_ZPX },
{ m740_ldy, 0xAC, A_ABS },
{ m740_ldy, 0xBC, A_ABSX },
{ m740_lsr, 0x4A, A_ACC },
{ m740_lsr, 0x46, A_ZP },
{ m740_lsr, 0x56, A_ZPX },
{ m740_lsr, 0x4E, A_ABS },
{ m740_lsr, 0x5E, A_ABSX },
{ m740_mul, 0x62, A_ZPX },
{ m740_nop, 0xEA, A_IMPL },
{ m740_ora, 0x09, A_IMM },
{ m740_ora, 0x05, A_ZP },
{ m740_ora, 0x15, A_ZPX },
{ m740_ora, 0x0D, A_ABS },
{ m740_ora, 0x1D, A_ABSX },
{ m740_ora, 0x19, A_ABSY },
{ m740_ora, 0x01, A_INDX },
{ m740_ora, 0x11, A_INDY },
{ m740_pha, 0x48, A_IMPL },
{ m740_php, 0x08, A_IMPL },
{ m740_pla, 0x68, A_IMPL },
{ m740_plp, 0x28, A_IMPL },
{ m740_rol, 0x2A, A_ACC },
{ m740_rol, 0x26, A_ZP },
{ m740_rol, 0x36, A_ZPX },
{ m740_rol, 0x2E, A_ABS },
{ m740_rol, 0x3E, A_ABSX },
{ m740_ror, 0x6A, A_ACC },
{ m740_ror, 0x66, A_ZP },
{ m740_ror, 0x76, A_ZPX },
{ m740_ror, 0x6E, A_ABS },
{ m740_ror, 0x7E, A_ABSX },
{ m740_rrf, 0x82, A_ZP },
{ m740_rti, 0x40, A_IMPL },
{ m740_rts, 0x60, A_IMPL },
{ m740_sbc, 0xE9, A_IMM },
{ m740_sbc, 0xE5, A_ZP },
{ m740_sbc, 0xF5, A_ZPX },
{ m740_sbc, 0xED, A_ABS },
{ m740_sbc, 0xFD, A_ABSX },
{ m740_sbc, 0xF9, A_ABSY },
{ m740_sbc, 0xE1, A_INDX },
{ m740_sbc, 0xF1, A_INDY },
{ m740_sec, 0x38, A_IMPL },
{ m740_sed, 0xF8, A_IMPL },
{ m740_sei, 0x78, A_IMPL },
{ m740_set, 0x32, A_IMPL },
{ m740_sta, 0x85, A_ZP },
{ m740_sta, 0x95, A_ZPX },
{ m740_sta, 0x8D, A_ABS },
{ m740_sta, 0x9D, A_ABSX },
{ m740_sta, 0x99, A_ABSY },
{ m740_sta, 0x81, A_INDX },
{ m740_sta, 0x91, A_INDY },
{ m740_stp, 0x42, A_IMPL },
{ m740_stx, 0x86, A_ZP },
{ m740_stx, 0x96, A_ZPY },
{ m740_stx, 0x8E, A_ABS },
{ m740_sty, 0x84, A_ZP },
{ m740_sty, 0x94, A_ZPX },
{ m740_sty, 0x8C, A_ABS },
{ m740_tax, 0xAA, A_IMPL },
{ m740_tay, 0xA8, A_IMPL },
{ m740_tst, 0x64, A_ZP },
{ m740_tsx, 0xBA, A_IMPL },
{ m740_txa, 0x8A, A_IMPL },
{ m740_txs, 0x9A, A_IMPL },
{ m740_tya, 0x98, A_IMPL },
{ m740_wit, 0xC2, A_IMPL }
};
struct opcode_flag
{
uint16 insn;
uchar flags;
#define MEM_R OP_ADDR_R // read access
#define MEM_W OP_ADDR_W // write access
};
static const struct opcode_flag opcodes_flags[] =
{
{ m740_adc, MEM_R },
{ m740_and, MEM_R },
{ m740_asl, MEM_W },
{ m740_bbc, MEM_R },
{ m740_bbs, MEM_R },
{ m740_bit, MEM_R },
{ m740_clb, MEM_W },
{ m740_cmp, MEM_R },
{ m740_com, MEM_W },
{ m740_cpx, MEM_R },
{ m740_cpy, MEM_R },
{ m740_dec, MEM_W },
{ m740_eor, MEM_R },
{ m740_inc, MEM_W },
{ m740_jmp, MEM_R },
{ m740_jsr, MEM_R },
{ m740_lda, MEM_R },
{ m740_ldm, MEM_W },
{ m740_ldx, MEM_R },
{ m740_ldy, MEM_R },
{ m740_lsr, MEM_W },
{ m740_ora, MEM_R },
{ m740_rol, MEM_W },
{ m740_ror, MEM_W },
{ m740_sbc, MEM_R },
{ m740_seb, MEM_W },
{ m740_sta, MEM_W },
{ m740_stx, MEM_W },
{ m740_sty, MEM_W },
{ m740_tst, MEM_R },
{ m740_rrf, MEM_W }
};
// fill operand as a register
inline static void set_op_reg(op_t &op, uint16 reg)
{
op.type = o_reg;
op.reg = reg;
op.dtype = dt_word; // XXX not sure
}
// a shortcut to make our live easier
#define set_op_acc(x) set_op_reg(x, rA)
// fill operand as a code address
inline static void set_op_addr(op_t &op, ea_t addr)
{
op.type = o_near;
op.addr = addr;
op.dtype = dt_code;
}
// fill operand as a displacement between a memory address and a register contents
inline static void set_op_displ(insn_t &insn, int addr, uint16 reg, char d_typ = dt_byte)
{
insn.Op1.type = o_displ;
insn.Op1.addr = addr;
insn.Op1.reg = reg;
insn.Op1.dtype = d_typ;
}
// fill operand as a data address
inline static void set_op_mem(op_t &op, int addr, const uchar flags = 0, char d_typ = dt_byte)
{
op.type = o_mem;
op.addr = addr;
op.dtype = d_typ;
op.specflag1 = flags;
}
// fill operand as an immediate value
inline static void set_op_imm(op_t &op, int imm)
{
op.type = o_imm;
op.value = imm;
op.dtype = dt_byte;
}
// fill the insn structure according to the addressing mode of the
// current analyzed instruction
static void fill_insn(insn_t &insn, m740_addr_mode_t addr, const uchar flags = 0)
{
switch ( addr )
{
case A_IMM: // immediate
set_op_imm(insn.Op1, insn.get_next_byte());
break;
case A_ACC: // accumulator
set_op_acc(insn.Op1);
break;
case A_ZP: // zero page
if ( insn.itype == m740_ldm ) // special case
{
set_op_imm(insn.Op1, insn.get_next_byte());
set_op_mem(insn.Op2, insn.get_next_byte(), flags);
}
else
{
set_op_mem(insn.Op1, insn.get_next_byte(), flags);
}
break;
case A_ZPX: // zero page X
set_op_displ(insn, insn.get_next_byte(), rX);
insn.auxpref |= INSN_DISPL_ZPX;
break;
case A_ZPY: // zero page Y
set_op_displ(insn, insn.get_next_byte(), rY);
insn.auxpref |= INSN_DISPL_ZPY;
break;
case A_ABS: // absolute
if ( insn.itype == m740_jmp || insn.itype == m740_jsr )
set_op_addr(insn.Op1, insn.get_next_word());
else
set_op_mem(insn.Op1, insn.get_next_word(), flags);
break;
case A_ABSX: // absolute X
set_op_displ(insn, insn.get_next_word(), rX);
insn.auxpref |= INSN_DISPL_ABSX;
break;
case A_ABSY: // absolute Y
set_op_displ(insn, insn.get_next_word(), rY);
insn.auxpref |= INSN_DISPL_ABSY;
break;
case A_IMPL: // implied
// nothing to do..
break;
case A_REL: // relative
set_op_addr(insn.Op1, (signed char) insn.get_next_byte() + insn.ea + 2);
break;
case A_INDX: // indirect X
set_op_displ(insn, insn.get_next_byte(), rX, dt_word);
insn.auxpref |= INSN_DISPL_INDX;
break;
case A_INDY: // indirect Y
set_op_displ(insn, insn.get_next_byte(), rY, dt_word);
insn.auxpref |= INSN_DISPL_INDY;
break;
case A_INDABS: // indirect absolute
set_op_mem(insn.Op1, insn.get_next_word(), flags, dt_word);
insn.Op1.specflag1 |= OP_ADDR_IND;
break;
case A_ZPIND: // zero page indirect
set_op_mem(insn.Op1, insn.get_next_byte(), 0, dt_word);
insn.Op1.specflag1 |= OP_ADDR_IND;
break;
case A_SP: // special page
set_op_addr(insn.Op1, insn.get_next_byte() | 0xFF00);
insn.Op1.specflag1 |= OP_ADDR_SP;
break;
case A_ZPB: // zero page bit
set_op_mem(insn.Op2, insn.get_next_byte(), flags, dt_word);
break;
case A_ACCB: // accumulator bit
set_op_acc(insn.Op2);
break;
case A_ACCBREL: // accumulator bit relative
set_op_acc(insn.Op2);
set_op_addr(insn.Op3, (signed char) insn.get_next_byte() + insn.ea + 2);
break;
case A_ZPBREL: // zero page bit relative
set_op_mem(insn.Op2, insn.get_next_byte(), flags);
set_op_addr(insn.Op3, (signed char) insn.get_next_byte() + insn.ea + 3);
break;
default:
INTERR(10025);
}
}
// try to find an opcode in our table from the fetched byte
static const struct opcode *get_opcode(int byte)
{
for ( int i = 0; i < qnumber(opcodes); i++ )
{
if ( opcodes[i].code != byte )
continue;
return &opcodes[i];
}
return NULL;
}
static uchar get_opcode_flags(const uint16 insn)
{
for ( int i = 0; i < qnumber(opcodes_flags); i++ )
{
if ( opcodes_flags[i].insn != insn )
continue;
return opcodes_flags[i].flags;
}
return 0;
}
// detect special instructions, whose we can't detect using the table and the
// get_opcode() routine
static bool ana_special(insn_t &insn, int byte)
{
bool special = false;
//lint -e{958} padding needed
struct spec_info_t
{
uint16 insn; // instruction ID
uchar val; // (20i + val)
m740_addr_mode_t addr; // which addressing mode ?
};
static const spec_info_t specials[] =
{
{ m740_bbc, 0x13, A_ACCBREL },
{ m740_bbc, 0x17, A_ZPBREL },
{ m740_bbs, 0x03, A_ACCBREL },
{ m740_bbs, 0x07, A_ZPBREL },
{ m740_clb, 0x1B, A_ACCB },
{ m740_clb, 0x1F, A_ZPB },
{ m740_seb, 0x0B, A_ACCB },
{ m740_seb, 0x0F, A_ZPB }
};
for ( int i = 0; i < qnumber(specials); i++ )
{
int t = (uchar) byte - specials[i].val;
if ( (t % 0x20) != 0 )
continue;
insn.itype = specials[i].insn;
set_op_imm(insn.Op1, t / 0x20);
insn.Op1.specflag1 |= OP_IMM_BIT;
fill_insn(insn, specials[i].addr, get_opcode_flags(specials[i].insn));
special = true;
break;
}
return special;
}
// analyze an instruction
int idaapi ana(insn_t *_insn)
{
insn_t &insn = *_insn;
bool special;
int byte;
byte = insn.get_next_byte();
special = ana_special(insn, byte);
if ( !special )
{
const struct opcode *op = get_opcode(byte);
if ( op == NULL ) // unmatched insn
return 0;
insn.itype = op->insn;
fill_insn(insn, op->addr, get_opcode_flags(op->insn));
}
return insn.size;
}