/*
 * 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.
 *
 *          Vx0 CPU emulation, mostly ported from reenigne's XTCE, which
 *          is cycle-accurate.
 *
 * Authors: Andrew Jenner, <https://www.reenigne.org>
 *          Miran Grca, <mgrca8@gmail.com>
 *
 *          Copyright 2015-2020 Andrew Jenner.
 *          Copyright 2016-2020 Miran Grca.
 */
#include <math.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>

#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_vx0(1)
#define do_cycle_no_modrm()  if (!nx)       \
                                 do_cycle()
#define do_cycle_i()         do_cycle()
#define do_cycles(c)         wait_vx0(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 */

int                nx                    = 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;

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_VX0_LOG
#if 0
void dumpregs(int);
#endif
int vx0_do_log = ENABLE_VX0_LOG;

static void
vx0_log(const char *fmt, ...)
{
    va_list ap;

    if (vx0_do_log) {
        va_start(ap, fmt);
        pclog_ex(fmt, ap);
        va_end(ap);
    }
}
#else
#    define vx0_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;
}

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)
{
    is_new_biu = 1;
    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("Vx0 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("Vx0 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("Vx0 register seteal()\n");
        return;
    } else
        writememl(easeg, cpu_state.eaaddr, val);
}

static void
seteaq(uint64_t val)
{
    if (cpu_mod == 3) {
        fatal("Vx0 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();
        vx0_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();
        vx0_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();
    vx0_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;

    vx0_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();
        vx0_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_modrm(void)
{
    uint8_t op_f;

    modrm_loaded = 0;

    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
decode(void)
{
    uint8_t prefix = 0;

    if (halted)
        opcode  = 0xf4;
    else
        opcode  = biu_pfq_fetchb_common();

    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();
    }
}

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);
                    /* Swap them or else the operation goes wrong. */
                    uint32_t tmpa2 = tmpa;
                    tmpa = cpu_data;
                    cpu_data = tmpa2;
                } 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);
            if (fpu_softfloat && (fpu_state.swd & FPU_SW_Summary) && !(fpu_state.cwd & FPU_SW_Summary))
                nmi = 1;
            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:
            vx0_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();
        }

#ifdef DEBUG_INSTRUCTIONS
        if (repeating) {
            if ((opcode >= MIN_INS) && (opcode <= MAX_INS) && (opcode != SKIP_INS)) {
                execx86_instruction();
                goto check_completed;
            }
        } else {
#else
        if (!repeating) {
#endif
            cpu_state.oldpc = cpu_state.pc;

            if (clear_lock) {
                in_lock    = 0;
                clear_lock = 0;
            }

            if (!is_nec || (cpu_state.flags & MD_FLAG)) {
                decode();

#ifdef DEBUG_INSTRUCTIONS
                if ((opcode >= MIN_INS) && (opcode <= MAX_INS) && (opcode != SKIP_INS)) {
                    oldc    = cpu_state.flags & C_FLAG;

                    execx86_instruction();
                    goto check_completed;
                }
#endif

                decode_modrm();
            }

            oldc    = cpu_state.flags & C_FLAG;
        }

        vx0_log("[%04X:%04X] Opcode: %02X\n", CS, cpu_state.pc, opcode);

        execute_instruction();

#ifdef DEBUG_INSTRUCTIONS
check_completed:
#endif
        if (completed) {
            if (opcode != 0xf4)
                finalize();

            check_interrupts();

            if (noint)
                noint = 0;
        }

#ifdef USE_GDBSTUB
        if (gdbstub_instruction())
            return;
#endif
    }
}
