blob: e3fb6d778c99bedb87025b654d3748eb41a70c8f [file] [log] [blame] [raw]
"use strict";
var t = [];
var t16 = [];
var t32 = [];
t[0x00] = cpu => { cpu.read_modrm_byte(); cpu.write_e8(cpu.add8(cpu.read_write_e8(), cpu.read_g8())); };
t16[0x01] = cpu => { cpu.read_modrm_byte(); cpu.write_e16(cpu.add16(cpu.read_write_e16(), cpu.read_g16())); };
t32[0x01] = cpu => { cpu.read_modrm_byte(); cpu.write_e32(cpu.add32(cpu.read_write_e32(), cpu.read_g32s())); };
t[0x02] = cpu => { cpu.read_modrm_byte(); cpu.write_g8(cpu.add8(cpu.read_g8(), cpu.read_e8())); };
t16[0x03] = cpu => { cpu.read_modrm_byte(); cpu.write_g16(cpu.add16(cpu.read_g16(), cpu.read_e16())); };
t32[0x03] = cpu => { cpu.read_modrm_byte(); cpu.write_g32(cpu.add32(cpu.read_g32s(), cpu.read_e32s())); };
t[0x04] = cpu => { cpu.reg8[reg_al] = cpu.add8(cpu.reg8[reg_al], cpu.read_op8()); };
t16[0x05] = cpu => { cpu.reg16[reg_ax] = cpu.add16(cpu.reg16[reg_ax], cpu.read_op16()); };
t32[0x05] = cpu => { cpu.reg32s[reg_eax] = cpu.add32(cpu.reg32s[reg_eax], cpu.read_op32s()); };
t16[0x06] = cpu => { cpu.push16(cpu.sreg[reg_es]); };
t32[0x06] = cpu => { cpu.push32(cpu.sreg[reg_es]); };
t16[0x07] = cpu => {
cpu.switch_seg(reg_es, cpu.safe_read16(cpu.get_stack_pointer(0)));
cpu.adjust_stack_reg(2);
};
t32[0x07] = cpu => {
cpu.switch_seg(reg_es, cpu.safe_read32s(cpu.get_stack_pointer(0)) & 0xFFFF);
cpu.adjust_stack_reg(4);
};
t[0x08] = cpu => { cpu.read_modrm_byte(); cpu.write_e8(cpu.or8(cpu.read_write_e8(), cpu.read_g8())); };
t16[0x09] = cpu => { cpu.read_modrm_byte(); cpu.write_e16(cpu.or16(cpu.read_write_e16(), cpu.read_g16())); };
t32[0x09] = cpu => { cpu.read_modrm_byte(); cpu.write_e32(cpu.or32(cpu.read_write_e32(), cpu.read_g32s())); };
t[0x0a] = cpu => { cpu.read_modrm_byte(); cpu.write_g8(cpu.or8(cpu.read_g8(), cpu.read_e8())); };
t16[0x0b] = cpu => { cpu.read_modrm_byte(); cpu.write_g16(cpu.or16(cpu.read_g16(), cpu.read_e16())); };
t32[0x0b] = cpu => { cpu.read_modrm_byte(); cpu.write_g32(cpu.or32(cpu.read_g32s(), cpu.read_e32s())); }
t[0x0c] = cpu => { cpu.reg8[reg_al] = cpu.or8(cpu.reg8[reg_al], cpu.read_op8()); };
t16[0x0d] = cpu => { cpu.reg16[reg_ax] = cpu.or16(cpu.reg16[reg_ax], cpu.read_op16()); };
t32[0x0d] = cpu => { cpu.reg32s[reg_eax] = cpu.or32(cpu.reg32s[reg_eax], cpu.read_op32s()); }
t16[0x0E] = cpu => { cpu.push16(cpu.sreg[reg_cs]); };
t32[0x0E] = cpu => { cpu.push32(cpu.sreg[reg_cs]); };
t16[0x0F] = cpu => {
cpu.table0F_16[cpu.read_op0F()](cpu);
};
t32[0x0F] = cpu => {
cpu.table0F_32[cpu.read_op0F()](cpu);
};
t[0x10] = cpu => { cpu.read_modrm_byte(); cpu.write_e8(cpu.adc8(cpu.read_write_e8(), cpu.read_g8())); };
t16[0x11] = cpu => { cpu.read_modrm_byte(); cpu.write_e16(cpu.adc16(cpu.read_write_e16(), cpu.read_g16())); };
t32[0x11] = cpu => { cpu.read_modrm_byte(); cpu.write_e32(cpu.adc32(cpu.read_write_e32(), cpu.read_g32s())); }
t[0x12] = cpu => { cpu.read_modrm_byte(); cpu.write_g8(cpu.adc8(cpu.read_g8(), cpu.read_e8())); };
t16[0x13] = cpu => { cpu.read_modrm_byte(); cpu.write_g16(cpu.adc16(cpu.read_g16(), cpu.read_e16())); };
t32[0x13] = cpu => { cpu.read_modrm_byte(); cpu.write_g32(cpu.adc32(cpu.read_g32s(), cpu.read_e32s())); }
t[0x14] = cpu => { cpu.reg8[reg_al] = cpu.adc8(cpu.reg8[reg_al], cpu.read_op8()); };
t16[0x15] = cpu => { cpu.reg16[reg_ax] = cpu.adc16(cpu.reg16[reg_ax], cpu.read_op16()); };
t32[0x15] = cpu => { cpu.reg32s[reg_eax] = cpu.adc32(cpu.reg32s[reg_eax], cpu.read_op32s()); }
t16[0x16] = cpu => { cpu.push16(cpu.sreg[reg_ss]); };
t32[0x16] = cpu => { cpu.push32(cpu.sreg[reg_ss]); };
t16[0x17] = cpu => {
cpu.switch_seg(reg_ss, cpu.safe_read16(cpu.get_stack_pointer(0)));
cpu.adjust_stack_reg(2);
cpu.clear_prefixes();
cpu.cycle_internal();
};
t32[0x17] = cpu => {
cpu.switch_seg(reg_ss, cpu.safe_read32s(cpu.get_stack_pointer(0)) & 0xFFFF);
cpu.adjust_stack_reg(4);
cpu.clear_prefixes();
cpu.cycle_internal();
};
t[0x18] = cpu => { cpu.read_modrm_byte(); cpu.write_e8(cpu.sbb8(cpu.read_write_e8(), cpu.read_g8())); };
t16[0x19] = cpu => { cpu.read_modrm_byte(); cpu.write_e16(cpu.sbb16(cpu.read_write_e16(), cpu.read_g16())); };
t32[0x19] = cpu => { cpu.read_modrm_byte(); cpu.write_e32(cpu.sbb32(cpu.read_write_e32(), cpu.read_g32s())); }
t[0x1a] = cpu => { cpu.read_modrm_byte(); cpu.write_g8(cpu.sbb8(cpu.read_g8(), cpu.read_e8())); };
t16[0x1b] = cpu => { cpu.read_modrm_byte(); cpu.write_g16(cpu.sbb16(cpu.read_g16(), cpu.read_e16())); };
t32[0x1b] = cpu => { cpu.read_modrm_byte(); cpu.write_g32(cpu.sbb32(cpu.read_g32s(), cpu.read_e32s())); }
t[0x1c] = cpu => { cpu.reg8[reg_al] = cpu.sbb8(cpu.reg8[reg_al], cpu.read_op8()); };
t16[0x1d] = cpu => { cpu.reg16[reg_ax] = cpu.sbb16(cpu.reg16[reg_ax], cpu.read_op16()); };
t32[0x1d] = cpu => { cpu.reg32s[reg_eax] = cpu.sbb32(cpu.reg32s[reg_eax], cpu.read_op32s()); }
t16[0x1E] = cpu => { cpu.push16(cpu.sreg[reg_ds]); };
t32[0x1E] = cpu => { cpu.push32(cpu.sreg[reg_ds]); };
t16[0x1F] = cpu => {
cpu.switch_seg(reg_ds, cpu.safe_read16(cpu.get_stack_pointer(0)));
cpu.adjust_stack_reg(2);
};
t32[0x1F] = cpu => {
cpu.switch_seg(reg_ds, cpu.safe_read32s(cpu.get_stack_pointer(0)) & 0xFFFF);
cpu.adjust_stack_reg(4);
};
t[0x20] = cpu => { cpu.read_modrm_byte(); cpu.write_e8(cpu.and8(cpu.read_write_e8(), cpu.read_g8())); };
t16[0x21] = cpu => { cpu.read_modrm_byte(); cpu.write_e16(cpu.and16(cpu.read_write_e16(), cpu.read_g16())); };
t32[0x21] = cpu => { cpu.read_modrm_byte(); cpu.write_e32(cpu.and32(cpu.read_write_e32(), cpu.read_g32s())); }
t[0x22] = cpu => { cpu.read_modrm_byte(); cpu.write_g8(cpu.and8(cpu.read_g8(), cpu.read_e8())); };
t16[0x23] = cpu => { cpu.read_modrm_byte(); cpu.write_g16(cpu.and16(cpu.read_g16(), cpu.read_e16())); };
t32[0x23] = cpu => { cpu.read_modrm_byte(); cpu.write_g32(cpu.and32(cpu.read_g32s(), cpu.read_e32s())); }
t[0x24] = cpu => { cpu.reg8[reg_al] = cpu.and8(cpu.reg8[reg_al], cpu.read_op8()); };
t16[0x25] = cpu => { cpu.reg16[reg_ax] = cpu.and16(cpu.reg16[reg_ax], cpu.read_op16()); };
t32[0x25] = cpu => { cpu.reg32s[reg_eax] = cpu.and32(cpu.reg32s[reg_eax], cpu.read_op32s()); }
t[0x26] = cpu => { cpu.segment_prefix_op(reg_es); };
t[0x27] = cpu => { cpu.bcd_daa(); };
t[0x28] = cpu => { cpu.read_modrm_byte(); cpu.write_e8(cpu.sub8(cpu.read_write_e8(), cpu.read_g8())); };
t16[0x29] = cpu => { cpu.read_modrm_byte(); cpu.write_e16(cpu.sub16(cpu.read_write_e16(), cpu.read_g16())); };
t32[0x29] = cpu => { cpu.read_modrm_byte(); cpu.write_e32(cpu.sub32(cpu.read_write_e32(), cpu.read_g32s())); }
t[0x2a] = cpu => { cpu.read_modrm_byte(); cpu.write_g8(cpu.sub8(cpu.read_g8(), cpu.read_e8())); };
t16[0x2b] = cpu => { cpu.read_modrm_byte(); cpu.write_g16(cpu.sub16(cpu.read_g16(), cpu.read_e16())); };
t32[0x2b] = cpu => { cpu.read_modrm_byte(); cpu.write_g32(cpu.sub32(cpu.read_g32s(), cpu.read_e32s())); }
t[0x2c] = cpu => { cpu.reg8[reg_al] = cpu.sub8(cpu.reg8[reg_al], cpu.read_op8()); };
t16[0x2d] = cpu => { cpu.reg16[reg_ax] = cpu.sub16(cpu.reg16[reg_ax], cpu.read_op16()); };
t32[0x2d] = cpu => { cpu.reg32s[reg_eax] = cpu.sub32(cpu.reg32s[reg_eax], cpu.read_op32s()); }
t[0x2E] = cpu => { cpu.segment_prefix_op(reg_cs); };
t[0x2F] = cpu => { cpu.bcd_das(); };
t[0x30] = cpu => { cpu.read_modrm_byte(); cpu.write_e8(cpu.xor8(cpu.read_write_e8(), cpu.read_g8())); };
t16[0x31] = cpu => { cpu.read_modrm_byte(); cpu.write_e16(cpu.xor16(cpu.read_write_e16(), cpu.read_g16())); };
t32[0x31] = cpu => { cpu.read_modrm_byte(); cpu.write_e32(cpu.xor32(cpu.read_write_e32(), cpu.read_g32s())); }
t[0x32] = cpu => { cpu.read_modrm_byte(); cpu.write_g8(cpu.xor8(cpu.read_g8(), cpu.read_e8())); };
t16[0x33] = cpu => { cpu.read_modrm_byte(); cpu.write_g16(cpu.xor16(cpu.read_g16(), cpu.read_e16())); };
t32[0x33] = cpu => { cpu.read_modrm_byte(); cpu.write_g32(cpu.xor32(cpu.read_g32s(), cpu.read_e32s())); }
t[0x34] = cpu => { cpu.reg8[reg_al] = cpu.xor8(cpu.reg8[reg_al], cpu.read_op8()); };
t16[0x35] = cpu => { cpu.reg16[reg_ax] = cpu.xor16(cpu.reg16[reg_ax], cpu.read_op16()); };
t32[0x35] = cpu => { cpu.reg32s[reg_eax] = cpu.xor32(cpu.reg32s[reg_eax], cpu.read_op32s()); }
t[0x36] = cpu => { cpu.segment_prefix_op(reg_ss); };
t[0x37] = cpu => { cpu.bcd_aaa(); };
t[0x38] = cpu => { cpu.read_modrm_byte(); cpu.cmp8(cpu.read_e8(), cpu.read_g8()); };
t16[0x39] = cpu => { cpu.read_modrm_byte(); cpu.cmp16(cpu.read_e16(), cpu.read_g16()); };
t32[0x39] = cpu => { cpu.read_modrm_byte(); cpu.cmp32(cpu.read_e32s(), cpu.read_g32s()); }
t[0x3A] = cpu => { cpu.read_modrm_byte(); cpu.cmp8(cpu.read_g8(), cpu.read_e8()); };
t16[0x3B] = cpu => { cpu.read_modrm_byte(); cpu.cmp16(cpu.read_g16(), cpu.read_e16()); };
t32[0x3B] = cpu => { cpu.read_modrm_byte(); cpu.cmp32(cpu.read_g32s(), cpu.read_e32s()); }
t[0x3C] = cpu => { cpu.cmp8(cpu.reg8[reg_al], cpu.read_op8()); };
t16[0x3D] = cpu => { cpu.cmp16(cpu.reg16[reg_ax], cpu.read_op16()); };
t32[0x3D] = cpu => { cpu.cmp32(cpu.reg32s[reg_eax], cpu.read_op32s()); }
t[0x3E] = cpu => { cpu.segment_prefix_op(reg_ds); };
t[0x3F] = cpu => { cpu.bcd_aas(); };
t16[0x40] = cpu => { cpu.reg16[reg_ax] = cpu.inc16(cpu.reg16[reg_ax]); };
t32[0x40] = cpu => { cpu.reg32s[reg_eax] = cpu.inc32(cpu.reg32s[reg_eax]); };
t16[0x41] = cpu => { cpu.reg16[reg_cx] = cpu.inc16(cpu.reg16[reg_cx]); };
t32[0x41] = cpu => { cpu.reg32s[reg_ecx] = cpu.inc32(cpu.reg32s[reg_ecx]); };
t16[0x42] = cpu => { cpu.reg16[reg_dx] = cpu.inc16(cpu.reg16[reg_dx]); };
t32[0x42] = cpu => { cpu.reg32s[reg_edx] = cpu.inc32(cpu.reg32s[reg_edx]); };
t16[0x43] = cpu => { cpu.reg16[reg_bx] = cpu.inc16(cpu.reg16[reg_bx]); };
t32[0x43] = cpu => { cpu.reg32s[reg_ebx] = cpu.inc32(cpu.reg32s[reg_ebx]); };
t16[0x44] = cpu => { cpu.reg16[reg_sp] = cpu.inc16(cpu.reg16[reg_sp]); };
t32[0x44] = cpu => { cpu.reg32s[reg_esp] = cpu.inc32(cpu.reg32s[reg_esp]); };
t16[0x45] = cpu => { cpu.reg16[reg_bp] = cpu.inc16(cpu.reg16[reg_bp]); };
t32[0x45] = cpu => { cpu.reg32s[reg_ebp] = cpu.inc32(cpu.reg32s[reg_ebp]); };
t16[0x46] = cpu => { cpu.reg16[reg_si] = cpu.inc16(cpu.reg16[reg_si]); };
t32[0x46] = cpu => { cpu.reg32s[reg_esi] = cpu.inc32(cpu.reg32s[reg_esi]); };
t16[0x47] = cpu => { cpu.reg16[reg_di] = cpu.inc16(cpu.reg16[reg_di]); };
t32[0x47] = cpu => { cpu.reg32s[reg_edi] = cpu.inc32(cpu.reg32s[reg_edi]); };
t16[0x48] = cpu => { cpu.reg16[reg_ax] = cpu.dec16(cpu.reg16[reg_ax]); };
t32[0x48] = cpu => { cpu.reg32s[reg_eax] = cpu.dec32(cpu.reg32s[reg_eax]); };
t16[0x49] = cpu => { cpu.reg16[reg_cx] = cpu.dec16(cpu.reg16[reg_cx]); };
t32[0x49] = cpu => { cpu.reg32s[reg_ecx] = cpu.dec32(cpu.reg32s[reg_ecx]); };
t16[0x4A] = cpu => { cpu.reg16[reg_dx] = cpu.dec16(cpu.reg16[reg_dx]); };
t32[0x4A] = cpu => { cpu.reg32s[reg_edx] = cpu.dec32(cpu.reg32s[reg_edx]); };
t16[0x4B] = cpu => { cpu.reg16[reg_bx] = cpu.dec16(cpu.reg16[reg_bx]); };
t32[0x4B] = cpu => { cpu.reg32s[reg_ebx] = cpu.dec32(cpu.reg32s[reg_ebx]); };
t16[0x4C] = cpu => { cpu.reg16[reg_sp] = cpu.dec16(cpu.reg16[reg_sp]); };
t32[0x4C] = cpu => { cpu.reg32s[reg_esp] = cpu.dec32(cpu.reg32s[reg_esp]); };
t16[0x4D] = cpu => { cpu.reg16[reg_bp] = cpu.dec16(cpu.reg16[reg_bp]); };
t32[0x4D] = cpu => { cpu.reg32s[reg_ebp] = cpu.dec32(cpu.reg32s[reg_ebp]); };
t16[0x4E] = cpu => { cpu.reg16[reg_si] = cpu.dec16(cpu.reg16[reg_si]); };
t32[0x4E] = cpu => { cpu.reg32s[reg_esi] = cpu.dec32(cpu.reg32s[reg_esi]); };
t16[0x4F] = cpu => { cpu.reg16[reg_di] = cpu.dec16(cpu.reg16[reg_di]); };
t32[0x4F] = cpu => { cpu.reg32s[reg_edi] = cpu.dec32(cpu.reg32s[reg_edi]); };
t16[0x50] = cpu => { cpu.push16(cpu.reg16[reg_ax]); };
t32[0x50] = cpu => { cpu.push32(cpu.reg32s[reg_eax]); }
t16[0x51] = cpu => { cpu.push16(cpu.reg16[reg_cx]); };
t32[0x51] = cpu => { cpu.push32(cpu.reg32s[reg_ecx]); }
t16[0x52] = cpu => { cpu.push16(cpu.reg16[reg_dx]); };
t32[0x52] = cpu => { cpu.push32(cpu.reg32s[reg_edx]); }
t16[0x53] = cpu => { cpu.push16(cpu.reg16[reg_bx]); };
t32[0x53] = cpu => { cpu.push32(cpu.reg32s[reg_ebx]); }
t16[0x54] = cpu => { cpu.push16(cpu.reg16[reg_sp]); };
t32[0x54] = cpu => { cpu.push32(cpu.reg32s[reg_esp]); }
t16[0x55] = cpu => { cpu.push16(cpu.reg16[reg_bp]); };
t32[0x55] = cpu => { cpu.push32(cpu.reg32s[reg_ebp]); }
t16[0x56] = cpu => { cpu.push16(cpu.reg16[reg_si]); };
t32[0x56] = cpu => { cpu.push32(cpu.reg32s[reg_esi]); }
t16[0x57] = cpu => { cpu.push16(cpu.reg16[reg_di]); };
t32[0x57] = cpu => { cpu.push32(cpu.reg32s[reg_edi]); }
t16[0x58] = cpu => { cpu.reg16[reg_ax] = cpu.pop16(); };
t32[0x58] = cpu => { cpu.reg32s[reg_eax] = cpu.pop32s(); }
t16[0x59] = cpu => { cpu.reg16[reg_cx] = cpu.pop16(); };
t32[0x59] = cpu => { cpu.reg32s[reg_ecx] = cpu.pop32s(); }
t16[0x5A] = cpu => { cpu.reg16[reg_dx] = cpu.pop16(); };
t32[0x5A] = cpu => { cpu.reg32s[reg_edx] = cpu.pop32s(); }
t16[0x5B] = cpu => { cpu.reg16[reg_bx] = cpu.pop16(); };
t32[0x5B] = cpu => { cpu.reg32s[reg_ebx] = cpu.pop32s(); }
t16[0x5C] = cpu => { cpu.reg16[reg_sp] = cpu.pop16(); };
t32[0x5C] = cpu => { cpu.reg32s[reg_esp] = cpu.pop32s(); }
t16[0x5D] = cpu => { cpu.reg16[reg_bp] = cpu.pop16(); };
t32[0x5D] = cpu => { cpu.reg32s[reg_ebp] = cpu.pop32s(); }
t16[0x5E] = cpu => { cpu.reg16[reg_si] = cpu.pop16(); };
t32[0x5E] = cpu => { cpu.reg32s[reg_esi] = cpu.pop32s(); }
t16[0x5F] = cpu => { cpu.reg16[reg_di] = cpu.pop16(); };
t32[0x5F] = cpu => { cpu.reg32s[reg_edi] = cpu.pop32s(); }
t16[0x60] = cpu => { cpu.pusha16(); };
t32[0x60] = cpu => { cpu.pusha32(); };
t16[0x61] = cpu => { cpu.popa16(); };
t32[0x61] = cpu => { cpu.popa32(); };
t[0x62] = cpu => {
// bound
dbg_log("Unimplemented BOUND instruction", LOG_CPU);
dbg_assert(false);
};
t[0x63] = cpu => { cpu.read_modrm_byte();
// arpl
//dbg_log("arpl", LOG_CPU);
if(cpu.protected_mode && !cpu.vm86_mode())
{
cpu.write_e16(cpu.arpl(cpu.read_write_e16(), cpu.modrm_byte >> 2 & 14));
}
else
{
dbg_log("arpl #ud", LOG_CPU);
cpu.trigger_ud();
}
};
t[0x64] = cpu => { cpu.segment_prefix_op(reg_fs); };
t[0x65] = cpu => { cpu.segment_prefix_op(reg_gs); };
t[0x66] = cpu => {
// Operand-size override prefix
cpu.prefixes |= PREFIX_MASK_OPSIZE;
cpu.run_prefix_instruction();
cpu.prefixes = 0;
};
t[0x67] = cpu => {
// Address-size override prefix
dbg_assert(cpu.is_asize_32() === cpu.is_32);
cpu.prefixes |= PREFIX_MASK_ADDRSIZE;
cpu.run_prefix_instruction();
cpu.prefixes = 0;
};
t16[0x68] = cpu => { cpu.push16(cpu.read_op16()); };
t32[0x68] = cpu => { cpu.push32(cpu.read_op32s()); };
t16[0x69] = cpu => { cpu.read_modrm_byte();
cpu.write_g16(cpu.imul_reg16(cpu.read_e16s(), cpu.read_op16() << 16 >> 16));
};
t32[0x69] = cpu => { cpu.read_modrm_byte();
cpu.write_g32(cpu.imul_reg32(cpu.read_e32s(), cpu.read_op32s()));
};
t16[0x6A] = cpu => { cpu.push16(cpu.read_op8s()); };
t32[0x6A] = cpu => { cpu.push32(cpu.read_op8s()); };
t16[0x6B] = cpu => { cpu.read_modrm_byte();
cpu.write_g16(cpu.imul_reg16(cpu.read_e16s(), cpu.read_op8s()));
};
t32[0x6B] = cpu => { cpu.read_modrm_byte();
cpu.write_g32(cpu.imul_reg32(cpu.read_e32s(), cpu.read_op8s()));
};
t[0x6C] = cpu => { insb(cpu); };
t16[0x6D] = cpu => { insw(cpu); };
t32[0x6D] = cpu => { insd(cpu); };
t[0x6E] = cpu => { outsb(cpu); };
t16[0x6F] = cpu => { outsw(cpu); };
t32[0x6F] = cpu => { outsd(cpu); };
t[0x70] = cpu => { cpu.jmpcc8( cpu.test_o()); };
t[0x71] = cpu => { cpu.jmpcc8(!cpu.test_o()); };
t[0x72] = cpu => { cpu.jmpcc8( cpu.test_b()); };
t[0x73] = cpu => { cpu.jmpcc8(!cpu.test_b()); };
t[0x74] = cpu => { cpu.jmpcc8( cpu.test_z()); };
t[0x75] = cpu => { cpu.jmpcc8(!cpu.test_z()); };
t[0x76] = cpu => { cpu.jmpcc8( cpu.test_be()); };
t[0x77] = cpu => { cpu.jmpcc8(!cpu.test_be()); };
t[0x78] = cpu => { cpu.jmpcc8( cpu.test_s()); };
t[0x79] = cpu => { cpu.jmpcc8(!cpu.test_s()); };
t[0x7A] = cpu => { cpu.jmpcc8( cpu.test_p()); };
t[0x7B] = cpu => { cpu.jmpcc8(!cpu.test_p()); };
t[0x7C] = cpu => { cpu.jmpcc8( cpu.test_l()); };
t[0x7D] = cpu => { cpu.jmpcc8(!cpu.test_l()); };
t[0x7E] = cpu => { cpu.jmpcc8( cpu.test_le()); };
t[0x7F] = cpu => { cpu.jmpcc8(!cpu.test_le()); };
t[0x80] = cpu => { cpu.read_modrm_byte();
switch(cpu.modrm_byte >> 3 & 7)
{
case 0: cpu.write_e8(cpu.add8(cpu.read_write_e8(), cpu.read_op8())); break;
case 1: cpu.write_e8(cpu. or8(cpu.read_write_e8(), cpu.read_op8())); break;
case 2: cpu.write_e8(cpu.adc8(cpu.read_write_e8(), cpu.read_op8())); break;
case 3: cpu.write_e8(cpu.sbb8(cpu.read_write_e8(), cpu.read_op8())); break;
case 4: cpu.write_e8(cpu.and8(cpu.read_write_e8(), cpu.read_op8())); break;
case 5: cpu.write_e8(cpu.sub8(cpu.read_write_e8(), cpu.read_op8())); break;
case 6: cpu.write_e8(cpu.xor8(cpu.read_write_e8(), cpu.read_op8())); break;
case 7: cpu.cmp8(cpu.read_e8(), cpu.read_op8()); break;
}
};
t16[0x81] = cpu => { cpu.read_modrm_byte();
switch(cpu.modrm_byte >> 3 & 7)
{
case 0: cpu.write_e16(cpu.add16(cpu.read_write_e16(), cpu.read_op16())); break;
case 1: cpu.write_e16(cpu. or16(cpu.read_write_e16(), cpu.read_op16())); break;
case 2: cpu.write_e16(cpu.adc16(cpu.read_write_e16(), cpu.read_op16())); break;
case 3: cpu.write_e16(cpu.sbb16(cpu.read_write_e16(), cpu.read_op16())); break;
case 4: cpu.write_e16(cpu.and16(cpu.read_write_e16(), cpu.read_op16())); break;
case 5: cpu.write_e16(cpu.sub16(cpu.read_write_e16(), cpu.read_op16())); break;
case 6: cpu.write_e16(cpu.xor16(cpu.read_write_e16(), cpu.read_op16())); break;
case 7: cpu.cmp16(cpu.read_e16(), cpu.read_op16()); break;
}
};
t32[0x81] = cpu => { cpu.read_modrm_byte();
switch(cpu.modrm_byte >> 3 & 7)
{
case 0: cpu.write_e32(cpu.add32(cpu.read_write_e32(), cpu.read_op32s())); break;
case 1: cpu.write_e32(cpu. or32(cpu.read_write_e32(), cpu.read_op32s())); break;
case 2: cpu.write_e32(cpu.adc32(cpu.read_write_e32(), cpu.read_op32s())); break;
case 3: cpu.write_e32(cpu.sbb32(cpu.read_write_e32(), cpu.read_op32s())); break;
case 4: cpu.write_e32(cpu.and32(cpu.read_write_e32(), cpu.read_op32s())); break;
case 5: cpu.write_e32(cpu.sub32(cpu.read_write_e32(), cpu.read_op32s())); break;
case 6: cpu.write_e32(cpu.xor32(cpu.read_write_e32(), cpu.read_op32s())); break;
case 7: cpu.cmp32(cpu.read_e32s(), cpu.read_op32s()); break;
}
};
t[0x82] = t[0x80]; // alias
t16[0x83] = cpu => { cpu.read_modrm_byte();
switch(cpu.modrm_byte >> 3 & 7)
{
case 0: cpu.write_e16(cpu.add16(cpu.read_write_e16(), cpu.read_op8s())); break;
case 1: cpu.write_e16(cpu. or16(cpu.read_write_e16(), cpu.read_op8s())); break;
case 2: cpu.write_e16(cpu.adc16(cpu.read_write_e16(), cpu.read_op8s())); break;
case 3: cpu.write_e16(cpu.sbb16(cpu.read_write_e16(), cpu.read_op8s())); break;
case 4: cpu.write_e16(cpu.and16(cpu.read_write_e16(), cpu.read_op8s())); break;
case 5: cpu.write_e16(cpu.sub16(cpu.read_write_e16(), cpu.read_op8s())); break;
case 6: cpu.write_e16(cpu.xor16(cpu.read_write_e16(), cpu.read_op8s())); break;
case 7: cpu.cmp16(cpu.read_e16(), cpu.read_op8s()); break;
}
};
t32[0x83] = cpu => { cpu.read_modrm_byte();
switch(cpu.modrm_byte >> 3 & 7)
{
case 0: cpu.write_e32(cpu.add32(cpu.read_write_e32(), cpu.read_op8s())); break;
case 1: cpu.write_e32(cpu. or32(cpu.read_write_e32(), cpu.read_op8s())); break;
case 2: cpu.write_e32(cpu.adc32(cpu.read_write_e32(), cpu.read_op8s())); break;
case 3: cpu.write_e32(cpu.sbb32(cpu.read_write_e32(), cpu.read_op8s())); break;
case 4: cpu.write_e32(cpu.and32(cpu.read_write_e32(), cpu.read_op8s())); break;
case 5: cpu.write_e32(cpu.sub32(cpu.read_write_e32(), cpu.read_op8s())); break;
case 6: cpu.write_e32(cpu.xor32(cpu.read_write_e32(), cpu.read_op8s())); break;
case 7: cpu.cmp32(cpu.read_e32s(), cpu.read_op8s()); break;
}
};
t[0x84] = cpu => { cpu.read_modrm_byte(); var data = cpu.read_e8(); cpu.test8(data, cpu.read_g8()); };
t16[0x85] = cpu => { cpu.read_modrm_byte(); var data = cpu.read_e16(); cpu.test16(data, cpu.read_g16()); };
t32[0x85] = cpu => { cpu.read_modrm_byte(); var data = cpu.read_e32s(); cpu.test32(data, cpu.read_g32s()); }
t[0x86] = cpu => { cpu.read_modrm_byte(); var data = cpu.read_write_e8(); cpu.write_e8(cpu.xchg8(data, cpu.modrm_byte)); };
t16[0x87] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_write_e16(); cpu.write_e16(cpu.xchg16(data, cpu.modrm_byte));
};
t32[0x87] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_write_e32(); cpu.write_e32(cpu.xchg32(data, cpu.modrm_byte));
};
t[0x88] = cpu => { cpu.read_modrm_byte(); cpu.set_e8(cpu.read_g8()); };
t16[0x89] = cpu => { cpu.read_modrm_byte(); cpu.set_e16(cpu.read_g16()); };
t32[0x89] = cpu => { cpu.read_modrm_byte(); cpu.set_e32(cpu.read_g32s()); }
t[0x8A] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_e8();
cpu.write_g8(data);
};
t16[0x8B] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_e16();
cpu.write_g16(data);
};
t32[0x8B] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_e32s();
cpu.write_g32(data);
};
t16[0x8C] = cpu => { cpu.read_modrm_byte();
cpu.set_e16(cpu.sreg[cpu.modrm_byte >> 3 & 7]);
};
t32[0x8C] = cpu => { cpu.read_modrm_byte();
cpu.set_e32(cpu.sreg[cpu.modrm_byte >> 3 & 7]);
};
t16[0x8D] = cpu => { cpu.read_modrm_byte();
// lea
if(cpu.modrm_byte >= 0xC0)
{
dbg_log("lea #ud", LOG_CPU);
cpu.trigger_ud();
}
var mod = cpu.modrm_byte >> 3 & 7;
// override prefix, so modrm_resolve does not return the segment part
cpu.prefixes |= SEG_PREFIX_ZERO;
cpu.reg16[mod << 1] = cpu.modrm_resolve(cpu.modrm_byte);
cpu.prefixes = 0;
};
t32[0x8D] = cpu => { cpu.read_modrm_byte();
if(cpu.modrm_byte >= 0xC0)
{
dbg_log("lea #ud", LOG_CPU);
cpu.trigger_ud();
}
var mod = cpu.modrm_byte >> 3 & 7;
cpu.prefixes |= SEG_PREFIX_ZERO;
cpu.reg32s[mod] = cpu.modrm_resolve(cpu.modrm_byte);
cpu.prefixes = 0;
};
t[0x8E] = cpu => { cpu.read_modrm_byte();
var mod = cpu.modrm_byte >> 3 & 7;
var data = cpu.read_e16();
cpu.switch_seg(mod, data);
if(mod === reg_ss)
{
// run next instruction, so no interrupts are handled
cpu.clear_prefixes();
cpu.cycle_internal();
}
};
t16[0x8F] = cpu => { cpu.read_modrm_byte();
// pop
var sp = cpu.safe_read16(cpu.get_stack_pointer(0));
cpu.adjust_stack_reg(2);
if(cpu.modrm_byte < 0xC0) {
var addr = cpu.modrm_resolve(cpu.modrm_byte);
cpu.adjust_stack_reg(-2);
cpu.safe_write16(addr, sp);
cpu.adjust_stack_reg(2);
} else {
cpu.write_reg_e16(sp);
}
};
t32[0x8F] = cpu => { cpu.read_modrm_byte();
var sp = cpu.safe_read32s(cpu.get_stack_pointer(0));
// change esp first, then resolve modrm address
cpu.adjust_stack_reg(4);
if(cpu.modrm_byte < 0xC0) {
var addr = cpu.modrm_resolve(cpu.modrm_byte);
// Before attempting a write that might cause a page fault,
// we must set esp to the old value. Fuck Intel.
cpu.adjust_stack_reg(-4);
cpu.safe_write32(addr, sp);
cpu.adjust_stack_reg(4);
} else {
cpu.write_reg_e32(sp);
}
};
t[0x90] = cpu => { };
t16[0x91] = cpu => { cpu.xchg16r(reg_cx) };
t32[0x91] = cpu => { cpu.xchg32r(reg_ecx) };
t16[0x92] = cpu => { cpu.xchg16r(reg_dx) };
t32[0x92] = cpu => { cpu.xchg32r(reg_edx) };
t16[0x93] = cpu => { cpu.xchg16r(reg_bx) };
t32[0x93] = cpu => { cpu.xchg32r(reg_ebx) };
t16[0x94] = cpu => { cpu.xchg16r(reg_sp) };
t32[0x94] = cpu => { cpu.xchg32r(reg_esp) };
t16[0x95] = cpu => { cpu.xchg16r(reg_bp) };
t32[0x95] = cpu => { cpu.xchg32r(reg_ebp) };
t16[0x96] = cpu => { cpu.xchg16r(reg_si) };
t32[0x96] = cpu => { cpu.xchg32r(reg_esi) };
t16[0x97] = cpu => { cpu.xchg16r(reg_di) };
t32[0x97] = cpu => { cpu.xchg32r(reg_edi) };
t16[0x98] = cpu => { /* cbw */ cpu.reg16[reg_ax] = cpu.reg8s[reg_al]; };
t32[0x98] = cpu => { /* cwde */ cpu.reg32s[reg_eax] = cpu.reg16s[reg_ax]; };
t16[0x99] = cpu => { /* cwd */ cpu.reg16[reg_dx] = cpu.reg16s[reg_ax] >> 15; };
t32[0x99] = cpu => { /* cdq */ cpu.reg32s[reg_edx] = cpu.reg32s[reg_eax] >> 31; };
t16[0x9A] = cpu => {
// callf
var new_ip = cpu.read_op16();
var new_cs = cpu.read_disp16();
cpu.far_jump(new_ip, new_cs, true);
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.diverged();
};
t32[0x9A] = cpu => {
var new_ip = cpu.read_op32s();
var new_cs = cpu.read_disp16();
if(!cpu.protected_mode || cpu.vm86_mode())
{
if(new_ip & 0xFFFF0000)
{
throw cpu.debug.unimpl("#GP handler");
}
}
cpu.far_jump(new_ip, new_cs, true);
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.diverged();
};
t[0x9B] = cpu => {
// fwait: check for pending fpu exceptions
if((cpu.cr[0] & (CR0_MP | CR0_TS)) === (CR0_MP | CR0_TS))
{
// task switched and MP bit is set
cpu.trigger_nm();
}
else
{
if(cpu.fpu)
{
cpu.fpu.fwait();
}
else
{
// EM bit isn't checked
// If there's no FPU, do nothing
}
}
};
t16[0x9C] = cpu => {
// pushf
if((cpu.flags & flag_vm) && cpu.getiopl() < 3)
{
dbg_assert(cpu.protected_mode);
dbg_log("pushf #gp", LOG_CPU);
cpu.trigger_gp(0);
}
else
{
cpu.push16(cpu.get_eflags());
}
};
t32[0x9C] = cpu => {
// pushf
if((cpu.flags & flag_vm) && cpu.getiopl() < 3)
{
// trap to virtual 8086 monitor
dbg_assert(cpu.protected_mode);
dbg_log("pushf #gp", LOG_CPU);
cpu.trigger_gp(0);
}
else
{
// vm and rf flag are cleared in image stored on the stack
cpu.push32(cpu.get_eflags() & 0x00FCFFFF);
}
};
t16[0x9D] = cpu => {
// popf
if((cpu.flags & flag_vm) && cpu.getiopl() < 3)
{
dbg_log("popf #gp", LOG_CPU);
cpu.trigger_gp(0);
}
cpu.update_eflags((cpu.flags & ~0xFFFF) | cpu.pop16());
if(cpu.flags & flag_trap)
{
// XXX: Problems with fdgame
//cpu.clear_prefixes();
//cpu.cycle_internal();
cpu.flags &= ~flag_trap;
//cpu.instruction_pointer = cpu.previous_ip;
//cpu.raise_exception(1);
}
else
{
cpu.handle_irqs();
}
};
t32[0x9D] = cpu => {
// popf
if((cpu.flags & flag_vm) && cpu.getiopl() < 3)
{
dbg_log("popf #gp", LOG_CPU);
cpu.trigger_gp(0);
}
cpu.update_eflags(cpu.pop32s());
cpu.handle_irqs();
};
t[0x9E] = cpu => {
// sahf
cpu.flags = (cpu.flags & ~0xFF) | cpu.reg8[reg_ah];
cpu.flags = (cpu.flags & flags_mask) | flags_default;
cpu.flags_changed = 0;
};
t[0x9F] = cpu => {
// lahf
cpu.reg8[reg_ah] = cpu.get_eflags();
};
t[0xA0] = cpu => {
// mov
var data = cpu.safe_read8(cpu.read_moffs());
cpu.reg8[reg_al] = data;
};
t16[0xA1] = cpu => {
// mov
var data = cpu.safe_read16(cpu.read_moffs());
cpu.reg16[reg_ax] = data;
};
t32[0xA1] = cpu => {
var data = cpu.safe_read32s(cpu.read_moffs());
cpu.reg32s[reg_eax] = data;
};
t[0xA2] = cpu => {
// mov
cpu.safe_write8(cpu.read_moffs(), cpu.reg8[reg_al]);
};
t16[0xA3] = cpu => {
// mov
cpu.safe_write16(cpu.read_moffs(), cpu.reg16[reg_ax]);
};
t32[0xA3] = cpu => {
cpu.safe_write32(cpu.read_moffs(), cpu.reg32s[reg_eax]);
};
t[0xA4] = cpu => { cpu.movsb(); };
t16[0xA5] = cpu => { cpu.movsw(); };
t32[0xA5] = cpu => { cpu.movsd(); };
t[0xA6] = cpu => { cmpsb(cpu); };
t16[0xA7] = cpu => { cmpsw(cpu); };
t32[0xA7] = cpu => { cmpsd(cpu); };
t[0xA8] = cpu => {
cpu.test8(cpu.reg8[reg_al], cpu.read_op8());
};
t16[0xA9] = cpu => {
cpu.test16(cpu.reg16[reg_ax], cpu.read_op16());
};
t32[0xA9] = cpu => {
cpu.test32(cpu.reg32s[reg_eax], cpu.read_op32s());
};
t[0xAA] = cpu => { stosb(cpu); };
t16[0xAB] = cpu => { stosw(cpu); };
t32[0xAB] = cpu => { stosd(cpu); };
t[0xAC] = cpu => { lodsb(cpu); };
t16[0xAD] = cpu => { lodsw(cpu); };
t32[0xAD] = cpu => { lodsd(cpu); };
t[0xAE] = cpu => { scasb(cpu); };
t16[0xAF] = cpu => { scasw(cpu); };
t32[0xAF] = cpu => { scasd(cpu); };
t[0xB0] = cpu => { cpu.reg8[reg_al] = cpu.read_op8(); };
t[0xB1] = cpu => { cpu.reg8[reg_cl] = cpu.read_op8(); };
t[0xB2] = cpu => { cpu.reg8[reg_dl] = cpu.read_op8(); };
t[0xB3] = cpu => { cpu.reg8[reg_bl] = cpu.read_op8(); };
t[0xB4] = cpu => { cpu.reg8[reg_ah] = cpu.read_op8(); };
t[0xB5] = cpu => { cpu.reg8[reg_ch] = cpu.read_op8(); };
t[0xB6] = cpu => { cpu.reg8[reg_dh] = cpu.read_op8(); };
t[0xB7] = cpu => { cpu.reg8[reg_bh] = cpu.read_op8(); };
t16[0xB8] = cpu => { cpu.reg16[reg_ax] = cpu.read_op16(); };
t32[0xB8] = cpu => { cpu.reg32s[reg_eax] = cpu.read_op32s(); };
t16[0xB9] = cpu => { cpu.reg16[reg_cx] = cpu.read_op16(); };
t32[0xB9] = cpu => { cpu.reg32s[reg_ecx] = cpu.read_op32s(); };
t16[0xBA] = cpu => { cpu.reg16[reg_dx] = cpu.read_op16(); };
t32[0xBA] = cpu => { cpu.reg32s[reg_edx] = cpu.read_op32s(); };
t16[0xBB] = cpu => { cpu.reg16[reg_bx] = cpu.read_op16(); };
t32[0xBB] = cpu => { cpu.reg32s[reg_ebx] = cpu.read_op32s(); };
t16[0xBC] = cpu => { cpu.reg16[reg_sp] = cpu.read_op16(); };
t32[0xBC] = cpu => { cpu.reg32s[reg_esp] = cpu.read_op32s(); };
t16[0xBD] = cpu => { cpu.reg16[reg_bp] = cpu.read_op16(); };
t32[0xBD] = cpu => { cpu.reg32s[reg_ebp] = cpu.read_op32s(); };
t16[0xBE] = cpu => { cpu.reg16[reg_si] = cpu.read_op16(); };
t32[0xBE] = cpu => { cpu.reg32s[reg_esi] = cpu.read_op32s(); };
t16[0xBF] = cpu => { cpu.reg16[reg_di] = cpu.read_op16(); };
t32[0xBF] = cpu => { cpu.reg32s[reg_edi] = cpu.read_op32s(); };
t[0xC0] = cpu => { cpu.read_modrm_byte();
var op1 = cpu.read_write_e8();
var op2 = cpu.read_op8() & 31;
var result = 0;
switch(cpu.modrm_byte >> 3 & 7)
{
case 0: result = cpu.rol8(op1, op2); break;
case 1: result = cpu.ror8(op1, op2); break;
case 2: result = cpu.rcl8(op1, op2); break;
case 3: result = cpu.rcr8(op1, op2); break;
case 4: result = cpu.shl8(op1, op2); break;
case 5: result = cpu.shr8(op1, op2); break;
case 6: result = cpu.shl8(op1, op2); break;
case 7: result = cpu.sar8(op1, op2); break;
}
cpu.write_e8(result);
};
t16[0xC1] = cpu => { cpu.read_modrm_byte();
var op1 = cpu.read_write_e16();
var op2 = cpu.read_op8() & 31;
var result = 0;
switch(cpu.modrm_byte >> 3 & 7)
{
case 0: result = cpu.rol16(op1, op2); break;
case 1: result = cpu.ror16(op1, op2); break;
case 2: result = cpu.rcl16(op1, op2); break;
case 3: result = cpu.rcr16(op1, op2); break;
case 4: result = cpu.shl16(op1, op2); break;
case 5: result = cpu.shr16(op1, op2); break;
case 6: result = cpu.shl16(op1, op2); break;
case 7: result = cpu.sar16(op1, op2); break;
}
cpu.write_e16(result);
};
t32[0xC1] = cpu => { cpu.read_modrm_byte();
var op1 = cpu.read_write_e32();
var op2 = cpu.read_op8() & 31;
var result = 0;
switch(cpu.modrm_byte >> 3 & 7)
{
case 0: result = cpu.rol32(op1, op2); break;
case 1: result = cpu.ror32(op1, op2); break;
case 2: result = cpu.rcl32(op1, op2); break;
case 3: result = cpu.rcr32(op1, op2); break;
case 4: result = cpu.shl32(op1, op2); break;
case 5: result = cpu.shr32(op1, op2); break;
case 6: result = cpu.shl32(op1, op2); break;
case 7: result = cpu.sar32(op1, op2); break;
}
cpu.write_e32(result);
};
t16[0xC2] = cpu => {
// retn
var imm16 = cpu.read_op16();
cpu.instruction_pointer = cpu.get_seg(reg_cs) + cpu.pop16() | 0;
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.adjust_stack_reg(imm16);
cpu.diverged();
};
t32[0xC2] = cpu => {
// retn
var imm16 = cpu.read_op16();
var ip = cpu.pop32s();
dbg_assert(cpu.is_asize_32() || ip < 0x10000);
cpu.instruction_pointer = cpu.get_seg(reg_cs) + ip | 0;
cpu.adjust_stack_reg(imm16);
cpu.diverged();
};
t16[0xC3] = cpu => {
// retn
cpu.instruction_pointer = cpu.get_seg(reg_cs) + cpu.pop16() | 0;
cpu.diverged();
};
t32[0xC3] = cpu => {
// retn
var ip = cpu.pop32s();
dbg_assert(cpu.is_asize_32() || ip < 0x10000);
cpu.instruction_pointer = cpu.get_seg(reg_cs) + ip | 0;
cpu.diverged();
};
t16[0xC4] = cpu => { cpu.read_modrm_byte();
cpu.lss16(reg_es);
};
t32[0xC4] = cpu => { cpu.read_modrm_byte();
cpu.lss32(reg_es);
};
t16[0xC5] = cpu => { cpu.read_modrm_byte();
cpu.lss16(reg_ds);
};
t32[0xC5] = cpu => { cpu.read_modrm_byte();
cpu.lss32(reg_ds);
};
t[0xC6] = cpu => { cpu.read_modrm_byte();
if(cpu.modrm_byte < 0xC0) {
cpu.safe_write8(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_op8());
} else {
cpu.reg8[cpu.modrm_byte << 2 & 0xC | cpu.modrm_byte >> 2 & 1] = cpu.read_op8();
}
}
t16[0xC7] = cpu => { cpu.read_modrm_byte();
if(cpu.modrm_byte < 0xC0) {
cpu.safe_write16(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_op16());
} else {
cpu.reg16[cpu.modrm_byte << 1 & 14] = cpu.read_op16();
}
};
t32[0xC7] = cpu => { cpu.read_modrm_byte();
if(cpu.modrm_byte < 0xC0) {
cpu.safe_write32(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_op32s());
} else {
cpu.reg32s[cpu.modrm_byte & 7] = cpu.read_op32s();
}
}
t16[0xC8] = cpu => { cpu.enter16(cpu.read_op16(), cpu.read_disp8()); };
t32[0xC8] = cpu => { cpu.enter32(cpu.read_op16(), cpu.read_disp8()); };
t16[0xC9] = cpu => {
// leave
var old_vbp = cpu.stack_size_32 ? cpu.reg32s[reg_ebp] : cpu.reg16[reg_bp];
var new_bp = cpu.safe_read16(cpu.get_seg(reg_ss) + old_vbp | 0);
cpu.set_stack_reg(old_vbp + 2 | 0);
cpu.reg16[reg_bp] = new_bp;
};
t32[0xC9] = cpu => {
var old_vbp = cpu.stack_size_32 ? cpu.reg32s[reg_ebp] : cpu.reg16[reg_bp];
var new_ebp = cpu.safe_read32s(cpu.get_seg(reg_ss) + old_vbp | 0);
cpu.set_stack_reg(old_vbp + 4 | 0);
cpu.reg32s[reg_ebp] = new_ebp;
};
t16[0xCA] = cpu => {
// retf
var imm16 = cpu.read_op16();
var ip = cpu.safe_read16(cpu.get_stack_pointer(0));
var cs = cpu.safe_read16(cpu.get_stack_pointer(2));
cpu.far_return(ip, cs, imm16);
cpu.diverged();
};
t32[0xCA] = cpu => {
// retf
var imm16 = cpu.read_op16();
var ip = cpu.safe_read32s(cpu.get_stack_pointer(0));
var cs = cpu.safe_read32s(cpu.get_stack_pointer(4)) & 0xFFFF;
cpu.far_return(ip, cs, imm16);
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.diverged();
};
t16[0xCB] = cpu => {
// retf
var ip = cpu.safe_read16(cpu.get_stack_pointer(0));
var cs = cpu.safe_read16(cpu.get_stack_pointer(2));
cpu.far_return(ip, cs, 0);
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.diverged();
};
t32[0xCB] = cpu => {
// retf
var ip = cpu.safe_read32s(cpu.get_stack_pointer(0));
var cs = cpu.safe_read32s(cpu.get_stack_pointer(4)) & 0xFFFF;
cpu.far_return(ip, cs, 0);
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.diverged();
};
t[0xCC] = cpu => {
// INT3
// TODO: inhibit iopl checks
dbg_log("INT3", LOG_CPU);
cpu.call_interrupt_vector(3, true, false);
cpu.diverged();
};
t[0xCD] = cpu => {
// INT
var imm8 = cpu.read_op8();
cpu.call_interrupt_vector(imm8, true, false);
cpu.diverged();
};
t[0xCE] = cpu => {
// INTO
dbg_log("INTO", LOG_CPU);
if(cpu.getof())
{
// TODO: inhibit iopl checks
cpu.call_interrupt_vector(4, true, false);
}
cpu.diverged();
};
t16[0xCF] = cpu => {
// iret
cpu.iret16();
cpu.diverged();
};
t32[0xCF] = cpu => {
cpu.iret32();
cpu.diverged();
};
t[0xD0] = cpu => { cpu.read_modrm_byte();
var op1 = cpu.read_write_e8();
var result = 0;
switch(cpu.modrm_byte >> 3 & 7)
{
case 0: result = cpu.rol8(op1, 1); break;
case 1: result = cpu.ror8(op1, 1); break;
case 2: result = cpu.rcl8(op1, 1); break;
case 3: result = cpu.rcr8(op1, 1); break;
case 4: result = cpu.shl8(op1, 1); break;
case 5: result = cpu.shr8(op1, 1); break;
case 6: result = cpu.shl8(op1, 1); break;
case 7: result = cpu.sar8(op1, 1); break;
}
cpu.write_e8(result);
};
t16[0xD1] = cpu => { cpu.read_modrm_byte();
var op1 = cpu.read_write_e16();
var result = 0;
switch(cpu.modrm_byte >> 3 & 7)
{
case 0: result = cpu.rol16(op1, 1); break;
case 1: result = cpu.ror16(op1, 1); break;
case 2: result = cpu.rcl16(op1, 1); break;
case 3: result = cpu.rcr16(op1, 1); break;
case 4: result = cpu.shl16(op1, 1); break;
case 5: result = cpu.shr16(op1, 1); break;
case 6: result = cpu.shl16(op1, 1); break;
case 7: result = cpu.sar16(op1, 1); break;
}
cpu.write_e16(result);
};
t32[0xD1] = cpu => { cpu.read_modrm_byte();
var op1 = cpu.read_write_e32();
var result = 0;
switch(cpu.modrm_byte >> 3 & 7)
{
case 0: result = cpu.rol32(op1, 1); break;
case 1: result = cpu.ror32(op1, 1); break;
case 2: result = cpu.rcl32(op1, 1); break;
case 3: result = cpu.rcr32(op1, 1); break;
case 4: result = cpu.shl32(op1, 1); break;
case 5: result = cpu.shr32(op1, 1); break;
case 6: result = cpu.shl32(op1, 1); break;
case 7: result = cpu.sar32(op1, 1); break;
}
cpu.write_e32(result);
};
t[0xD2] = cpu => { cpu.read_modrm_byte();
var op1 = cpu.read_write_e8();
var op2 = cpu.reg8[reg_cl] & 31;
var result = 0;
switch(cpu.modrm_byte >> 3 & 7)
{
case 0: result = cpu.rol8(op1, op2); break;
case 1: result = cpu.ror8(op1, op2); break;
case 2: result = cpu.rcl8(op1, op2); break;
case 3: result = cpu.rcr8(op1, op2); break;
case 4: result = cpu.shl8(op1, op2); break;
case 5: result = cpu.shr8(op1, op2); break;
case 6: result = cpu.shl8(op1, op2); break;
case 7: result = cpu.sar8(op1, op2); break;
}
cpu.write_e8(result);
};
t16[0xD3] = cpu => { cpu.read_modrm_byte();
var op1 = cpu.read_write_e16();
var op2 = cpu.reg8[reg_cl] & 31;
var result = 0;
switch(cpu.modrm_byte >> 3 & 7)
{
case 0: result = cpu.rol16(op1, op2); break;
case 1: result = cpu.ror16(op1, op2); break;
case 2: result = cpu.rcl16(op1, op2); break;
case 3: result = cpu.rcr16(op1, op2); break;
case 4: result = cpu.shl16(op1, op2); break;
case 5: result = cpu.shr16(op1, op2); break;
case 6: result = cpu.shl16(op1, op2); break;
case 7: result = cpu.sar16(op1, op2); break;
}
cpu.write_e16(result);
};
t32[0xD3] = cpu => { cpu.read_modrm_byte();
var op1 = cpu.read_write_e32();
var op2 = cpu.reg8[reg_cl] & 31;
var result = 0;
switch(cpu.modrm_byte >> 3 & 7)
{
case 0: result = cpu.rol32(op1, op2); break;
case 1: result = cpu.ror32(op1, op2); break;
case 2: result = cpu.rcl32(op1, op2); break;
case 3: result = cpu.rcr32(op1, op2); break;
case 4: result = cpu.shl32(op1, op2); break;
case 5: result = cpu.shr32(op1, op2); break;
case 6: result = cpu.shl32(op1, op2); break;
case 7: result = cpu.sar32(op1, op2); break;
}
cpu.write_e32(result);
};
t[0xD4] = cpu => {
cpu.bcd_aam(cpu.read_op8());
};
t[0xD5] = cpu => {
cpu.bcd_aad(cpu.read_op8());
};
t[0xD6] = cpu => {
// salc
cpu.reg8[reg_al] = -cpu.getcf();
};
t[0xD7] = cpu => {
// xlat
if(cpu.is_asize_32())
{
cpu.reg8[reg_al] = cpu.safe_read8(cpu.get_seg_prefix(reg_ds) + cpu.reg32s[reg_ebx] + cpu.reg8[reg_al] | 0);
}
else
{
cpu.reg8[reg_al] = cpu.safe_read8(cpu.get_seg_prefix(reg_ds) + (cpu.reg16[reg_bx] + cpu.reg8[reg_al] & 0xFFFF) | 0);
}
};
t[0xD8] = cpu => { cpu.read_modrm_byte();
cpu.task_switch_test();
if(cpu.modrm_byte < 0xC0)
cpu.fpu.op_D8_mem(cpu.modrm_byte, cpu.modrm_resolve(cpu.modrm_byte));
else
cpu.fpu.op_D8_reg(cpu.modrm_byte);
};
t[0xD9] = cpu => { cpu.read_modrm_byte();
cpu.task_switch_test();
if(cpu.modrm_byte < 0xC0)
cpu.fpu.op_D9_mem(cpu.modrm_byte, cpu.modrm_resolve(cpu.modrm_byte));
else
cpu.fpu.op_D9_reg(cpu.modrm_byte);
};
t[0xDA] = cpu => { cpu.read_modrm_byte();
cpu.task_switch_test();
if(cpu.modrm_byte < 0xC0)
cpu.fpu.op_DA_mem(cpu.modrm_byte, cpu.modrm_resolve(cpu.modrm_byte));
else
cpu.fpu.op_DA_reg(cpu.modrm_byte);
};
t[0xDB] = cpu => { cpu.read_modrm_byte();
cpu.task_switch_test();
if(cpu.modrm_byte < 0xC0)
cpu.fpu.op_DB_mem(cpu.modrm_byte, cpu.modrm_resolve(cpu.modrm_byte));
else
cpu.fpu.op_DB_reg(cpu.modrm_byte);
};
t[0xDC] = cpu => { cpu.read_modrm_byte();
cpu.task_switch_test();
if(cpu.modrm_byte < 0xC0)
cpu.fpu.op_DC_mem(cpu.modrm_byte, cpu.modrm_resolve(cpu.modrm_byte));
else
cpu.fpu.op_DC_reg(cpu.modrm_byte);
};
t[0xDD] = cpu => { cpu.read_modrm_byte();
cpu.task_switch_test();
if(cpu.modrm_byte < 0xC0)
cpu.fpu.op_DD_mem(cpu.modrm_byte, cpu.modrm_resolve(cpu.modrm_byte));
else
cpu.fpu.op_DD_reg(cpu.modrm_byte);
};
t[0xDE] = cpu => { cpu.read_modrm_byte();
cpu.task_switch_test();
if(cpu.modrm_byte < 0xC0)
cpu.fpu.op_DE_mem(cpu.modrm_byte, cpu.modrm_resolve(cpu.modrm_byte));
else
cpu.fpu.op_DE_reg(cpu.modrm_byte);
};
t[0xDF] = cpu => { cpu.read_modrm_byte();
cpu.task_switch_test();
if(cpu.modrm_byte < 0xC0)
cpu.fpu.op_DF_mem(cpu.modrm_byte, cpu.modrm_resolve(cpu.modrm_byte));
else
cpu.fpu.op_DF_reg(cpu.modrm_byte);
};
t[0xE0] = cpu => { cpu.loopne(cpu.read_op8s()); };
t[0xE1] = cpu => { cpu.loope(cpu.read_op8s()); };
t[0xE2] = cpu => { cpu.loop(cpu.read_op8s()); };
t[0xE3] = cpu => { cpu.jcxz(cpu.read_op8s()); };
t[0xE4] = cpu => {
var port = cpu.read_op8();
cpu.test_privileges_for_io(port, 1);
cpu.reg8[reg_al] = cpu.io.port_read8(port);
cpu.diverged();
};
t16[0xE5] = cpu => {
var port = cpu.read_op8();
cpu.test_privileges_for_io(port, 2);
cpu.reg16[reg_ax] = cpu.io.port_read16(port);
cpu.diverged();
};
t32[0xE5] = cpu => {
var port = cpu.read_op8();
cpu.test_privileges_for_io(port, 4);
cpu.reg32s[reg_eax] = cpu.io.port_read32(port);
cpu.diverged();
};
t[0xE6] = cpu => {
var port = cpu.read_op8();
cpu.test_privileges_for_io(port, 1);
cpu.io.port_write8(port, cpu.reg8[reg_al]);
cpu.diverged();
};
t16[0xE7] = cpu => {
var port = cpu.read_op8();
cpu.test_privileges_for_io(port, 2);
cpu.io.port_write16(port, cpu.reg16[reg_ax]);
cpu.diverged();
};
t32[0xE7] = cpu => {
var port = cpu.read_op8();
cpu.test_privileges_for_io(port, 4);
cpu.io.port_write32(port, cpu.reg32s[reg_eax]);
cpu.diverged();
};
t16[0xE8] = cpu => {
// call
var imm16 = cpu.read_op16();
cpu.push16(cpu.get_real_eip());
cpu.jmp_rel16(imm16);
cpu.diverged();
};
t32[0xE8] = cpu => {
// call
var imm32s = cpu.read_op32s();
cpu.push32(cpu.get_real_eip());
cpu.instruction_pointer = cpu.instruction_pointer + imm32s | 0;
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.diverged();
};
t16[0xE9] = cpu => {
// jmp
var imm16 = cpu.read_op16();
cpu.jmp_rel16(imm16);
cpu.diverged();
};
t32[0xE9] = cpu => {
// jmp
var imm32s = cpu.read_op32s();
cpu.instruction_pointer = cpu.instruction_pointer + imm32s | 0;
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.diverged();
};
t16[0xEA] = cpu => {
// jmpf
var ip = cpu.read_op16();
var cs = cpu.read_disp16();
cpu.far_jump(ip, cs, false);
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.diverged();
};
t32[0xEA] = cpu => {
// jmpf
var new_ip = cpu.read_op32s();
var cs = cpu.read_disp16();
cpu.far_jump(new_ip, cs, false);
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.diverged();
};
t[0xEB] = cpu => {
// jmp near
var imm8 = cpu.read_op8s();
cpu.instruction_pointer = cpu.instruction_pointer + imm8 | 0;
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.diverged();
};
t[0xEC] = cpu => {
var port = cpu.reg16[reg_dx];
cpu.test_privileges_for_io(port, 1);
cpu.reg8[reg_al] = cpu.io.port_read8(port);
cpu.diverged();
};
t16[0xED] = cpu => {
var port = cpu.reg16[reg_dx];
cpu.test_privileges_for_io(port, 2);
cpu.reg16[reg_ax] = cpu.io.port_read16(port);
cpu.diverged();
};
t32[0xED] = cpu => {
var port = cpu.reg16[reg_dx];
cpu.test_privileges_for_io(port, 4);
cpu.reg32s[reg_eax] = cpu.io.port_read32(port);
cpu.diverged();
};
t[0xEE] = cpu => {
var port = cpu.reg16[reg_dx];
cpu.test_privileges_for_io(port, 1);
cpu.io.port_write8(port, cpu.reg8[reg_al]);
cpu.diverged();
};
t16[0xEF] = cpu => {
var port = cpu.reg16[reg_dx];
cpu.test_privileges_for_io(port, 2);
cpu.io.port_write16(port, cpu.reg16[reg_ax]);
cpu.diverged();
};
t32[0xEF] = cpu => {
var port = cpu.reg16[reg_dx];
cpu.test_privileges_for_io(port, 4);
cpu.io.port_write32(port, cpu.reg32s[reg_eax]);
cpu.diverged();
};
t[0xF0] = cpu => {
// lock
//dbg_log("lock", LOG_CPU);
// TODO
// This triggers UD when used with
// some instructions that don't write to memory
cpu.run_prefix_instruction();
};
t[0xF1] = cpu => {
// INT1
// https://code.google.com/p/corkami/wiki/x86oddities#IceBP
throw cpu.debug.unimpl("int1 instruction");
};
t[0xF2] = cpu => {
// repnz
dbg_assert((cpu.prefixes & PREFIX_MASK_REP) === 0);
cpu.prefixes |= PREFIX_REPNZ;
cpu.run_prefix_instruction();
cpu.prefixes = 0;
};
t[0xF3] = cpu => {
// repz
dbg_assert((cpu.prefixes & PREFIX_MASK_REP) === 0);
cpu.prefixes |= PREFIX_REPZ;
cpu.run_prefix_instruction();
cpu.prefixes = 0;
};
t[0xF4] = cpu => {
cpu.hlt_op();
};
t[0xF5] = cpu => {
// cmc
cpu.flags = (cpu.flags | 1) ^ cpu.getcf();
cpu.flags_changed &= ~1;
};
t[0xF6] = cpu => { cpu.read_modrm_byte();
switch(cpu.modrm_byte >> 3 & 7)
{
case 0:
var data = cpu.read_e8(); cpu.test8(data, cpu.read_op8());
break;
case 1:
var data = cpu.read_e8(); cpu.test8(data, cpu.read_op8());
break;
case 2:
var data = cpu.read_write_e8(); cpu.write_e8(~(data));
break;
case 3:
var data = cpu.read_write_e8(); cpu.write_e8(cpu.neg8(data));
break;
case 4:
var data = cpu.read_e8(); cpu.mul8(data);
break;
case 5:
var data = cpu.read_e8s(); cpu.imul8(data);
break;
case 6:
var data = cpu.read_e8(); cpu.div8(data);
break;
case 7:
var data = cpu.read_e8s(); cpu.idiv8(data);
break;
}
};
t16[0xF7] = cpu => { cpu.read_modrm_byte();
switch(cpu.modrm_byte >> 3 & 7)
{
case 0:
var data = cpu.read_e16(); cpu.test16(data, cpu.read_op16());
break;
case 1:
var data = cpu.read_e16(); cpu.test16(data, cpu.read_op16());
break;
case 2:
var data = cpu.read_write_e16(); cpu.write_e16(~(data));
break;
case 3:
var data = cpu.read_write_e16(); cpu.write_e16(cpu.neg16(data));
break;
case 4:
var data = cpu.read_e16(); cpu.mul16(data);
break;
case 5:
var data = cpu.read_e16s(); cpu.imul16(data);
break;
case 6:
var data = cpu.read_e16(); cpu.div16(data);
break;
case 7:
var data = cpu.read_e16s(); cpu.idiv16(data);
break;
}
};
t32[0xF7] = cpu => { cpu.read_modrm_byte();
switch(cpu.modrm_byte >> 3 & 7)
{
case 0:
var data = cpu.read_e32s(); cpu.test32(data, cpu.read_op32s());
break;
case 1:
var data = cpu.read_e32s(); cpu.test32(data, cpu.read_op32s());
break;
case 2:
var data = cpu.read_write_e32(); cpu.write_e32(~(data));
break;
case 3:
var data = cpu.read_write_e32(); cpu.write_e32(cpu.neg32(data));
break;
case 4:
var data = cpu.read_e32(); cpu.mul32(data);
break;
case 5:
var data = cpu.read_e32s(); cpu.imul32(data);
break;
case 6:
var data = cpu.read_e32(); cpu.div32(data);
break;
case 7:
var data = cpu.read_e32s(); cpu.idiv32(data);
break;
}
};
t[0xF8] = cpu => {
// clc
cpu.flags &= ~flag_carry;
cpu.flags_changed &= ~1;
};
t[0xF9] = cpu => {
// stc
cpu.flags |= flag_carry;
cpu.flags_changed &= ~1;
};
t[0xFA] = cpu => {
// cli
//dbg_log("interrupts off");
if(!cpu.protected_mode || ((cpu.flags & flag_vm) ?
cpu.getiopl() === 3 : cpu.getiopl() >= cpu.cpl))
{
cpu.flags &= ~flag_interrupt;
}
else
{
//if(cpu.getiopl() < 3 && ((cpu.flags & flag_vm) ?
// (cpu.cr[4] & CR4_VME) :
// (cpu.cpl === 3 && (cpu.cr[4] & CR4_PVI))))
//{
// cpu.flags &= ~flag_vif;
//}
//else
{
dbg_log("cli #gp", LOG_CPU);
cpu.trigger_gp(0);
}
}
};
t[0xFB] = cpu => {
// sti
//dbg_log("interrupts on");
if(!cpu.protected_mode || ((cpu.flags & flag_vm) ?
cpu.getiopl() === 3 : cpu.getiopl() >= cpu.cpl))
{
cpu.flags |= flag_interrupt;
cpu.clear_prefixes();
cpu.cycle_internal();
cpu.handle_irqs();
}
else
{
//if(cpu.getiopl() < 3 && (cpu.flags & flag_vip) === 0 && ((cpu.flags & flag_vm) ?
// (cpu.cr[4] & CR4_VME) :
// (cpu.cpl === 3 && (cpu.cr[4] & CR4_PVI))))
//{
// cpu.flags |= flag_vif;
//}
//else
{
dbg_log("sti #gp", LOG_CPU);
cpu.trigger_gp(0);
}
}
};
t[0xFC] = cpu => {
// cld
cpu.flags &= ~flag_direction;
};
t[0xFD] = cpu => {
// std
cpu.flags |= flag_direction;
};
t[0xFE] = cpu => { cpu.read_modrm_byte();
var mod = cpu.modrm_byte & 56;
if(mod === 0)
{
var data = cpu.read_write_e8(); cpu.write_e8(cpu.inc8(data));
}
else if(mod === 8)
{
var data = cpu.read_write_e8(); cpu.write_e8(cpu.dec8(data));
}
else
{
cpu.todo();
}
};
t16[0xFF] = cpu => { cpu.read_modrm_byte();
switch(cpu.modrm_byte >> 3 & 7)
{
case 0:
var data = cpu.read_write_e16(); cpu.write_e16(cpu.inc16(data));
break;
case 1:
var data = cpu.read_write_e16(); cpu.write_e16(cpu.dec16(data));
break;
case 2:
// 2, call near
var data = cpu.read_e16();
cpu.push16(cpu.get_real_eip());
cpu.instruction_pointer = cpu.get_seg(reg_cs) + data | 0;
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.diverged();
break;
case 3:
// 3, callf
if(cpu.modrm_byte >= 0xC0)
{
dbg_log("callf #ud", LOG_CPU);
cpu.trigger_ud();
dbg_assert(false, "unreachable");
}
var virt_addr = cpu.modrm_resolve(cpu.modrm_byte);
var new_ip = cpu.safe_read16(virt_addr);
var new_cs = cpu.safe_read16(virt_addr + 2 | 0);
cpu.far_jump(new_ip, new_cs, true);
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.diverged();
break;
case 4:
// 4, jmp near
var data = cpu.read_e16();
cpu.instruction_pointer = cpu.get_seg(reg_cs) + data | 0;
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.diverged();
break;
case 5:
// 5, jmpf
if(cpu.modrm_byte >= 0xC0)
{
dbg_log("jmpf #ud", LOG_CPU);
cpu.trigger_ud();
dbg_assert(false, "unreachable");
}
var virt_addr = cpu.modrm_resolve(cpu.modrm_byte);
var new_ip = cpu.safe_read16(virt_addr);
var new_cs = cpu.safe_read16(virt_addr + 2 | 0);
cpu.far_jump(new_ip, new_cs, false);
dbg_assert(cpu.is_asize_32() || cpu.get_real_eip() < 0x10000);
cpu.diverged();
break;
case 6:
// 6, push
var data = cpu.read_e16();
cpu.push16(data);
break;
case 7:
cpu.todo();
}
};
t32[0xFF] = cpu => { cpu.read_modrm_byte();
switch(cpu.modrm_byte >> 3 & 7)
{
case 0:
var data = cpu.read_write_e32(); cpu.write_e32(cpu.inc32(data));
break;
case 1:
var data = cpu.read_write_e32(); cpu.write_e32(cpu.dec32(data));
break;
case 2:
// 2, call near
var data = cpu.read_e32s();
cpu.push32(cpu.get_real_eip());
dbg_assert(cpu.is_asize_32() || data < 0x10000);
cpu.instruction_pointer = cpu.get_seg(reg_cs) + data | 0;
cpu.diverged();
break;
case 3:
// 3, callf
if(cpu.modrm_byte >= 0xC0)
{
dbg_log("callf #ud", LOG_CPU);
cpu.trigger_ud();
dbg_assert(false, "unreachable");
}
var virt_addr = cpu.modrm_resolve(cpu.modrm_byte);
var new_ip = cpu.safe_read32s(virt_addr);
var new_cs = cpu.safe_read16(virt_addr + 4 | 0);
if(!cpu.protected_mode || cpu.vm86_mode())
{
if(new_ip & 0xFFFF0000)
{
throw cpu.debug.unimpl("#GP handler");
}
}
cpu.far_jump(new_ip, new_cs, true);
dbg_assert(cpu.is_asize_32() || new_ip < 0x10000);
cpu.diverged();
break;
case 4:
// 4, jmp near
var data = cpu.read_e32s();
dbg_assert(cpu.is_asize_32() || data < 0x10000);
cpu.instruction_pointer = cpu.get_seg(reg_cs) + data | 0;
cpu.diverged();
break;
case 5:
// 5, jmpf
if(cpu.modrm_byte >= 0xC0)
{
dbg_log("jmpf #ud", LOG_CPU);
cpu.trigger_ud();
dbg_assert(false, "unreachable");
}
var virt_addr = cpu.modrm_resolve(cpu.modrm_byte);
var new_ip = cpu.safe_read32s(virt_addr);
var new_cs = cpu.safe_read16(virt_addr + 4 | 0);
if(!cpu.protected_mode || cpu.vm86_mode())
{
if(new_ip & 0xFFFF0000)
{
throw cpu.debug.unimpl("#GP handler");
}
}
cpu.far_jump(new_ip, new_cs, false);
dbg_assert(cpu.is_asize_32() || new_ip < 0x10000);
cpu.diverged();
break;
case 6:
// push
var data = cpu.read_e32s();
cpu.push32(data);
break;
case 7:
cpu.todo();
}
};
var table16 = [];
var table32 = [];
CPU.prototype.table16 = table16;
CPU.prototype.table32 = table32;
for(var i = 0; i < 256; i++)
{
if(t[i])
{
//dbg_assert(!t16[i]);
//dbg_assert(!t32[i]);
table16[i] = table32[i] = t[i];
}
else if(t16[i])
{
//dbg_assert(!t[i]);
//dbg_assert(t32[i]);
table16[i] = t16[i];
table32[i] = t32[i];
}
}
t = [];
t16 = [];
t32 = [];
// 0F ops start here
t[0x00] = cpu => { cpu.read_modrm_byte();
if(!cpu.protected_mode || cpu.vm86_mode())
{
// No GP, UD is correct here
dbg_log("0f 00 #ud", LOG_CPU);
cpu.trigger_ud();
}
switch(cpu.modrm_byte >> 3 & 7)
{
case 0:
// sldt
cpu.set_e16(cpu.sreg[reg_ldtr]);
if(cpu.is_osize_32() && cpu.modrm_byte >= 0xC0)
{
cpu.reg32s[cpu.modrm_byte & 7] &= 0xFFFF;
}
break;
case 1:
// str
cpu.set_e16(cpu.sreg[reg_tr]);
if(cpu.is_osize_32() && cpu.modrm_byte >= 0xC0)
{
cpu.reg32s[cpu.modrm_byte & 7] &= 0xFFFF;
}
break;
case 2:
// lldt
if(cpu.cpl)
{
cpu.trigger_gp(0);
}
var data = cpu.read_e16();
cpu.load_ldt(data);
break;
case 3:
// ltr
if(cpu.cpl)
{
cpu.trigger_gp(0);
}
var data = cpu.read_e16();
cpu.load_tr(data);
break;
case 4:
cpu.verr(cpu.read_e16());
break;
case 5:
cpu.verw(cpu.read_e16());
break;
default:
dbg_log(cpu.modrm_byte >> 3 & 7, LOG_CPU);
cpu.todo();
}
};
t[0x01] = cpu => { cpu.read_modrm_byte();
var mod = cpu.modrm_byte >> 3 & 7;
if(mod === 4)
{
// smsw
if(cpu.modrm_byte >= 0xC0 && cpu.is_osize_32())
{
cpu.set_e32(cpu.cr[0]);
}
else
{
cpu.set_e16(cpu.cr[0]);
}
return;
}
else if(mod === 6)
{
// lmsw
if(cpu.cpl)
{
cpu.trigger_gp(0);
}
var cr0 = cpu.read_e16();
cr0 = (cpu.cr[0] & ~0xF) | (cr0 & 0xF);
if(cpu.protected_mode)
{
// lmsw cannot be used to switch back
cr0 |= CR0_PE;
}
cpu.set_cr0(cr0);
return;
}
if(cpu.modrm_byte >= 0xC0)
{
// only memory
dbg_log("0f 01 #ud", LOG_CPU);
cpu.trigger_ud();
}
var addr = cpu.modrm_resolve(cpu.modrm_byte);
switch(mod)
{
case 0:
// sgdt
cpu.writable_or_pagefault(addr, 6);
cpu.safe_write16(addr, cpu.gdtr_size);
var mask = cpu.is_osize_32() ? -1 : 0x00FFFFFF;
cpu.safe_write32(addr + 2, cpu.gdtr_offset & mask);
break;
case 1:
// sidt
cpu.writable_or_pagefault(addr, 6);
cpu.safe_write16(addr, cpu.idtr_size);
var mask = cpu.is_osize_32() ? -1 : 0x00FFFFFF;
cpu.safe_write32(addr + 2, cpu.idtr_offset & mask);
break;
case 2:
// lgdt
if(cpu.cpl)
{
cpu.trigger_gp(0);
}
var size = cpu.safe_read16(addr);
var offset = cpu.safe_read32s(addr + 2);
cpu.gdtr_size = size;
cpu.gdtr_offset = offset;
if(!cpu.is_osize_32())
{
cpu.gdtr_offset &= 0xFFFFFF;
}
//dbg_log("gdt at " + h(cpu.gdtr_offset) + ", " + cpu.gdtr_size + " bytes", LOG_CPU);
//cpu.debug.dump_state();
//cpu.debug.dump_regs_short();
//cpu.debug.dump_gdt_ldt();
break;
case 3:
// lidt
if(cpu.cpl)
{
cpu.trigger_gp(0);
}
var size = cpu.safe_read16(addr);
var offset = cpu.safe_read32s(addr + 2);
cpu.idtr_size = size;
cpu.idtr_offset = offset;
if(!cpu.is_osize_32())
{
cpu.idtr_offset &= 0xFFFFFF;
}
//dbg_log("[" + h(cpu.instruction_pointer) + "] idt at " +
// h(idtr_offset) + ", " + cpu.idtr_size + " bytes " + h(addr), LOG_CPU);
break;
case 7:
// flush translation lookaside buffer
if(cpu.cpl)
{
cpu.trigger_gp(0);
}
cpu.invlpg(addr);
break;
default:
dbg_log(mod);
cpu.todo();
}
};
t16[0x02] = cpu => { cpu.read_modrm_byte();
// lar
if(!cpu.protected_mode || cpu.vm86_mode())
{
dbg_log("lar #ud", LOG_CPU);
cpu.trigger_ud();
}
var data = cpu.read_e16();
cpu.write_g16(cpu.lar(data, cpu.read_g16()));
};
t32[0x02] = cpu => { cpu.read_modrm_byte();
if(!cpu.protected_mode || cpu.vm86_mode())
{
dbg_log("lar #ud", LOG_CPU);
cpu.trigger_ud();
}
var data = cpu.read_e16();
cpu.write_g32(cpu.lar(data, cpu.read_g32s()));
};
t16[0x03] = cpu => { cpu.read_modrm_byte();
// lsl
if(!cpu.protected_mode || cpu.vm86_mode())
{
dbg_log("lsl #ud", LOG_CPU);
cpu.trigger_ud();
}
var data = cpu.read_e16();
cpu.write_g16(cpu.lsl(data, cpu.read_g16()));
};
t32[0x03] = cpu => { cpu.read_modrm_byte();
if(!cpu.protected_mode || cpu.vm86_mode())
{
dbg_log("lsl #ud", LOG_CPU);
cpu.trigger_ud();
}
var data = cpu.read_e16();
cpu.write_g32(cpu.lsl(data, cpu.read_g32s()));
};
t[0x04] = cpu => { cpu.undefined_instruction(); };
t[0x05] = cpu => { cpu.undefined_instruction(); };
t[0x06] = cpu => {
// clts
if(cpu.cpl)
{
dbg_log("clts #gp", LOG_CPU);
cpu.trigger_gp(0);
}
else
{
//dbg_log("clts", LOG_CPU);
cpu.cr[0] &= ~CR0_TS;
}
};
t[0x07] = cpu => { cpu.undefined_instruction(); };
t[0x08] = cpu => {
// invd
cpu.todo();
};
t[0x09] = cpu => {
if(cpu.cpl)
{
dbg_log("wbinvd #gp", LOG_CPU);
cpu.trigger_gp(0);
}
// wbinvd
};
t[0x0A] = cpu => { cpu.undefined_instruction(); };
t[0x0B] = cpu => {
// UD2
cpu.trigger_ud();
};
t[0x0C] = cpu => { cpu.undefined_instruction(); };
t[0x0D] = cpu => {
// nop
cpu.todo();
};
t[0x0E] = cpu => { cpu.undefined_instruction(); };
t[0x0F] = cpu => { cpu.undefined_instruction(); };
t[0x10] = cpu => { cpu.unimplemented_sse(); };
t[0x11] = cpu => { cpu.unimplemented_sse(); };
t[0x12] = cpu => { cpu.unimplemented_sse(); };
t[0x13] = cpu => { cpu.unimplemented_sse(); };
t[0x14] = cpu => { cpu.unimplemented_sse(); };
t[0x15] = cpu => { cpu.unimplemented_sse(); };
t[0x16] = cpu => { cpu.unimplemented_sse(); };
t[0x17] = cpu => { cpu.unimplemented_sse(); };
t[0x18] = cpu => { cpu.read_modrm_byte();
// prefetch
// nop for us
if(cpu.modrm_byte < 0xC0)
cpu.modrm_resolve(cpu.modrm_byte);
};
t[0x19] = cpu => { cpu.unimplemented_sse(); };
t[0x1A] = cpu => { cpu.unimplemented_sse(); };
t[0x1B] = cpu => { cpu.unimplemented_sse(); };
t[0x1C] = cpu => { cpu.unimplemented_sse(); };
t[0x1D] = cpu => { cpu.unimplemented_sse(); };
t[0x1E] = cpu => { cpu.unimplemented_sse(); };
t[0x1F] = cpu => { cpu.read_modrm_byte()
// multi-byte nop
if(cpu.modrm_byte < 0xC0)
cpu.modrm_resolve(cpu.modrm_byte);
};
t[0x20] = cpu => { cpu.read_modrm_byte();
if(cpu.cpl)
{
cpu.trigger_gp(0);
}
//dbg_log("cr" + (cpu.modrm_byte >> 3 & 7) + " read", LOG_CPU);
// mov addr, cr
// mod = which control register
switch(cpu.modrm_byte >> 3 & 7)
{
case 0:
cpu.write_reg_e32(cpu.cr[0]);
break;
case 2:
//dbg_log("read cr2 at " + h(cpu.instruction_pointer >>> 0, 8));
cpu.write_reg_e32(cpu.cr[2]);
break;
case 3:
//dbg_log("read cr3 (" + h(cpu.cr[3], 8) + ")", LOG_CPU);
cpu.write_reg_e32(cpu.cr[3]);
break;
case 4:
cpu.write_reg_e32(cpu.cr[4]);
break;
default:
dbg_log(cpu.modrm_byte >> 3 & 7);
cpu.todo();
}
};
t[0x21] = cpu => { cpu.read_modrm_byte();
if(cpu.cpl)
{
cpu.trigger_gp(0);
}
var dreg = cpu.modrm_byte >> 3 & 7;
if((cpu.cr[4] & CR4_DE) && (dreg === 4 || dreg === 5))
{
dbg_log("#ud mov dreg 4/5 with cr4.DE set", LOG_CPU);
cpu.trigger_ud();
}
// high two bits of modrm are ignored
cpu.reg32s[cpu.modrm_byte & 7] = cpu.dreg[dreg];
//dbg_log("read dr" + dreg + ": " + h(cpu.dreg[dreg] >>> 0), LOG_CPU);
};
t[0x22] = cpu => { cpu.read_modrm_byte();
if(cpu.cpl)
{
cpu.trigger_gp(0);
}
var data = cpu.read_reg_e32s();
//dbg_log("cr" + (cpu.modrm_byte >> 3 & 7) + " written: " + h(data >>> 0, 8), LOG_CPU);
// mov cr, addr
// mod = which control register
switch(cpu.modrm_byte >> 3 & 7)
{
case 0:
cpu.set_cr0(data);
//dbg_log("cr0=" + h(data >>> 0), LOG_CPU);
break;
case 2:
cpu.cr[2] = data;
//dbg_log("cr2=" + h(data >>> 0), LOG_CPU);
break;
case 3:
//dbg_log("cr3=" + h(data >>> 0), LOG_CPU);
data &= ~0b111111100111;
dbg_assert((data & 0xFFF) === 0, "TODO");
cpu.cr[3] = data;
cpu.clear_tlb();
//dump_page_directory();
//dbg_log("page directory loaded at " + h(cpu.cr[3] >>> 0, 8), LOG_CPU);
break;
case 4:
if(data & (1 << 11 | 1 << 12 | 1 << 15 | 1 << 16 | 1 << 19 | 0xFFC00000))
{
cpu.trigger_gp(0);
}
if((cpu.cr[4] ^ data) & CR4_PGE)
{
if(data & CR4_PGE)
{
// The PGE bit has been enabled. The global TLB is
// still empty, so we only have to copy it over
cpu.clear_tlb();
}
else
{
// Clear the global TLB
cpu.full_clear_tlb();
}
}
cpu.cr[4] = data;
cpu.page_size_extensions = (cpu.cr[4] & CR4_PSE) ? PSE_ENABLED : 0;
if(cpu.cr[4] & CR4_PAE)
{
throw cpu.debug.unimpl("PAE");
}
dbg_log("cr4=" + h(cpu.cr[4] >>> 0), LOG_CPU);
break;
default:
dbg_log(cpu.modrm_byte >> 3 & 7);
cpu.todo();
}
};
t[0x23] = cpu => { cpu.read_modrm_byte();
if(cpu.cpl)
{
cpu.trigger_gp(0);
}
var dreg = cpu.modrm_byte >> 3 & 7;
if((cpu.cr[4] & CR4_DE) && (dreg === 4 || dreg === 5))
{
dbg_log("#ud mov dreg 4/5 with cr4.DE set", LOG_CPU);
cpu.trigger_ud();
}
// high two bits of modrm are ignored
cpu.dreg[dreg] = cpu.read_reg_e32s();
//dbg_log("write dr" + dreg + ": " + h(cpu.dreg[dreg] >>> 0), LOG_CPU);
};
t[0x24] = cpu => { cpu.undefined_instruction(); };
t[0x25] = cpu => { cpu.undefined_instruction(); };
t[0x26] = cpu => { cpu.undefined_instruction(); };
t[0x27] = cpu => { cpu.undefined_instruction(); };
t[0x28] = cpu => { cpu.unimplemented_sse(); };
t[0x29] = cpu => { cpu.unimplemented_sse(); };
t[0x2A] = cpu => { cpu.unimplemented_sse(); };
t[0x2B] = cpu => { cpu.unimplemented_sse(); };
t[0x2C] = cpu => { cpu.unimplemented_sse(); };
t[0x2D] = cpu => { cpu.unimplemented_sse(); };
t[0x2E] = cpu => { cpu.unimplemented_sse(); };
t[0x2F] = cpu => { cpu.unimplemented_sse(); };
// wrmsr
t[0x30] = cpu => {
// wrmsr - write maschine specific register
if(cpu.cpl)
{
// cpl > 0 or vm86 mode (vm86 mode is always runs with cpl=3)
cpu.trigger_gp(0);
}
var index = cpu.reg32s[reg_ecx];
var low = cpu.reg32s[reg_eax];
var high = cpu.reg32s[reg_edx];
if(index !== IA32_SYSENTER_ESP)
{
dbg_log("wrmsr ecx=" + h(index >>> 0, 8) +
" data=" + h(high >>> 0, 8) + ":" + h(low >>> 0, 8), LOG_CPU);
}
switch(index)
{
case IA32_SYSENTER_CS:
cpu.sysenter_cs = low & 0xFFFF;
break;
case IA32_SYSENTER_EIP:
cpu.sysenter_eip = low;
break;
case IA32_SYSENTER_ESP:
cpu.sysenter_esp = low;
break;
case IA32_APIC_BASE_MSR:
dbg_assert(high === 0, "Changing APIC address (high 32 bits) not supported");
let address = low & ~(IA32_APIC_BASE_BSP | IA32_APIC_BASE_EXTD | IA32_APIC_BASE_EN);
dbg_assert((address >>> 0) === APIC_ADDRESS, "Changing APIC address not supported");
dbg_assert((low & IA32_APIC_BASE_EXTD) === 0, "x2apic not supported");
cpu.apic_enabled = (low & IA32_APIC_BASE_EN) === IA32_APIC_BASE_EN;
break;
case IA32_TIME_STAMP_COUNTER:
var new_tick = (low >>> 0) + 0x100000000 * (high >>> 0);
cpu.tsc_offset = v86.microtick() - new_tick / TSC_RATE;
break;
case IA32_BIOS_SIGN_ID:
break;
case IA32_MISC_ENABLE: // Enable Misc. Processor Features
break;
case IA32_MCG_CAP:
// netbsd
break;
case IA32_KERNEL_GS_BASE:
// Only used in 64 bit mode (by SWAPGS), but set by kvm-unit-test
dbg_log("GS Base written", LOG_CPU);
break;
default:
dbg_assert(false, "Unknown msr: " + h(index >>> 0, 8));
}
};
t[0x31] = cpu => {
// rdtsc - read timestamp counter
if(!cpu.cpl || !(cpu.cr[4] & CR4_TSD))
{
var n = v86.microtick() - cpu.tsc_offset;
dbg_assert(isFinite(n), "non-finite tsc: " + n);
cpu.reg32s[reg_eax] = n * TSC_RATE;
cpu.reg32s[reg_edx] = n * (TSC_RATE / 0x100000000);
//dbg_log("rdtsc edx:eax=" + h(cpu.reg32[reg_edx], 8) + ":" + h(cpu.reg32[reg_eax], 8), LOG_CPU);
}
else
{
cpu.trigger_gp(0);
}
};
t[0x32] = cpu => {
// rdmsr - read maschine specific register
if(cpu.cpl)
{
cpu.trigger_gp(0);
}
var index = cpu.reg32s[reg_ecx];
dbg_log("rdmsr ecx=" + h(index >>> 0, 8), LOG_CPU);
var low = 0;
var high = 0;
switch(index)
{
case IA32_SYSENTER_CS:
low = cpu.sysenter_cs;
break;
case IA32_SYSENTER_EIP:
low = cpu.sysenter_eip;
break;
case IA32_SYSENTER_ESP:
low = cpu.sysenter_esp;
break;
case IA32_TIME_STAMP_COUNTER:
var n = v86.microtick() - cpu.tsc_offset;
low = n * TSC_RATE;
high = n * (TSC_RATE / 0x100000000);
break;
case IA32_PLATFORM_ID:
break;
case IA32_APIC_BASE_MSR:
if(ENABLE_ACPI)
{
low = APIC_ADDRESS;
if(cpu.apic_enabled)
{
low |= IA32_APIC_BASE_EN;
}
}
break;
case IA32_BIOS_SIGN_ID:
break;
case IA32_MISC_ENABLE: // Enable Misc. Processor Features
break;
case IA32_RTIT_CTL:
// linux4
break;
case MSR_SMI_COUNT:
break;
case IA32_MCG_CAP:
// netbsd
break;
case MSR_PKG_C2_RESIDENCY:
break;
case MSR_EBC_FREQUENCY_ID:
low = 1 << 24;
break;
default:
dbg_assert(false, "Unknown msr: " + h(index >>> 0, 8));
}
cpu.reg32s[reg_eax] = low;
cpu.reg32s[reg_edx] = high;
};
t[0x33] = cpu => {
// rdpmc
cpu.todo();
};
t[0x34] = cpu => {
// sysenter
var seg = cpu.sysenter_cs & 0xFFFC;
if(!cpu.protected_mode || seg === 0)
{
cpu.trigger_gp(0);
}
dbg_log("sysenter cs:eip=" + h(seg , 4) + ":" + h(cpu.sysenter_eip >>> 0, 8) +
" ss:esp=" + h(seg + 8, 4) + ":" + h(cpu.sysenter_esp >>> 0, 8), LOG_CPU);
cpu.flags &= ~flag_vm & ~flag_interrupt;
cpu.instruction_pointer = cpu.sysenter_eip;
cpu.reg32s[reg_esp] = cpu.sysenter_esp;
cpu.sreg[reg_cs] = seg;
cpu.segment_is_null[reg_cs] = 0;
cpu.segment_limits[reg_cs] = -1;
cpu.segment_offsets[reg_cs] = 0;
cpu.update_cs_size(true);
cpu.cpl = 0;
cpu.cpl_changed();
cpu.sreg[reg_ss] = seg + 8;
cpu.segment_is_null[reg_ss] = 0;
cpu.segment_limits[reg_ss] = -1;
cpu.segment_offsets[reg_ss] = 0;
cpu.stack_size_32 = true;
cpu.diverged();
};
t[0x35] = cpu => {
// sysexit
var seg = cpu.sysenter_cs & 0xFFFC;
if(!cpu.protected_mode || cpu.cpl || seg === 0)
{
cpu.trigger_gp(0);
}
dbg_log("sysexit cs:eip=" + h(seg + 16, 4) + ":" + h(cpu.reg32s[reg_edx] >>> 0, 8) +
" ss:esp=" + h(seg + 24, 4) + ":" + h(cpu.reg32s[reg_ecx] >>> 0, 8), LOG_CPU);
cpu.instruction_pointer = cpu.reg32s[reg_edx];
cpu.reg32s[reg_esp] = cpu.reg32s[reg_ecx];
cpu.sreg[reg_cs] = seg + 16 | 3;
cpu.segment_is_null[reg_cs] = 0;
cpu.segment_limits[reg_cs] = -1;
cpu.segment_offsets[reg_cs] = 0;
cpu.update_cs_size(true);
cpu.cpl = 3;
cpu.cpl_changed();
cpu.sreg[reg_ss] = seg + 24 | 3;
cpu.segment_is_null[reg_ss] = 0;
cpu.segment_limits[reg_ss] = -1;
cpu.segment_offsets[reg_ss] = 0;
cpu.stack_size_32 = true;
cpu.diverged();
};
t[0x36] = cpu => { cpu.undefined_instruction(); };
t[0x37] = cpu => {
// getsec
cpu.todo();
};
t[0x38] = cpu => { cpu.unimplemented_sse(); };
t[0x39] = cpu => { cpu.unimplemented_sse(); };
t[0x3A] = cpu => { cpu.unimplemented_sse(); };
t[0x3B] = cpu => { cpu.unimplemented_sse(); };
t[0x3C] = cpu => { cpu.unimplemented_sse(); };
t[0x3D] = cpu => { cpu.unimplemented_sse(); };
t[0x3E] = cpu => { cpu.unimplemented_sse(); };
t[0x3F] = cpu => { cpu.unimplemented_sse(); };
// cmov
t16[0x40] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16( cpu.test_o()); };
t32[0x40] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32( cpu.test_o()); };
t16[0x41] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16(!cpu.test_o()); };
t32[0x41] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32(!cpu.test_o()); };
t16[0x42] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16( cpu.test_b()); };
t32[0x42] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32( cpu.test_b()); };
t16[0x43] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16(!cpu.test_b()); };
t32[0x43] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32(!cpu.test_b()); };
t16[0x44] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16( cpu.test_z()); };
t32[0x44] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32( cpu.test_z()); };
t16[0x45] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16(!cpu.test_z()); };
t32[0x45] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32(!cpu.test_z()); };
t16[0x46] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16( cpu.test_be()); };
t32[0x46] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32( cpu.test_be()); };
t16[0x47] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16(!cpu.test_be()); };
t32[0x47] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32(!cpu.test_be()); };
t16[0x48] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16( cpu.test_s()); };
t32[0x48] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32( cpu.test_s()); };
t16[0x49] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16(!cpu.test_s()); };
t32[0x49] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32(!cpu.test_s()); };
t16[0x4A] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16( cpu.test_p()); };
t32[0x4A] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32( cpu.test_p()); };
t16[0x4B] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16(!cpu.test_p()); };
t32[0x4B] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32(!cpu.test_p()); };
t16[0x4C] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16( cpu.test_l()); };
t32[0x4C] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32( cpu.test_l()); };
t16[0x4D] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16(!cpu.test_l()); };
t32[0x4D] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32(!cpu.test_l()); };
t16[0x4E] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16( cpu.test_le()); };
t32[0x4E] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32( cpu.test_le()); };
t16[0x4F] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc16(!cpu.test_le()); };
t32[0x4F] = cpu => { cpu.read_modrm_byte(); cpu.cmovcc32(!cpu.test_le()); };
t[0x50] = cpu => { cpu.unimplemented_sse(); };
t[0x51] = cpu => { cpu.unimplemented_sse(); };
t[0x52] = cpu => { cpu.unimplemented_sse(); };
t[0x53] = cpu => { cpu.unimplemented_sse(); };
t[0x54] = cpu => { cpu.unimplemented_sse(); };
t[0x55] = cpu => { cpu.unimplemented_sse(); };
t[0x56] = cpu => { cpu.unimplemented_sse(); };
t[0x57] = cpu => { cpu.unimplemented_sse(); };
t[0x58] = cpu => { cpu.unimplemented_sse(); };
t[0x59] = cpu => { cpu.unimplemented_sse(); };
t[0x5A] = cpu => { cpu.unimplemented_sse(); };
t[0x5B] = cpu => { cpu.unimplemented_sse(); };
t[0x5C] = cpu => { cpu.unimplemented_sse(); };
t[0x5D] = cpu => { cpu.unimplemented_sse(); };
t[0x5E] = cpu => { cpu.unimplemented_sse(); };
t[0x5F] = cpu => { cpu.unimplemented_sse(); };
t[0x60] = cpu => {
// punpcklbw mm, mm/m32
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem32s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let byte0 = destination_low & 0xFF;
let byte1 = source & 0xFF;
let byte2 = (destination_low >> 8) & 0xFF;
let byte3 = (source >> 8) & 0xFF;
let byte4 = (destination_low >> 16) & 0xFF;
let byte5 = (source >> 16) & 0xFF;
let byte6 = destination_low >>> 24;
let byte7 = source >>> 24;
let low = byte0 | byte1 << 8 | byte2 << 16 | byte3 << 24;
let high = byte4 | byte5 << 8 | byte6 << 16 | byte7 << 24;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x61] = cpu => {
// punpcklwd mm, mm/m32
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem32s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let word0 = destination_low & 0xFFFF;
let word1 = source & 0xFFFF;
let word2 = destination_low >>> 16;
let word3 = source >>> 16;
let low = word0 | word1 << 16;
let high = word2 | word3 << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x62] = cpu => {
// punpckldq mm, mm/m32
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem32s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let low = destination_low;
let high = source;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x63] = cpu => {
// packsswb mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let low = 0;
low |= (cpu.saturate_sw_to_sb((destination_low) & 0xFFFF));
low |= (cpu.saturate_sw_to_sb(destination_low >>> 16)) << 8;
low |= (cpu.saturate_sw_to_sb((destination_high) & 0xFFFF)) << 16;
low |= (cpu.saturate_sw_to_sb(destination_high >>> 16)) << 24;
let high = 0;
high |= (cpu.saturate_sw_to_sb((source[0]) & 0xFFFF));
high |= (cpu.saturate_sw_to_sb(source[0] >>> 16)) << 8;
high |= (cpu.saturate_sw_to_sb((source[1]) & 0xFFFF)) << 16;
high |= (cpu.saturate_sw_to_sb(source[1] >>> 16)) << 24;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x64] = cpu => {
// pcmpgtb mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source64s = cpu.read_xmm_mem64s();
let source8s = new Int8Array(source64s.buffer);
let reg_offset = 8 * (cpu.modrm_byte >> 3 & 7);
let destination8s = cpu.reg_mmx8s;
let byte0 = destination8s[reg_offset] > source8s[0] ? 0xFF : 0;
let byte1 = destination8s[reg_offset + 1] > source8s[1] ? 0xFF : 0;
let byte2 = destination8s[reg_offset + 2] > source8s[2] ? 0xFF : 0;
let byte3 = destination8s[reg_offset + 3] > source8s[3] ? 0xFF : 0;
let byte4 = destination8s[reg_offset + 4] > source8s[4] ? 0xFF : 0;
let byte5 = destination8s[reg_offset + 5] > source8s[5] ? 0xFF : 0;
let byte6 = destination8s[reg_offset + 6] > source8s[6] ? 0xFF : 0;
let byte7 = destination8s[reg_offset + 7] > source8s[7] ? 0xFF : 0;
let low = byte0 | byte1 << 8 | byte2 << 16 | byte3 << 24;
let high = byte4 | byte5 << 8 | byte6 << 16 | byte7 << 24;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x65] = cpu => {
// pcmpgtw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let word0 = 0;
let word1 = 0;
let word2 = 0;
let word3 = 0;
if ((destination_low << 16 >> 16) > (source[0] << 16 >> 16)) {
word0 = 0xFFFF;
}
if ((destination_low >> 16) > (source[0] >> 16)) {
word1 = 0xFFFF;
}
if ((destination_high << 16 >> 16) > (source[1] << 16 >> 16)) {
word2 = 0xFFFF;
}
if ((destination_high >> 16) > (source[1] >> 16)) {
word3 = 0xFFFF;
}
let low = word0 | word1 << 16;
let high = word2 | word3 << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x66] = cpu => {
// pcmpgtd mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let low = 0;
let high = 0;
if (destination_low > source[0]) {
low = 0xFFFFFFFF;
}
if (destination_high > source[1]) {
high = 0xFFFFFFFF;
}
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x67] = cpu => {
// packuswb mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let low = 0;
low |= (cpu.saturate_sw_to_ub((destination_low) & 0xFFFF));
low |= (cpu.saturate_sw_to_ub(destination_low >>> 16)) << 8;
low |= (cpu.saturate_sw_to_ub((destination_high) & 0xFFFF)) << 16;
low |= (cpu.saturate_sw_to_ub(destination_high >>> 16)) << 24;
let high = 0;
high |= (cpu.saturate_sw_to_ub((source[0]) & 0xFFFF));
high |= (cpu.saturate_sw_to_ub(source[0] >>> 16)) << 8;
high |= (cpu.saturate_sw_to_ub((source[1]) & 0xFFFF)) << 16;
high |= (cpu.saturate_sw_to_ub(source[1] >>> 16)) << 24;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x68] = cpu => {
// punpckhbw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let byte0 = destination_high & 0xFF;
let byte1 = source[1] & 0xFF;
let byte2 = (destination_high >> 8) & 0xFF;
let byte3 = (source[1] >> 8) & 0xFF;
let byte4 = (destination_high >> 16) & 0xFF;
let byte5 = (source[1] >> 16) & 0xFF;
let byte6 = destination_high >>> 24;
let byte7 = source[1] >>> 24;
let low = byte0 | byte1 << 8 | byte2 << 16 | byte3 << 24;
let high = byte4 | byte5 << 8 | byte6 << 16 | byte7 << 24;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x69] = cpu => {
// punpckhwd mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let word0 = destination_high & 0xFFFF;
let word1 = source[1] & 0xFFFF;
let word2 = destination_high >>> 16;
let word3 = source[1] >>> 16;
let low = word0 | word1 << 16;
let high = word2 | word3 << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x6A] = cpu => {
// punpckhdq mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let low = destination_high;
let high = source[1];
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x6B] = cpu => {
// packssdw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let low = 0;
low |= cpu.saturate_sd_to_sw(destination_low);
low |= cpu.saturate_sd_to_sw(destination_high) << 16;
let high = 0;
high |= cpu.saturate_sd_to_sw(source[0]);
high |= cpu.saturate_sd_to_sw(source[1]) << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x6C] = cpu => { cpu.unimplemented_sse(); };
t[0x6D] = cpu => { cpu.unimplemented_sse(); };
t[0x6E] = cpu => {
// movd mm, r/m32
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let data = cpu.create_atom64s(cpu.read_e32s(), 0);
cpu.write_xmm64s(data);
};
t[0x6F] = cpu => {
// movq mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let data = cpu.read_xmm_mem64s();
cpu.write_xmm64s(data);
};
t[0x70] = cpu => {
// pshufw mm1, mm2/m64, imm8
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let order = cpu.read_op8();
let word0_shift = order & 0b11;
let word0 = source[word0_shift >> 1] >>> ((word0_shift & 1) * 16) & 0xFFFF;
let word1_shift = (order >> 2) & 0b11;
let word1 = source[word1_shift >> 1] >>> ((word1_shift & 1) * 16);
let low = word0 | word1 << 16;
let word2_shift = (order >> 4) & 0b11;
let word2 = source[word2_shift >> 1] >>> ((word2_shift & 1) * 16) & 0xFFFF;
let word3_shift = (order >>> 6);
let word3 = source[word3_shift >> 1] >>> ((word3_shift & 1) * 16);
let high = word2 | word3 << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x71] = cpu => {
cpu.read_modrm_byte();
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
dbg_assert(cpu.modrm_byte >= 0xC0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
// psrlw, psraw, psllw
// 2, 4, 6
switch(cpu.modrm_byte >> 3 & 7)
{
case 2:
// psrlw mm, imm8
var source = cpu.read_op8();
var destination = cpu.modrm_byte & 7;
var destination_low = cpu.reg_mmxs[2 * destination];
var destination_high = cpu.reg_mmxs[2 * destination + 1];
var shift = source;
var low = 0;
var high = 0;
if (shift <= 15) {
var word0 = (destination_low & 0xFFFF) >>> shift;
var word1 = (destination_low >>> 16) >>> shift;
low = word0 | word1 << 16;
var word2 = (destination_high & 0xFFFF) >>> shift;
var word3 = (destination_high >>> 16) >>> shift;
high = word2 | word3 << 16;
}
cpu.reg_mmxs[2 * destination] = low;
cpu.reg_mmxs[2 * destination + 1] = high;
break;
case 4:
// psraw mm, imm8
var source = cpu.read_op8();
var destination = cpu.modrm_byte & 7;
var destination_low = cpu.reg_mmxs[2 * destination];
var destination_high = cpu.reg_mmxs[2 * destination + 1];
var shift = source;
if (shift > 15) {
shift = 16;
}
var word0 = ((destination_low << 16 >> 16) >> shift) & 0xFFFF;
var word1 = ((destination_low >> 16) >> shift) & 0xFFFF;
var low = word0 | word1 << 16;
var word2 = ((destination_high << 16 >> 16) >> shift) & 0xFFFF;
var word3 = ((destination_high >> 16) >> shift) & 0xFFFF;
var high = word2 | word3 << 16;
cpu.reg_mmxs[2 * destination] = low;
cpu.reg_mmxs[2 * destination + 1] = high;
break;
case 6:
// psllw mm, imm8
var source = cpu.read_op8();
var destination = cpu.modrm_byte & 7;
var destination_low = cpu.reg_mmxs[2 * destination];
var destination_high = cpu.reg_mmxs[2 * destination + 1];
var shift = source;
var low = 0;
var high = 0;
if (shift <= 15) {
var word0 = ((destination_low & 0xFFFF) << shift) & 0xFFFF;
var word1 = (destination_low >>> 16) << shift;
low = word0 | word1 << 16;
var word2 = ((destination_high & 0xFFFF) << shift) & 0xFFFF;
var word3 = (destination_high >>> 16) << shift;
high = word2 | word3 << 16;
}
cpu.reg_mmxs[2 * destination] = low;
cpu.reg_mmxs[2 * destination + 1] = high;
break;
default:
cpu.unimplemented_sse();
break;
}
};
t[0x72] = cpu => {
cpu.read_modrm_byte();
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
dbg_assert(cpu.modrm_byte >= 0xC0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
// psrld, psrad, pslld
// 2, 4, 6
switch(cpu.modrm_byte >> 3 & 7)
{
case 2:
// psrld mm, imm8
var source = cpu.read_op8();
var destination = cpu.modrm_byte & 7;
var destination_low = cpu.reg_mmxs[2 * destination];
var destination_high = cpu.reg_mmxs[2 * destination + 1];
// JS will right shift as expected only if shift is < 32
var shift = source;
var low = 0;
var high = 0;
if (shift <= 31) {
low = destination_low >>> shift;
high = destination_high >>> shift;
}
cpu.reg_mmxs[2 * destination] = low;
cpu.reg_mmxs[2 * destination + 1] = high;
break;
case 4:
// psrad mm, imm8
var source = cpu.read_op8();
var destination = cpu.modrm_byte & 7;
var destination_low = cpu.reg_mmxs[2 * destination];
var destination_high = cpu.reg_mmxs[2 * destination + 1];
var shift = source;
if (shift > 31) {
shift = 31;
}
var low = destination_low >> shift;
var high = destination_high >> shift;
cpu.reg_mmxs[2 * destination] = low;
cpu.reg_mmxs[2 * destination + 1] = high;
break;
case 6:
// pslld mm, imm8
var source = cpu.read_op8();
var destination = cpu.modrm_byte & 7;
var destination_low = cpu.reg_mmxs[2 * destination];
var destination_high = cpu.reg_mmxs[2 * destination + 1];
var shift = source;
var low = 0;
var high = 0;
if (shift <= 31) {
low = destination_low << shift;
high = destination_high << shift;
}
cpu.reg_mmxs[2 * destination] = low;
cpu.reg_mmxs[2 * destination + 1] = high;
break;
default:
cpu.unimplemented_sse();
break;
}
};
t[0x73] = cpu => {
cpu.read_modrm_byte();
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
dbg_assert(cpu.modrm_byte >= 0xC0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
// psrlq, psllq
// 2, 6
switch(cpu.modrm_byte >> 3 & 7)
{
case 2:
// psrlq mm, imm8
var source = cpu.read_op8();
var destination = cpu.modrm_byte & 7;
var destination_low = cpu.reg_mmxs[2 * destination];
var destination_high = cpu.reg_mmxs[2 * destination + 1];
var shift = source;
var low = 0;
var high = 0;
if (shift <= 31) {
low = destination_low >>> shift
| (destination_high << (32 - shift));
high = destination_high >>> shift;
}
else if (shift <= 63) {
low = destination_high >>> (shift & 0x1F);
high = 0;
}
cpu.reg_mmxs[2 * destination] = low;
cpu.reg_mmxs[2 * destination + 1] = high;
break;
case 6:
// psllq mm, imm8
var source = cpu.read_op8();
var destination = cpu.modrm_byte & 7;
var destination_low = cpu.reg_mmxs[2 * destination];
var destination_high = cpu.reg_mmxs[2 * destination + 1];
var shift = source;
var low = 0;
var high = 0;
if (shift <= 31) {
low = destination_low << shift;
high = destination_high << shift
| (destination_low >>> (32 - shift));
}
else if (shift <= 63) {
high = destination_low << (shift & 0x1F);
low = 0;
}
cpu.reg_mmxs[2 * destination] = low;
cpu.reg_mmxs[2 * destination + 1] = high;
break;
default:
cpu.unimplemented_sse();
break;
}
};
t[0x74] = cpu => {
// pcmpeqb mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source64s = cpu.read_xmm_mem64s();
let source8s = new Int8Array(source64s.buffer);
let reg_offset = 8 * (cpu.modrm_byte >> 3 & 7);
let destination8s = cpu.reg_mmx8s;
let byte0 = destination8s[reg_offset] === source8s[0] ? 0xFF : 0;
let byte1 = destination8s[reg_offset + 1] === source8s[1] ? 0xFF : 0;
let byte2 = destination8s[reg_offset + 2] === source8s[2] ? 0xFF : 0;
let byte3 = destination8s[reg_offset + 3] === source8s[3] ? 0xFF : 0;
let byte4 = destination8s[reg_offset + 4] === source8s[4] ? 0xFF : 0;
let byte5 = destination8s[reg_offset + 5] === source8s[5] ? 0xFF : 0;
let byte6 = destination8s[reg_offset + 6] === source8s[6] ? 0xFF : 0;
let byte7 = destination8s[reg_offset + 7] === source8s[7] ? 0xFF : 0;
let low = byte0 | byte1 << 8 | byte2 << 16 | byte3 << 24;
let high = byte4 | byte5 << 8 | byte6 << 16 | byte7 << 24;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x75] = cpu => {
// pcmpeqw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let word0 = 0;
let word1 = 0;
let word2 = 0;
let word3 = 0;
if ((destination_low & 0xFFFF) === (source[0] & 0xFFFF)) {
word0 = 0xFFFF;
}
if ((destination_low & 0xFFFF0000) === (source[0] & 0xFFFF0000)) {
word1 = 0xFFFF;
}
if ((destination_high & 0xFFFF) === (source[1] & 0xFFFF)) {
word2 = 0xFFFF;
}
if ((destination_high & 0xFFFF0000) === (source[1] & 0xFFFF0000)) {
word3 = 0xFFFF;
}
let low = word0 | word1 << 16;
let high = word2 | word3 << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x76] = cpu => {
// pcmpeqd mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let low = 0;
let high = 0;
if (destination_low === source[0]) {
low = 0xFFFFFFFF;
}
if (destination_high === source[1]) {
high = 0xFFFFFFFF;
}
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0x77] = cpu => {
// emms
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.fpu.safe_tag_word(0xFFFF);
};
t[0x78] = cpu => { cpu.unimplemented_sse(); };
t[0x79] = cpu => { cpu.unimplemented_sse(); };
t[0x7A] = cpu => { cpu.unimplemented_sse(); };
t[0x7B] = cpu => { cpu.unimplemented_sse(); };
t[0x7C] = cpu => { cpu.unimplemented_sse(); };
t[0x7D] = cpu => { cpu.unimplemented_sse(); };
t[0x7E] = cpu => {
// movd r/m32, mm
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let data = cpu.read_xmm64s();
cpu.set_e32(data[0]);
};
t[0x7F] = cpu => {
// movq mm/m64, mm
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let data = cpu.read_xmm64s();
cpu.set_xmm_mem64s(data);
};
// jmpcc
t16[0x80] = cpu => { cpu.jmpcc16( cpu.test_o()); };
t32[0x80] = cpu => { cpu.jmpcc32( cpu.test_o()); };
t16[0x81] = cpu => { cpu.jmpcc16(!cpu.test_o()); };
t32[0x81] = cpu => { cpu.jmpcc32(!cpu.test_o()); };
t16[0x82] = cpu => { cpu.jmpcc16( cpu.test_b()); };
t32[0x82] = cpu => { cpu.jmpcc32( cpu.test_b()); };
t16[0x83] = cpu => { cpu.jmpcc16(!cpu.test_b()); };
t32[0x83] = cpu => { cpu.jmpcc32(!cpu.test_b()); };
t16[0x84] = cpu => { cpu.jmpcc16( cpu.test_z()); };
t32[0x84] = cpu => { cpu.jmpcc32( cpu.test_z()); };
t16[0x85] = cpu => { cpu.jmpcc16(!cpu.test_z()); };
t32[0x85] = cpu => { cpu.jmpcc32(!cpu.test_z()); };
t16[0x86] = cpu => { cpu.jmpcc16( cpu.test_be()); };
t32[0x86] = cpu => { cpu.jmpcc32( cpu.test_be()); };
t16[0x87] = cpu => { cpu.jmpcc16(!cpu.test_be()); };
t32[0x87] = cpu => { cpu.jmpcc32(!cpu.test_be()); };
t16[0x88] = cpu => { cpu.jmpcc16( cpu.test_s()); };
t32[0x88] = cpu => { cpu.jmpcc32( cpu.test_s()); };
t16[0x89] = cpu => { cpu.jmpcc16(!cpu.test_s()); };
t32[0x89] = cpu => { cpu.jmpcc32(!cpu.test_s()); };
t16[0x8A] = cpu => { cpu.jmpcc16( cpu.test_p()); };
t32[0x8A] = cpu => { cpu.jmpcc32( cpu.test_p()); };
t16[0x8B] = cpu => { cpu.jmpcc16(!cpu.test_p()); };
t32[0x8B] = cpu => { cpu.jmpcc32(!cpu.test_p()); };
t16[0x8C] = cpu => { cpu.jmpcc16( cpu.test_l()); };
t32[0x8C] = cpu => { cpu.jmpcc32( cpu.test_l()); };
t16[0x8D] = cpu => { cpu.jmpcc16(!cpu.test_l()); };
t32[0x8D] = cpu => { cpu.jmpcc32(!cpu.test_l()); };
t16[0x8E] = cpu => { cpu.jmpcc16( cpu.test_le()); };
t32[0x8E] = cpu => { cpu.jmpcc32( cpu.test_le()); };
t16[0x8F] = cpu => { cpu.jmpcc16(!cpu.test_le()); };
t32[0x8F] = cpu => { cpu.jmpcc32(!cpu.test_le()); };
// setcc
t[0x90] = cpu => { cpu.read_modrm_byte(); cpu.setcc( cpu.test_o()); };
t[0x91] = cpu => { cpu.read_modrm_byte(); cpu.setcc(!cpu.test_o()); };
t[0x92] = cpu => { cpu.read_modrm_byte(); cpu.setcc( cpu.test_b()); };
t[0x93] = cpu => { cpu.read_modrm_byte(); cpu.setcc(!cpu.test_b()); };
t[0x94] = cpu => { cpu.read_modrm_byte(); cpu.setcc( cpu.test_z()); };
t[0x95] = cpu => { cpu.read_modrm_byte(); cpu.setcc(!cpu.test_z()); };
t[0x96] = cpu => { cpu.read_modrm_byte(); cpu.setcc( cpu.test_be()); };
t[0x97] = cpu => { cpu.read_modrm_byte(); cpu.setcc(!cpu.test_be()); };
t[0x98] = cpu => { cpu.read_modrm_byte(); cpu.setcc( cpu.test_s()); };
t[0x99] = cpu => { cpu.read_modrm_byte(); cpu.setcc(!cpu.test_s()); };
t[0x9A] = cpu => { cpu.read_modrm_byte(); cpu.setcc( cpu.test_p()); };
t[0x9B] = cpu => { cpu.read_modrm_byte(); cpu.setcc(!cpu.test_p()); };
t[0x9C] = cpu => { cpu.read_modrm_byte(); cpu.setcc( cpu.test_l()); };
t[0x9D] = cpu => { cpu.read_modrm_byte(); cpu.setcc(!cpu.test_l()); };
t[0x9E] = cpu => { cpu.read_modrm_byte(); cpu.setcc( cpu.test_le()); };
t[0x9F] = cpu => { cpu.read_modrm_byte(); cpu.setcc(!cpu.test_le()); };
t16[0xA0] = cpu => { cpu.push16(cpu.sreg[reg_fs]); };
t32[0xA0] = cpu => { cpu.push32(cpu.sreg[reg_fs]); };
t16[0xA1] = cpu => {
cpu.switch_seg(reg_fs, cpu.safe_read16(cpu.get_stack_pointer(0)));
cpu.adjust_stack_reg(2);
};
t32[0xA1] = cpu => {
cpu.switch_seg(reg_fs, cpu.safe_read32s(cpu.get_stack_pointer(0)) & 0xFFFF);
cpu.adjust_stack_reg(4);
};
t[0xA2] = cpu => { cpu.cpuid(); };
t16[0xA3] = cpu => { cpu.read_modrm_byte();
if(cpu.modrm_byte < 0xC0)
{
cpu.bt_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_g16s());
}
else
{
cpu.bt_reg(cpu.read_reg_e16(), cpu.read_g16() & 15);
}
};
t32[0xA3] = cpu => { cpu.read_modrm_byte();
if(cpu.modrm_byte < 0xC0)
{
cpu.bt_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_g32s());
}
else
{
cpu.bt_reg(cpu.read_reg_e32s(), cpu.read_g32s() & 31);
}
};
t16[0xA4] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_write_e16(); cpu.write_e16(cpu.shld16(data, cpu.read_g16(), cpu.read_op8() & 31));
};
t32[0xA4] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_write_e32(); cpu.write_e32(cpu.shld32(data, cpu.read_g32s(), cpu.read_op8() & 31));
};
t16[0xA5] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_write_e16(); cpu.write_e16(cpu.shld16(data, cpu.read_g16(), cpu.reg8[reg_cl] & 31));
};
t32[0xA5] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_write_e32(); cpu.write_e32(cpu.shld32(data, cpu.read_g32s(), cpu.reg8[reg_cl] & 31));
};
t[0xA6] = cpu => {
// obsolete cmpxchg (os/2)
cpu.trigger_ud();
};
t[0xA7] = cpu => { cpu.undefined_instruction(); };
t16[0xA8] = cpu => { cpu.push16(cpu.sreg[reg_gs]); };
t32[0xA8] = cpu => { cpu.push32(cpu.sreg[reg_gs]); };
t16[0xA9] = cpu => {
cpu.switch_seg(reg_gs, cpu.safe_read16(cpu.get_stack_pointer(0)));
cpu.adjust_stack_reg(2);
};
t32[0xA9] = cpu => {
cpu.switch_seg(reg_gs, cpu.safe_read32s(cpu.get_stack_pointer(0)) & 0xFFFF);
cpu.adjust_stack_reg(4);
};
t[0xAA] = cpu => {
// rsm
cpu.todo();
};
t16[0xAB] = cpu => { cpu.read_modrm_byte();
if(cpu.modrm_byte < 0xC0) {
cpu.bts_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_g16s());
} else {
cpu.write_reg_e16(cpu.bts_reg(cpu.read_reg_e16(), cpu.read_g16s() & 15));
}
};
t32[0xAB] = cpu => { cpu.read_modrm_byte();
if(cpu.modrm_byte < 0xC0) {
cpu.bts_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_g32s());
} else {
cpu.write_reg_e32(cpu.bts_reg(cpu.read_reg_e32s(), cpu.read_g32s() & 31));
}
};
t16[0xAC] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_write_e16(); cpu.write_e16(cpu.shrd16(data, cpu.read_g16(), cpu.read_op8() & 31));
};
t32[0xAC] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_write_e32(); cpu.write_e32(cpu.shrd32(data, cpu.read_g32s(), cpu.read_op8() & 31));
};
t16[0xAD] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_write_e16(); cpu.write_e16(cpu.shrd16(data, cpu.read_g16(), cpu.reg8[reg_cl] & 31));
};
t32[0xAD] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_write_e32(); cpu.write_e32(cpu.shrd32(data, cpu.read_g32s(), cpu.reg8[reg_cl] & 31));
};
t[0xAE] = cpu => { cpu.read_modrm_byte();
// fxsave, fxrstor, ldmxcsr ...
switch(cpu.modrm_byte >> 3 & 7)
{
case 5:
// lfence
dbg_assert(cpu.modrm_byte >= 0xC0, "Unexpected mfence encoding");
break;
case 6:
// mfence
dbg_assert(cpu.modrm_byte >= 0xC0, "Unexpected mfence encoding");
break;
default:
dbg_log("missing " + (cpu.modrm_byte >> 3 & 7), LOG_CPU);
cpu.todo();
}
};
t16[0xAF] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_e16s();
cpu.write_g16(cpu.imul_reg16(cpu.read_g16s(), data));
};
t32[0xAF] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_e32s();
cpu.write_g32(cpu.imul_reg32(cpu.read_g32s(), data));
};
t[0xB0] = cpu => { cpu.read_modrm_byte();
// cmpxchg8
if(cpu.modrm_byte < 0xC0)
{
var virt_addr = cpu.modrm_resolve(cpu.modrm_byte);
cpu.writable_or_pagefault(virt_addr, 1);
var data = cpu.safe_read8(virt_addr);
}
else
data = cpu.reg8[cpu.modrm_byte << 2 & 0xC | cpu.modrm_byte >> 2 & 1];
cpu.cmp8(cpu.reg8[reg_al], data);
if(cpu.getzf())
{
if(cpu.modrm_byte < 0xC0)
cpu.safe_write8(virt_addr, cpu.read_g8());
else
cpu.reg8[cpu.modrm_byte << 2 & 0xC | cpu.modrm_byte >> 2 & 1] = cpu.read_g8();
}
else
{
if(cpu.modrm_byte < 0xC0)
cpu.safe_write8(virt_addr, data);
cpu.reg8[reg_al] = data;
}
};
t16[0xB1] = cpu => { cpu.read_modrm_byte();
// cmpxchg16/32
if(cpu.modrm_byte < 0xC0)
{
var virt_addr = cpu.modrm_resolve(cpu.modrm_byte);
cpu.writable_or_pagefault(virt_addr, 2);
var data = cpu.safe_read16(virt_addr);
}
else
data = cpu.read_reg_e16();
cpu.cmp16(cpu.reg16[reg_ax], data);
if(cpu.getzf())
{
if(cpu.modrm_byte < 0xC0)
cpu.safe_write16(virt_addr, cpu.read_g16());
else
cpu.write_reg_e16(cpu.read_g16());
}
else
{
if(cpu.modrm_byte < 0xC0)
cpu.safe_write16(virt_addr, data);
cpu.reg16[reg_ax] = data;
}
};
t32[0xB1] = cpu => { cpu.read_modrm_byte();
if(cpu.modrm_byte < 0xC0)
{
var virt_addr = cpu.modrm_resolve(cpu.modrm_byte);
cpu.writable_or_pagefault(virt_addr, 4);
var data = cpu.safe_read32s(virt_addr);
}
else
{
data = cpu.read_reg_e32s();
}
cpu.cmp32(cpu.reg32s[reg_eax], data);
if(cpu.getzf())
{
if(cpu.modrm_byte < 0xC0)
cpu.safe_write32(virt_addr, cpu.read_g32s());
else
cpu.write_reg_e32(cpu.read_g32s());
}
else
{
if(cpu.modrm_byte < 0xC0)
cpu.safe_write32(virt_addr, data);
cpu.reg32s[reg_eax] = data;
}
};
// lss
t16[0xB2] = cpu => { cpu.read_modrm_byte();
cpu.lss16(reg_ss);
};
t32[0xB2] = cpu => { cpu.read_modrm_byte();
cpu.lss32(reg_ss);
};
t16[0xB3] = cpu => { cpu.read_modrm_byte();
if(cpu.modrm_byte < 0xC0) {
cpu.btr_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_g16s());
} else {
cpu.write_reg_e16(cpu.btr_reg(cpu.read_reg_e16(), cpu.read_g16s() & 15));
}
};
t32[0xB3] = cpu => { cpu.read_modrm_byte();
if(cpu.modrm_byte < 0xC0) {
cpu.btr_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_g32s());
} else {
cpu.write_reg_e32(cpu.btr_reg(cpu.read_reg_e32s(), cpu.read_g32s() & 31));
}
};
// lfs, lgs
t16[0xB4] = cpu => { cpu.read_modrm_byte();
cpu.lss16(reg_fs);
};
t32[0xB4] = cpu => { cpu.read_modrm_byte();
cpu.lss32(reg_fs);
};
t16[0xB5] = cpu => { cpu.read_modrm_byte();
cpu.lss16(reg_gs);
};
t32[0xB5] = cpu => { cpu.read_modrm_byte();
cpu.lss32(reg_gs);
};
t16[0xB6] = cpu => { cpu.read_modrm_byte();
// movzx
var data = cpu.read_e8();
cpu.write_g16(data);
};
t32[0xB6] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_e8();
cpu.write_g32(data);
};
t16[0xB7] = cpu => { cpu.read_modrm_byte();
// movzx
dbg_assert(false, "Possibly invalid encoding");
var data = cpu.read_e16();
cpu.write_g16(data);
};
t32[0xB7] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_e16();
cpu.write_g32(data);
};
t16[0xB8] = cpu => { cpu.read_modrm_byte();
// popcnt
var data = cpu.read_e16();
cpu.write_g16(cpu.popcnt(data));
};
t32[0xB8] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_e32s();
cpu.write_g32(cpu.popcnt(data));
};
t[0xB9] = cpu => {
// UD
cpu.todo();
};
t16[0xBA] = cpu => { cpu.read_modrm_byte();
switch(cpu.modrm_byte >> 3 & 7)
{
case 4:
if(cpu.modrm_byte < 0xC0)
{
cpu.bt_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_op8() & 15);
}
else
{
cpu.bt_reg(cpu.read_reg_e16(), cpu.read_op8() & 15);
}
break;
case 5:
if(cpu.modrm_byte < 0xC0) {
cpu.bts_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_op8() & 15);
} else {
cpu.write_reg_e16(cpu.bts_reg(cpu.read_reg_e16(), cpu.read_op8() & 15));
}
break;
case 6:
if(cpu.modrm_byte < 0xC0) {
cpu.btr_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_op8() & 15);
} else {
cpu.write_reg_e16(cpu.btr_reg(cpu.read_reg_e16(), cpu.read_op8() & 15));
}
break;
case 7:
if(cpu.modrm_byte < 0xC0) {
cpu.btc_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_op8() & 15);
} else {
cpu.write_reg_e16(cpu.btc_reg(cpu.read_reg_e16(), cpu.read_op8() & 15));
}
break;
default:
dbg_log(cpu.modrm_byte >> 3 & 7);
cpu.todo();
}
};
t32[0xBA] = cpu => { cpu.read_modrm_byte();
switch(cpu.modrm_byte >> 3 & 7)
{
case 4:
if(cpu.modrm_byte < 0xC0)
{
cpu.bt_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_op8() & 31);
}
else
{
cpu.bt_reg(cpu.read_reg_e32s(), cpu.read_op8() & 31);
}
break;
case 5:
if(cpu.modrm_byte < 0xC0) {
cpu.bts_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_op8() & 31);
} else {
cpu.write_reg_e32(cpu.bts_reg(cpu.read_reg_e32s(), cpu.read_op8() & 31));
}
break;
case 6:
if(cpu.modrm_byte < 0xC0) {
cpu.btr_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_op8() & 31);
} else {
cpu.write_reg_e32(cpu.btr_reg(cpu.read_reg_e32s(), cpu.read_op8() & 31));
}
break;
case 7:
if(cpu.modrm_byte < 0xC0) {
cpu.btc_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_op8() & 31);
} else {
cpu.write_reg_e32(cpu.btc_reg(cpu.read_reg_e32s(), cpu.read_op8() & 31));
}
break;
default:
dbg_log(cpu.modrm_byte >> 3 & 7);
cpu.todo();
}
};
t16[0xBB] = cpu => { cpu.read_modrm_byte();
if(cpu.modrm_byte < 0xC0) {
cpu.btc_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_g16s());
} else {
cpu.write_reg_e16(cpu.btc_reg(cpu.read_reg_e16(), cpu.read_g16s() & 15));
}
};
t32[0xBB] = cpu => { cpu.read_modrm_byte();
if(cpu.modrm_byte < 0xC0) {
cpu.btc_mem(cpu.modrm_resolve(cpu.modrm_byte), cpu.read_g32s());
} else {
cpu.write_reg_e32(cpu.btc_reg(cpu.read_reg_e32s(), cpu.read_g32s() & 31));
}
};
t16[0xBC] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_e16();
cpu.write_g16(cpu.bsf16(cpu.read_g16(), data));
};
t32[0xBC] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_e32s();
cpu.write_g32(cpu.bsf32(cpu.read_g32s(), data));
};
t16[0xBD] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_e16();
cpu.write_g16(cpu.bsr16(cpu.read_g16(), data));
};
t32[0xBD] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_e32s();
cpu.write_g32(cpu.bsr32(cpu.read_g32s(), data));
};
t16[0xBE] = cpu => { cpu.read_modrm_byte();
// movsx
var data = cpu.read_e8s();
cpu.write_g16(data);
};
t32[0xBE] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_e8s();
cpu.write_g32(data);
};
t16[0xBF] = cpu => { cpu.read_modrm_byte();
// movsx
dbg_assert(false, "Possibly invalid encoding");
var data = cpu.read_e16();
cpu.write_g16(data);
};
t32[0xBF] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_e16s();
cpu.write_g32(data);
};
t[0xC0] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_write_e8(); cpu.write_e8(cpu.xadd8(data, cpu.modrm_byte >> 1 & 0xC | cpu.modrm_byte >> 5 & 1));
};
t16[0xC1] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_write_e16();
cpu.write_e16(cpu.xadd16(data, cpu.modrm_byte >> 2 & 14));
};
t32[0xC1] = cpu => { cpu.read_modrm_byte();
var data = cpu.read_write_e32();
cpu.write_e32(cpu.xadd32(data, cpu.modrm_byte >> 3 & 7));
};
t[0xC2] = cpu => { cpu.unimplemented_sse(); };
t[0xC3] = cpu => { cpu.unimplemented_sse(); };
t[0xC4] = cpu => { cpu.unimplemented_sse(); };
t[0xC5] = cpu => { cpu.unimplemented_sse(); };
t[0xC6] = cpu => { cpu.unimplemented_sse(); };
t[0xC7] = cpu => {
cpu.read_modrm_byte();
switch(cpu.modrm_byte >> 3 & 7)
{
case 1:
// cmpxchg8b
if(cpu.modrm_byte >= 0xC0)
{
cpu.trigger_ud();
}
var addr = cpu.modrm_resolve(cpu.modrm_byte);
cpu.writable_or_pagefault(addr, 8);
var m64_low = cpu.safe_read32s(addr);
var m64_high = cpu.safe_read32s(addr + 4 | 0);
if(cpu.reg32s[reg_eax] === m64_low &&
cpu.reg32s[reg_edx] === m64_high)
{
cpu.flags |= flag_zero;
cpu.safe_write32(addr, cpu.reg32s[reg_ebx]);
cpu.safe_write32(addr + 4 | 0, cpu.reg32s[reg_ecx]);
}
else
{
cpu.flags &= ~flag_zero;
cpu.reg32s[reg_eax] = m64_low;
cpu.reg32s[reg_edx] = m64_high;
cpu.safe_write32(addr, m64_low);
cpu.safe_write32(addr + 4 | 0, m64_high);
}
cpu.flags_changed &= ~flag_zero;
break;
case 6:
var has_rand = v86util.has_rand_int();
if(has_rand)
{
var rand = v86util.get_rand_int();
}
else
{
var rand = 0;
}
//dbg_log("rdrand -> " + h(rand >>> 0, 8), LOG_CPU);
if(cpu.is_osize_32())
{
cpu.set_e32(rand);
}
else
{
cpu.set_e16(rand);
}
cpu.flags &= ~flags_all;
cpu.flags |= has_rand;
cpu.flags_changed = 0;
break;
default:
dbg_log(cpu.modrm_byte >> 3 & 7, LOG_CPU);
cpu.todo();
}
};
t[0xC8] = cpu => { cpu.bswap(reg_eax); };
t[0xC9] = cpu => { cpu.bswap(reg_ecx); };
t[0xCA] = cpu => { cpu.bswap(reg_edx); };
t[0xCB] = cpu => { cpu.bswap(reg_ebx); };
t[0xCC] = cpu => { cpu.bswap(reg_esp); };
t[0xCD] = cpu => { cpu.bswap(reg_ebp); };
t[0xCE] = cpu => { cpu.bswap(reg_esi); };
t[0xCF] = cpu => { cpu.bswap(reg_edi); };
t[0xD0] = cpu => { cpu.unimplemented_sse(); };
t[0xD1] = cpu => {
// psrlw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let shift = source[0] >>> 0;
let low = 0;
let high = 0;
if (shift <= 15) {
let word0 = (destination_low & 0xFFFF) >>> shift;
let word1 = (destination_low >>> 16) >>> shift;
low = word0 | word1 << 16;
let word2 = (destination_high & 0xFFFF) >>> shift;
let word3 = (destination_high >>> 16) >>> shift;
high = word2 | word3 << 16;
}
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xD2] = cpu => {
// psrld mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let shift = source[0] >>> 0;
let low = 0;
let high = 0;
if (shift <= 31) {
low = destination_low >>> shift;
high = destination_high >>> shift;
}
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xD3] = cpu => {
// psrlq mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let shift = source[0] >>> 0;
let low = 0;
let high = 0;
if (shift <= 31) {
low = destination_low >>> shift | (destination_high << (32 - shift));
high = destination_high >>> shift;
}
else if (shift <= 63) {
low = destination_high >>> (shift & 0x1F);
high = 0;
}
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xD4] = cpu => { cpu.unimplemented_sse(); };
t[0xD5] = cpu => {
// pmullw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let word0 = ((destination_low & 0xFFFF) * (source[0] & 0xFFFF)) & 0xFFFF;
let word1 = ((destination_low >>> 16) * (source[0] >>> 16)) & 0xFFFF;
let low = word0 | word1 << 16;
let word2 = ((destination_high & 0xFFFF) * (source[1] & 0xFFFF)) & 0xFFFF;
let word3 = ((destination_high >>> 16) * (source[1] >>> 16)) & 0xFFFF;
let high = word2 | word3 << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xD6] = cpu => { cpu.unimplemented_sse(); };
t[0xD7] = cpu => { cpu.unimplemented_sse(); };
t[0xD8] = cpu => {
// psubusb mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source64s = cpu.read_xmm_mem64s();
let source8 = new Uint8Array(source64s.buffer);
let reg_offset = 8 * (cpu.modrm_byte >> 3 & 7);
let destination8 = cpu.reg_mmx8;
let byte0 = cpu.saturate_sd_to_ub(destination8[reg_offset] - source8[0]);
let byte1 = cpu.saturate_sd_to_ub(destination8[reg_offset + 1] - source8[1]);
let byte2 = cpu.saturate_sd_to_ub(destination8[reg_offset + 2] - source8[2]);
let byte3 = cpu.saturate_sd_to_ub(destination8[reg_offset + 3] - source8[3]);
let byte4 = cpu.saturate_sd_to_ub(destination8[reg_offset + 4] - source8[4]);
let byte5 = cpu.saturate_sd_to_ub(destination8[reg_offset + 5] - source8[5]);
let byte6 = cpu.saturate_sd_to_ub(destination8[reg_offset + 6] - source8[6]);
let byte7 = cpu.saturate_sd_to_ub(destination8[reg_offset + 7] - source8[7]);
let low = byte0 | byte1 << 8 | byte2 << 16 | byte3 << 24;
let high = byte4 | byte5 << 8 | byte6 << 16 | byte7 << 24;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xD9] = cpu => {
// psubusw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let word0 = (destination_low & 0xFFFF) - (source[0] & 0xFFFF);
let word1 = (destination_low >>> 16) - (source[0] >>> 16);
if (word0 < 0) {
word0 = 0;
}
if (word1 < 0) {
word1 = 0;
}
let word2 = (destination_high & 0xFFFF) - (source[1] & 0xFFFF);
let word3 = (destination_high >>> 16) - (source[1] >>> 16);
if (word2 < 0) {
word2 = 0;
}
if (word3 < 0) {
word3 = 0;
}
let low = word0 | word1 << 16;
let high = word2 | word3 << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xDA] = cpu => { cpu.unimplemented_sse(); };
t[0xDB] = cpu => {
// pand mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let data = cpu.create_atom64s(
(source[0] & destination_low),
(source[1] & destination_high)
);
cpu.write_xmm64s(data);
};
t[0xDC] = cpu => {
// paddusb mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source64s = cpu.read_xmm_mem64s();
let source8 = new Uint8Array(source64s.buffer);
let reg_offset = 8 * (cpu.modrm_byte >> 3 & 7);
let destination8 = cpu.reg_mmx8;
let byte0 = cpu.saturate_ud_to_ub(destination8[reg_offset] + source8[0]);
let byte1 = cpu.saturate_ud_to_ub(destination8[reg_offset + 1] + source8[1]);
let byte2 = cpu.saturate_ud_to_ub(destination8[reg_offset + 2] + source8[2]);
let byte3 = cpu.saturate_ud_to_ub(destination8[reg_offset + 3] + source8[3]);
let byte4 = cpu.saturate_ud_to_ub(destination8[reg_offset + 4] + source8[4]);
let byte5 = cpu.saturate_ud_to_ub(destination8[reg_offset + 5] + source8[5]);
let byte6 = cpu.saturate_ud_to_ub(destination8[reg_offset + 6] + source8[6]);
let byte7 = cpu.saturate_ud_to_ub(destination8[reg_offset + 7] + source8[7]);
let low = byte0 | byte1 << 8 | byte2 << 16 | byte3 << 24;
let high = byte4 | byte5 << 8 | byte6 << 16 | byte7 << 24;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xDD] = cpu => {
// paddusw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let word0 = (destination_low & 0xFFFF) + (source[0] & 0xFFFF);
let word1 = (destination_low >>> 16) + (source[0] >>> 16);
if (word0 > 0xFFFF) {
word0 = 0xFFFF;
}
if (word1 > 0xFFFF) {
word1 = 0xFFFF;
}
let word2 = (destination_high & 0xFFFF) + (source[1] & 0xFFFF);
let word3 = (destination_high >>> 16) + (source[1] >>> 16);
if (word2 > 0xFFFF) {
word2 = 0xFFFF;
}
if (word3 > 0xFFFF) {
word3 = 0xFFFF;
}
let low = word0 | word1 << 16;
let high = word2 | word3 << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xDE] = cpu => { cpu.unimplemented_sse(); };
t[0xDF] = cpu => {
// pandn mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let data = cpu.create_atom64s(
(source[0] & ~destination_low),
(source[1] & ~destination_high)
);
cpu.write_xmm64s(data);
};
t[0xE0] = cpu => { cpu.unimplemented_sse(); };
t[0xE1] = cpu => {
// psraw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let shift = source[0] >>> 0;
if (shift > 15) {
shift = 16;
}
let word0 = ((destination_low << 16 >> 16) >> shift) & 0xFFFF;
let word1 = ((destination_low >> 16) >> shift) & 0xFFFF;
let low = word0 | word1 << 16;
let word2 = ((destination_high << 16 >> 16) >> shift) & 0xFFFF;
let word3 = ((destination_high >> 16) >> shift) & 0xFFFF;
let high = word2 | word3 << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xE2] = cpu => {
// psrad mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let shift = source[0] >>> 0;
if (shift > 31) {
shift = 31;
}
let low = destination_low >> shift;
let high = destination_high >> shift;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xE3] = cpu => { cpu.unimplemented_sse(); };
t[0xE4] = cpu => { cpu.unimplemented_sse(); };
t[0xE5] = cpu => {
// pmulhw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let word0 = ((destination_low << 16 >> 16) * (source[0] << 16 >> 16)) >>> 16;
let word1 = ((destination_low >> 16) * (source[0] >> 16)) >>> 16;
let word2 = ((destination_high << 16 >> 16) * (source[1] << 16 >> 16)) >>> 16;
let word3 = ((destination_high >> 16) * (source[1] >> 16)) >>> 16;
let low = word0 | word1 << 16;
let high = word2 | word3 << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xE6] = cpu => { cpu.unimplemented_sse(); };
t[0xE7] = cpu => { cpu.unimplemented_sse(); };
t[0xE8] = cpu => {
// psubsb mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source64s = cpu.read_xmm_mem64s();
let source8s = new Int8Array(source64s.buffer);
let reg_offset = 8 * (cpu.modrm_byte >> 3 & 7);
let destination8s = cpu.reg_mmx8s;
let byte0 = cpu.saturate_sd_to_sb(destination8s[reg_offset] - source8s[0]);
let byte1 = cpu.saturate_sd_to_sb(destination8s[reg_offset + 1] - source8s[1]);
let byte2 = cpu.saturate_sd_to_sb(destination8s[reg_offset + 2] - source8s[2]);
let byte3 = cpu.saturate_sd_to_sb(destination8s[reg_offset + 3] - source8s[3]);
let byte4 = cpu.saturate_sd_to_sb(destination8s[reg_offset + 4] - source8s[4]);
let byte5 = cpu.saturate_sd_to_sb(destination8s[reg_offset + 5] - source8s[5]);
let byte6 = cpu.saturate_sd_to_sb(destination8s[reg_offset + 6] - source8s[6]);
let byte7 = cpu.saturate_sd_to_sb(destination8s[reg_offset + 7] - source8s[7]);
let low = byte0 | byte1 << 8 | byte2 << 16 | byte3 << 24;
let high = byte4 | byte5 << 8 | byte6 << 16 | byte7 << 24;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xE9] = cpu => {
// psubsw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let word0 = cpu.saturate_sd_to_sw((destination_low << 16 >> 16) - (source[0] << 16 >> 16));
let word1 = cpu.saturate_sd_to_sw((destination_low >> 16) - (source[0] >> 16));
let word2 = cpu.saturate_sd_to_sw((destination_high << 16 >> 16) - (source[1] << 16 >> 16));
let word3 = cpu.saturate_sd_to_sw((destination_high >> 16) - (source[1] >> 16));
let low = word0 | word1 << 16;
let high = word2 | word3 << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xEA] = cpu => { cpu.unimplemented_sse(); };
t[0xEB] = cpu => {
// por mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let data = cpu.create_atom64s(
(source[0] | destination_low),
(source[1] | destination_high)
);
cpu.write_xmm64s(data);
};
t[0xEC] = cpu => {
// paddsb mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source64s = cpu.read_xmm_mem64s();
let source8s = new Int8Array(source64s.buffer);
let reg_offset = 8 * (cpu.modrm_byte >> 3 & 7);
let destination8s = cpu.reg_mmx8s;
let byte0 = cpu.saturate_sd_to_sb(destination8s[reg_offset] + source8s[0]);
let byte1 = cpu.saturate_sd_to_sb(destination8s[reg_offset + 1] + source8s[1]);
let byte2 = cpu.saturate_sd_to_sb(destination8s[reg_offset + 2] + source8s[2]);
let byte3 = cpu.saturate_sd_to_sb(destination8s[reg_offset + 3] + source8s[3]);
let byte4 = cpu.saturate_sd_to_sb(destination8s[reg_offset + 4] + source8s[4]);
let byte5 = cpu.saturate_sd_to_sb(destination8s[reg_offset + 5] + source8s[5]);
let byte6 = cpu.saturate_sd_to_sb(destination8s[reg_offset + 6] + source8s[6]);
let byte7 = cpu.saturate_sd_to_sb(destination8s[reg_offset + 7] + source8s[7]);
let low = byte0 | byte1 << 8 | byte2 << 16 | byte3 << 24;
let high = byte4 | byte5 << 8 | byte6 << 16 | byte7 << 24;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xED] = cpu => {
// paddsw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let word0 = cpu.saturate_sd_to_sw((destination_low << 16 >> 16) + (source[0] << 16 >> 16));
let word1 = cpu.saturate_sd_to_sw((destination_low >> 16) + (source[0] >> 16));
let word2 = cpu.saturate_sd_to_sw((destination_high << 16 >> 16) + (source[1] << 16 >> 16));
let word3 = cpu.saturate_sd_to_sw((destination_high >> 16) + (source[1] >> 16));
let low = word0 | word1 << 16;
let high = word2 | word3 << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xEE] = cpu => { cpu.unimplemented_sse(); };
t[0xEF] = cpu => {
// pxor mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let low = source[0] ^ destination_low;
let high = source[1] ^ destination_high;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xF0] = cpu => { cpu.unimplemented_sse(); };
t[0xF1] = cpu => {
// psllw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let shift = source[0] >>> 0;
let low = 0;
let high = 0;
if (shift <= 15) {
let word0 = ((destination_low & 0xFFFF) << shift) & 0xFFFF;
let word1 = (destination_low >>> 16) << shift;
low = word0 | word1 << 16;
let word2 = ((destination_high & 0xFFFF) << shift) & 0xFFFF;
let word3 = (destination_high >>> 16) << shift;
high = word2 | word3 << 16;
}
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xF2] = cpu => {
// pslld mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let shift = source[0] >>> 0;
let low = 0;
let high = 0;
if (shift <= 31) {
low = destination_low << shift;
high = destination_high << shift;
}
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xF3] = cpu => {
// psllq mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let shift = source[0] >>> 0;
let low = 0;
let high = 0;
if (shift <= 31) {
low = destination_low << shift;
high = destination_high << shift
| (destination_low >>> (32 - shift));
}
else if (shift <= 63) {
high = destination_low << (shift & 0x1F);
low = 0;
}
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xF4] = cpu => { cpu.unimplemented_sse(); };
t[0xF5] = cpu => {
// pmaddwd mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let mul0 = ((destination_low << 16 >> 16) * (source[0] << 16 >> 16));
let mul1 = ((destination_low >> 16) * (source[0] >> 16));
let mul2 = ((destination_high << 16 >> 16) * (source[1] << 16 >> 16));
let mul3 = ((destination_high >> 16) * (source[1] >> 16));
let low = mul0 + mul1;
let high = mul2 + mul3;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xF6] = cpu => { cpu.unimplemented_sse(); };
t[0xF7] = cpu => { cpu.unimplemented_sse(); };
t[0xF8] = cpu => {
// psubb mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source64s = cpu.read_xmm_mem64s();
let source8s = new Int8Array(source64s.buffer);
let reg_offset = 8 * (cpu.modrm_byte >> 3 & 7);
let destination8s = cpu.reg_mmx8s;
let byte0 = (destination8s[reg_offset] - source8s[0]) & 0xFF;
let byte1 = (destination8s[reg_offset + 1] - source8s[1]) & 0xFF;
let byte2 = (destination8s[reg_offset + 2] - source8s[2]) & 0xFF;
let byte3 = (destination8s[reg_offset + 3] - source8s[3]) & 0xFF;
let byte4 = (destination8s[reg_offset + 4] - source8s[4]) & 0xFF;
let byte5 = (destination8s[reg_offset + 5] - source8s[5]) & 0xFF;
let byte6 = (destination8s[reg_offset + 6] - source8s[6]) & 0xFF;
let byte7 = (destination8s[reg_offset + 7] - source8s[7]) & 0xFF;
let low = byte0 | byte1 << 8 | byte2 << 16 | byte3 << 24;
let high = byte4 | byte5 << 8 | byte6 << 16 | byte7 << 24;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xF9] = cpu => {
// psubw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let word0 = (destination_low - source[0]) & 0xFFFF;
let word1 = ((destination_low >>> 16) - (source[0] >>> 16)) & 0xFFFF;
let low = word0 | word1 << 16;
let word2 = (destination_high - source[1]) & 0xFFFF;
let word3 = ((destination_high >>> 16) - (source[1] >>> 16)) & 0xFFFF;
let high = word2 | word3 << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xFA] = cpu => {
// psubd mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let low = destination_low - source[0];
let high = destination_high - source[1];
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xFB] = cpu => { cpu.unimplemented_sse(); };
t[0xFC] = cpu => {
// paddb mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source64s = cpu.read_xmm_mem64s();
let source8s = new Int8Array(source64s.buffer);
let reg_offset = 8 * (cpu.modrm_byte >> 3 & 7);
let destination8s = cpu.reg_mmx8s;
let byte0 = (destination8s[reg_offset] + source8s[0]) & 0xFF;
let byte1 = (destination8s[reg_offset + 1] + source8s[1]) & 0xFF;
let byte2 = (destination8s[reg_offset + 2] + source8s[2]) & 0xFF;
let byte3 = (destination8s[reg_offset + 3] + source8s[3]) & 0xFF;
let byte4 = (destination8s[reg_offset + 4] + source8s[4]) & 0xFF;
let byte5 = (destination8s[reg_offset + 5] + source8s[5]) & 0xFF;
let byte6 = (destination8s[reg_offset + 6] + source8s[6]) & 0xFF;
let byte7 = (destination8s[reg_offset + 7] + source8s[7]) & 0xFF;
let low = byte0 | byte1 << 8 | byte2 << 16 | byte3 << 24;
let high = byte4 | byte5 << 8 | byte6 << 16 | byte7 << 24;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xFD] = cpu => {
// paddw mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let word0 = (destination_low + source[0]) & 0xFFFF;
let word1 = ((destination_low >>> 16) + (source[0] >>> 16)) & 0xFFFF;
let low = word0 | word1 << 16;
let word2 = (destination_high + source[1]) & 0xFFFF;
let word3 = ((destination_high >>> 16) + (source[1] >>> 16)) & 0xFFFF;
let high = word2 | word3 << 16;
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xFE] = cpu => {
// paddd mm, mm/m64
dbg_assert((cpu.prefixes & (PREFIX_MASK_REP | PREFIX_MASK_OPSIZE)) == 0);
if(cpu.cr[0] & (CR0_EM | CR0_TS)) {
if(cpu.cr[0] & CR0_TS) {
cpu.trigger_nm();
}
else {
cpu.trigger_ud();
}
}
cpu.read_modrm_byte();
let source = cpu.read_xmm_mem64s();
let destination_low = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7)];
let destination_high = cpu.reg_mmxs[2 * (cpu.modrm_byte >> 3 & 7) + 1];
let low = destination_low + source[0];
let high = destination_high + source[1];
let data = cpu.create_atom64s(low, high);
cpu.write_xmm64s(data);
};
t[0xFF] = cpu => {
// Windows 98
dbg_log("#ud: 0F FF");
cpu.trigger_ud();
};
var table0F_16 = [];
var table0F_32 = [];
CPU.prototype.table0F_16 = table0F_16;
CPU.prototype.table0F_32 = table0F_32;
for(i = 0; i < 256; i++)
{
if(t[i])
{
//dbg_assert(!t16[i]);
//dbg_assert(!t32[i]);
table0F_16[i] = table0F_32[i] = t[i];
}
else if(t16[i])
{
//dbg_assert(!t[i]);
//dbg_assert(t32[i]);
table0F_16[i] = t16[i];
table0F_32[i] = t32[i];
}
}