Repository URL to install this package:
|
Version:
9.0~240925-3.fc42 ▾
|
/*
* Interactive disassembler (IDA).
* Intel 80196 module
*
*/
#include "i196.hpp"
#include "ins.hpp"
//----------------------------------------------------------------------
struct wsr_mapping_t
{
ushort base;
uchar wsr;
uchar wsrbase;
uchar wsr1base;
};
static const wsr_mapping_t mappings[] =
{
{ 0x0000, 0x10, 0x80, 0xFF }, // 0080-00FF
{ 0x0080, 0x11, 0x80, 0xFF },
{ 0x0100, 0x12, 0x80, 0xFF },
{ 0x0180, 0x13, 0x80, 0xFF },
{ 0x0200, 0x14, 0x80, 0xFF },
{ 0x0280, 0x15, 0x80, 0xFF },
{ 0x0300, 0x16, 0x80, 0xFF },
{ 0x0380, 0x17, 0x80, 0xFF },
{ 0x1F00, 0x1E, 0x80, 0xFF },
{ 0x1F80, 0x1F, 0x80, 0xFF },
{ 0x0000, 0x20, 0xC0, 0x40 }, // 00C0-00FF or 0040-007F
{ 0x0040, 0x21, 0xC0, 0x40 },
{ 0x0080, 0x22, 0xC0, 0x40 },
{ 0x00C0, 0x23, 0xC0, 0x40 },
{ 0x0100, 0x24, 0xC0, 0x40 },
{ 0x0140, 0x25, 0xC0, 0x40 },
{ 0x0180, 0x26, 0xC0, 0x40 },
{ 0x01C0, 0x27, 0xC0, 0x40 },
{ 0x0200, 0x28, 0xC0, 0x40 },
{ 0x0240, 0x29, 0xC0, 0x40 },
{ 0x0280, 0x2A, 0xC0, 0x40 },
{ 0x02C0, 0x2B, 0xC0, 0x40 },
{ 0x0300, 0x2C, 0xC0, 0x40 },
{ 0x0340, 0x2D, 0xC0, 0x40 },
{ 0x0380, 0x2E, 0xC0, 0x40 },
{ 0x03C0, 0x2F, 0xC0, 0x40 },
{ 0x1F00, 0x3C, 0xC0, 0x40 },
{ 0x1F40, 0x3D, 0xC0, 0x40 },
{ 0x1F80, 0x3E, 0xC0, 0x40 },
{ 0x1FC0, 0x3F, 0xC0, 0x40 },
{ 0x0000, 0x40, 0xE0, 0x60 }, // 00E0-00FF or 0060-007F
{ 0x0020, 0x41, 0xE0, 0x60 },
{ 0x0040, 0x42, 0xE0, 0x60 },
{ 0x0060, 0x43, 0xE0, 0x60 },
{ 0x0080, 0x44, 0xE0, 0x60 },
{ 0x00A0, 0x45, 0xE0, 0x60 },
{ 0x00C0, 0x46, 0xE0, 0x60 },
{ 0x00E0, 0x47, 0xE0, 0x60 },
{ 0x0100, 0x48, 0xE0, 0x60 },
{ 0x0120, 0x49, 0xE0, 0x60 },
{ 0x0140, 0x4A, 0xE0, 0x60 },
{ 0x0160, 0x4B, 0xE0, 0x60 },
{ 0x0180, 0x4C, 0xE0, 0x60 },
{ 0x01A0, 0x4D, 0xE0, 0x60 },
{ 0x01C0, 0x4E, 0xE0, 0x60 },
{ 0x01E0, 0x4F, 0xE0, 0x60 },
{ 0x0200, 0x50, 0xE0, 0x60 },
{ 0x0220, 0x51, 0xE0, 0x60 },
{ 0x0240, 0x52, 0xE0, 0x60 },
{ 0x0260, 0x53, 0xE0, 0x60 },
{ 0x0280, 0x54, 0xE0, 0x60 },
{ 0x02A0, 0x55, 0xE0, 0x60 },
{ 0x02C0, 0x56, 0xE0, 0x60 },
{ 0x02E0, 0x57, 0xE0, 0x60 },
{ 0x0300, 0x58, 0xE0, 0x60 },
{ 0x0320, 0x59, 0xE0, 0x60 },
{ 0x0340, 0x5A, 0xE0, 0x60 },
{ 0x0360, 0x5B, 0xE0, 0x60 },
{ 0x0380, 0x5C, 0xE0, 0x60 },
{ 0x03A0, 0x5D, 0xE0, 0x60 },
{ 0x03C0, 0x5E, 0xE0, 0x60 },
{ 0x03E0, 0x5F, 0xE0, 0x60 },
{ 0x1F00, 0x78, 0xE0, 0x60 },
{ 0x1F20, 0x79, 0xE0, 0x60 },
{ 0x1F40, 0x7A, 0xE0, 0x60 },
{ 0x1F60, 0x7B, 0xE0, 0x60 },
{ 0x1F80, 0x7C, 0xE0, 0x60 },
{ 0x1FA0, 0x7D, 0xE0, 0x60 },
{ 0x1FC0, 0x7E, 0xE0, 0x60 },
{ 0x1FE0, 0x7F, 0xE0, 0x60 },
};
static int NT_CDECL cmp(const void *x, const void *y)
{
const wsr_mapping_t *a = (const wsr_mapping_t *)x;
const wsr_mapping_t *b = (const wsr_mapping_t *)y;
return a->wsr - b->wsr;
}
//----------------------------------------------------------------------
// perform WSR/WSR1 mapping
ea_t i196_t::map(ea_t iea, ea_t v) const
{
if ( !extended )
return v;
if ( v < 0x40 )
return v;
sel_t wsr = get_sreg(iea, v < 0x80 ? WSR1 : WSR) & 0x7F;
if ( wsr < 0x10 )
return v;
wsr_mapping_t key;
key.wsr = (char)wsr;
wsr_mapping_t *p = (wsr_mapping_t *)
bsearch(&key, mappings, qnumber(mappings), sizeof(key), cmp);
if ( p == nullptr )
return v;
int delta = v < 0x80 ? p->wsr1base : p->wsrbase;
if ( v < delta )
return v;
return v - delta + p->base;
}
//----------------------------------------------------------------------
void i196_t::aop(insn_t &insn, uint code, op_t &op)
{
switch ( code & 3 )
{
case 0: // direct
op.type = o_mem;
op.addr = map(insn.ea, insn.get_next_byte());
break;
case 1: // immediate
op.type = o_imm;
if ( (code & 0x10) == 0 && (code & 0xFC) != 0xAC ) // ldbze always baop
{
op.dtype = dt_word;
op.value = insn.get_next_word();
}
else
{
op.value = insn.get_next_byte();
}
break;
case 2: // indirect
op.dtype = dt_word;
op.addr = insn.get_next_byte();
op.type = (op.addr & 1) ? o_indirect_inc : o_indirect;
op.addr = map(insn.ea, op.addr & ~1);
break;
case 3: // indexed
op.dtype = dt_word;
op.type = o_indexed;
op.value = insn.get_next_byte(); // short (reg file)
op.addr = (op.value & 1) ? insn.get_next_word() : insn.get_next_byte();
op.value = map(insn.ea, op.value & ~1);
}
}
//----------------------------------------------------------------------
int i196_t::ld_st(insn_t &insn, ushort itype, char dtype, bool indirect, op_t ®, op_t &mem)
{
if ( !extended )
return 0;
insn.itype = itype;
reg.dtype = dtype;
mem.dtype = dtype;
mem.addr = insn.get_next_byte();
if ( indirect ) // indirect
{
mem.type = (mem.addr & 1) ? o_indirect_inc : o_indirect;
mem.addr = map(insn.ea, mem.addr & ~1);
}
else
{
mem.type = o_indexed;
mem.value = map(insn.ea, mem.addr);
mem.addr = insn.get_next_word();
mem.addr |= insn.get_next_byte() << 16;
}
reg.type = o_mem;
reg.addr = map(insn.ea, insn.get_next_byte());
return insn.size;
}
//----------------------------------------------------------------------
int i196_t::ana(insn_t *_insn)
{
if ( _insn == nullptr )
return 0;
insn_t &insn = *_insn;
insn.Op1.dtype = dt_byte;
insn.Op2.dtype = dt_byte;
insn.Op3.dtype = dt_byte;
uint code = insn.get_next_byte();
uint nibble0 = (code & 0xF);
uint nibble1 = (code >> 4);
char offc;
int32 off;
uint tmp;
if ( nibble1 < 2 ) // 0,1
{
static const char cmd01[] =
{
I196_skip, I196_clr, I196_not, I196_neg,
I196_xch, I196_dec, I196_ext, I196_inc,
I196_shr, I196_shl, I196_shra, I196_xch,
I196_shrl, I196_shll, I196_shral, I196_norml,
I196_null, I196_clrb, I196_notb, I196_negb,
I196_xchb, I196_decb, I196_extb, I196_incb,
I196_shrb, I196_shlb, I196_shrab, I196_xchb,
I196_est, I196_est, I196_estb, I196_estb
};
insn.itype = cmd01[code & 0x1F];
if ( insn.itype == I196_null )
return 0; // unknown instruction
switch ( code )
{
case 0x4: case 0x14: // xch reg,aop direct
case 0xB: case 0x1B: // xch reg,aop indexed
if ( (code & 0x10) == 0 )
insn.Op2.dtype = dt_word;
aop(insn, code, insn.Op2);
insn.Op1.addr = map(insn.ea, insn.get_next_byte());
insn.Op1.type = o_mem;
break;
case 0xF: // norml lreg,breg
insn.Op2.addr = map(insn.ea, insn.get_next_byte());
insn.Op2.type = o_mem;
insn.Op1.addr = map(insn.ea, insn.get_next_byte());
insn.Op1.type = o_mem;
break;
case 0x1C: // est.indirect
case 0x1D: // est.indexed
return ld_st(insn, I196_est, dt_word, code == 0x1C, insn.Op1, insn.Op2);
case 0x1E: // estb.indirect
case 0x1F: // estb.indexed
return ld_st(insn, I196_estb, dt_byte, code == 0x1E, insn.Op1, insn.Op2);
default: // shifts
tmp = insn.get_next_byte();
if ( tmp < 16 )
{
insn.Op2.value = tmp;
insn.Op2.type = o_imm;
}
else
{
insn.Op2.addr = map(insn.ea, tmp);
insn.Op2.type = o_mem;
}
// fallthrough
case 0x0: case 0x1: case 0x2: case 0x3:
case 0x5: case 0x6: case 0x7: case 0x11:
case 0x12: case 0x13: case 0x15: case 0x16: case 0x17:
insn.Op1.addr = map(insn.ea, insn.get_next_byte());
insn.Op1.type = o_mem;
}
switch ( code )
{
case 0x1: case 0x2: case 0x3: case 0x4: case 0x5:
case 0x7: case 0x8: case 0x9: case 0xA: case 0xB: case 0x16:
insn.Op1.dtype = dt_word;
break;
case 0x6: case 0xC: case 0xD: case 0xE: case 0xF:
insn.Op1.dtype = dt_dword;
break;
}
}
else if ( nibble1 < 4 ) // 2,3
{
static const char cmd23[] = { I196_sjmp, I196_scall, I196_jbc, I196_jbs };
insn.itype = cmd23[ ((code - 0x20) >> 3) & 3 ];
if ( nibble1 == 2 ) // sjmp/scall
{
insn.Op1.type = o_near;
off = insn.get_next_byte() + ((code & 7) << 8);
if ( off & 0x400 )
off |= ~0x7FF;
else
off &= 0x7FF; // make signed
insn.Op1.addr = truncate(insn.ip + insn.size + off); // signed addition
// insn.Op1.dtype = dt_word;
}
else // jbc/jbs
{
insn.Op2.type = o_bit;
insn.Op2.reg = code & 7;
insn.Op1.addr = map(insn.ea, insn.get_next_byte());
insn.Op1.type = o_mem;
insn.Op3.type = o_near;
offc = insn.get_next_byte();
insn.Op3.addr = truncate(insn.ip + insn.size + offc); // signed addition
// insn.Op3.dtype = dt_word;
}
}
else if ( nibble1 < 6 ) // 4,5
{
static const char cmd45[] =
{
I196_and3, I196_add3, I196_sub3, I196_mulu3,
I196_andb3, I196_addb3, I196_subb3, I196_mulub3
};
insn.itype = cmd45[ ((code - 0x40) >> 2) & 7 ];
if ( (code & 0x10) == 0 )
insn.Op1.dtype = insn.Op2.dtype = insn.Op3.dtype = dt_word;
if ( (code & 0xc) == 0xc ) // mulu/mulub
insn.Op1.dtype++; // word->dword/byte->word
aop(insn, code, insn.Op3);
insn.Op2.addr = map(insn.ea, insn.get_next_byte());
insn.Op2.type = o_mem;
insn.Op1.addr = map(insn.ea, insn.get_next_byte());
insn.Op1.type = o_mem;
}
else if ( nibble1 < 0xD ) // 6,7,8,9,A,B,C
{
static const char cmd6c[] =
{
I196_and2, I196_add2, I196_sub2, I196_mulu2,
I196_andb2, I196_addb2, I196_subb2, I196_mulub2,
I196_or, I196_xor, I196_cmp, I196_divu,
I196_orb, I196_xorb, I196_cmpb, I196_divub,
I196_ld, I196_addc, I196_subc, I196_ldbze,
I196_ldb, I196_addcb, I196_subcb, I196_ldbse,
I196_st, I196_stb, I196_push, I196_pop,
I196_null, I196_null, I196_null, I196_null,
};
insn.itype = cmd6c[ ((code - 0x60) >> 2) & 31 ];
switch ( nibble1 )
{
case 6: // and/add/sub/mulu
case 8: // or/xor/cmp/duvu
insn.Op1.dtype = insn.Op2.dtype = dt_word;
if ( (nibble0 & 0xC) == 0xC )
insn.Op1.dtype++; // mulu/divu
break;
case 0xA: // ld/addc/subc/ldbze
insn.Op1.dtype = insn.Op2.dtype = dt_word;
if ( (nibble0 & 0xC) == 0xC )
insn.Op2.dtype = dt_byte; // ldbze
break;
}
switch ( code & 0xFC )
{
case 0xC0: // st
insn.Op2.dtype = dt_word;
case 0x7C: case 0x9C: case 0xBC: case 0xC8: case 0xCC:
insn.Op1.dtype = dt_word;
}
switch ( code )
{
case 0xC1:
insn.itype = I196_bmov;
goto cont1;
case 0xC5:
insn.itype = I196_cmpl;
insn.Op2.dtype = dt_dword;
goto cont2;
case 0xCD:
insn.itype = I196_bmovi;
cont1:
insn.Op2.dtype = dt_word;
cont2:
insn.Op2.addr = map(insn.ea, insn.get_next_byte());
insn.Op2.type = o_mem;
insn.Op1.dtype = dt_dword;
// insn.Op1.addr = insn.get_next_byte();
// insn.Op1.type = o_mem;
goto cont3;
default:
if ( code > 0xC7 )
{
aop(insn, code, insn.Op1);
}
else
{
aop(insn, code, insn.Op2);
cont3:
insn.Op1.addr = map(insn.ea, insn.get_next_byte());
insn.Op1.type = o_mem;
}
}
}
else if ( nibble1 == 0xD ) // jcc
{
static const char cmdd[] =
{
I196_jnst, I196_jnh, I196_jgt, I196_jnc,
I196_jnvt, I196_jnv, I196_jge, I196_jne,
I196_jst, I196_jh, I196_jle, I196_jc,
I196_jvt, I196_jv, I196_jlt, I196_je
};
insn.itype = cmdd[nibble0];
insn.Op1.type = o_near;
offc = insn.get_next_byte();
insn.Op1.addr = truncate(insn.ip + insn.size + offc); // signed addition
// insn.Op1.dtype = dt_word;
}
else if ( nibble1 == 0xE ) // Ex
{
switch ( nibble0 )
{
case 0x0: case 0x1: // djnz, djnzw
if ( nibble0 & 1 )
{
insn.itype = I196_djnzw;
insn.Op1.dtype = dt_word;
}
else
{
insn.itype = I196_djnz;
}
insn.Op1.type = o_mem;
insn.Op1.addr = map(insn.ea, insn.get_next_byte());
offc = insn.get_next_byte();
insn.Op2.type = o_near;
insn.Op2.addr = truncate(insn.ip + insn.size + offc); // signed addition
break;
case 0x2: // tijmp
insn.itype = I196_tijmp;
insn.Op1.dtype = insn.Op2.dtype = dt_word;
insn.Op2.type = o_indirect;
insn.Op2.addr = map(insn.ea, insn.get_next_byte());
insn.Op3.type = o_imm;
insn.Op3.value = insn.get_next_byte();
insn.Op1.type = o_mem;
insn.Op1.addr = map(insn.ea, insn.get_next_byte());
break;
case 0x3: // br
insn.itype = extended ? I196_ebr : I196_br;
aop(insn, 2, insn.Op1);
break;
case 0x4: // ebmovi
if ( !extended )
return 0;
insn.itype = I196_ebmovi;
insn.Op1.type = o_mem;
insn.Op1.addr = map(insn.ea, insn.get_next_byte());
insn.Op2.type = o_mem;
insn.Op2.addr = map(insn.ea, insn.get_next_byte());
break;
case 0x6: // ejmp
if ( !extended )
return 0;
insn.itype = I196_ejmp;
insn.Op1.type = o_near;
off = insn.get_next_word();
off |= int32(insn.get_next_byte()) << 16;
insn.Op1.addr = truncate(insn.ip + insn.size + off); // signed addition
break;
case 0x8: // eld.indirect
case 0x9: // eld.indexed
return ld_st(insn, I196_eld, dt_word, nibble0 == 0x8, insn.Op1, insn.Op2);
case 0xA: // eldb.indirect
case 0xB: // eldb.indexed
return ld_st(insn, I196_eldb, dt_byte, nibble0 == 0xA, insn.Op1, insn.Op2);
case 0xC: // dpts
insn.itype = I196_dpts;
break;
case 0xD: // epts
insn.itype = I196_epts;
break;
case 0x7: case 0xF: // ljmp, lcall
insn.itype = (nibble0 & 8) ? I196_lcall : I196_ljmp;
insn.Op1.type = o_near;
off = short(insn.get_next_word());
insn.Op1.addr = truncate(insn.ip + insn.size + off); // signed addition
insn.Op1.dtype = dt_word;
break;
default:
return 0;
}
}
else
{
static const char cmdf[] =
{
I196_ret, I196_ecall,I196_pushf, I196_popf,
I196_pusha, I196_popa, I196_idlpd, I196_trap,
I196_clrc, I196_setc, I196_di, I196_ei,
I196_clrvt, I196_nop, I196_null, I196_rst
};
insn.itype = cmdf[nibble0];
if ( nibble0 == 1 ) // ecall
{
if ( !extended )
return 0;
off = insn.get_next_word();
off |= int32(insn.get_next_byte()) << 16;
insn.Op1.type = o_near;
insn.Op1.addr = truncate(insn.ip + insn.size + off);
}
else if ( nibble0 == 6 ) // idlpd
{
insn.Op1.type = o_imm;
insn.Op1.value = insn.get_next_byte();
}
else if ( nibble0 == 0xE ) // prefix
{
code = insn.get_next_byte();
switch ( code & 0xFC )
{
case 0x4C: case 0x5C:
if ( code & 0x10 )
{
insn.itype = I196_mulb3;
insn.Op1.dtype = dt_word;
}
else
{
insn.itype = I196_mul3;
insn.Op3.dtype = insn.Op2.dtype = dt_word;
insn.Op1.dtype = dt_dword;
}
aop(insn, code, insn.Op3);
insn.Op2.addr = map(insn.ea, insn.get_next_byte());
insn.Op2.type = o_mem;
insn.Op1.addr = map(insn.ea, insn.get_next_byte());
insn.Op1.type = o_mem;
break;
case 0x6C: case 0x7C: case 0x8C: case 0x9C:
insn.itype = (code & 0x80)
? (code & 0x10) ? I196_divb : I196_div
: (code & 0x10) ? I196_mulb2 : I196_mul2;
if ( code & 0x10 )
{
insn.Op1.dtype = dt_word;
}
else
{
insn.Op1.dtype = dt_dword;
insn.Op2.dtype = dt_word;
}
aop(insn, code, insn.Op2);
insn.Op1.addr = map(insn.ea, insn.get_next_byte());
insn.Op1.type = o_mem;
break;
default:
return 0;
}
}
}
return insn.size;
}