1084 lines
32 KiB
C++
1084 lines
32 KiB
C++
/*
|
|
* Interactive disassembler (IDA).
|
|
* Copyright (c) 1990-98 by Ilfak Guilfanov.
|
|
* ALL RIGHTS RESERVED.
|
|
* E-mail: ig@estar.msk.su, ig@datarescue.com
|
|
* FIDO: 2:5020/209
|
|
*
|
|
*/
|
|
|
|
#include "i51.hpp"
|
|
|
|
//----------------------------------------------------------------------
|
|
inline uint32 get_next_24bits(insn_t &insn)
|
|
{
|
|
uint32 high = insn.get_next_byte();
|
|
uint32 low = insn.get_next_word();
|
|
return low | (high<<16);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static void operand1(insn_t &insn, int nibble)
|
|
{
|
|
switch ( nibble )
|
|
{
|
|
case 4:
|
|
insn.Op1.type = o_reg;
|
|
insn.Op1.reg = rAcc;
|
|
break;
|
|
case 5:
|
|
insn.Op1.type = o_mem;
|
|
insn.Op1.addr = insn.get_next_byte();
|
|
break;
|
|
case 6:
|
|
insn.Op1.type = o_phrase;
|
|
insn.Op1.phrase = fR0;
|
|
break;
|
|
case 7:
|
|
insn.Op1.type = o_phrase;
|
|
insn.Op1.phrase = fR1;
|
|
break;
|
|
default:
|
|
insn.Op1.type = o_reg;
|
|
insn.Op1.phrase = uint16(rR0 + (nibble-8));
|
|
break;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static void operand2(insn_t &insn, ushort nibble)
|
|
{
|
|
switch ( nibble )
|
|
{
|
|
case 4:
|
|
insn.Op2.type = o_imm;
|
|
insn.Op2.value = insn.get_next_byte();
|
|
break;
|
|
case 5:
|
|
insn.Op2.type = o_mem;
|
|
insn.Op2.addr = insn.get_next_byte();
|
|
break;
|
|
case 6:
|
|
insn.Op2.type = o_phrase;
|
|
insn.Op2.phrase = fR0;
|
|
break;
|
|
case 7:
|
|
insn.Op2.type = o_phrase;
|
|
insn.Op2.phrase = fR1;
|
|
break;
|
|
default:
|
|
insn.Op2.type = o_reg;
|
|
insn.Op2.phrase = rR0 + (nibble-8);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline void opAcc(op_t &op)
|
|
{
|
|
op.type = o_reg;
|
|
op.reg = rAcc;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline void opC(op_t &op)
|
|
{
|
|
op.type = o_reg;
|
|
op.reg = rC;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// register direct
|
|
static int op_rd(op_t &x, uint16 reg, uchar dtyp)
|
|
{
|
|
if ( reg >= rDR32 && reg <= rDR52 )
|
|
return 0;
|
|
x.type = o_reg;
|
|
x.dtype = dtyp;
|
|
x.reg = reg;
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// register indirect (fRi registers)
|
|
static int op_ph(op_t &x, int reg, uchar dtyp)
|
|
{
|
|
if ( reg >= rDR32 && reg <= rDR52 )
|
|
return 0;
|
|
x.type = o_phrase;
|
|
x.dtype = dtyp;
|
|
x.reg = fRi;
|
|
x.indreg = uchar(reg);
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// register indirect
|
|
static int op_ph(op_t &op, i51_phrases phr, uchar dtyp, uval_t disp = 0)
|
|
{
|
|
op.type = o_phrase;
|
|
op.dtype = dtyp;
|
|
op.phrase = phr;
|
|
if ( disp > 0 )
|
|
{
|
|
op.imm_disp = 1;
|
|
op.value = disp;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
// register indirect with displacement
|
|
static int op_ds(op_t &x, uint16 phrase, uval_t disp, uchar dtyp)
|
|
{
|
|
if ( phrase >= rDR32 && phrase <= rDR52 )
|
|
return 0;
|
|
x.type = o_displ;
|
|
x.dtype = dtyp;
|
|
x.phrase = phrase;
|
|
x.addr = disp;
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline void op_mm(op_t &x, uval_t addr, uchar dtyp)
|
|
{
|
|
x.type = o_mem;
|
|
x.dtype = dtyp;
|
|
x.addr = addr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline void op_near(op_t &x, uval_t addr)
|
|
{
|
|
x.type = o_near;
|
|
x.dtype = dt_word;
|
|
x.addr = addr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline void op_im(op_t &x, uval_t value, uchar dtyp)
|
|
{
|
|
x.type = o_imm;
|
|
x.dtype = dtyp;
|
|
x.value = value;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
uint32 i51_t::truncate(uval_t addr) const
|
|
{
|
|
if ( ptype == prc_51 )
|
|
return addr & 0xFFFF;
|
|
else
|
|
return addr & 0xFFFFFF;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static int make_short(insn_t &insn, uint16 itype, uchar b)
|
|
{
|
|
insn.itype = itype;
|
|
static const uchar bregs[] = { rR0, rWR0, 0, rDR0 };
|
|
static const uchar dtyps[] = { dt_byte, dt_word, dt_dword };
|
|
int idx = (b >> 2) & 3;
|
|
if ( !op_rd(insn.Op1, bregs[idx] + (b>>4), dtyps[idx]) )
|
|
return 0;
|
|
b &= 3;
|
|
if ( b == 3 )
|
|
return 0;
|
|
op_im(insn.Op2, uval_t(1)<<b, dt_byte);
|
|
return insn.size;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// analyze extended instruction set
|
|
|
|
int i51_t::ana_extended(insn_t &insn)
|
|
{
|
|
int code = insn.get_next_byte();
|
|
if ( (code & 8) == 0 )
|
|
return 0;
|
|
if ( (code & 0xF0) >= 0xE0 )
|
|
return 0;
|
|
|
|
static const uchar itypes[] =
|
|
{
|
|
/* 8 9 A B C D E F */
|
|
/* 0 */ I51_jsle, I51_mov, I51_movz, I51_mov, I51_null, I51_null, I51_sra, I51_null,
|
|
/* 1 */ I51_jsg, I51_mov, I51_movs, I51_mov, I51_null, I51_null, I51_srl, I51_null,
|
|
/* 2 */ I51_jle, I51_mov, I51_null, I51_null, I51_add, I51_add, I51_add, I51_add,
|
|
/* 3 */ I51_jg, I51_mov, I51_null, I51_null, I51_null, I51_null, I51_sll, I51_null,
|
|
/* 4 */ I51_jsl, I51_mov, I51_null, I51_null, I51_orl, I51_orl, I51_orl, I51_null,
|
|
/* 5 */ I51_jsge, I51_mov, I51_null, I51_null, I51_anl, I51_anl, I51_anl, I51_null,
|
|
/* 6 */ I51_je, I51_mov, I51_null, I51_null, I51_xrl, I51_xrl, I51_xrl, I51_null,
|
|
/* 7 */ I51_jne, I51_mov, I51_mov, I51_null, I51_mov, I51_mov, I51_mov, I51_mov,
|
|
/* 8 */ I51_null, I51_ljmp, I51_ejmp, I51_null, I51_div, I51_div, I51_null, I51_null,
|
|
/* 9 */ I51_null, I51_lcall, I51_ecall, I51_null, I51_sub, I51_sub, I51_sub, I51_sub,
|
|
/* A */ I51_null, I51_last, I51_eret, I51_null, I51_mul, I51_mul, I51_null, I51_null,
|
|
/* B */ I51_null, I51_trap, I51_null, I51_null, I51_cmp, I51_cmp, I51_cmp, I51_cmp,
|
|
/* C */ I51_null, I51_null, I51_push, I51_null, I51_null, I51_null, I51_null, I51_null,
|
|
/* D */ I51_null, I51_null, I51_pop, I51_null, I51_null, I51_null, I51_null, I51_null,
|
|
};
|
|
insn.itype = itypes[ ((code&0xF0)>>1) | (code & 7) ];
|
|
if ( insn.itype == I51_null )
|
|
return 0;
|
|
|
|
uchar b1, b2;
|
|
int oax = 0;
|
|
switch ( code )
|
|
{
|
|
case 0x08: // rel
|
|
case 0x18:
|
|
case 0x28:
|
|
case 0x38:
|
|
case 0x48:
|
|
case 0x58:
|
|
case 0x68:
|
|
case 0x78:
|
|
{
|
|
insn.Op1.type = o_near;
|
|
insn.Op1.dtype = dt_word;
|
|
signed char off = insn.get_next_byte();
|
|
insn.Op1.addr = truncate(insn.ip + insn.size + off); // signed addition
|
|
}
|
|
break;
|
|
|
|
case 0x09: // mov Rm, @WRj+dis
|
|
b1 = insn.get_next_byte();
|
|
op_rd(insn.Op1, rR0 +(b1>>4), dt_byte);
|
|
insn.Op2.offb = (uchar)insn.size;
|
|
op_ds(insn.Op2, rWR0+(b1&15), insn.get_next_word(), dt_byte);
|
|
break;
|
|
|
|
case 0x49: // mov WRk, @WRj+dis
|
|
b1 = insn.get_next_byte();
|
|
op_rd(insn.Op1, rWR0+(b1>>4), dt_word);
|
|
insn.Op2.offb = (uchar)insn.size;
|
|
op_ds(insn.Op2, rWR0+(b1&15), insn.get_next_word(), dt_word);
|
|
break;
|
|
|
|
case 0x29: // mov Rm, @DRj+dis
|
|
b1 = insn.get_next_byte();
|
|
op_rd(insn.Op1, rR0 +(b1>>4), dt_byte);
|
|
insn.Op2.offb = (uchar)insn.size;
|
|
if ( !op_ds(insn.Op2, rDR0+(b1&15), insn.get_next_word(), dt_byte) )
|
|
return 0;
|
|
break;
|
|
|
|
case 0x69: // mov WRj, @DRk+dis
|
|
b1 = insn.get_next_byte();
|
|
op_rd(insn.Op1, rWR0+(b1>>4), dt_word);
|
|
insn.Op2.offb = (uchar)insn.size;
|
|
if ( !op_ds(insn.Op2, rDR0+(b1&15), insn.get_next_word(), dt_word) )
|
|
return 0;
|
|
break;
|
|
|
|
case 0x19: // mov @WRj+dis, Rm
|
|
b1 = insn.get_next_byte();
|
|
insn.Op1.offb = (uchar)insn.size;
|
|
op_ds(insn.Op1, rWR0+(b1&15), insn.get_next_word(), dt_byte);
|
|
op_rd(insn.Op2, rR0 +(b1>>4), dt_byte);
|
|
break;
|
|
|
|
case 0x59: // mov @WRj+dis, WRk
|
|
b1 = insn.get_next_byte();
|
|
insn.Op1.offb = (uchar)insn.size;
|
|
op_ds(insn.Op1, rWR0+(b1&15), insn.get_next_word(), dt_word);
|
|
op_rd(insn.Op2, rWR0+(b1>>4), dt_word);
|
|
break;
|
|
|
|
case 0x39: // mov @DRj+dis, Rm
|
|
b1 = insn.get_next_byte();
|
|
insn.Op1.offb = (uchar)insn.size;
|
|
if ( !op_ds(insn.Op1, rDR0+(b1&15), insn.get_next_word(), dt_byte) )
|
|
return 0;
|
|
op_rd(insn.Op2, rR0 +(b1>>4), dt_byte);
|
|
break;
|
|
|
|
case 0x79: // mov @DRk+dis, WRj
|
|
b1 = insn.get_next_byte();
|
|
insn.Op1.offb = (uchar)insn.size;
|
|
if ( !op_ds(insn.Op1, rDR0+(b1&15), insn.get_next_word(), dt_word) )
|
|
return 0;
|
|
op_rd(insn.Op2, rWR0+(b1>>4), dt_word);
|
|
break;
|
|
|
|
case 0x0A: // movz WRj, Rm
|
|
case 0x1A: // movs WRj, Rm
|
|
b1 = insn.get_next_byte();
|
|
op_rd(insn.Op1, rWR0+(b1>>4), dt_word);
|
|
op_rd(insn.Op2, rR0 +(b1&15), dt_byte);
|
|
break;
|
|
|
|
case 0x0B: // 1000 mov WRj, @WRj
|
|
// 1010 mov WRj, @DRk
|
|
{
|
|
b1 = insn.get_next_byte();
|
|
int ri;
|
|
switch ( b1 & 15 )
|
|
{
|
|
case 0x8: ri = rWR0; break;
|
|
case 0xA: ri = rDR0; break;
|
|
case 0x9: return 0;
|
|
case 0xB: return 0;
|
|
default: return make_short(insn, I51_inc, b1);
|
|
}
|
|
b2 = insn.get_next_byte();
|
|
if ( b2 & 15 )
|
|
return 0;
|
|
op_rd(insn.Op1, rWR0+(b2>>4), dt_word);
|
|
if ( !op_ph(insn.Op2, ri + (b1>>4), dt_word) )
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case 0x1B: // 1000 mov @WRj, WRj
|
|
// 1010 mov @DRk, WRj
|
|
{
|
|
b1 = insn.get_next_byte();
|
|
int ri;
|
|
switch ( b1 & 15 )
|
|
{
|
|
case 0x8: ri = rWR0; break;
|
|
case 0xA: ri = rDR0; break;
|
|
case 0x9: return 0;
|
|
case 0xB: return 0;
|
|
default: return make_short(insn, I51_dec, b1);
|
|
}
|
|
b2 = insn.get_next_byte();
|
|
if ( b2 & 15 )
|
|
return 0;
|
|
if ( !op_ph(insn.Op1, ri + (b1>>4), dt_word) )
|
|
return 0;
|
|
op_rd(insn.Op2, rWR0+(b2>>4), dt_word);
|
|
}
|
|
break;
|
|
|
|
case 0x7A:
|
|
{
|
|
b1 = insn.get_next_byte();
|
|
switch ( b1&15 )
|
|
{
|
|
case 9: // 1001 mov @WRj, Rm
|
|
case 0xB: // 1011 mov @DRk, Rm
|
|
b2 = insn.get_next_byte();
|
|
if ( b2 & 15 )
|
|
return 0;
|
|
if ( !op_ph(insn.Op1, ((b1&2) ? rDR0 : rWR0) + (b1>>4), dt_byte) )
|
|
return 0;
|
|
op_rd(insn.Op2, rR0+(b2>>4), dt_byte);
|
|
break;
|
|
case 0xC: // movh DRk, #data16
|
|
insn.itype = I51_movh;
|
|
if ( !op_rd(insn.Op1, rDR0+(b1>>4), dt_dword) )
|
|
return 0;
|
|
insn.Op2.offb = (uchar)insn.size;
|
|
op_im(insn.Op2, insn.get_next_word(), dt_word);
|
|
break;
|
|
default:
|
|
goto CONT;
|
|
}
|
|
break;
|
|
CONT:
|
|
uval_t addr = (b1&2) ? insn.get_next_word() : insn.get_next_byte();
|
|
switch ( b1&15 )
|
|
{
|
|
case 0x1: // mov dir8, Rm
|
|
case 0x3: // mov dir16, Rm
|
|
op_mm(insn.Op1, addr, dt_byte);
|
|
op_rd(insn.Op2, rR0+(b1>>4), dt_byte);
|
|
break;
|
|
case 0x5: // mov dir8, WRj
|
|
case 0x7: // mov dir16, WRj
|
|
op_mm(insn.Op1, addr, dt_word);
|
|
op_rd(insn.Op2, rWR0+(b1>>4), dt_word);
|
|
break;
|
|
case 0xD: // mov dir8, DRj
|
|
case 0xF: // mov dir16, DRj
|
|
op_mm(insn.Op1, addr, dt_dword);
|
|
if ( !op_rd(insn.Op2, rDR0+(b1>>4), dt_dword) )
|
|
return 0;
|
|
break;
|
|
default: return 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0x89: // ljmp @WRj or @DRj
|
|
case 0x99: // lcall @WRj or @DRj
|
|
{
|
|
int r;
|
|
uchar dt;
|
|
b1 = insn.get_next_byte();
|
|
switch ( b1 & 15 )
|
|
{
|
|
case 4:
|
|
r = rWR0;
|
|
dt = dt_word;
|
|
break;
|
|
case 8:
|
|
r = rDR0;
|
|
dt = dt_dword;
|
|
insn.itype = (insn.itype == I51_ljmp) ? I51_ejmp : I51_ecall;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
if ( !op_ph(insn.Op1, r+(b1>>4), dt) )
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case 0x8A: // ejmp addr24
|
|
case 0x9A: // ecall addr24
|
|
op_near(insn.Op1, get_next_24bits(insn));
|
|
break;
|
|
|
|
case 0xAA: // eret
|
|
case 0xB9: // trap
|
|
break;
|
|
|
|
case 0xCA: // push
|
|
case 0xDA: // pop
|
|
b1 = insn.get_next_byte();
|
|
switch ( b1 & 15 )
|
|
{
|
|
case 0x1: // mov DRk, PC
|
|
if ( code != 0xCA )
|
|
return 0;
|
|
insn.itype = I51_mov;
|
|
if ( !op_rd(insn.Op1, rDR0+(b1>>4), dt_dword) )
|
|
return 0;
|
|
op_rd(insn.Op2, rPC, dt_dword);
|
|
break;
|
|
case 0x2: // #data8
|
|
insn.Op1.offb = (uchar)insn.size;
|
|
op_im(insn.Op1, insn.get_next_byte(), dt_byte);
|
|
break;
|
|
case 0x6: // #data16
|
|
insn.Op1.offb = (uchar)insn.size;
|
|
op_im(insn.Op1, insn.get_next_word(), dt_word);
|
|
break;
|
|
case 0x8: // Rm
|
|
op_rd(insn.Op1, rR0+(b1>>4), dt_byte);
|
|
break;
|
|
case 0x9: // WRj
|
|
op_rd(insn.Op1, rWR0+(b1>>4), dt_word);
|
|
break;
|
|
case 0xB: // DRj
|
|
if ( !op_rd(insn.Op1, rDR0+(b1>>4), dt_dword) )
|
|
return 0;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case 0xA9: // bit instructions
|
|
{
|
|
static const uchar subtypes[] =
|
|
{
|
|
I51_null, I51_jbc, I51_jb, I51_jnb,
|
|
I51_null, I51_null, I51_null, I51_orl,
|
|
I51_anl, I51_mov, I51_mov, I51_cpl,
|
|
I51_clr, I51_setb, I51_orl, I51_anl
|
|
};
|
|
b1 = insn.get_next_byte();
|
|
if ( b1 & 8 )
|
|
return 0;
|
|
insn.itype = subtypes[ b1 >> 4];
|
|
if ( insn.itype == I51_null )
|
|
return 0;
|
|
insn.Op1.type = o_bit251;
|
|
insn.Op1.dtype = dt_byte;
|
|
insn.Op1.b251_bit = b1 & 7;
|
|
insn.Op1.addr = insn.get_next_byte();
|
|
insn.Op1.b251_bitneg = 0;
|
|
switch ( b1 >> 4 )
|
|
{
|
|
case 0x1: // jbc bit, rel
|
|
case 0x2: // jb bit, rel
|
|
case 0x3: // jnb bit, rel
|
|
{
|
|
signed char rel = insn.get_next_byte();
|
|
op_near(insn.Op2, truncate(insn.ip + insn.size + rel));
|
|
}
|
|
break;
|
|
case 0xE: // orl cy, /bit
|
|
case 0xF: // anl cy, /bit
|
|
insn.Op1.b251_bitneg = 1;
|
|
/* no break */
|
|
case 0x7: // orl cy, bit
|
|
case 0x8: // anl cy, bit
|
|
case 0xA: // mov cy, bit
|
|
insn.Op2 = insn.Op1;
|
|
opC(insn.Op1);
|
|
break;
|
|
case 0x9: // mov bit, cy
|
|
opC(insn.Op2);
|
|
break;
|
|
case 0xB: // cpl bit
|
|
case 0xC: // clr bit
|
|
case 0xD: // setb bit
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0x0E: // sra
|
|
case 0x1E: // srl
|
|
case 0x3E: // sll
|
|
b1 = insn.get_next_byte();
|
|
switch ( b1 & 15 )
|
|
{
|
|
case 0:
|
|
op_rd(insn.Op1, rR0 +(b1>>4), dt_byte);
|
|
break;
|
|
case 4:
|
|
op_rd(insn.Op1, rWR0+(b1>>4), dt_word);
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case 0x2C: // add Rm, Rm
|
|
case 0x4C: // orl Rm, Rm
|
|
case 0x5C: // anl Rm, Rm
|
|
case 0x6C: // xrl Rm, Rm
|
|
case 0x7C: // mov Rm, Rm
|
|
case 0x8C: // div Rm, Rm
|
|
case 0x9C: // sub Rm, Rm
|
|
case 0xAC: // mul Rm, Rm
|
|
case 0xBC: // cmp Rm, Rm
|
|
b1 = insn.get_next_byte();
|
|
op_rd(insn.Op1, rR0+(b1>>4), dt_byte);
|
|
op_rd(insn.Op2, rR0+(b1&15), dt_byte);
|
|
break;
|
|
|
|
case 0x2D: // add WRj, WRj
|
|
case 0x4D: // orl WRj, WRj
|
|
case 0x5D: // anl WRj, WRj
|
|
case 0x6D: // xrl WRj, WRj
|
|
case 0x7D: // mov WRj, WRj
|
|
case 0x8D: // div WRj, WRj
|
|
case 0x9D: // sub WRj, WRj
|
|
case 0xAD: // mul WRj, WRj
|
|
case 0xBD: // cmp WRj, WRj
|
|
b1 = insn.get_next_byte();
|
|
op_rd(insn.Op1, rWR0+(b1>>4), dt_word);
|
|
op_rd(insn.Op2, rWR0+(b1&15), dt_word);
|
|
break;
|
|
|
|
case 0x2F: // add DRj, DRj
|
|
case 0x7F: // mov DRj, DRj
|
|
case 0x9F: // sub DRj, DRj
|
|
case 0xBF: // cmp DRj, DRj
|
|
b1 = insn.get_next_byte();
|
|
if ( !op_rd(insn.Op1, rDR0+(b1>>4), dt_dword) )
|
|
return 0;
|
|
if ( !op_rd(insn.Op2, rDR0+(b1&15), dt_dword) )
|
|
return 0;
|
|
break;
|
|
|
|
case 0x4E: // orl reg, op2
|
|
case 0x5E: // anl reg, op2
|
|
case 0x6E: // xrl reg, op2
|
|
oax = 1; // orl, anl, xrl
|
|
/* no break */
|
|
case 0x2E: // add reg, op2
|
|
case 0x7E: // mov reg, op2
|
|
case 0x8E: // div reg, op2
|
|
case 0x9E: // sub reg, op2
|
|
case 0xAE: // mul reg, op2
|
|
case 0xBE: // cmp reg, op2
|
|
b1 = insn.get_next_byte();
|
|
switch ( b1 & 15 )
|
|
{
|
|
case 0x0: // Rm, #8
|
|
op_rd(insn.Op1, rR0+(b1>>4), dt_byte);
|
|
insn.Op2.offb = (uchar)insn.size;
|
|
op_im(insn.Op2, insn.get_next_byte(), dt_byte);
|
|
break;
|
|
case 0x4: // WRj, #16
|
|
op_rd(insn.Op1, rWR0+(b1>>4), dt_word);
|
|
insn.Op2.offb = (uchar)insn.size;
|
|
op_im(insn.Op2, insn.get_next_word(), dt_word);
|
|
break;
|
|
case 0x8: // DRk, #16
|
|
if ( oax )
|
|
return 0;
|
|
if ( !op_rd(insn.Op1, rDR0+(b1>>4), dt_dword) )
|
|
return 0;
|
|
insn.Op2.offb = (uchar)insn.size;
|
|
op_im(insn.Op2, insn.get_next_word(), dt_word);
|
|
break;
|
|
case 0xC: // DRk, #(1)16
|
|
if ( oax )
|
|
return 0;
|
|
if ( !op_rd(insn.Op1, rDR0+(b1>>4), dt_dword) )
|
|
return 0;
|
|
insn.Op2.offb = (uchar)insn.size;
|
|
op_im(insn.Op2, insn.get_next_word(), dt_word);
|
|
insn.auxpref |= aux_1ext;
|
|
break;
|
|
case 0x1: // Rm, dir8
|
|
op_rd(insn.Op1, rR0+(b1>>4), dt_byte);
|
|
op_mm(insn.Op2, insn.get_next_byte(), dt_byte);
|
|
break;
|
|
case 0x5: // WRj, dir8
|
|
op_rd(insn.Op1, rWR0+(b1>>4), dt_word);
|
|
op_mm(insn.Op2, insn.get_next_byte(), dt_word);
|
|
break;
|
|
case 0xD: // DRk, dir8
|
|
if ( oax )
|
|
return 0;
|
|
if ( !op_rd(insn.Op1, rDR0+(b1>>4), dt_dword) )
|
|
return 0;
|
|
op_mm(insn.Op2, insn.get_next_byte(), dt_word);
|
|
break;
|
|
case 0x3: // Rm, dir16
|
|
op_rd(insn.Op1, rR0+(b1>>4), dt_byte);
|
|
op_mm(insn.Op2, insn.get_next_word(), dt_byte);
|
|
break;
|
|
case 0x7: // WRj, dir16
|
|
op_rd(insn.Op1, rWR0+(b1>>4), dt_word);
|
|
op_mm(insn.Op2, insn.get_next_word(), dt_word);
|
|
break;
|
|
case 0xF: // DRk, dir16
|
|
if ( code != 0x7E )
|
|
return 0; // only mov works
|
|
if ( !op_rd(insn.Op1, rDR0+(b1>>4), dt_dword) )
|
|
return 0;
|
|
op_mm(insn.Op2, insn.get_next_word(), dt_word);
|
|
break;
|
|
case 0x9: // Rm, @WRj
|
|
b2 = insn.get_next_byte();
|
|
if ( b2 & 15 )
|
|
return 0;
|
|
op_rd(insn.Op1, rR0 +(b2>>4), dt_byte);
|
|
op_ph(insn.Op2, rWR0+(b1>>4), dt_byte);
|
|
break;
|
|
case 0xB: // Rm, @DRk
|
|
b2 = insn.get_next_byte();
|
|
if ( b2 & 15 )
|
|
return 0;
|
|
op_rd(insn.Op1, rR0 +(b2>>4), dt_byte);
|
|
if ( !op_ph(insn.Op2, rDR0+(b1>>4), dt_byte) )
|
|
return 0;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
error("%a: internal ana_extended() error, code=%x", insn.ea, code);
|
|
}
|
|
|
|
return insn.size;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// analyze an basic instruction
|
|
int i51_t::ana_basic(insn_t &insn)
|
|
{
|
|
ushort code = insn.get_next_byte();
|
|
bool mx_a5 = false;
|
|
if ( code == 0xA5 && ptype == prc_51mx )
|
|
{
|
|
code = insn.get_next_byte();
|
|
mx_a5 = true;
|
|
}
|
|
|
|
ushort nibble0 = (code & 0xF);
|
|
ushort nibble1 = (code >> 4);
|
|
char off;
|
|
if ( mx_a5 )
|
|
{
|
|
if ( nibble1 == 0x6 && nibble0 >= 0x8 ) // ADD PRi,#data2 (0 1 1 0 1 i d1 d2)
|
|
{
|
|
insn.itype = I51_add;
|
|
op_rd(insn.Op1, (nibble0 & 4) == 0 ? rPR0 : rPR1, dt_dword);
|
|
int val = nibble0 & 3;
|
|
op_im(insn.Op2, val == 0 ? 4 : val, dt_byte);
|
|
return insn.size;
|
|
}
|
|
if ( nibble1 == 0x4 && nibble0 >= 0x8 ) // EMOVE A, @PRi+#data2
|
|
{
|
|
insn.itype = I51_emov;
|
|
opAcc(insn.Op1);
|
|
int val = nibble0 & 3;
|
|
op_ph(insn.Op2, (nibble0 & 4) == 0 ? fPr0 : fPr1, dt_dword, val == 0 ? 4 : val);
|
|
return insn.size;
|
|
}
|
|
if ( nibble1 == 0x5 && nibble0 >= 0x8 ) // EMOVE @PRi+#data2, A
|
|
{
|
|
insn.itype = I51_emov;
|
|
int val = nibble0 & 3;
|
|
op_ph(insn.Op1, (nibble0 & 4) == 0 ? fPr0 : fPr1, dt_dword, val == 0 ? 4 : val);
|
|
opAcc(insn.Op2);
|
|
return insn.size;
|
|
}
|
|
}
|
|
if ( nibble0 < 4 ) // complex coding, misc instructions
|
|
{
|
|
switch ( nibble0 )
|
|
{
|
|
case 0:
|
|
{
|
|
static const uchar misc0[16] =
|
|
{
|
|
I51_nop, I51_jbc, I51_jb, I51_jnb,
|
|
I51_jc, I51_jnc, I51_jz, I51_jnz,
|
|
I51_sjmp,I51_mov, I51_orl, I51_anl,
|
|
I51_push,I51_pop, I51_movx,I51_movx
|
|
};
|
|
insn.itype = misc0[nibble1];
|
|
}
|
|
switch ( nibble1 )
|
|
{
|
|
case 0x1: case 0x2: case 0x3: // jbc, jb, jnb
|
|
insn.Op1.type = o_bit;
|
|
insn.Op1.reg = insn.get_next_byte();
|
|
insn.Op2.type = o_near;
|
|
off = insn.get_next_byte();
|
|
insn.Op2.addr = truncate(insn.ip + insn.size + off); // signed addition
|
|
insn.Op2.dtype = dt_word;
|
|
break;
|
|
case 0x4: case 0x5: case 0x6: case 0x7: case 0x8: // jc, jnc, jz, jnz, sjmp
|
|
insn.Op1.type = o_near;
|
|
off = insn.get_next_byte();
|
|
insn.Op1.addr = truncate(insn.ip + insn.size + off); // signed addition
|
|
insn.Op1.dtype = dt_word;
|
|
break;
|
|
case 0x9: // mov
|
|
insn.Op1.type = o_reg;
|
|
insn.Op1.reg = mx_a5 ? rEptr : rDptr;
|
|
insn.Op1.dtype = mx_a5 ? dt_dword : dt_word;
|
|
insn.Op2.type = o_imm;
|
|
insn.Op2.offb = (uchar)insn.size;
|
|
if ( mx_a5 )
|
|
{
|
|
insn.Op2.value = (((ea_t)insn.get_next_word()) << 8) + insn.get_next_byte();
|
|
insn.Op2.dtype = dt_dword;
|
|
}
|
|
else
|
|
{
|
|
insn.Op2.value = insn.get_next_word();
|
|
insn.Op2.dtype = dt_word;
|
|
}
|
|
break;
|
|
case 0xA: case 0xB: // orl, anl
|
|
opC(insn.Op1);
|
|
insn.Op2.type = o_bitnot;
|
|
insn.Op2.reg = insn.get_next_byte();
|
|
break;
|
|
case 0xC: case 0xD: // push, pop
|
|
insn.Op1.type = o_mem;
|
|
insn.Op1.addr = insn.get_next_byte();
|
|
break;
|
|
case 0xE: // movx
|
|
opAcc(insn.Op1);
|
|
insn.Op2.type = o_phrase;
|
|
insn.Op2.phrase = mx_a5 ? fEptr : fDptr;
|
|
break;
|
|
case 0xF: // movx
|
|
opAcc(insn.Op2);
|
|
insn.Op1.type = o_phrase;
|
|
insn.Op1.phrase = mx_a5 ? fEptr : fDptr;
|
|
break;
|
|
}
|
|
break;
|
|
case 1: // acall, ajmp
|
|
{
|
|
ushort lowbits = insn.get_next_byte();
|
|
insn.Op1.type = o_near;
|
|
insn.Op1.addr = truncate((code&0xE0)<<3) + lowbits + ((insn.ip+insn.size) & ~0x7FF);
|
|
insn.Op1.dtype = dt_word;
|
|
insn.itype = (nibble1 & 1) ? I51_acall : I51_ajmp;
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
static const uchar misc2[16] =
|
|
{
|
|
I51_ljmp,I51_lcall,I51_ret,I51_reti,
|
|
I51_orl, I51_anl, I51_xrl, I51_orl,
|
|
I51_anl, I51_mov, I51_mov, I51_cpl,
|
|
I51_clr, I51_setb,I51_movx,I51_movx
|
|
};
|
|
insn.itype = misc2[nibble1];
|
|
}
|
|
switch ( nibble1 )
|
|
{
|
|
case 0x0: case 0x1: // ljump (ejmp), lcall (ecall)
|
|
insn.Op1.type = o_near;
|
|
if ( mx_a5 ) // ecall
|
|
{
|
|
insn.itype = nibble1 == 0 ? I51_ejmp : I51_ecall;
|
|
insn.Op1.addr = (((ea_t)insn.get_next_word()) << 8) + insn.get_next_byte();
|
|
if ( insn.Op1.addr >= 0x800000 )
|
|
insn.Op1.addr -= 0x800000;
|
|
}
|
|
else
|
|
{
|
|
insn.Op1.addr = insn.get_next_word();
|
|
}
|
|
insn.Op1.addr|= (insn.ip+insn.size) & ~0xFFFF;
|
|
insn.Op1.dtype = mx_a5 ? dt_dword : dt_word;
|
|
break;
|
|
case 0x2: // ret (eret);
|
|
if ( mx_a5 )
|
|
insn.itype = I51_eret;
|
|
break;
|
|
case 0x4: case 0x5: case 0x6: // orl, anl, xrl,
|
|
insn.Op1.type = o_mem;
|
|
insn.Op1.addr = insn.get_next_byte();
|
|
opAcc(insn.Op2);
|
|
break;
|
|
case 0x7: case 0x8: case 0xA: // orl, anl, mov
|
|
opC(insn.Op1);
|
|
insn.Op2.type = o_bit;
|
|
insn.Op2.reg = insn.get_next_byte();
|
|
break;
|
|
case 0x9: // mov
|
|
opC(insn.Op2);
|
|
/* no break */
|
|
case 0xB: case 0xC: case 0xD: // cpl, clr, setb
|
|
insn.Op1.type = o_bit;
|
|
insn.Op1.reg = insn.get_next_byte();
|
|
break;
|
|
case 0xE: // movx
|
|
opAcc(insn.Op1);
|
|
insn.Op2.type = o_phrase;
|
|
insn.Op2.phrase = fR0;
|
|
break;
|
|
case 0xF: // movx
|
|
insn.Op1.type = o_phrase;
|
|
insn.Op1.phrase = fR0;
|
|
opAcc(insn.Op2);
|
|
break;
|
|
}
|
|
break;
|
|
case 3:
|
|
{
|
|
static const uchar misc3[16] =
|
|
{
|
|
I51_rr, I51_rrc, I51_rl, I51_rlc,
|
|
I51_orl, I51_anl, I51_xrl, I51_jmp,
|
|
I51_movc,I51_movc,I51_inc, I51_cpl,
|
|
I51_clr, I51_setb,I51_movx,I51_movx
|
|
};
|
|
insn.itype = misc3[nibble1];
|
|
}
|
|
switch ( nibble1 )
|
|
{
|
|
case 0x0: case 0x1: case 0x2: case 0x3: // rr, rrc, rl, rlc
|
|
opAcc(insn.Op1);
|
|
break;
|
|
case 0x4: case 0x5: case 0x6: // orl, anl, xrl
|
|
insn.Op1.type = o_mem;
|
|
insn.Op1.addr = insn.get_next_byte();
|
|
insn.Op2.offb = (uchar)insn.size;
|
|
insn.Op2.type = o_imm;
|
|
insn.Op2.value = insn.get_next_byte();
|
|
break;
|
|
case 0x7: // jmp
|
|
insn.Op1.type = o_phrase;
|
|
insn.Op1.phrase = mx_a5 ? fAeptr : fAdptr;
|
|
break;
|
|
case 0x8: case 0x9: // movc
|
|
opAcc(insn.Op1);
|
|
insn.Op2.type = o_phrase;
|
|
insn.Op2.phrase = nibble1 == 0x8 ? fApc : (mx_a5 ? fAeptr : fAdptr);
|
|
break;
|
|
case 0xA: // inc
|
|
insn.Op1.type = o_reg;
|
|
insn.Op1.reg = mx_a5 ? rEptr : rDptr;
|
|
insn.Op1.dtype = mx_a5 ? dt_dword : dt_word;
|
|
break;
|
|
case 0xB: case 0xC: case 0xD: // cpl, clr, setb
|
|
opC(insn.Op1);
|
|
break;
|
|
case 0xE: // movx
|
|
opAcc(insn.Op1);
|
|
insn.Op2.type = o_phrase;
|
|
insn.Op2.phrase = fR1;
|
|
break;
|
|
case 0xF: // movx
|
|
insn.Op1.type = o_phrase;
|
|
insn.Op1.phrase = fR1;
|
|
opAcc(insn.Op2);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{ // i.e. nibble0 >= 4
|
|
static const uchar regulars[16] =
|
|
{
|
|
I51_inc, I51_dec, I51_add, I51_addc,
|
|
I51_orl, I51_anl, I51_xrl, I51_mov,
|
|
I51_mov, I51_subb,I51_mov, I51_cjne,
|
|
I51_xch, I51_djnz,I51_mov, I51_mov
|
|
};
|
|
insn.itype = regulars[nibble1];
|
|
switch ( nibble1 )
|
|
{
|
|
case 0x00: case 0x01: // inc, dec
|
|
operand1(insn, nibble0);
|
|
break;
|
|
case 0x0C: // xch
|
|
if ( nibble0 == 4 )
|
|
{
|
|
insn.itype = I51_swap;
|
|
opAcc(insn.Op1);
|
|
break;
|
|
}
|
|
// fallthrough
|
|
case 0x02: case 0x03: case 0x04: // add, addc, orl
|
|
case 0x05: case 0x06: case 0x09: // anl, xrl, subb
|
|
operand2(insn, nibble0);
|
|
opAcc(insn.Op1);
|
|
break;
|
|
case 0x07: // mov
|
|
operand1(insn, nibble0);
|
|
insn.Op2.offb = (uchar)insn.size;
|
|
insn.Op2.type = o_imm;
|
|
insn.Op2.value = insn.get_next_byte();
|
|
break;
|
|
case 0x08: // mov
|
|
if ( nibble0 == 4 )
|
|
{
|
|
insn.itype = I51_div;
|
|
insn.Op1.type = o_reg;
|
|
insn.Op1.reg = rAB;
|
|
break;
|
|
}
|
|
operand2(insn, nibble0);
|
|
insn.Op1.type = o_mem;
|
|
insn.Op1.addr = insn.get_next_byte();
|
|
break;
|
|
case 0x0A: // mov
|
|
if ( nibble0 == 4 )
|
|
{
|
|
insn.itype = I51_mul;
|
|
insn.Op1.type = o_reg;
|
|
insn.Op1.reg = rAB;
|
|
break;
|
|
}
|
|
if ( nibble0 == 5 )
|
|
return 0; // mov to imm - no sense (0xA5)
|
|
operand1(insn, nibble0);
|
|
insn.Op2.type = o_mem;
|
|
insn.Op2.addr = insn.get_next_byte();
|
|
break;
|
|
case 0x0B: // cjne
|
|
if ( nibble0 == 5 )
|
|
{
|
|
opAcc(insn.Op1);
|
|
insn.Op2.type = o_mem;
|
|
insn.Op2.addr = insn.get_next_byte();
|
|
}
|
|
else
|
|
{
|
|
operand1(insn, nibble0);
|
|
insn.Op2.offb = (uchar)insn.size;
|
|
insn.Op2.type = o_imm;
|
|
insn.Op2.value = insn.get_next_byte();
|
|
}
|
|
insn.Op3.type = o_near;
|
|
off = insn.get_next_byte();
|
|
insn.Op3.addr = truncate(insn.ip + insn.size + off); // signed addition
|
|
insn.Op3.dtype = dt_word;
|
|
break;
|
|
case 0x0D: // djnz
|
|
switch ( nibble0 )
|
|
{
|
|
case 4:
|
|
insn.itype = I51_da;
|
|
opAcc(insn.Op1);
|
|
break;
|
|
case 6: case 7:
|
|
insn.itype = I51_xchd;
|
|
opAcc(insn.Op1);
|
|
operand2(insn, nibble0);
|
|
break;
|
|
default:
|
|
operand1(insn, nibble0);
|
|
off = insn.get_next_byte();
|
|
insn.Op2.type = o_near;
|
|
insn.Op2.addr = truncate(insn.ip + insn.size + off); // signed addition
|
|
insn.Op2.dtype = dt_word;
|
|
break;
|
|
}
|
|
break;
|
|
case 0x0E: // mov
|
|
opAcc(insn.Op1);
|
|
if ( nibble0 == 4 )
|
|
{
|
|
insn.itype = I51_clr;
|
|
break;
|
|
}
|
|
operand2(insn, nibble0);
|
|
break;
|
|
case 0x0F: // mov
|
|
if ( nibble0 == 4 )
|
|
{
|
|
insn.itype = I51_cpl;
|
|
opAcc(insn.Op1);
|
|
break;
|
|
}
|
|
operand1(insn, nibble0);
|
|
insn.Op2.type = o_reg;
|
|
insn.Op2.reg = rAcc;
|
|
break;
|
|
}
|
|
}
|
|
return insn.size;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// analyze an instruction
|
|
int i51_t::ana(insn_t *_insn)
|
|
{
|
|
insn_t &insn = *_insn;
|
|
insn.Op1.dtype = dt_byte;
|
|
insn.Op2.dtype = dt_byte;
|
|
insn.Op3.dtype = dt_byte;
|
|
|
|
uchar code = get_byte(insn.ea);
|
|
switch ( ptype )
|
|
{
|
|
case prc_51:
|
|
case prc_51mx:
|
|
return ana_basic(insn);
|
|
|
|
case prc_251_src:
|
|
case prc_930_src:
|
|
if ( code == 0xA5 )
|
|
{
|
|
insn.size++; // skip A5
|
|
code = get_byte(insn.ea+1);
|
|
if ( (code & 15) < 6 )
|
|
return 0;
|
|
return ana_basic(insn);
|
|
}
|
|
if ( (code & 15) < 6 )
|
|
return ana_basic(insn);
|
|
return ana_extended(insn);
|
|
|
|
case prc_251_bin:
|
|
case prc_930_bin:
|
|
if ( code == 0xA5 )
|
|
{
|
|
insn.size++; // skip A5
|
|
return ana_extended(insn);
|
|
}
|
|
return ana_basic(insn);
|
|
}
|
|
return 0; //lint !e527 statement is unreachable
|
|
}
|