/* * 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 == NULL ) 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 == NULL ) 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; }