/* * 86Box A hypervisor and IBM PC system emulator that specializes in * running old operating systems and software designed for IBM * PC systems and compatibles from 1981 through fairly recent * system designs based on the PCI bus. * * This file is part of the 86Box distribution. * * 808x CPU emulation, mostly ported from reenigne's XTCE, which * is cycle-accurate. * * Authors: Andrew Jenner, * Miran Grca, * * Copyright 2015-2020 Andrew Jenner. * Copyright 2016-2020 Miran Grca. */ #include #include #include #include #include #include #include "i8080.h" #define HAVE_STDARG_H #include <86box/86box.h> #include "cpu.h" #include "x86.h" #include <86box/machine.h> #include <86box/io.h> #include <86box/mem.h> #include <86box/rom.h> #include <86box/nmi.h> #include <86box/pic.h> #include <86box/ppi.h> #include <86box/timer.h> #include <86box/gdbstub.h> #include <86box/plat_fallthrough.h> #include <86box/plat_unused.h> #include "vx0_biu.h" #define do_cycle() wait(1) #define do_cycle_no_modrm() if (!nx) \ do_cycle() #define do_cycle_i() do_cycle() #define do_cycles(c) wait(c) #define do_cycles_i(c) do_cycles(c) #define do_cycle_nx() nx = 1 #define do_cycle_nx_i() nx = 1 #define do_cycles_nx(c) nx = 1; \ if (c > 1) \ do_cycles(c - 1) #define do_cycles_nx_i(c) nx = 1; \ if (c > 1) \ do_cycles(c - 1) #define addr_mode_match() cpu_mod == 3 #define math_op(o) cpu_alu_op = o; \ alu_op(bits) /* Various things needed for 8087. */ #define OP_TABLE(name) ops_##name #define CPU_BLOCK_END() #define SEG_CHECK_READ(seg) #define SEG_CHECK_WRITE(seg) #define CHECK_READ(a, b, c) #define CHECK_WRITE(a, b, c) #define UN_USED(x) (void) (x) #define fetch_ea_16(val) #define fetch_ea_32(val) #define PREFETCH_RUN(a, b, c, d, e, f, g, h) #define CYCLES(val) \ { \ do_cycles(val); \ } #define CLOCK_CYCLES_ALWAYS(val) \ { \ do_cycles(val); \ } #define CLOCK_CYCLES_FPU(val) \ { \ do_cycles(val); \ } # define CLOCK_CYCLES(val) \ { \ if (fpu_cycles > 0) { \ fpu_cycles -= (val); \ if (fpu_cycles < 0) { \ do_cycles(val); \ } \ } else { \ do_cycles(val); \ } \ } #define CONCURRENCY_CYCLES(c) fpu_cycles = (c) #define OP_MRM 1 #define OP_EA 2 #define OP_MEA (OP_MRM | OP_EA) #define OP_GRP 4 #define OP_DELAY 8 #define OP_PRE 16 #define readmemb readmemb_vx0 #define readmemw readmemw_vx0 #define readmem readmem_vx0 #define readmeml readmeml_vx0 #define readmemq readmemq_vx0 #define writememb writememb_vx0 #define writememw writememw_vx0 #define writemem writemem_vx0 #define writememl writememl_vx0 #define writememq writememq_vx0 typedef int (*OpFn)(uint32_t fetchdat); static void farret(int far); const uint8_t opf[256] = { OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, 0, /* 00 */ OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, 0, /* 08 */ OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, 0, /* 10 */ OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, 0, /* 18 */ OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 20 */ OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 28 */ OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 30 */ OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 38 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 40 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 48 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 58 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 60 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 68 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 70 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 78 */ OP_GRP, OP_GRP, OP_GRP, OP_GRP, OP_MEA, OP_MEA, OP_MEA, OP_MEA, /* 80 */ OP_MRM, OP_MRM, OP_MEA, OP_MEA, OP_MRM, OP_MRM, OP_MEA, OP_MRM, /* 88 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 90 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 98 */ 0, 0, 0, 0, 0, 0, 0, 0, /* A0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* A8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* B0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* B8 */ 0, 0, 0, 0, OP_MEA, OP_MEA, OP_MRM, OP_MRM, /* C0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* C8 */ OP_GRP, OP_GRP, OP_GRP, OP_GRP, 0, 0, 0, 0, /* D0 */ OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, /* D8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* E0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* E8 */ OP_PRE, OP_PRE, OP_PRE, OP_PRE, 0, 0, OP_GRP, OP_GRP, /* F0 */ 0, 0, 0, 0, 0, 0, OP_GRP, OP_GRP }; /* F8 */ const uint8_t opf_nec[256] = { OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, 0, /* 00 */ OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, OP_PRE, /* 08 */ OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, 0, /* 10 */ OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, 0, /* 18 */ OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 20 */ OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 28 */ OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 30 */ OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 38 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 40 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 48 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 58 */ 0, 0, OP_MRM, OP_MRM, OP_PRE, OP_PRE, OP_MRM, OP_MRM, /* 60 */ 0, OP_MRM, 0, OP_MEA, 0, 0, 0, 0, /* 68 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 70 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 78 */ OP_GRP, OP_GRP, OP_GRP, OP_GRP, OP_MEA, OP_MEA, OP_MEA, OP_MEA, /* 80 */ OP_MRM, OP_MRM, OP_MEA, OP_MEA, OP_MRM, OP_MRM, OP_MEA, OP_MRM, /* 88 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 90 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 98 */ 0, 0, 0, 0, 0, 0, 0, 0, /* A0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* A8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* B0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* B8 */ OP_GRP, OP_GRP, 0, 0, OP_MEA, OP_MEA, OP_MRM, OP_MRM, /* C0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* C8 */ OP_GRP, OP_GRP, OP_GRP, OP_GRP, 0, 0, 0, 0, /* D0 */ OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, /* D8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* E0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* E8 */ OP_PRE, OP_PRE, OP_PRE, OP_PRE, 0, 0, OP_GRP, OP_GRP, /* F0 */ 0, 0, 0, 0, 0, 0, OP_GRP, OP_GRP }; /* F8 */ const uint8_t opf_0f[256] = { 0, 0, 0, 0, 0, 0, 0, 0, /* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 08 */ OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, /* 10 */ OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, /* 18 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 20 */ OP_MRM, 0, OP_MRM, 0, 0, 0, 0, 0, /* 28 */ OP_MRM, 0, OP_MRM, 0, 0, 0, 0, 0, /* 30 */ OP_MRM, 0, OP_MRM, 0, 0, 0, 0, 0, /* 38 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 40 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 48 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 58 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 60 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 68 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 70 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 78 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 80 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 88 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 90 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 98 */ 0, 0, 0, 0, 0, 0, 0, 0, /* A0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* A8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* B0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* B8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* C0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* C8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* D0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* D8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* E0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* E8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* F0 */ 0, 0, 0, 0, 0, 0, 0, 0 }; /* F8 */ uint8_t use_custom_nmi_vector = 0; uint32_t custom_nmi_vector = 0x00000000; /* Is the CPU 8088 or 8086. */ int is8086 = 0; int nx = 0; static uint32_t cpu_src = 0; static uint32_t cpu_dest = 0; static uint32_t cpu_data = 0; static int oldc; static int cpu_alu_op; static int completed = 1; static int in_rep = 0; static int repeating = 0; static int rep_c_flag = 0; static int clear_lock = 0; static int noint = 0; static int tempc_fpu = 0; static int started = 0; static int group_delay = 0; static int modrm_loaded = 0; static int in_0f = 0; static int in_hlt = 0; static int retem = 0; static int halted = 0; static uint32_t * ovr_seg = NULL; /* Pointer tables needed for segment overrides. */ static uint32_t * opseg[4]; static x86seg * _opseg[4]; enum { MODRM_ADDR_BX_SI = 0x00, MODRM_ADDR_BX_DI, MODRM_ADDR_BP_SI, MODRM_ADDR_BP_DI, MODRM_ADDR_SI, MODRM_ADDR_DI, MODRM_ADDR_DISP16, MODRM_ADDR_BX, MODRM_ADDR_BX_SI_DISP8 = 0x40, MODRM_ADDR_BX_DI_DISP8, MODRM_ADDR_BP_SI_DISP8, MODRM_ADDR_BP_DI_DISP8, MODRM_ADDR_SI_DISP8, MODRM_ADDR_DI_DISP8, MODRM_ADDR_BP_DISP8, MODRM_ADDR_BX_DISP8, MODRM_ADDR_BX_SI_DISP16 = 0x80, MODRM_ADDR_BX_DI_DISP16, MODRM_ADDR_BP_SI_DISP16, MODRM_ADDR_BP_DI_DISP16, MODRM_ADDR_SI_DISP16, MODRM_ADDR_DI_DISP16, MODRM_ADDR_BP_DISP16, MODRM_ADDR_BX_DISP16 }; static uint8_t modrm_cycs_pre[256] = { [MODRM_ADDR_BX_SI] = 4, [MODRM_ADDR_BX_DI] = 5, [MODRM_ADDR_BP_SI] = 5, [MODRM_ADDR_BP_DI] = 4, [MODRM_ADDR_SI] = 2, [MODRM_ADDR_DI] = 2, [MODRM_ADDR_DISP16] = 0, [MODRM_ADDR_BX] = 2, [0x08 ... 0x3f] = 0, [MODRM_ADDR_BX_SI_DISP8] = 4, [MODRM_ADDR_BX_DI_DISP8] = 5, [MODRM_ADDR_BP_SI_DISP8] = 5, [MODRM_ADDR_BP_DI_DISP8] = 4, [MODRM_ADDR_SI_DISP8] = 2, [MODRM_ADDR_DI_DISP8] = 2, [MODRM_ADDR_BP_DISP8] = 2, [MODRM_ADDR_BX_DISP8] = 2, [0x48 ... 0x7f] = 0, [MODRM_ADDR_BX_SI_DISP16] = 4, [MODRM_ADDR_BX_DI_DISP16] = 5, [MODRM_ADDR_BP_SI_DISP16] = 5, [MODRM_ADDR_BP_DI_DISP16] = 4, [MODRM_ADDR_SI_DISP16] = 2, [MODRM_ADDR_DI_DISP16] = 2, [MODRM_ADDR_BP_DISP16] = 2, [MODRM_ADDR_BX_DISP16] = 2, [0x88 ... 0xff] = 0 }; static uint8_t modrm_cycs_post[256] = { [MODRM_ADDR_BX_SI] = 0, [MODRM_ADDR_BX_DI] = 0, [MODRM_ADDR_BP_SI] = 0, [MODRM_ADDR_BP_DI] = 0, [MODRM_ADDR_SI] = 0, [MODRM_ADDR_DI] = 0, [MODRM_ADDR_DISP16] = 1, [MODRM_ADDR_BX] = 0, [0x08 ... 0x3f] = 0, [MODRM_ADDR_BX_SI_DISP8] = 3, [MODRM_ADDR_BX_DI_DISP8] = 3, [MODRM_ADDR_BP_SI_DISP8] = 3, [MODRM_ADDR_BP_DI_DISP8] = 3, [MODRM_ADDR_SI_DISP8] = 3, [MODRM_ADDR_DI_DISP8] = 3, [MODRM_ADDR_BP_DISP8] = 3, [MODRM_ADDR_BX_DISP8] = 3, [0x48 ... 0x7f] = 0, [MODRM_ADDR_BX_SI_DISP16] = 2, [MODRM_ADDR_BX_DI_DISP16] = 2, [MODRM_ADDR_BP_SI_DISP16] = 2, [MODRM_ADDR_BP_DI_DISP16] = 2, [MODRM_ADDR_SI_DISP16] = 2, [MODRM_ADDR_DI_DISP16] = 2, [MODRM_ADDR_BP_DISP16] = 2, [MODRM_ADDR_BX_DISP16] = 2, [0x88 ... 0xff] = 0 }; #ifdef ENABLE_808X_LOG #if 0 void dumpregs(int); #endif int x808x_do_log = ENABLE_808X_LOG; static void x808x_log(const char *fmt, ...) { va_list ap; if (x808x_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else # define x808x_log(fmt, ...) #endif static i8080 emulated_processor; static bool cpu_md_write_disable = 1; static void set_if(int cond) { cpu_state.flags = (cpu_state.flags & ~I_FLAG) | (cond ? I_FLAG : 0); } void sync_from_i8080(void) { AL = emulated_processor.a; BH = emulated_processor.h; BL = emulated_processor.l; CH = emulated_processor.b; CL = emulated_processor.c; DH = emulated_processor.d; DL = emulated_processor.e; BP = emulated_processor.sp; cpu_state.pc = emulated_processor.pc; cpu_state.flags &= 0xFF00; cpu_state.flags |= emulated_processor.sf << 7; cpu_state.flags |= emulated_processor.zf << 6; cpu_state.flags |= emulated_processor.hf << 4; cpu_state.flags |= emulated_processor.pf << 2; cpu_state.flags |= 1 << 1; cpu_state.flags |= emulated_processor.cf << 0; set_if(emulated_processor.iff); } void sync_to_i8080(void) { if (!is_nec) return; emulated_processor.a = AL; emulated_processor.h = BH; emulated_processor.l = BL; emulated_processor.b = CH; emulated_processor.c = CL; emulated_processor.d = DH; emulated_processor.e = DL; emulated_processor.sp = BP; emulated_processor.pc = cpu_state.pc; emulated_processor.iff = !!(cpu_state.flags & I_FLAG); emulated_processor.sf = (cpu_state.flags >> 7) & 1; emulated_processor.zf = (cpu_state.flags >> 6) & 1; emulated_processor.hf = (cpu_state.flags >> 4) & 1; emulated_processor.pf = (cpu_state.flags >> 2) & 1; emulated_processor.cf = (cpu_state.flags >> 0) & 1; emulated_processor.interrupt_delay = noint; } uint16_t get_last_addr(void) { return last_addr; } static void set_ip(uint16_t new_ip) { cpu_state.pc = new_ip; } static void startx86(void) { /* Reset takes 6 cycles before first fetch. */ do_cycle(); biu_suspend_fetch(); do_cycles_i(2); biu_queue_flush(); do_cycles_i(3); } static void load_cs(uint16_t seg) { cpu_state.seg_cs.base = seg << 4; cpu_state.seg_cs.seg = seg & 0xffff; } static void load_seg(uint16_t seg, x86seg *s) { s->base = seg << 4; s->seg = seg & 0xffff; } static uint8_t fetch_i8080_opcode(UNUSED(void* priv), uint16_t addr) { return readmemb(cs, addr); } static uint8_t fetch_i8080_data(UNUSED(void* priv), uint16_t addr) { return readmemb(ds, addr); } static void put_i8080_data(UNUSED(void* priv), uint16_t addr, uint8_t val) { writememb(ds, addr, val); } static uint8_t i8080_port_in(UNUSED(void* priv), uint8_t port) { cpu_data = port; cpu_state.eaaddr = cpu_data; cpu_io_vx0(8, 0, cpu_state.eaaddr); return AL; } static void i8080_port_out(UNUSED(void* priv), uint8_t port, uint8_t val) { cpu_data = DX; AL = val; do_cycle_i(); cpu_state.eaaddr = cpu_data; cpu_data = AL; cpu_io_vx0(8, 1, port); } void reset_vx0(int hard) { halted = 0; in_hlt = 0; in_0f = 0; in_rep = 0; in_lock = 0; completed = 1; repeating = 0; clear_lock = 0; ovr_seg = NULL; if (hard) { opseg[0] = &es; opseg[1] = &cs; opseg[2] = &ss; opseg[3] = &ds; _opseg[0] = &cpu_state.seg_es; _opseg[1] = &cpu_state.seg_cs; _opseg[2] = &cpu_state.seg_ss; _opseg[3] = &cpu_state.seg_ds; } load_cs(0xFFFF); cpu_state.pc = 0; if (is_nec) cpu_state.flags |= MD_FLAG; rammask = 0xfffff; cpu_alu_op = 0; nx = 0; use_custom_nmi_vector = 0x00; custom_nmi_vector = 0x00000000; biu_reset(); started = 1; modrm_loaded = 0; cpu_md_write_disable = 1; i8080_init(&emulated_processor); emulated_processor.write_byte = put_i8080_data; emulated_processor.read_byte = fetch_i8080_data; emulated_processor.read_byte_seg = fetch_i8080_opcode; emulated_processor.port_in = i8080_port_in; emulated_processor.port_out = i8080_port_out; } static uint16_t get_accum(int bits) { return (bits == 16) ? AX : AL; } static void set_accum(int bits, uint16_t val) { if (bits == 16) AX = val; else AL = val; } static uint16_t sign_extend(uint8_t data) { return data + (data < 0x80 ? 0 : 0xff00); } #undef getr8 #define getr8(r) ((r & 4) ? cpu_state.regs[r & 3].b.h : cpu_state.regs[r & 3].b.l) #undef setr8 #define setr8(r, v) \ if (r & 4) \ cpu_state.regs[r & 3].b.h = v; \ else \ cpu_state.regs[r & 3].b.l = v; /* Reads a byte from the effective address. */ static uint8_t geteab(void) { uint8_t ret; if (cpu_mod == 3) ret = (getr8(cpu_rm)); else ret = readmemb(easeg, cpu_state.eaaddr); return ret; } /* Reads a word from the effective address. */ static uint16_t geteaw(void) { uint16_t ret; if (cpu_mod == 3) ret = cpu_state.regs[cpu_rm].w; else ret = readmemw(easeg, cpu_state.eaaddr); return ret; } /* Neede for 8087 - memory only. */ static uint32_t geteal(void) { uint32_t ret; if (cpu_mod == 3) { fatal("808x register geteal()\n"); ret = 0xffffffff; } else ret = readmeml(easeg, cpu_state.eaaddr); return ret; } /* Neede for 8087 - memory only. */ static uint64_t geteaq(void) { uint32_t ret; if (cpu_mod == 3) { fatal("808x register geteaq()\n"); ret = 0xffffffff; } else ret = readmemq(easeg, cpu_state.eaaddr); return ret; } static void read_ea(int memory_only, int bits) { if (cpu_mod != 3) { if (bits == 16) cpu_data = readmemw(easeg, cpu_state.eaaddr); else cpu_data = readmemb(easeg, cpu_state.eaaddr); return; } if (!memory_only) { if (bits == 8) { cpu_data = getr8(cpu_rm); } else cpu_data = cpu_state.regs[cpu_rm].w; } } static void read_ea_8to16(void) { cpu_data = cpu_state.regs[cpu_rm & 3].w; } static void read_ea2(int bits) { cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff; if (bits == 16) cpu_data = readmemw(easeg, cpu_state.eaaddr); else cpu_data = readmemb(easeg, cpu_state.eaaddr); } /* Writes a byte to the effective address. */ static void seteab(uint8_t val) { if (cpu_mod == 3) { setr8(cpu_rm, val); } else { do_cycle(); writememb(easeg, cpu_state.eaaddr, val); } } /* Writes a word to the effective address. */ static void seteaw(uint16_t val) { if (cpu_mod == 3) cpu_state.regs[cpu_rm].w = val; else { do_cycle(); writememw(easeg, cpu_state.eaaddr, val); } } static void seteal(uint32_t val) { if (cpu_mod == 3) { fatal("808x register seteal()\n"); return; } else writememl(easeg, cpu_state.eaaddr, val); } static void seteaq(uint64_t val) { if (cpu_mod == 3) { fatal("808x register seteaq()\n"); return; } else writememq(easeg, cpu_state.eaaddr, val); } /* Leave out the 686 stuff as it's not needed and complicates compiling. */ #define FPU_8087 #define FPU_NEC #define tempc tempc_fpu #include "x87_sf.h" #include "x87.h" #include "x87_ops.h" #undef tempc #undef FPU_8087 static void set_cf(int cond) { cpu_state.flags = (cpu_state.flags & ~C_FLAG) | (cond ? C_FLAG : 0); } static void set_df(int cond) { cpu_state.flags = (cpu_state.flags & ~D_FLAG) | (cond ? D_FLAG : 0); } static void set_of(int of) { cpu_state.flags = (cpu_state.flags & ~0x800) | (of ? 0x800 : 0); } static int top_bit(uint16_t w, int bits) { return (w & (1 << (bits - 1))); } static void set_of_add(int bits) { set_of(top_bit((cpu_data ^ cpu_src) & (cpu_data ^ cpu_dest), bits)); } static void set_of_sub(int bits) { set_of(top_bit((cpu_dest ^ cpu_src) & (cpu_data ^ cpu_dest), bits)); } static void set_af(int af) { cpu_state.flags = (cpu_state.flags & ~0x10) | (af ? 0x10 : 0); } static void do_af(void) { set_af(((cpu_data ^ cpu_src ^ cpu_dest) & 0x10) != 0); } static void set_sf(int bits) { cpu_state.flags = (cpu_state.flags & ~0x80) | (top_bit(cpu_data, bits) ? 0x80 : 0); } static void set_pf(void) { cpu_state.flags = (cpu_state.flags & ~4) | (!__builtin_parity(cpu_data & 0xFF) << 2); } static void set_of_rotate(int bits) { set_of(top_bit(cpu_data ^ cpu_dest, bits)); } static void set_zf_ex(int zf) { cpu_state.flags = (cpu_state.flags & ~0x40) | (zf ? 0x40 : 0); } static void set_zf(int bits) { int size_mask = (1 << bits) - 1; set_zf_ex((cpu_data & size_mask) == 0); } static void set_pzs(int bits) { set_pf(); set_zf(bits); set_sf(bits); } static void set_apzs(int bits) { set_pzs(bits); do_af(); } static void add(int bits) { int size_mask = (1 << bits) - 1; cpu_data = cpu_dest + cpu_src; set_apzs(bits); set_of_add(bits); /* Anything - FF with carry on is basically anything + 0x100: value stays unchanged but carry goes on. */ if ((cpu_alu_op == 2) && !(cpu_src & size_mask) && (cpu_state.flags & C_FLAG)) cpu_state.flags |= C_FLAG; else set_cf((cpu_src & size_mask) > (cpu_data & size_mask)); } static void sub(int bits) { int size_mask = (1 << bits) - 1; cpu_data = cpu_dest - cpu_src; set_apzs(bits); set_of_sub(bits); /* Anything - FF with carry on is basically anything - 0x100: value stays unchanged but carry goes on. */ if ((cpu_alu_op == 3) && !(cpu_src & size_mask) && (cpu_state.flags & C_FLAG)) cpu_state.flags |= C_FLAG; else set_cf((cpu_src & size_mask) > (cpu_dest & size_mask)); } static void bitwise(int bits, uint16_t data) { cpu_data = data; cpu_state.flags &= ~(C_FLAG | A_FLAG | V_FLAG); set_pzs(bits); } static void test(int bits, uint16_t dest, uint16_t src) { cpu_dest = dest; cpu_src = src; bitwise(bits, (cpu_dest & cpu_src)); } static void alu_op(int bits) { switch (cpu_alu_op) { case 1: bitwise(bits, (cpu_dest | cpu_src)); break; case 2: if (cpu_state.flags & C_FLAG) cpu_src++; fallthrough; case 0: add(bits); break; case 3: if (cpu_state.flags & C_FLAG) cpu_src++; fallthrough; case 5: case 7: sub(bits); break; case 4: test(bits, cpu_dest, cpu_src); break; case 6: bitwise(bits, (cpu_dest ^ cpu_src)); break; default: break; } } static void mul(uint16_t a, uint16_t b) { int negate = 0; int bit_count = 8; int carry; uint16_t high_bit = 0x80; uint16_t size_mask; uint16_t c; uint16_t r; size_mask = (1 << bit_count) - 1; if (opcode != 0xd5) { if (opcode & 1) { bit_count = 16; high_bit = 0x8000; } else do_cycles(8); size_mask = (1 << bit_count) - 1; if ((rmdat & 0x38) == 0x28) { if (!top_bit(a, bit_count)) { if (top_bit(b, bit_count)) { do_cycle(); if ((b & size_mask) != ((opcode & 1) ? 0x8000 : 0x80)) do_cycle(); b = ~b + 1; negate = 1; } } else { do_cycle(); a = ~a + 1; negate = 1; if (top_bit(b, bit_count)) { b = ~b + 1; negate = 0; } else do_cycles(4); } do_cycles(10); } do_cycles(3); } c = 0; a &= size_mask; carry = (a & 1) != 0; a >>= 1; for (int i = 0; i < bit_count; ++i) { do_cycles(7); if (carry) { cpu_src = c; cpu_dest = b; add(bit_count); c = cpu_data & size_mask; do_cycles(1); carry = !!(cpu_state.flags & C_FLAG); } r = (c >> 1) + (carry ? high_bit : 0); carry = (c & 1) != 0; c = r; r = (a >> 1) + (carry ? high_bit : 0); carry = (a & 1) != 0; a = r; } if (negate) { c = ~c; a = (~a + 1) & size_mask; if (a == 0) ++c; do_cycles(9); } cpu_data = a; cpu_dest = c; set_sf(bit_count); set_pf(); set_af(0); } static void set_co_mul(UNUSED(int bits), int carry) { set_cf(carry); set_of(carry); set_zf_ex(!carry); if (!carry) do_cycle(); } /* Pushes a word to the stack. */ static void push(uint16_t *val) { if ((is186 && !is_nec) && (SP == 1)) { writememw(ss - 1, 0, *val); SP = cpu_state.eaaddr = 0xFFFF; return; } SP -= 2; cpu_state.eaaddr = (SP & 0xffff); writememw(ss, cpu_state.eaaddr, *val); } /* Pops a word from the stack. */ static uint16_t pop(void) { cpu_state.eaaddr = (SP & 0xffff); SP += 2; return readmemw(ss, cpu_state.eaaddr); } static void nearcall(uint16_t new_ip) { uint16_t ret_ip = cpu_state.pc & 0xffff; do_cycle_i(); set_ip(new_ip); biu_queue_flush(); do_cycles_i(3); push(&ret_ip); } static void farcall2(uint16_t new_cs, uint16_t new_ip) { do_cycles_i(3); push(&CS); load_cs(new_cs); do_cycles_i(2); nearcall(new_ip); } /* The INTR microcode routine. */ static void intr_routine(uint16_t intr, int skip_first) { uint16_t vector = intr * 4; uint16_t tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); uint16_t new_cs; uint16_t new_ip; if (!(cpu_state.flags & MD_FLAG) && is_nec) { sync_from_i8080(); x808x_log("CALLN/INT#/NMI#\n"); } if (!skip_first) do_cycle_i(); do_cycles_i(2); cpu_state.eaaddr = vector & 0xffff; new_ip = readmemw(0, cpu_state.eaaddr); do_cycle_i(); cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff; new_cs = readmemw(0, cpu_state.eaaddr); biu_suspend_fetch(); do_cycles_i(2); push(&tempf); cpu_state.flags &= ~(I_FLAG | T_FLAG); if (is_nec) cpu_state.flags |= MD_FLAG; do_cycle_i(); farcall2(new_cs, new_ip); } /* Was div(), renamed to avoid conflicts with stdlib div(). */ static int x86_div(uint16_t l, uint16_t h) { int bit_count = 8; int negative = 0; int dividend_negative = 0; int size_mask; int carry; uint16_t r; if (opcode & 1) { l = AX; h = DX; bit_count = 16; } size_mask = (1 << bit_count) - 1; if (opcode != 0xd4) { if ((rmdat & 0x38) == 0x38) { if (top_bit(h, bit_count)) { h = ~h; l = (~l + 1) & size_mask; if (l == 0) ++h; h &= size_mask; negative = 1; dividend_negative = 1; do_cycles(4); } if (top_bit(cpu_src, bit_count)) { cpu_src = ~cpu_src + 1; negative = !negative; } else do_cycle(); do_cycles(9); } do_cycles(3); } do_cycles(8); cpu_src &= size_mask; if (h >= cpu_src) { if (opcode != 0xd4) do_cycle(); intr_routine(0, 0); return 0; } if (opcode != 0xd4) do_cycle(); do_cycles(2); carry = 1; for (int b = 0; b < bit_count; ++b) { r = (l << 1) + (carry ? 1 : 0); carry = top_bit(l, bit_count); l = r; r = (h << 1) + (carry ? 1 : 0); carry = top_bit(h, bit_count); h = r; do_cycles(8); if (carry) { carry = 0; h -= cpu_src; if (b == bit_count - 1) do_cycles(2); } else { carry = cpu_src > h; if (!carry) { h -= cpu_src; do_cycle(); if (b == bit_count - 1) do_cycles(2); } } } l = ~((l << 1) + (carry ? 1 : 0)); if (opcode != 0xd4 && (rmdat & 0x38) == 0x38) { do_cycles(4); if (top_bit(l, bit_count)) { if (cpu_mod == 3) do_cycle(); intr_routine(0, 0); return 0; } do_cycles(7); if (negative) l = ~l + 1; if (dividend_negative) h = ~h + 1; } if (opcode == 0xd4) { AL = h & 0xff; AH = l & 0xff; } else { AH = h & 0xff; AL = l & 0xff; if (opcode & 1) { DX = h; AX = l; } } return 1; } static uint16_t string_increment(int bits) { int d = bits >> 3; if (cpu_state.flags & D_FLAG) cpu_state.eaaddr -= d; else cpu_state.eaaddr += d; cpu_state.eaaddr &= 0xffff; return cpu_state.eaaddr; } static void lods(int bits) { cpu_state.eaaddr = SI; if (bits == 16) cpu_data = readmemw((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); else cpu_data = readmemb((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); SI = string_increment(bits); } static void lods_di(int bits) { cpu_state.eaaddr = DI; if (bits == 16) cpu_data = readmemw(es, cpu_state.eaaddr); else cpu_data = readmemb(es, cpu_state.eaaddr); DI = string_increment(bits); } static void stos(int bits) { cpu_state.eaaddr = DI; if (bits == 16) writememw(es, cpu_state.eaaddr, cpu_data); else writememb(es, cpu_state.eaaddr, (uint8_t) (cpu_data & 0xff)); DI = string_increment(bits); } static uint16_t get_ea(void) { if (opcode & 1) return geteaw(); else return (uint16_t) geteab(); } static uint16_t get_reg(uint8_t reg) { if (opcode & 1) return cpu_state.regs[reg].w; else return (uint16_t) getr8(reg); } static void set_ea(uint16_t val) { if (opcode & 1) seteaw(val); else seteab((uint8_t) (val & 0xff)); } static void set_reg(uint8_t reg, uint16_t val) { if (opcode & 1) cpu_state.regs[reg].w = val; else setr8(reg, (uint8_t) (val & 0xff)); } static void cpu_data_opff_rm(void) { if (!(opcode & 1)) { if (cpu_mod != 3) cpu_data |= 0xff00; else cpu_data = cpu_state.regs[cpu_rm].w; } } static void farcall(uint16_t new_cs, uint16_t new_ip, int jump) { if (jump) do_cycle_i(); biu_suspend_fetch(); do_cycles_i(2); push(&CS); load_cs(new_cs); do_cycles_i(2); nearcall(new_ip); } /* Calls an interrupt. */ static void sw_int(uint16_t intr) { uint16_t vector = intr * 4; uint16_t tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); uint16_t new_cs; uint16_t new_ip; uint16_t old_ip; if (!(cpu_state.flags & MD_FLAG) && is_nec) { sync_from_i8080(); x808x_log("CALLN/INT#/NMI#\n"); } do_cycles_i(3); cpu_state.eaaddr = vector & 0xffff; new_ip = readmemw(0, cpu_state.eaaddr); do_cycle_i(); cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff; new_cs = readmemw(0, cpu_state.eaaddr); biu_suspend_fetch(); do_cycles_i(2); push(&tempf); cpu_state.flags &= ~(I_FLAG | T_FLAG); if (is_nec) cpu_state.flags |= MD_FLAG; /* FARCALL2 */ do_cycles_i(4); push(&CS); load_cs(new_cs); do_cycle_i(); /* NEARCALL */ old_ip = cpu_state.pc & 0xffff; do_cycles_i(2); set_ip(new_ip); biu_queue_flush(); do_cycles_i(3); push(&old_ip); } static void int3(void) { do_cycles_i(4); intr_routine(3, 0); } /* Ditto, but for breaking into emulation mode. */ static void interrupt_brkem(uint16_t addr) { uint16_t tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); uint16_t new_cs; uint16_t new_ip; uint16_t old_ip; do_cycles_i(3); cpu_state.eaaddr = addr << 2; new_ip = readmemw(0, cpu_state.eaaddr); do_cycle_i(); cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff; new_cs = readmemw(0, cpu_state.eaaddr); biu_suspend_fetch(); do_cycles_i(2); push(&tempf); cpu_state.flags &= ~(MD_FLAG); cpu_md_write_disable = 0; /* FARCALL2 */ do_cycles_i(4); push(&CS); load_cs(new_cs); do_cycle_i(); /* NEARCALL */ old_ip = cpu_state.pc & 0xffff; do_cycles_i(2); set_ip(new_ip); biu_queue_flush(); do_cycles_i(3); push(&old_ip); sync_to_i8080(); x808x_log("BRKEM mode\n"); } void retem_i8080(void) { sync_from_i8080(); do_cycle_i(); farret(1); /* pop_flags() */ cpu_state.flags = pop(); do_cycle_i(); noint = 1; nmi_enable = 1; emulated_processor.iff = !!(cpu_state.flags & I_FLAG); cpu_md_write_disable = 1; retem = 1; x808x_log("RETEM mode\n"); } void interrupt_808x(uint16_t addr) { biu_suspend_fetch(); do_cycles_i(2); intr_routine(addr, 0); } static void custom_nmi(void) { uint16_t tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); uint16_t new_cs; uint16_t new_ip; if (!(cpu_state.flags & MD_FLAG) && is_nec) { sync_from_i8080(); x808x_log("CALLN/INT#/NMI#\n"); } do_cycle_i(); do_cycles_i(2); cpu_state.eaaddr = 0x0002; (void) readmemw(0, cpu_state.eaaddr); new_ip = custom_nmi_vector & 0xffff; do_cycle_i(); cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff; (void) readmemw(0, cpu_state.eaaddr); new_cs = custom_nmi_vector >> 16; biu_suspend_fetch(); do_cycles_i(2); push(&tempf); cpu_state.flags &= ~(I_FLAG | T_FLAG); if (is_nec) cpu_state.flags |= MD_FLAG; do_cycle_i(); farcall2(new_cs, new_ip); } static int irq_pending(void) { uint8_t temp; temp = (nmi && nmi_enable && nmi_mask) || ((cpu_state.flags & T_FLAG) && !noint) || ((in_hlt || (cpu_state.flags & I_FLAG)) && pic.int_pending && !noint); return temp; } static int bus_pic_ack(void) { int old_in_lock = in_lock; in_lock = 1; bus_request_type = BUS_PIC; biu_begin_eu(); biu_wait_for_read_finish(); in_lock = old_in_lock; return pic_data; } static void hw_int(uint16_t vector) { biu_suspend_fetch(); do_cycles_i(2); intr_routine(vector, 0); } static void int1(void) { do_cycles_i(2); intr_routine(1, 1); } static void int2(void) { do_cycles_i(2); intr_routine(2, 1); } static void check_interrupts(void) { int temp; if (irq_pending()) { if ((cpu_state.flags & T_FLAG) && !noint) { int1(); return; } if (nmi && nmi_enable && nmi_mask) { nmi_enable = 0; if (use_custom_nmi_vector) { do_cycles(2); custom_nmi(); } else int2(); return; } if ((in_hlt || (cpu_state.flags & I_FLAG)) && pic.int_pending && !noint) { repeating = 0; completed = 1; ovr_seg = NULL; do_cycles(4); /* ACK to PIC */ biu_begin_eu(); temp = bus_pic_ack(); do_cycle(); /* ACK to PIC */ temp = bus_pic_ack(); in_lock = 0; clear_lock = 0; /* Here is where temp should be filled, but we cheat. */ opcode = 0x00; hw_int(temp); } } } /* The FARRET microcode routine. */ static void farret(int far) { uint8_t far2 = !!(opcode & 0x08); do_cycle_i(); set_ip(pop()); biu_suspend_fetch(); do_cycles_i(2); if ((!!far) != far2) fatal("Far call distance mismatch (%i = %i)\n", !!far, far2); if (far) { do_cycle_i(); load_cs(pop()); biu_queue_flush(); do_cycles_i(2); } else { biu_queue_flush(); do_cycles_i(2); } do_cycles_i(2); } static void iret_routine(void) { do_cycle_i(); farret(1); /* pop_flags() */ if (is_nec && cpu_md_write_disable) cpu_state.flags = pop() | 0x8002; else cpu_state.flags = pop() | 0x0002; do_cycle_i(); noint = 1; nmi_enable = 1; } static void rep_end(void) { repeating = 0; in_rep = 0; completed = 1; } static int rep_start(void) { if (!repeating) { if (in_rep != 0) { if (CX == 0) { do_cycles_i(is_nec ? 1 : 4); rep_end(); return 0; } else do_cycles_i(is_nec ? 1 : 7); } } completed = 1; return 1; } static void rep_interrupt(void) { biu_suspend_fetch(); do_cycles_i(4); biu_queue_flush(); if (is_nec && (ovr_seg != NULL)) set_ip((cpu_state.pc - 3) & 0xffff); else set_ip((cpu_state.pc - 2) & 0xffff); rep_end(); } static void sign_extend_al(void) { if ((AL & 0x80) != 0) AH = 0xff; else AH = 0x00; } static void sign_extend_ax(void) { do_cycles(3); if ((AX & 0x8000) == 0) DX = 0x0000; else { do_cycle(); DX = 0xffff; } } static void reljmp(uint16_t new_ip, int jump) { if (!is_nec && jump) do_cycle_i(); biu_suspend_fetch(); if (!is_nec) do_cycles_i(3); set_ip(new_ip); biu_queue_flush(); do_cycle_i(); } static void daa(void) { uint16_t old_cf = cpu_state.flags & C_FLAG; uint16_t old_af = cpu_state.flags & A_FLAG; uint8_t old_al = AL; uint8_t al_check; cpu_state.flags &= ~C_FLAG; al_check = (old_af ? 0x9f : 0x99); cpu_state.flags &= ~V_FLAG; if (old_cf) { if ((AL >= 0x1a) && (AL <= 0x7f)) cpu_state.flags |= V_FLAG; } else if ((AL >= 0x7a) && (AL <= 0x7f)) cpu_state.flags |= V_FLAG; if (((AL & 0x0f) > 9) || (cpu_state.flags & A_FLAG)) { AL += 6; cpu_state.flags |= A_FLAG; } else cpu_state.flags &= ~A_FLAG; if ((old_al > al_check) || old_cf) { AL += 0x60; cpu_state.flags |= C_FLAG; } else cpu_state.flags &= ~C_FLAG; set_pzs(8); } static void das(void) { uint8_t old_al = AL; uint16_t old_af = cpu_state.flags & A_FLAG; uint16_t old_cf = cpu_state.flags & C_FLAG; uint8_t al_check = (old_af ? 0x9f : 0x99); cpu_state.flags &= ~V_FLAG; if (!old_af && !old_cf) { if ((AL >= 0x9a) && (AL <= 0xdf)) cpu_state.flags |= V_FLAG; } else if (old_af && !old_cf) { if (((AL >= 0x80) && (AL <= 0x85)) || ((AL >= 0xa0) && (AL <= 0xe5))) cpu_state.flags |= V_FLAG; } else if (!old_af && old_cf) { if ((AL >= 0x80) && (AL <= 0xdf)) cpu_state.flags |= V_FLAG; } else if (old_af && old_cf) { if ((AL >= 0x80) && (AL <= 0xe5)) cpu_state.flags |= V_FLAG; } cpu_state.flags &= ~C_FLAG; if (((AL & 0x0f) > 9) || (cpu_state.flags & A_FLAG)) { AL -= 6; cpu_state.flags |= A_FLAG; } else cpu_state.flags &= ~A_FLAG; if ((old_al > al_check) || old_cf) { AL -= 0x60; cpu_state.flags |= C_FLAG; } else cpu_state.flags &= ~C_FLAG; set_pzs(8); } static void aaa(void) { uint8_t old_al = AL; uint8_t new_al; if (((AL & 0x0f) > 9) || (cpu_state.flags & A_FLAG)) { AH += 1; new_al = AL + 6; AL = new_al & 0x0f; cpu_state.flags |= A_FLAG; cpu_state.flags |= C_FLAG; } else { new_al = AL; AL &= 0x0f; cpu_state.flags &= ~A_FLAG; cpu_state.flags &= ~C_FLAG; do_cycle_i(); } cpu_state.flags &= ~(V_FLAG | Z_FLAG | N_FLAG); if (new_al == 0x00) cpu_state.flags |= Z_FLAG; if ((old_al >= 0x7a) && (old_al <= 0x7f)) cpu_state.flags |= V_FLAG; if ((old_al >= 0x7a) && (old_al <= 0xf9)) cpu_state.flags |= N_FLAG; cpu_data = new_al; set_pf(); } static void aas(void) { uint8_t old_al = AL; uint16_t old_af = cpu_state.flags & A_FLAG; uint8_t new_al; do_cycles_i(6); if (((AL & 0x0f) > 9) || old_af) { new_al = AL - 6; AH++; AL = new_al & 0x0f; cpu_state.flags |= (A_FLAG | C_FLAG); } else { new_al = AL; AL &= 0x0f; cpu_state.flags &= ~(C_FLAG | A_FLAG); do_cycle_i(); } cpu_state.flags &= ~(V_FLAG | Z_FLAG | N_FLAG); if (new_al == 0x00) cpu_state.flags |= Z_FLAG; if (old_af && (old_al >= 0x80) && (old_al <= 0x85)) cpu_state.flags |= V_FLAG; if (!old_af && (old_al >= 0x80)) cpu_state.flags |= N_FLAG; if (old_af && ((old_al <= 0x05) || (old_al >= 0x86))) cpu_state.flags |= N_FLAG; cpu_data = new_al; set_pf(); } static void finalize(void) { in_0f = 0; repeating = 0; ovr_seg = NULL; in_rep = 0; rep_c_flag = 0; if (in_lock) clear_lock = 1; cpu_alu_op = 0; if (pfq_pos == 0) { do { if (nx) nx = 0; do_cycle(); } while (pfq_pos == 0); biu_preload_byte = biu_pfq_read(); biu_queue_preload = 1; do_cycle(); } else { biu_queue_preload = 1; biu_preload_byte = biu_pfq_read(); biu_resume_on_queue_read(); do_cycle(); } if (irq_pending()) cpu_state.pc--; } /* Fetches the effective address from the prefetch queue according to MOD and R/M. */ static void do_mod_rm(void) { rmdat = biu_pfq_fetchb(); cpu_reg = (rmdat >> 3) & 7; cpu_mod = (rmdat >> 6) & 3; cpu_rm = rmdat & 7; if (cpu_mod != 3) { do_cycle(); if (is_nec) do_cycle(); else if (modrm_cycs_pre[rmdat & 0xc7]) do_cycles(modrm_cycs_pre[rmdat & 0xc7]); if ((rmdat & 0xc7) == 0x06) { cpu_state.eaaddr = biu_pfq_fetchw(); easeg = ovr_seg ? *ovr_seg : ds; } else { cpu_state.eaaddr = (*mod1add[0][cpu_rm]) + (*mod1add[1][cpu_rm]); easeg = ovr_seg ? *ovr_seg : *mod1seg[cpu_rm]; switch (rmdat & 0xc0) { default: break; case 0x40: cpu_state.eaaddr += sign_extend(biu_pfq_fetchb()); break; case 0x80: cpu_state.eaaddr += biu_pfq_fetchw(); break; } cpu_state.eaaddr &= 0xffff; } if (!is_nec && modrm_cycs_post[rmdat & 0xc7]) do_cycles(modrm_cycs_post[rmdat & 0xc7]); } } static void decode(void) { uint8_t op_f; uint8_t prefix = 0; if (halted) opcode = 0xf4; else opcode = biu_pfq_fetchb_common(); modrm_loaded = 0; while (1) { prefix = 0; switch (opcode) { case 0x0f: /* NEC/186 */ if (is_nec) { in_0f = 1; prefix = 1; } break; case 0x26: /* ES: */ case 0x2e: /* CS: */ case 0x36: /* SS: */ case 0x3e: /* DS: */ ovr_seg = opseg[(opcode >> 3) & 0x03]; prefix = 1; break; case 0x64: /* REPNC */ case 0x65: /* REPC */ if (is_nec) { in_rep = (opcode == 0x64 ? 1 : 2); rep_c_flag = 1; prefix = 1; } break; case 0xf0: case 0xf1: /* LOCK - F1 is alias */ in_lock = 1; prefix = 1; break; case 0xf2: /* REPNE */ case 0xf3: /* REPE */ in_rep = (opcode == 0xf2 ? 1 : 2); rep_c_flag = 0; prefix = 1; break; default: break; } if (prefix == 0) break; do_cycle(); opcode = biu_pfq_fetchb_common(); } if (is_nec) { if (in_0f) op_f = (uint8_t) opf_0f[opcode]; else op_f = (uint8_t) opf_nec[opcode]; } else op_f = (uint8_t) opf[opcode]; if (op_f & OP_GRP) { do_mod_rm(); modrm_loaded = 1; op_f |= (OP_MRM | OP_EA); if (opcode >= 0xf0) { op_f |= OP_DELAY; group_delay = 1; } } if (!modrm_loaded && (op_f & OP_MRM)) { do_mod_rm(); modrm_loaded = 1; } if (modrm_loaded && !(op_f & OP_EA)) { if (is_nec) do_cycle(); else { if (opcode == 0x8f) { if (cpu_mod == 3) do_cycles_i(2); } else do_cycles_i(2); } } } static void string_op(int bits) { uint16_t tmpa; uint16_t old_ax; if ((opcode & 0xf0) == 0x60) switch (opcode & 0x0e) { case 0x0c: old_ax = AX; cpu_data = DX; cpu_state.eaaddr = cpu_data; cpu_io_vx0(bits, 0, cpu_state.eaaddr); cpu_data = AX; stos(bits); AX = old_ax; break; case 0x0e: old_ax = AX; lods(bits); set_accum(bits, cpu_data); cpu_data = DX; do_cycle_i(); /* biu_io_write_u16() */ cpu_state.eaaddr = cpu_data; cpu_io_vx0(bits, 1, cpu_state.eaaddr); AX = old_ax; break; } else switch (opcode & 0x0e) { case 0x04: lods(bits); do_cycle_i(); stos(bits); break; case 0x06: do_cycle_i(); if (is_nec) { if (in_rep) { lods_di(bits); tmpa = cpu_data; lods(bits); } else { lods(bits); tmpa = cpu_data; lods_di(bits); } } else { lods(bits); tmpa = cpu_data; do_cycles_i(2); lods_di(bits); do_cycles_i(3); } cpu_src = cpu_data; cpu_dest = tmpa; sub(bits); break; case 0x0e: tmpa = AX; do_cycles_i(2); lods_di(bits); do_cycles_i(3); cpu_src = cpu_data; cpu_dest = tmpa; sub(bits); break; case 0x0a: cpu_data = AX; stos(bits); break; case 0x0c: lods(bits); set_accum(bits, cpu_data); break; } } static int do_print = 1; static void execvx0_0f(void) { uint8_t bit; uint8_t odd; uint8_t nibbles_count; uint8_t destcmp; uint8_t destbyte; uint8_t srcbyte; uint8_t nibble_result; uint8_t temp_val; uint8_t temp_al; uint8_t bit_length; uint8_t bit_offset; int8_t nibble_result_s; int bits; uint32_t i; uint32_t carry; uint32_t nibble; uint32_t srcseg; uint32_t byteaddr; switch (opcode) { case 0x10: /* TEST1 r8/m8, CL*/ case 0x11: /* TEST1 r16/m16, CL*/ case 0x18: /* TEST1 r8/m8, imm3 */ case 0x19: /* TEST1 r16/m16, imm4 */ bits = 8 << (opcode & 0x1); do_cycles(2); bit = (opcode & 0x8) ? biu_pfq_fetchb() : CL; bit &= ((1 << (3 + (opcode & 0x1))) - 1); read_ea(0, bits); set_zf_ex(!(cpu_data & (1 << bit))); cpu_state.flags &= ~(V_FLAG | C_FLAG); break; case 0x12: /* CLR1 r8/m8, CL*/ case 0x13: /* CLR1 r16/m16, CL*/ case 0x1a: /* CLR1 r8/m8, imm3 */ case 0x1b: /* CLR1 r16/m16, imm4 */ bits = 8 << (opcode & 0x1); do_cycles(2); bit = (opcode & 0x8) ? biu_pfq_fetchb() : CL; bit &= ((1 << (3 + (opcode & 0x1))) - 1); read_ea(0, bits); if (bits == 8) seteab((cpu_data & 0xff) & ~(1 << bit)); else seteaw((cpu_data & 0xffff) & ~(1 << bit)); break; case 0x14: /* SET1 r8/m8, CL*/ case 0x15: /* SET1 r16/m16, CL*/ case 0x1c: /* SET1 r8/m8, imm3 */ case 0x1d: /* SET1 r16/m16, imm4 */ bits = 8 << (opcode & 0x1); do_cycles(2); bit = (opcode & 0x8) ? biu_pfq_fetchb() : CL; bit &= ((1 << (3 + (opcode & 0x1))) - 1); read_ea(0, bits); if (bits == 8) seteab((cpu_data & 0xff) | (1 << bit)); else seteaw((cpu_data & 0xffff) | (1 << bit)); break; case 0x16: /* NOT1 r8/m8, CL*/ case 0x17: /* NOT1 r16/m16, CL*/ case 0x1e: /* NOT1 r8/m8, imm3 */ case 0x1f: /* NOT1 r16/m16, imm4 */ bits = 8 << (opcode & 0x1); do_cycles(2); bit = (opcode & 0x8) ? (biu_pfq_fetchb()) : (CL); bit &= ((1 << (3 + (opcode & 0x1))) - 1); read_ea(0, bits); if (bits == 8) seteab((cpu_data & 0xff) ^ (1 << bit)); else seteaw((cpu_data & 0xffff) ^ (1 << bit)); break; case 0x20: /* ADD4S */ odd = !!(CL % 2); zero = 1; nibbles_count = CL - odd; i = 0; carry = 0; nibble = 0; srcseg = ovr_seg ? *ovr_seg : ds; do_cycles(4); for (i = 0; i < ((nibbles_count / 2) + odd); i++) { do_cycles(19); destcmp = read_mem_b((es) + DI + i); for (nibble = 0; nibble < 2; nibble++) { destbyte = destcmp >> (nibble ? 4 : 0); srcbyte = read_mem_b(srcseg + SI + i) >> (nibble ? 4 : 0); destbyte &= 0xF; srcbyte &= 0xF; nibble_result = (i == (nibbles_count / 2) && nibble == 1) ? (destbyte + carry) : ((uint8_t) (destbyte)) + ((uint8_t) (srcbyte)) + ((uint32_t) carry); carry = 0; while (nibble_result >= 10) { nibble_result -= 10; carry++; } if (zero != 0 || (i == (nibbles_count / 2) && nibble == 1)) zero = (nibble_result == 0); destcmp = ((destcmp & (nibble ? 0x0F : 0xF0)) | (nibble_result << (4 * nibble))); } write_mem_b(es + DI + i, destcmp); } set_cf(!!carry); set_zf(!!zero); break; case 0x22: /* SUB4S */ odd = !!(CL % 2); zero = 1; nibbles_count = CL - odd; i = 0; carry = 0; nibble = 0; srcseg = ovr_seg ? *ovr_seg : ds; do_cycles(4); for (i = 0; i < ((nibbles_count / 2) + odd); i++) { do_cycles(19); destcmp = read_mem_b((es) + DI + i); for (nibble = 0; nibble < 2; nibble++) { destbyte = destcmp >> (nibble ? 4 : 0); srcbyte = read_mem_b(srcseg + SI + i) >> (nibble ? 4 : 0); destbyte &= 0xF; srcbyte &= 0xF; nibble_result_s = (i == (nibbles_count / 2) && nibble == 1) ? ((int8_t) destbyte - (int8_t) carry) : ((int8_t) (destbyte)) - ((int8_t) (srcbyte)) - ((int8_t) carry); carry = 0; while (nibble_result_s < 0) { nibble_result_s += 10; carry++; } if (zero != 0 || (i == (nibbles_count / 2) && nibble == 1)) zero = (nibble_result_s == 0); destcmp = ((destcmp & (nibble ? 0x0F : 0xF0)) | (nibble_result_s << (4 * nibble))); } write_mem_b(es + DI + i, destcmp); } set_cf(!!carry); set_zf(!!zero); break; case 0x26: /* CMP4S */ odd = !!(CL % 2); zero = 1; nibbles_count = CL - odd; i = 0; carry = 0; nibble = 0; srcseg = ovr_seg ? *ovr_seg : ds; do_cycles(4); for (i = 0; i < ((nibbles_count / 2) + odd); i++) { do_cycles(19); destcmp = read_mem_b((es) + DI + i); for (nibble = 0; nibble < 2; nibble++) { destbyte = destcmp >> (nibble ? 4 : 0); srcbyte = read_mem_b(srcseg + SI + i) >> (nibble ? 4 : 0); destbyte &= 0xF; srcbyte &= 0xF; nibble_result_s = ((int8_t) (destbyte)) - ((int8_t) (srcbyte)) - ((int8_t) carry); carry = 0; while (nibble_result_s < 0) { nibble_result_s += 10; carry++; } if (zero != 0 || (i == (nibbles_count / 2) && nibble == 1)) zero = (nibble_result_s == 0); destcmp = ((destcmp & (nibble ? 0x0F : 0xF0)) | (nibble_result_s << (4 * nibble))); } } set_cf(!!carry); set_zf(!!zero); break; case 0x28: /* ROL4 r/m */ do_cycles(20); temp_val = geteab(); temp_al = AL; temp_al &= 0x0f; temp_al |= (temp_val & 0xf0); temp_val = (temp_al & 0x0f) | ((temp_val & 0x0f) << 4); temp_al >>= 4; temp_al &= 0x0f; seteab(temp_val); AL = temp_al; break; case 0x2a: /* ROR4 r/m */ do_cycles(20); temp_val = geteab(); temp_al = AL; AL = temp_val & 0x0f; temp_val = (temp_val >> 4) | ((temp_al & 0x0f) << 4); seteab(temp_val); break; case 0x31: /* INS reg1, reg2 */ case 0x39: /* INS reg8, imm4 */ bit_length = ((opcode & 0x8) ? (biu_pfq_fetchb() & 0x0f) : (getr8(cpu_reg) & 0x0f)) + 1; bit_offset = getr8(cpu_rm) & 0x0f; byteaddr = (es) + DI; i = 0; if (bit_offset >= 8) { DI++; byteaddr++; bit_offset -= 8; } for (i = 0; i < bit_length; i++) { byteaddr = (es) + DI; writememb(es, DI, (read_mem_b(byteaddr) & ~(1 << (bit_offset))) | ((!!(AX & (1 << i))) << bit_offset)); bit_offset++; if (bit_offset == 8) { DI++; bit_offset = 0; } } setr8(cpu_rm, bit_offset); break; case 0x33: /* EXT reg1, reg2 */ case 0x3b: /* EXT reg8, imm4 */ bit_length = ((opcode & 0x8) ? (biu_pfq_fetchb() & 0x0f) : (getr8(cpu_reg) & 0x0f)) + 1; bit_offset = getr8(cpu_rm) & 0x0f; byteaddr = (ds) + SI; i = 0; if (bit_offset >= 8) { SI++; byteaddr++; bit_offset -= 8; } AX = 0; for (i = 0; i < bit_length; i++) { byteaddr = (ds) + SI; AX |= (!!(readmemb(ds, SI) & (1 << bit_offset))) << i; bit_offset++; if (bit_offset == 8) { SI++; bit_offset = 0; } } setr8(cpu_rm, bit_offset); break; case 0xff: /* BRKEM */ interrupt_brkem(biu_pfq_fetchb()); break; default: do_cycles_nx_i(2); /* Guess, based on NOP. */ break; } } static void execvx0_6x(uint16_t *jump) { uint16_t lowbound; uint16_t highbound; uint16_t regval; uint16_t wordtopush; uint16_t immediate; uint16_t tempw; int bits; int32_t templ; switch (opcode) { case 0x60: /* PUSHA/PUSH R */ writememw(ss, ((SP - 2) & 0xffff), AX); biu_state_set_eu(); writememw(ss, ((SP - 4) & 0xffff), CX); biu_state_set_eu(); writememw(ss, ((SP - 6) & 0xffff), DX); biu_state_set_eu(); writememw(ss, ((SP - 8) & 0xffff), BX); biu_state_set_eu(); writememw(ss, ((SP - 10) & 0xffff), SP); biu_state_set_eu(); writememw(ss, ((SP - 12) & 0xffff), BP); biu_state_set_eu(); writememw(ss, ((SP - 14) & 0xffff), SI); biu_state_set_eu(); writememw(ss, ((SP - 16) & 0xffff), DI); SP -= 16; break; case 0x61: /* POPA/POP R */ DI = readmemw(ss, ((SP) & 0xffff)); biu_state_set_eu(); SI = readmemw(ss, ((SP + 2) & 0xffff)); biu_state_set_eu(); BP = readmemw(ss, ((SP + 4) & 0xffff)); biu_state_set_eu(); BX = readmemw(ss, ((SP + 8) & 0xffff)); biu_state_set_eu(); DX = readmemw(ss, ((SP + 10) & 0xffff)); biu_state_set_eu(); CX = readmemw(ss, ((SP + 12) & 0xffff)); biu_state_set_eu(); AX = readmemw(ss, ((SP + 14) & 0xffff)); SP += 16; break; case 0x62: /* BOUND r/m */ lowbound = 0; highbound = 0; regval = 0; lowbound = readmemw(easeg, cpu_state.eaaddr); highbound = readmemw(easeg, cpu_state.eaaddr + 2); regval = get_reg(cpu_reg); if ((lowbound > regval) || (highbound < regval)) { sw_int(5); *jump = 1; } break; case 0x63: if (is_nec) { /* read_operand16() */ if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ tempw = cpu_state.pc; geteaw(); do_cycles(60); } break; case 0x64: case 0x65: if (!is_nec) { do_cycles_nx_i(2); /* Guess, based on NOP. */ } break; case 0x66 ... 0x67: /* FPO2 - NEC FPU instructions. */ /* read_operand16() */ if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ tempw = cpu_state.pc; geteaw(); /* fpu_op() */ cpu_state.pc = tempw; /* Do this as the x87 code advances it, which is needed on the 286+ core, but not here. */ break; case 0x68: wordtopush = biu_pfq_fetchw(); push(&wordtopush); break; case 0x69: bits = 16; read_ea(0, 16); immediate = biu_pfq_fetchw(); templ = ((int) cpu_data) * ((int) immediate); if ((templ >> 15) != 0 && (templ >> 15) != -1) cpu_state.flags |= C_FLAG | V_FLAG; else cpu_state.flags &= ~(C_FLAG | V_FLAG); set_reg(cpu_reg, templ & 0xffff); do_cycles((cpu_mod == 3) ? 20 : 26); break; case 0x6a: wordtopush = sign_extend(biu_pfq_fetchb()); push(&wordtopush); break; case 0x6b: /* IMUL reg16,reg16/mem16,imm8 */ read_ea(0, 16); immediate = biu_pfq_fetchb(); immediate = geteaw(); if (immediate & 0x80) immediate |= 0xff00; templ = ((int) cpu_data) * ((int) immediate); if ((templ >> 15) != 0 && (templ >> 15) != -1) cpu_state.flags |= C_FLAG | V_FLAG; else cpu_state.flags &= ~(C_FLAG | V_FLAG); set_reg(cpu_reg, templ & 0xffff); do_cycles((cpu_mod == 3) ? 24 : 30); break; case 0x6c: case 0x6d: /* INM dst, DW/INS dst, DX */ bits = 8 << (opcode & 1); if (rep_start()) { string_op(bits); do_cycle_i(); if (in_rep != 0) { completed = 0; repeating = 1; do_cycle_i(); if (irq_pending()) { do_cycle_i(); rep_interrupt(); } do_cycle_i(); /* decrement_register16() */ CX--; if (CX == 0) rep_end(); else do_cycle_i(); } else do_cycle_i(); } break; case 0x6e: case 0x6f: /* OUTM DW, src/OUTS DX, src */ bits = 8 << (opcode & 1); if (rep_start()) { string_op(bits); do_cycles_i(3); if (in_rep != 0) { completed = 0; repeating = 1; do_cycle_i(); /* decrement_register16() */ CX--; if (irq_pending()) { do_cycles_i(2); rep_interrupt(); } else { do_cycles_i(2); if (CX == 0) rep_end(); else do_cycle_i(); } } } break; default: do_cycles_nx_i(2); /* Guess, based on NOP. */ break; } } /* Executes a single instruction. */ void execute_instruction(void) { uint8_t temp = 0; uint8_t tempb; uint8_t nests; int8_t rel8; int8_t temps; uint16_t addr; uint16_t jump = 0; uint16_t new_cs; uint16_t new_ip; uint16_t tempw; uint16_t zero_cond; uint16_t old_cs; uint16_t old_ip; uint16_t old_flags; uint16_t prod16; uint16_t tempw_int; uint16_t size; uint16_t tempbp; uint16_t src16; int16_t rel16; int16_t temps16; uint32_t prod32; uint32_t templ; uint32_t templ2 = 0; int bits; int negate; int tempws = 0; int tempws2 = 0; completed = 1; if (nx) { do_cycle(); nx = 0; } else if (!modrm_loaded) do_cycle(); if (group_delay) { group_delay = 0; do_cycle(); nx = 0; } if (!is_nec && (opcode >= 0xc0) && (opcode <= 0xc1)) opcode |= 0x02; if (is_nec && !(cpu_state.flags & MD_FLAG)) { i8080_step(&emulated_processor); set_if(emulated_processor.iff); if (retem) jump = 1; retem = 0; do_cycles(emulated_processor.cyc); emulated_processor.cyc = 0; } else if (is_nec && in_0f) execvx0_0f(); else if (is_nec && ((opcode & 0xf0) == 0x60)) execvx0_6x(&jump); else switch (opcode) { case 0x00: /* ADD r/m8, r8; r8, r/m8; al, imm8 */ case 0x02: case 0x04: case 0x08: /* OR r/m8, r8; r8, r/m8; al, imm8 */ case 0x0a: case 0x0c: case 0x10: /* ADC r/m8, r8; r8, r/m8; al, imm8 */ case 0x12: case 0x14: case 0x18: /* SBB r/m8, r8; r8, r/m8; al, imm8 */ case 0x1a: case 0x1c: case 0x20: /* AND r/m8, r8; r8, r/m8; al, imm8 */ case 0x22: case 0x24: case 0x28: /* SUB r/m8, r8; r8, r/m8; al, imm8 */ case 0x2a: case 0x2c: case 0x30: /* XOR r/m8, r8; r8, r/m8; al, imm8 */ case 0x32: case 0x34: bits = 8; /* read_operand8() */ if (opcode & 0x04) { cpu_data = biu_pfq_fetch(); cpu_dest = get_accum(bits); /* AX/AL */ cpu_src = cpu_data; } else { if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ tempw = get_ea(); if (opcode & 2) { cpu_dest = get_reg(cpu_reg); cpu_src = tempw; } else { cpu_dest = tempw; cpu_src = get_reg(cpu_reg); } } do_cycles_nx_i(2); if (!(opcode & 0x06) && (cpu_mod != 3)) do_cycles_i(2); /* math_op8() */ math_op((opcode >> 3) & 7); /* write_operand8() */ if (opcode & 0x04) set_accum(bits, cpu_data); else { if (opcode & 2) set_reg(cpu_reg, cpu_data); else set_ea(cpu_data); } break; case 0x01: /* ADD r/m16, r16; r16, r/m16; ax, imm16 */ case 0x03: case 0x05: case 0x09: /* OR r/m16, r16; r16, r/m16; ax, imm16 */ case 0x0b: case 0x0d: case 0x11: /* ADC r/m16, r16; r16, r/m16; ax, imm16 */ case 0x13: case 0x15: case 0x19: /* SBB r/m16, r16; r16, r/m16; ax, imm16 */ case 0x1b: case 0x1d: case 0x21: /* AND r/m16, r16; r16, r/m16; ax, imm16 */ case 0x23: case 0x25: case 0x29: /* SUB r/m16, r16; r16, r/m16; ax, imm16 */ case 0x2b: case 0x2d: case 0x31: /* XOR r/m16, r16; r16, r/m16; ax, imm16 */ case 0x33: case 0x35: bits = 16; nx = 1; /* read_operand16() */ if (opcode & 0x04) { cpu_data = biu_pfq_fetch(); cpu_dest = get_accum(bits); /* AX/AL */ cpu_src = cpu_data; } else { if (cpu_mod != 3) do_cycles_i(is_nec ? 1 : 2); /* load_operand() */ tempw = get_ea(); if (opcode & 2) { cpu_dest = get_reg(cpu_reg); cpu_src = tempw; } else { cpu_dest = tempw; cpu_src = get_reg(cpu_reg); } } do_cycles_nx_i(2); if (!(opcode & 0x06) && (cpu_mod != 3)) do_cycles_i(2); /* math_op16() */ math_op((opcode >> 3) & 7); /* write_operand16() */ if (opcode & 0x04) set_accum(bits, cpu_data); else { if (opcode & 0x02) set_reg(cpu_reg, cpu_data); else set_ea(cpu_data); } break; case 0x06: case 0x0e: case 0x16: case 0x1e: /* PUSH seg */ do_cycles_i(3); push(&(_opseg[(opcode >> 3) & 0x03]->seg)); break; case 0x07: case 0x17: case 0x1f: /* POP seg */ load_seg(pop(), _opseg[(opcode >> 3) & 0x03]); /* All POP segment instructions suppress interrupts for one instruction. */ noint = 1; break; case 0x0f: if (!is_nec) { load_cs(pop()); /* All POP segment instructions suppress interrupts for one instruction. */ noint = 1; } break; case 0x26: /* ES: */ case 0x2e: /* CS: */ case 0x36: /* SS: */ case 0x3e: /* DS: */ break; case 0x27: /* DAA */ do_cycles_nx_i(3); daa(); break; case 0x2f: /* DAS */ do_cycles_nx_i(3); das(); break; case 0x37: /* AAA */ aaa(); break; case 0x38: /* CMP r/m8, r8; r8, r/m8; al, imm8 */ case 0x3a: case 0x3c: bits = 8; /* read_operand8() */ if (opcode & 0x04) { cpu_data = biu_pfq_fetch(); cpu_dest = get_accum(bits); /* AX/AL */ cpu_src = cpu_data; } else { if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ tempw = get_ea(); if (opcode & 2) { cpu_dest = get_reg(cpu_reg); cpu_src = tempw; } else { cpu_dest = tempw; cpu_src = get_reg(cpu_reg); } } do_cycles_nx_i(2); /* math_op8() */ math_op(7); break; case 0x39: /* CMP r/m16, r16; r16, r/m16; ax, imm16 */ case 0x3b: case 0x3d: bits = 16; /* read_operand16() */ if (opcode & 0x04) { cpu_data = biu_pfq_fetch(); cpu_dest = get_accum(bits); /* AX/AL */ cpu_src = cpu_data; } else { if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ tempw = get_ea(); if (opcode & 2) { cpu_dest = get_reg(cpu_reg); cpu_src = tempw; } else { cpu_dest = tempw; cpu_src = get_reg(cpu_reg); } } do_cycles_nx_i((opcode == 0x3d) ? 1 : 2); /* math_op16() */ math_op(7); break; case 0x3f: /*AAS*/ aas(); break; case 0x40 ... 0x47: /* INC r16 */ /* read_operand16() */ cpu_dest = cpu_state.regs[opcode & 7].w; cpu_src = 1; bits = 16; /* math_op16() */ cpu_data = cpu_dest + cpu_src; set_of_add(bits); do_af(); set_pzs(16); /* write_operand16() */ cpu_state.regs[opcode & 7].w = cpu_data; do_cycles_nx(1); break; case 0x48 ... 0x4f: /* DEC r16 */ /* read_operand16() */ cpu_dest = cpu_state.regs[opcode & 7].w; cpu_src = 1; bits = 16; /* math_op16() */ cpu_data = cpu_dest - cpu_src; set_of_sub(bits); do_af(); set_pzs(16); /* write_operand16() */ cpu_state.regs[opcode & 7].w = cpu_data; do_cycles_nx(1); break; case 0x50 ... 0x57: /* PUSH r16 */ do_cycles_i(3); push(&(cpu_state.regs[opcode & 0x07].w)); break; case 0x58 ... 0x5f: /* POP r16 */ cpu_state.regs[opcode & 0x07].w = pop(); do_cycle_nx_i(); break; case 0x60 ... 0x7f: /* JMP rel8 */ switch ((opcode >> 1) & 0x07) { case 0x00: jump = cpu_state.flags & V_FLAG; break; case 0x01: jump = cpu_state.flags & C_FLAG; break; case 0x02: jump = cpu_state.flags & Z_FLAG; break; case 0x03: jump = cpu_state.flags & (C_FLAG | Z_FLAG); break; case 0x04: jump = cpu_state.flags & N_FLAG; break; case 0x05: jump = cpu_state.flags & P_FLAG; break; case 0x06: jump = (!!(cpu_state.flags & N_FLAG)) != (!!(cpu_state.flags & V_FLAG)); break; case 0x07: jump = (cpu_state.flags & Z_FLAG) || ((!!(cpu_state.flags & N_FLAG)) != (!!(cpu_state.flags & V_FLAG))); break; } if (opcode & 1) jump = !jump; rel8 = (int8_t) biu_pfq_fetchb(); new_ip = cpu_state.pc + rel8; if (!is_nec) do_cycle_i(); if (jump) reljmp(new_ip, 1); break; case 0x80: /* ADD/OR/ADC/SBB/AND/SUB/XOR/CMP r/m8, imm8 */ case 0x82: bits = 8; /* read_opreand8() */ if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ cpu_data = get_ea(); cpu_dest = cpu_data; cpu_src = biu_pfq_fetchb() | 0xff00; do_cycle_nx(); math_op((rmdat & 0x38) >> 3); if (cpu_mod != 3) do_cycles_i(2); /* write_operand8() */ if (cpu_alu_op != 7) set_ea(cpu_data); break; case 0x81: /* ADD/OR/ADC/SBB/AND/SUB/XOR/CMP r/m16, imm16 */ bits = 16; /* read_opreand16() */ if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ cpu_data = get_ea(); cpu_dest = cpu_data; cpu_src = biu_pfq_fetchw(); do_cycle_nx(); math_op((rmdat & 0x38) >> 3); if (cpu_mod != 3) { if (cpu_alu_op != 7) do_cycles_i(2); else { do_cycles_nx_i(2); } } /* write_operand16() */ if (cpu_alu_op != 7) set_ea(cpu_data); break; case 0x83: /* ADD/OR/ADC/SBB/AND/SUB/XOR/CMP r/m16, imm8 (sign-extended) */ bits = 16; /* read_opreand16() */ if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ cpu_data = get_ea(); cpu_dest = cpu_data; cpu_src = sign_extend(biu_pfq_fetchb()); do_cycle_nx(); math_op((rmdat & 0x38) >> 3); if (cpu_mod != 3) do_cycles_i(2); /* write_operand16() */ if (cpu_alu_op != 7) set_ea(cpu_data); break; case 0x84: /* TEST r/m8, r8 */ bits = 8; /* read_operand8() */ if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ cpu_data = get_ea(); /* math_op8() */ test(bits, cpu_data, get_reg(cpu_reg)); do_cycles_nx_i(2); break; case 0x85: /* TEST r/m16, r16 */ bits = 16; /* read_operand16() */ if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ cpu_data = get_ea(); /* math_op16() */ test(bits, cpu_data, get_reg(cpu_reg)); do_cycles_nx_i(2); break; case 0x86: /* XHG r8, r/m8 */ bits = 8; /* read_operand8() */ if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ cpu_data = get_ea(); cpu_src = get_reg(cpu_reg); do_cycles_nx(3); if (cpu_mod != 3) do_cycles(2); /* write_operand8() */ set_reg(cpu_reg, cpu_data); set_ea(cpu_src); break; case 0x87: /* XCHG r16, r/m16 */ bits = 16; /* read_operand16() */ if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ cpu_data = get_ea(); cpu_src = get_reg(cpu_reg); do_cycles_nx(3); if (cpu_mod != 3) do_cycles(2); /* write_operand16() */ set_reg(cpu_reg, cpu_data); set_ea(cpu_src); break; case 0x88: /* MOV r/m8, r8; MOV r8, r/m8 */ case 0x8a: bits = 8; if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ do_cycle_nx(); /* read_operand8() */ if (opcode == 0x88) tempb = get_reg(cpu_reg); else tempb = get_ea(); if ((opcode == 0x88) && (cpu_mod != 3)) do_cycles_i(2); /* write_operand8() */ if (opcode == 0x88) set_ea(tempb); else set_reg(cpu_reg, tempb); break; case 0x89: /* MOV r/m16, r16; MOV r16, r/m16 */ case 0x8b: bits = 16; if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ do_cycle_nx(); /* read_operand16() */ if (opcode == 0x89) tempw = get_reg(cpu_reg); else tempw = get_ea(); if ((opcode == 0x89) && (cpu_mod != 3)) do_cycles_i(2); /* write_operand16() */ if (opcode == 0x89) set_ea(tempw); else set_reg(cpu_reg, tempw); break; case 0x8c: /* MOV r/m16, SReg; MOV SReg, r/m16 */ case 0x8e: bits = 16; if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ if ((opcode == 0x8c) && (cpu_mod != 3)) do_cycle_i(); /* read_operand16() */ if (opcode == 0x8c) tempw = _opseg[(rmdat & 0x18) >> 3]->seg; else tempw = geteaw(); /* write_operand16() */ if (opcode == 0x8c) seteaw(tempw); else { if ((rmdat & 0x18) == 0x08) load_cs(tempw); else load_seg(tempw, _opseg[(rmdat & 0x18) >> 3]); if (((rmdat & 0x18) >> 3) == 2) noint = 1; } break; case 0x8d: /* LEA */ if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ cpu_state.regs[cpu_reg].w = cpu_state.eaaddr; break; case 0x8f: /* POP r/m16 */ if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ do_cycle_i(); /* pop_u16() */ cpu_src = cpu_state.eaaddr; cpu_data = pop(); do_cycle_i(); if (cpu_mod != 3) do_cycles_i(2); /* write_operand16() */ cpu_state.eaaddr = cpu_src; seteaw(cpu_data); break; case 0x90 ... 0x97: /* XCHG AX, r */ cpu_data = cpu_state.regs[opcode & 7].w; do_cycles_nx_i(2); cpu_state.regs[opcode & 7].w = AX; AX = cpu_data; break; case 0x98: /* CBW */ sign_extend_al(); break; case 0x99: /* CWD */ sign_extend_ax(); break; case 0x9a: /* CALLF */ /* read_operand_faraddr() */ new_ip = biu_pfq_fetchw(); new_cs = biu_pfq_fetchw(); farcall(new_cs, new_ip, 1); jump = 1; break; case 0x9b: /* WAIT */ do_cycles(3); break; case 0x9c: /* PUSHF */ do_cycles(3); /* push_flags() */ if (is_nec) tempw = (cpu_state.flags & 0x8fd7) | 0x7000; else tempw = (cpu_state.flags & 0x0fd7) | 0xf000; push(&tempw); break; case 0x9d: /* POPF */ /* pop_flags() */ if (is_nec && cpu_md_write_disable) cpu_state.flags = pop() | 0x8002; else cpu_state.flags = pop() | 0x0002; sync_to_i8080(); break; case 0x9e: /* SAHF */ /* store_flags() */ cpu_state.flags = (cpu_state.flags & 0xff02) | AH; break; case 0x9f: /*LAHF*/ /* load_flags() */ /* set_register8() */ AH = cpu_state.flags & 0xd7; break; case 0xa0: /* MOV al, offset8 */ bits = 8; /* read_operand8() */ cpu_state.eaaddr = biu_pfq_fetchw(); tempb = readmem(ovr_seg ? *ovr_seg : ds); /* set_register8() */ set_accum(bits, tempb); break; case 0xa1: /* MOV al, offset16 */ bits = 16; /* read_operand16() */ cpu_state.eaaddr = biu_pfq_fetchw(); tempw = readmem(ovr_seg ? *ovr_seg : ds); /* set_register16() */ set_accum(bits, tempw); break; case 0xa2: /* MOV offset8, Al */ bits = 8; tempb = get_accum(bits); /* write_operand8() */ cpu_state.eaaddr = biu_pfq_fetchw(); writemem((ovr_seg ? *ovr_seg : ds), tempb); break; case 0xa3: /* MOV offset16, AX */ bits = 16; tempw = get_accum(bits); /* write_operand16() */ cpu_state.eaaddr = biu_pfq_fetchw(); writemem((ovr_seg ? *ovr_seg : ds), tempw); break; case 0xa4: /* MOVSB & MOVSW */ case 0xa5: bits = 8 << (opcode & 1); if (rep_start()) { string_op(bits); do_cycle_i(); if (in_rep != 0) { completed = 0; repeating = 1; /* decrement_register16() */ CX--; if (irq_pending()) { do_cycles_i(2); rep_interrupt(); } else { do_cycles_i(2); if (CX == 0) rep_end(); else do_cycle_i(); } } else do_cycle_i(); } break; case 0xa6: /* CMPSB, CMPSW, SCASB, SCASW */ case 0xa7: case 0xae: case 0xaf: bits = 8 << (opcode & 1); if (rep_start()) { string_op(bits); if (in_rep) { uint8_t end = 0; completed = 0; repeating = 1; do_cycle_i(); /* decrement_register16() */ CX--; if ((!!(cpu_state.flags & (rep_c_flag ? C_FLAG : Z_FLAG))) == (in_rep == 1)) { rep_end(); do_cycle_i(); end = 1; } if (!end) { do_cycle_i(); if (irq_pending()) { do_cycle_i(); rep_interrupt(); } do_cycle_i(); if (CX == 0) rep_end(); else do_cycle_i(); } else do_cycle_i(); } } break; case 0xa8: /* TEST al, imm8 */ bits = 8; /* read_operand8() */ cpu_data = biu_pfq_fetch(); /* math_op8() */ test(bits, get_accum(bits), cpu_data); break; case 0xa9: /* TEST ax, imm16 */ bits = 16; /* read_operand16() */ cpu_data = biu_pfq_fetch(); /* math_op16() */ test(bits, get_accum(bits), cpu_data); break; case 0xaa: /* STOSB & STOSW */ case 0xab: bits = 8 << (opcode & 1); if (rep_start()) { string_op(bits); do_cycle_i(); if (in_rep != 0) { completed = 0; repeating = 1; do_cycle_i(); if (irq_pending()) { do_cycle_i(); rep_interrupt(); } do_cycle_i(); /* decrement_register16() */ CX--; if (CX == 0) rep_end(); else do_cycle_i(); } else do_cycle_i(); } break; case 0xac: /* LODSB * LODSW */ case 0xad: bits = 8 << (opcode & 1); if (rep_start()) { string_op(bits); do_cycles_i(3); if (in_rep != 0) { completed = 0; repeating = 1; do_cycle_i(); /* decrement_register16() */ CX--; if (irq_pending()) { do_cycles_i(2); rep_interrupt(); } else { do_cycles_i(2); if (CX == 0) rep_end(); else do_cycle_i(); } } } break; case 0xb0 ... 0xb7: /* MOV r8, imm8 */ bits = 8; /* read_operand8() */ tempb = biu_pfq_fetchb(); /* set_register8() */ if (opcode & 0x04) cpu_state.regs[opcode & 0x03].b.h = tempb; else cpu_state.regs[opcode & 0x03].b.l = tempb; do_cycle_i(); break; case 0xb8 ... 0xbf: /* MOV r16, imm16 */ bits = 16; /* read_operand16() */ tempw = biu_pfq_fetchw(); /* set_register16() */ cpu_state.regs[opcode & 0x07].w = tempw; break; case 0xc0: /* rot imm8 */ case 0xc1: /* rot rm */ bits = 8 << (opcode & 1); if (cpu_mod != 3) do_cycles_i(is_nec ? 1 : 2); /* load_operand() */ /* read_operand() */ cpu_data = get_ea(); cpu_src = biu_pfq_fetchb(); if (is186 && !is_nec) cpu_src &= 0x1F; do_cycles_i(6); if (cpu_src > 0) { for (uint8_t i = 0; i < cpu_src; i++) do_cycles_i(4); } if (cpu_mod != 3) do_cycle_i(); /* bitshift_op() */ while (cpu_src != 0) { cpu_dest = cpu_data; oldc = cpu_state.flags & C_FLAG; switch (rmdat & 0x38) { case 0x00: /* ROL */ set_cf(top_bit(cpu_data, bits)); cpu_data <<= 1; cpu_data |= ((cpu_state.flags & C_FLAG) ? 1 : 0); set_of_rotate(bits); set_af(0); break; case 0x08: /* ROR */ set_cf((cpu_data & 1) != 0); cpu_data >>= 1; if (cpu_state.flags & C_FLAG) cpu_data |= (!(opcode & 1) ? 0x80 : 0x8000); set_of_rotate(bits); set_af(0); break; case 0x10: /* RCL */ set_cf(top_bit(cpu_data, bits)); cpu_data = (cpu_data << 1) | (oldc ? 1 : 0); set_of_rotate(bits); set_af(0); break; case 0x18: /* RCR */ set_cf((cpu_data & 1) != 0); cpu_data >>= 1; if (oldc) cpu_data |= (!(opcode & 0x01) ? 0x80 : 0x8000); set_cf((cpu_dest & 1) != 0); set_of_rotate(bits); set_af(0); break; case 0x20: /* SHL */ set_cf(top_bit(cpu_data, bits)); cpu_data <<= 1; set_of_rotate(bits); set_af((cpu_data & 0x10) != 0); set_pzs(bits); break; case 0x28: /* SHR */ set_cf((cpu_data & 1) != 0); cpu_data >>= 1; set_of_rotate(bits); set_af(0); set_pzs(bits); break; case 0x30: /* SETMO - undocumented? */ bitwise(bits, 0xffff); set_cf(0); set_of_rotate(bits); set_af(0); set_pzs(bits); break; case 0x38: /* SAR */ set_cf((cpu_data & 1) != 0); cpu_data >>= 1; if (!(opcode & 1)) cpu_data |= (cpu_dest & 0x80); else cpu_data |= (cpu_dest & 0x8000); set_of_rotate(bits); set_af(0); set_pzs(bits); break; default: break; } --cpu_src; } if (opcode <= 0xd1) { if (cpu_mod != 3) do_cycle_i(); } /* write_operand() */ set_ea(cpu_data); break; case 0xc2: /* RETN imm16 */ bits = 8; cpu_src = biu_pfq_fetchw(); do_cycle_i(); new_ip = pop(); biu_suspend_fetch(); set_ip(new_ip); do_cycles_i(2); biu_queue_flush(); do_cycles_i(3); /* release() */ SP += cpu_src; jump = 1; break; case 0xc3: /* RETN */ bits = 8; new_ip = pop(); biu_suspend_fetch(); set_ip(new_ip); do_cycle_i(); biu_queue_flush(); do_cycles_i(2); jump = 1; break; case 0xc4: /* LES */ case 0xc5: /* LDS */ bits = 16; if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ do_cycles_i(2); /* read_operand_farptr() */ read_ea(1, bits); cpu_state.regs[cpu_reg].w = cpu_data; read_ea2(bits); /* write_operand16() */ load_seg(cpu_data, (opcode & 0x01) ? &cpu_state.seg_ds : &cpu_state.seg_es); break; case 0xc6: /* MOV r/m8, imm8 */ bits = 8; /* read_operand8() */ if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ cpu_data = biu_pfq_fetch(); do_cycles(2); /* write_operand8() */ set_ea(cpu_data); break; case 0xc7: /* MOV r/m16, imm16 */ bits = 16; /* read_operand16() */ if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ cpu_data = biu_pfq_fetch(); do_cycle_i(); /* write_operand16() */ set_ea(cpu_data); break; case 0xc8: /* RETF imm16 */ if (is_nec) { /* ENTER/PREPARE */ tempw_int = 0; size = biu_pfq_fetchw(); nests = biu_pfq_fetchb(); push(&BP); tempw_int = SP; if (nests > 0) { while (--nests) { tempbp = 0; BP -= 2; tempbp = readmemw(ss, BP); push(&tempbp); } push(&tempw_int); } BP = tempw_int; SP -= size; break; } else fallthrough; case 0xca: bits = 16; /* read_operand16() */ cpu_src = biu_pfq_fetchw(); farret(1); /* release() */ SP += cpu_src; do_cycle_i(); jump = 1; break; case 0xc9: /* RETF */ if (is_nec) { /* LEAVE/DISPOSE */ SP = BP; BP = pop(); break; } else fallthrough; case 0xcb: bits = 16; do_cycle_i(); farret(1); jump = 1; break; case 0xcc: /* INT 3 */ do_cycles_i(4); int3(); jump = 1; break; case 0xcd: /* INT imm8 */ /* read_operand8() */ temp = biu_pfq_fetchb(); do_cycle_i(); sw_int(temp); jump = 1; break; case 0xce: /* INTO */ if (cpu_state.flags & V_FLAG) { sw_int(4); jump = 1; } break; case 0xcf: /* IRET */ iret_routine(); if (is_nec && !(cpu_state.flags & MD_FLAG)) sync_to_i8080(); jump = 1; break; case 0xd0: /* ROL, ROR, RCL, RCR, SHL, SHR, SAR: r/m 8, 0x01 */ case 0xd1: /* ROL, ROR, RCL, RCR, SHL, SHR, SAR: r/m 16, 0x01 */ case 0xd2: /* ROL, ROR, RCL, RCR, SHL, SHR, SAR: r/m 8, cl */ case 0xd3: /* ROL, ROR, RCL, RCR, SHL, SHR, SAR: r/m 16, cl */ /* rot rm */ bits = 8 << (opcode & 1); if (cpu_mod != 3) do_cycles_i(is_nec ? 1 : 2); /* load_operand() */ /* read_operand() */ cpu_data = get_ea(); if ((opcode & 2) == 0) cpu_src = 1; else cpu_src = CL; if (is186 && !is_nec) cpu_src &= 0x1F; if (opcode >= 0xd2) { do_cycles_i(6); if (CL > 0) { for (uint8_t i = 0; i < CL; i++) do_cycles_i(4); } if (cpu_mod != 3) do_cycle_i(); } /* bitshift_op() */ while (cpu_src != 0) { cpu_dest = cpu_data; oldc = cpu_state.flags & C_FLAG; if (is_nec && ((rmdat & 0x38) == 0x30)) rmdat &= 0xef; /* Make it 0x20, so it aliases to SHL. */ switch (rmdat & 0x38) { case 0x00: /* ROL */ set_cf(top_bit(cpu_data, bits)); cpu_data <<= 1; cpu_data |= ((cpu_state.flags & C_FLAG) ? 1 : 0); set_of_rotate(bits); set_af(0); break; case 0x08: /* ROR */ set_cf((cpu_data & 1) != 0); cpu_data >>= 1; if (cpu_state.flags & C_FLAG) cpu_data |= (!(opcode & 1) ? 0x80 : 0x8000); set_of_rotate(bits); set_af(0); break; case 0x10: /* RCL */ set_cf(top_bit(cpu_data, bits)); cpu_data = (cpu_data << 1) | (oldc ? 1 : 0); set_of_rotate(bits); set_af(0); break; case 0x18: /* RCR */ set_cf((cpu_data & 1) != 0); cpu_data >>= 1; if (oldc) cpu_data |= (!(opcode & 0x01) ? 0x80 : 0x8000); set_cf((cpu_dest & 1) != 0); set_of_rotate(bits); set_af(0); break; case 0x20: /* SHL */ set_cf(top_bit(cpu_data, bits)); cpu_data <<= 1; set_of_rotate(bits); set_af((cpu_data & 0x10) != 0); set_pzs(bits); break; case 0x28: /* SHR */ set_cf((cpu_data & 1) != 0); cpu_data >>= 1; set_of_rotate(bits); set_af(0); set_pzs(bits); break; case 0x30: /* SETMO - undocumented? */ bitwise(bits, 0xffff); set_cf(0); set_of_rotate(bits); set_af(0); set_pzs(bits); break; case 0x38: /* SAR */ set_cf((cpu_data & 1) != 0); cpu_data >>= 1; if (!(opcode & 1)) cpu_data |= (cpu_dest & 0x80); else cpu_data |= (cpu_dest & 0x8000); set_of_rotate(bits); set_af(0); set_pzs(bits); break; default: break; } --cpu_src; } if (opcode <= 0xd1) { if (cpu_mod != 3) do_cycle_i(); } /* write_operand() */ set_ea(cpu_data); break; case 0xd4: /* AAM */ /* read_operand8() */ cpu_src = biu_pfq_fetchb(); if (is_nec) { if (!cpu_src) cpu_src = 10; AH = AL / cpu_src; AL %= cpu_src; cpu_data = AL; set_pzs(8); do_cycles(12); } else { /* Confirmed to be identical on V20/V30 to 808x, per XTIDE working correctly on both (it uses AAM with parameter other than 10. */ /* aam() */ if (x86_div(AL, 0)) { cpu_data = AL; set_pzs(8); } } break; case 0xd5: /* AAD */ /* read_operand8() */ cpu_src = biu_pfq_fetchb(); if (is_nec) { cpu_src = 10; AL = (AH * cpu_src) + AL; AH = 0; cpu_data = AL; set_pzs(8); do_cycles(4); } else { if (is_nec) cpu_src = 10; /* aad() */ mul(cpu_src, AH); cpu_dest = AL; cpu_src = cpu_data; add(8); AL = cpu_data; AH = 0x00; set_pzs(8); } break; case 0xd6: /* SALC */ if (is_nec) { do_cycles(14); fallthrough; } else { AL = (cpu_state.flags & C_FLAG) ? 0xff : 0x00; break; } case 0xd7: /* XLAT */ do_cycles_i(3); /* biu_read_u8() */ cpu_state.eaaddr = (BX + AL) & 0xffff; tempb = readmemb((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); /* set_register8() */ AL = tempb; break; case 0xd8 ... 0xdf: /* ESC - FPU instructions. */ /* read_operand16() */ if (cpu_mod != 3) do_cycles_i(2); /* load_operand() */ tempw = cpu_state.pc; geteaw(); /* fpu_op() */ if (hasfpu) { if (fpu_softfloat) { switch (opcode) { case 0xd8: ops_sf_fpu_8087_d8[(rmdat >> 3) & 0x1f](rmdat); break; case 0xd9: ops_sf_fpu_8087_d9[rmdat & 0xff](rmdat); break; case 0xda: ops_sf_fpu_8087_da[rmdat & 0xff](rmdat); break; case 0xdb: ops_sf_fpu_8087_db[rmdat & 0xff](rmdat); break; case 0xdc: ops_sf_fpu_8087_dc[(rmdat >> 3) & 0x1f](rmdat); break; case 0xdd: ops_sf_fpu_8087_dd[rmdat & 0xff](rmdat); break; case 0xde: ops_sf_fpu_8087_de[rmdat & 0xff](rmdat); break; case 0xdf: ops_sf_fpu_8087_df[rmdat & 0xff](rmdat); break; default: break; } } else { switch (opcode) { case 0xd8: ops_fpu_8087_d8[(rmdat >> 3) & 0x1f](rmdat); break; case 0xd9: ops_fpu_8087_d9[rmdat & 0xff](rmdat); break; case 0xdA: ops_fpu_8087_da[rmdat & 0xff](rmdat); break; case 0xdb: ops_fpu_8087_db[rmdat & 0xff](rmdat); break; case 0xdc: ops_fpu_8087_dc[(rmdat >> 3) & 0x1f](rmdat); break; case 0xdd: ops_fpu_8087_dd[rmdat & 0xff](rmdat); break; case 0xde: ops_fpu_8087_de[rmdat & 0xff](rmdat); break; case 0xdf: ops_fpu_8087_df[rmdat & 0xff](rmdat); break; default: break; } } } cpu_state.pc = tempw; /* Do this as the x87 code advances it, which is needed on the 286+ core, but not here. */ break; case 0xe0: /* LOOPNE & LOOPE */ case 0xe1: /* decrement_register16() */ --CX; do_cycles_i(2); zero_cond = !(cpu_state.flags & Z_FLAG); if (opcode == 0xe1) zero_cond = !zero_cond; /* read_operand8() */ cpu_data = biu_pfq_fetchb(); if ((CX != 0x0000) && zero_cond) { rel8 = (int8_t) cpu_data; new_ip = (cpu_state.pc + rel8) & 0xffff; reljmp(new_ip, 1); jump = 1; do_print = 0; } else { do_cycle_i(); do_print = 1; } break; case 0xe2: /* LOOP */ /* decrement_register16() */ --CX; do_cycles_i(2); /* read_operand8() */ cpu_data = biu_pfq_fetchb(); if (CX != 0x0000) { rel8 = (int8_t) cpu_data; new_ip = (cpu_state.pc + rel8) & 0xffff; reljmp(new_ip, 1); jump = 1; do_print = 0; } if (!jump) { do_cycle(); do_print = 1; } break; case 0xe3: /* JCXZ */ do_cycles_i(2); /* read_operand8() */ cpu_data = biu_pfq_fetchb(); do_cycle_i(); if (CX == 0x0000) { rel8 = (int8_t) cpu_data; new_ip = (cpu_state.pc + rel8) & 0xffff; reljmp(new_ip, 1); jump = 1; } else do_cycle_i(); break; case 0xe4: /* IN al, imm8 */ bits = 8; /* read_operand8() */ cpu_data = biu_pfq_fetchb(); do_cycles_i(2); /* biu_io_read_u8() */ cpu_state.eaaddr = cpu_data; cpu_io_vx0(bits, 0, cpu_state.eaaddr); /* set_register8() */ break; case 0xe5: /* IN ax, imm8 */ bits = 16; /* read_operand16() */ cpu_data = biu_pfq_fetchb(); do_cycles_i(2); /* biu_io_read_u16() */ cpu_state.eaaddr = cpu_data; cpu_io_vx0(bits, 0, cpu_state.eaaddr); /* set_register16() */ break; case 0xe6: /* OUT imm8, al */ bits = 8; /* read_operand8() */ cpu_data = biu_pfq_fetchb(); /* read_operand8() */ tempb = AL; do_cycles_i(2); /* biu_io_write_u8() */ cpu_state.eaaddr = cpu_data; cpu_data = tempb; cpu_io_vx0(bits, 1, cpu_state.eaaddr); break; case 0xe7: /* OUT imm8, ax */ bits = 16; /* read_operand8() */ cpu_data = biu_pfq_fetchb(); /* read_operand16() */ tempw = AX; do_cycles_i(2); /* biu_io_write_u16() */ cpu_state.eaaddr = cpu_data; cpu_data = tempw; cpu_io_vx0(bits, 1, cpu_state.eaaddr); break; case 0xe8: /* CALL rel16 */ /* read_operand16() */ rel16 = (int16_t) biu_pfq_fetchw(); biu_suspend_fetch(); do_cycles_i(4); old_ip = cpu_state.pc; new_ip = cpu_state.pc + rel16; set_ip(new_ip); biu_queue_flush(); do_cycles_i(3); push(&old_ip); jump = 1; break; case 0xe9: /* JMP rel16 */ /* read_operand16() */ rel16 = (int16_t) biu_pfq_fetchw(); new_ip = (cpu_state.pc + rel16) & 0xffff; reljmp(new_ip, 1); jump = 1; break; case 0xea: /* JMP far [addr16:16] */ /* read_operand_faraddr() */ addr = biu_pfq_fetchw(); tempw = biu_pfq_fetchw(); load_cs(tempw); set_ip(addr); biu_suspend_fetch(); do_cycles_i(2); biu_queue_flush(); do_cycle_i(); jump = 1; break; case 0xeb: /* JMP rel8 */ /* read_operand8() */ rel8 = (int8_t) biu_pfq_fetchb(); new_ip = (cpu_state.pc + rel8) & 0xffff; reljmp(new_ip, 1); jump = 1; break; case 0xec: /* IN al, dx */ bits = 8; /* read_operand8() */ cpu_data = DX; /* biu_io_read_u8() */ cpu_state.eaaddr = cpu_data; cpu_io_vx0(bits, 0, cpu_state.eaaddr); /* set_register8() */ break; case 0xed: /* IN ax, dx */ bits = 16; /* read_operand16() */ cpu_data = DX; /* biu_io_read_u16() */ cpu_state.eaaddr = cpu_data; cpu_io_vx0(bits, 0, cpu_state.eaaddr); /* set_register16() */ break; case 0xee: /* OUT dx, al */ bits = 8; /* read_operand8() */ cpu_data = DX; /* read_operand8() */ tempb = AL; do_cycle_i(); /* biu_io_write_u8() */ cpu_state.eaaddr = cpu_data; cpu_data = tempb; cpu_io_vx0(bits, 1, cpu_state.eaaddr); break; case 0xef: /* OUT dx, ax */ bits = 16; /* read_operand8() */ cpu_data = DX; /* read_operand16() */ tempw = AX; do_cycle_i(); /* biu_io_write_u16() */ cpu_state.eaaddr = cpu_data; cpu_data = tempw; cpu_io_vx0(bits, 1, cpu_state.eaaddr); break; case 0xf0: case 0xf1: /* LOCK - F1 is alias */ break; case 0xf2: /* REPNE */ case 0xf3: /* REPE */ break; case 0xf4: /* HLT */ if (is_nec) in_hlt = 1; if (!repeating) { biu_suspend_fetch(); // biu_queue_flush(); do_cycles(2); /* TODO: biu_halt(); */ do_cycle(); } do_cycle_i(); do_cycle_i(); do_cycle_i(); if (irq_pending()) { halted = 0; check_interrupts(); } else { repeating = 1; completed = 0; halted = 1; } if (is_nec) in_hlt = 0; break; case 0xf5: /* CMC */ cpu_state.flags ^= C_FLAG; break; case 0xf6: /* Miscellaneuous Opcode Extensions, r/m8, imm8 */ bits = 8; if (cpu_mod != 3) do_cycles_i(is_nec ? 1 : 2); /* load_operand() */ negate = !!in_rep; if (is_nec && ((rmdat & 0x38) >= 0x20)) switch (rmdat & 0x38) { case 0x20: /* MUL */ /* read_operand8() */ cpu_data = get_ea(); AX = AL * cpu_data; if (AH) cpu_state.flags |= (C_FLAG | V_FLAG); else cpu_state.flags &= ~(C_FLAG | V_FLAG); do_cycles((cpu_mod == 3) ? 24 : 30); break; case 0x28: /* IMUL */ /* read_operand8() */ cpu_data = get_ea(); tempws = (int) ((int8_t) AL) * (int) ((int8_t) cpu_data); AX = tempws & 0xffff; if (((int16_t) AX >> 7) != 0 && ((int16_t) AX >> 7) != -1) cpu_state.flags |= (C_FLAG | V_FLAG); else cpu_state.flags &= ~(C_FLAG | V_FLAG); do_cycles((cpu_mod == 3) ? 13 : 19); break; case 0x30: /* DIV */ /* read_operand8() */ cpu_data = get_ea(); src16 = AX; if (cpu_data) tempw = src16 / cpu_data; if (cpu_data && !(tempw & 0xff00)) { AH = src16 % cpu_data; AL = (src16 / cpu_data) & 0xff; cpu_state.flags |= 0x8D5; cpu_state.flags &= ~1; } else { intr_routine(0, 0); break; } do_cycles((cpu_mod == 3) ? 21 : 27); break; case 0x38: /* IDIV */ /* read_operand8() */ cpu_data = get_ea(); tempws = (int) (int16_t) AX; if (cpu_data != 0) tempws2 = tempws / (int) ((int8_t) cpu_data); temps = tempws2 & 0xff; if (cpu_data && ((int) temps == tempws2)) { AH = (tempws % (int) ((int8_t) cpu_data)) & 0xff; AL = tempws2 & 0xff; cpu_state.flags |= 0x8D5; cpu_state.flags &= ~1; } else { intr_routine(0, 0); break; } do_cycles((cpu_mod == 3) ? 11 : 17); break; } else switch (rmdat & 0x38) { case 0x00: /* TEST */ case 0x08: /* read_operand8() */ cpu_data = get_ea(); /* read_operand8() */ cpu_src = biu_pfq_fetch(); do_cycles_i(is_nec ? 1 : 2); /* math_op8() */ test(bits, cpu_data, cpu_src); break; case 0x10: /* NOT */ case 0x18: /* NEG */ /* read_operand8() */ cpu_data = get_ea(); /* math_op8() */ if ((rmdat & 0x38) == 0x10) cpu_data = ~cpu_data; else { cpu_src = cpu_data; cpu_dest = 0; sub(bits); } if (cpu_mod != 3) do_cycles_i(2); /* write_operand8() */ set_ea(cpu_data); break; case 0x20: /* MUL */ case 0x28: /* IMUL */ /* read_operand8() */ cpu_data = get_ea(); /* mul8() */ old_flags = cpu_state.flags; mul(get_accum(bits), cpu_data); prod16 = ((cpu_dest & 0xff) << 8) | (cpu_data & 0xff); if (negate) prod16 = -prod16; cpu_dest = prod16 >> 8; cpu_data = prod16 & 0xff; AL = (uint8_t) cpu_data; AH = (uint8_t) cpu_dest; set_co_mul(bits, AH != ((AL & 0x80) == 0 || (rmdat & 0x38) == 0x20 ? 0 : 0xff)); if (!is_nec) cpu_data = AH; set_sf(bits); set_pf(); /* NOTE: When implementing the V20, care should be taken to not change the zero flag. */ if (is_nec) cpu_state.flags = (cpu_state.flags & ~Z_FLAG) | (old_flags & Z_FLAG); break; case 0x30: /* DIV */ case 0x38: /* IDIV */ /* read_operand8() */ cpu_data = get_ea(); cpu_src = cpu_data; if (x86_div(AL, AH)) { if (!is_nec && negate) AL = -AL; do_cycle(); } break; } break; case 0xf7: /* Miscellaneuous Opcode Extensions, r/m16, imm16 */ bits = 16; if (cpu_mod != 3) do_cycles_i(is_nec ? 1 : 2); /* load_operand() */ negate = !!in_rep; if (is_nec && ((rmdat & 0x38) >= 0x20)) switch (rmdat & 0x38) { case 0x20: /* MUL */ /* read_operand16() */ cpu_data = get_ea(); templ = AX * cpu_data; AX = templ & 0xFFFF; DX = templ >> 16; if (DX) cpu_state.flags |= (C_FLAG | V_FLAG); else cpu_state.flags &= ~(C_FLAG | V_FLAG); do_cycles((cpu_mod == 3) ? 29 : 35); break; case 0x28: /* IMUL */ /* read_operand16() */ cpu_data = get_ea(); templ = (int) ((int16_t) AX) * (int) ((int16_t) cpu_data); AX = templ & 0xFFFF; DX = templ >> 16; if (((int32_t) templ >> 15) != 0 && ((int32_t) templ >> 15) != -1) cpu_state.flags |= (C_FLAG | V_FLAG); else cpu_state.flags &= ~(C_FLAG | V_FLAG); do_cycles((cpu_mod == 3) ? 17 : 27); break; case 0x30: /* DIV */ /* read_operand16() */ cpu_data = get_ea(); templ = (DX << 16) | AX; if (cpu_data) templ2 = templ / cpu_data; if (cpu_data && !(templ2 & 0xffff0000)) { DX = templ % cpu_data; AX = (templ / cpu_data) & 0xffff; cpu_data = AX; set_pzs(16); } else { intr_routine(0, 0); break; } do_cycles((cpu_mod == 3) ? 26 : 36); break; case 0x38: /* IDIV */ /* read_operand16() */ cpu_data = get_ea(); tempws = (int) ((DX << 16) | AX); if (cpu_data) tempws2 = tempws / (int) ((int16_t) cpu_data); temps16 = tempws2 & 0xffff; if ((cpu_data != 0) && ((int) temps16 == tempws2)) { DX = tempws % (int) ((int16_t) cpu_data); AX = tempws2 & 0xffff; cpu_data = AX; set_pzs(16); } else { intr_routine(0, 0); break; } do_cycles((cpu_mod == 3) ? 13 : 23); break; } else switch (rmdat & 0x38) { case 0x00: /* TEST */ case 0x08: /* read_operand16() */ cpu_data = get_ea(); /* read_operand16() */ cpu_src = biu_pfq_fetch(); do_cycle_i(); /* math_op16() */ test(bits, cpu_data, cpu_src); break; case 0x10: /* NOT */ case 0x18: /* NEG */ /* read_operand16() */ cpu_data = get_ea(); /* math_op16() */ if ((rmdat & 0x38) == 0x10) cpu_data = ~cpu_data; else { cpu_src = cpu_data; cpu_dest = 0; sub(bits); } if (cpu_mod != 3) do_cycles_i(2); /* write_operand16() */ set_ea(cpu_data); break; case 0x20: /* MUL */ case 0x28: /* IMUL */ /* read_operand16() */ cpu_data = get_ea(); /* mul8() */ old_flags = cpu_state.flags; mul(get_accum(bits), cpu_data); prod32 = (((uint32_t) cpu_dest) << 16) | cpu_data; if (negate) prod32 = -prod32; cpu_dest = prod32 >> 16; cpu_data = prod32 & 0xffff; AX = cpu_data; DX = cpu_dest; set_co_mul(bits, DX != ((AX & 0x8000) == 0 || (rmdat & 0x38) == 0x20 ? 0 : 0xffff)); cpu_data = DX; set_sf(bits); set_pf(); /* NOTE: When implementing the V20, care should be taken to not change the zero flag. */ if (is_nec) cpu_state.flags = (cpu_state.flags & ~Z_FLAG) | (old_flags & Z_FLAG); break; case 0x30: /* DIV */ case 0x38: /* IDIV */ /* read_operand16() */ cpu_data = get_ea(); cpu_src = cpu_data; if (x86_div(AX, DX)) { if (!is_nec && negate) AX = -AX; do_cycle(); } break; } break; case 0xf8: /* CLCSTC */ case 0xf9: set_cf(opcode & 1); break; case 0xfa: /* CLISTI */ case 0xfb: set_if(opcode & 1); break; case 0xfc: /* CLDSTD */ case 0xfd: set_df(opcode & 1); break; case 0xfe: bits = 8; if (cpu_mod != 3) do_cycles_i(is_nec ? 1 : 2); /* load_operand() */ read_ea(((rmdat & 0x38) == 0x18) || ((rmdat & 0x38) == 0x28), bits); switch (rmdat & 0x38) { case 0x00: /* INC rm */ case 0x08: /* DEC rm */ /* read_operand8() */ /* math_op8() */ cpu_dest = cpu_data; cpu_src = 1; if ((rmdat & 0x38) == 0x00) { cpu_data = cpu_dest + cpu_src; set_of_add(bits); } else { cpu_data = cpu_dest - cpu_src; set_of_sub(bits); } do_af(); set_pzs(bits); if (cpu_mod != 3) do_cycles_i(2); /* write_operand8() */ set_ea(cpu_data); break; case 0x10: /* CALL rm */ /* read_operand8() */ cpu_data_opff_rm(); cpu_state.oldpc = cpu_state.pc; push((uint16_t *) &(cpu_state.oldpc)); biu_suspend_fetch(); do_cycles(4); biu_queue_flush(); set_ip(cpu_data | 0xff00); break; case 0x18: /* CALL rmd */ if (cpu_mod == 3) { /* biu_read_u8() */ cpu_state.eaaddr = 0x0004; tempb = readmemb((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); old_cs = CS & 0x00ff; push(&old_cs); old_ip = cpu_state.pc & 0x00ff; push(&old_ip); biu_suspend_fetch(); do_cycles(4); biu_queue_flush(); read_ea_8to16(); set_ip(cpu_data); } else { /* read_operand8() */ new_ip = cpu_data | 0xff00; do_cycles_i(3); /* biu_read_u8() */ read_ea2(bits); cpu_data |= 0xff00; new_cs = cpu_data; do_cycle_i(); biu_suspend_fetch(); do_cycles_i(3); old_cs = CS & 0x00ff; push(&old_cs); old_ip = cpu_state.pc & 0x00ff; load_cs(new_cs); set_ip(new_ip); do_cycles_i(3); biu_queue_flush(); do_cycles_i(3); push(&old_ip); } break; case 0x20: /* JMP rm */ /* read_operand8() */ cpu_data_opff_rm(); set_ip(cpu_data | 0xff00); biu_suspend_fetch(); do_cycles(4); biu_queue_flush(); break; case 0x28: /* JMP rmd */ if (cpu_mod == 3) { /* biu_read_u8() */ cpu_state.eaaddr = 0x0004; tempb = readmemb((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); biu_suspend_fetch(); do_cycles(4); biu_queue_flush(); read_ea_8to16(); set_ip(cpu_data); } else { /* read_operand8() */ new_ip = cpu_data | 0xff00; /* biu_read_u8() */ read_ea2(bits); cpu_data |= 0xff00; new_cs = cpu_data; biu_suspend_fetch(); do_cycles(4); biu_queue_flush(); load_cs(new_cs); set_ip(new_ip); } break; case 0x30: /* PUSH rm */ case 0x38: /* read_operand8() */ do_cycles_i(3); cpu_data &= 0x00ff; push((uint16_t *) &cpu_data); break; } break; case 0xff: bits = 16; if (cpu_mod != 3) do_cycles_i(is_nec ? 1 : 2); /* load_operand() */ read_ea(((rmdat & 0x38) == 0x18) || ((rmdat & 0x38) == 0x28), bits); switch (rmdat & 0x38) { case 0x00: /* INC rm */ case 0x08: /* DEC rm */ /* read_operand16() */ /* math_op16() */ cpu_dest = cpu_data; cpu_src = 1; if ((rmdat & 0x38) == 0x00) { cpu_data = cpu_dest + cpu_src; set_of_add(bits); } else { cpu_data = cpu_dest - cpu_src; set_of_sub(bits); } do_af(); set_pzs(bits); if (cpu_mod != 3) do_cycles_i(2); /* write_operand16() */ set_ea(cpu_data); break; case 0x10: /* CALL rm */ /* read_operand16() */ cpu_data_opff_rm(); biu_suspend_fetch(); do_cycles(4); cpu_state.oldpc = cpu_state.pc; old_ip = cpu_state.pc; set_ip(cpu_data); biu_queue_flush(); do_cycles_i(3); push(&old_ip); break; case 0x18: /* CALL rmd */ if (cpu_mod == 3) { new_ip = cpu_data; /* biu_read_u16() */ cpu_state.eaaddr = 0x0004; new_cs = readmemw((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); do_cycle_i(); biu_suspend_fetch(); do_cycles_i(3); push(&CS); old_ip = cpu_state.pc; set_ip(new_ip); load_cs(new_cs); do_cycles_i(3); biu_queue_flush(); do_cycles_i(3); push(&old_ip); } else { do_cycle_i(); /* read_operand_farptr() */ new_ip = cpu_data; read_ea2(bits); new_cs = cpu_data; do_cycle_i(); biu_suspend_fetch(); do_cycles_i(3); push(&CS); load_cs(new_cs); old_ip = cpu_state.pc; set_ip(new_ip); do_cycles_i(3); biu_queue_flush(); do_cycles_i(3); push(&old_ip); } break; case 0x20: /* JMP rm */ /* read_operand16() */ cpu_data_opff_rm(); biu_suspend_fetch(); do_cycle_i(); set_ip(cpu_data); biu_queue_flush(); break; case 0x28: /* JMP rmd */ if (cpu_mod == 3) { new_ip = cpu_data; do_cycle(); biu_suspend_fetch(); do_cycle(); /* biu_read_u16() */ cpu_state.eaaddr = 0x0004; new_cs = readmemw((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); push(&CS); biu_queue_flush(); } else { do_cycle_i(); biu_suspend_fetch(); do_cycle_i(); /* read_operand_farptr() */ new_ip = cpu_data; read_ea2(bits); new_cs = cpu_data; load_cs(new_cs); set_ip(new_ip); biu_queue_flush(); } break; case 0x30: /* PUSH rm */ case 0x38: /* read_operand16() */ do_cycles_i(3); if (cpu_rm == 4) cpu_rm -= 2; push((uint16_t *) &cpu_data); break; } break; default: x808x_log("Illegal opcode: %02X\n", opcode); biu_pfq_fetchb(); do_cycles(8); break; } } /* Executes instructions up to the specified number of cycles. */ void execvx0(int cycs) { cycles += cycs; while (cycles > 0) { if (started) { started = 0; startx86(); } if (!repeating) { cpu_state.oldpc = cpu_state.pc; if (clear_lock) { in_lock = 0; clear_lock = 0; } if (!is_nec || (cpu_state.flags & MD_FLAG)) decode(); oldc = cpu_state.flags & C_FLAG; } x808x_log("[%04X:%04X] Opcode: %02X\n", CS, cpu_state.pc, opcode); execute_instruction(); if (completed) { if (opcode != 0xf4) finalize(); check_interrupts(); if (noint) noint = 0; } #ifdef USE_GDBSTUB if (gdbstub_instruction()) return; #endif } }