root/sys/dev/x86emu/x86emu.c
/*      $OpenBSD: x86emu.c,v 1.13 2024/09/04 07:54:52 mglocker Exp $    */
/*      $NetBSD: x86emu.c,v 1.7 2009/02/03 19:26:29 joerg Exp $ */

/*
 *
 *  Realmode X86 Emulator Library
 *
 *  Copyright (C) 1996-1999 SciTech Software, Inc.
 *  Copyright (C) David Mosberger-Tang
 *  Copyright (C) 1999 Egbert Eich
 *  Copyright (C) 2007 Joerg Sonnenberger
 *
 *  ========================================================================
 *
 *  Permission to use, copy, modify, distribute, and sell this software and
 *  its documentation for any purpose is hereby granted without fee,
 *  provided that the above copyright notice appear in all copies and that
 *  both that copyright notice and this permission notice appear in
 *  supporting documentation, and that the name of the authors not be used
 *  in advertising or publicity pertaining to distribution of the software
 *  without specific, written prior permission.  The authors makes no
 *  representations about the suitability of this software for any purpose.
 *  It is provided "as is" without express or implied warranty.
 *
 *  THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 *  INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 *  EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 *  CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
 *  USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 *  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 *  PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include <dev/x86emu/x86emu.h>
#include <dev/x86emu/x86emu_regs.h>

static void     x86emu_intr_raise (struct x86emu *, uint8_t type);

static void     x86emu_exec_one_byte(struct x86emu *);
static void     x86emu_exec_two_byte(struct x86emu *);

static void     fetch_decode_modrm (struct x86emu *);
static uint8_t  fetch_byte_imm (struct x86emu *);
static uint16_t fetch_word_imm (struct x86emu *);
static uint32_t fetch_long_imm (struct x86emu *);
static uint8_t  fetch_data_byte (struct x86emu *, uint32_t offset);
static uint8_t  fetch_byte (struct x86emu *, uint segment, uint32_t offset);
static uint16_t fetch_data_word (struct x86emu *, uint32_t offset);
static uint16_t fetch_word (struct x86emu *, uint32_t segment, uint32_t offset);
static uint32_t fetch_data_long (struct x86emu *, uint32_t offset);
static uint32_t fetch_long (struct x86emu *, uint32_t segment, uint32_t offset);
static void     store_data_byte (struct x86emu *, uint32_t offset, uint8_t val);
static void     store_byte (struct x86emu *, uint32_t segment, uint32_t offset, uint8_t val);
static void     store_data_word (struct x86emu *, uint32_t offset, uint16_t val);
static void     store_word (struct x86emu *, uint32_t segment, uint32_t offset, uint16_t val);
static void     store_data_long (struct x86emu *, uint32_t offset, uint32_t val);
static void     store_long (struct x86emu *, uint32_t segment, uint32_t offset, uint32_t val);
static uint8_t* decode_rl_byte_register(struct x86emu *);
static uint16_t*        decode_rl_word_register(struct x86emu *);
static uint32_t*        decode_rl_long_register(struct x86emu *);
static uint8_t*         decode_rh_byte_register(struct x86emu *);
static uint16_t*        decode_rh_word_register(struct x86emu *);
static uint32_t*        decode_rh_long_register(struct x86emu *);
static uint16_t*        decode_rh_seg_register(struct x86emu *);
static uint32_t decode_rl_address(struct x86emu *);

static uint8_t  decode_and_fetch_byte(struct x86emu *);
static uint16_t         decode_and_fetch_word(struct x86emu *);
static uint32_t         decode_and_fetch_long(struct x86emu *);

static uint8_t  decode_and_fetch_byte_imm8(struct x86emu *, uint8_t *);
static uint16_t         decode_and_fetch_word_imm8(struct x86emu *, uint8_t *);
static uint32_t         decode_and_fetch_long_imm8(struct x86emu *, uint8_t *);

static uint16_t         decode_and_fetch_word_disp(struct x86emu *, int16_t);
static uint32_t         decode_and_fetch_long_disp(struct x86emu *, int16_t);

static void     write_back_byte(struct x86emu *, uint8_t);
static void     write_back_word(struct x86emu *, uint16_t);
static void     write_back_long(struct x86emu *, uint32_t);

static uint16_t aaa_word (struct x86emu *, uint16_t d);
static uint16_t aas_word (struct x86emu *, uint16_t d);
static uint16_t aad_word (struct x86emu *, uint16_t d);
static uint16_t aam_word (struct x86emu *, uint8_t d);
static uint8_t  adc_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t adc_word (struct x86emu *, uint16_t d, uint16_t s);
static uint32_t adc_long (struct x86emu *, uint32_t d, uint32_t s);
static uint8_t  add_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t add_word (struct x86emu *, uint16_t d, uint16_t s);
static uint32_t add_long (struct x86emu *, uint32_t d, uint32_t s);
static uint8_t  and_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t and_word (struct x86emu *, uint16_t d, uint16_t s);
static uint32_t and_long (struct x86emu *, uint32_t d, uint32_t s);
static uint8_t  cmp_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t cmp_word (struct x86emu *, uint16_t d, uint16_t s);
static uint32_t cmp_long (struct x86emu *, uint32_t d, uint32_t s);
static void     cmp_byte_no_return (struct x86emu *, uint8_t d, uint8_t s);
static void     cmp_word_no_return (struct x86emu *, uint16_t d, uint16_t s);
static void     cmp_long_no_return (struct x86emu *, uint32_t d, uint32_t s);
static uint8_t  daa_byte (struct x86emu *, uint8_t d);
static uint8_t  das_byte (struct x86emu *, uint8_t d);
static uint8_t  dec_byte (struct x86emu *, uint8_t d);
static uint16_t dec_word (struct x86emu *, uint16_t d);
static uint32_t dec_long (struct x86emu *, uint32_t d);
static uint8_t  inc_byte (struct x86emu *, uint8_t d);
static uint16_t inc_word (struct x86emu *, uint16_t d);
static uint32_t inc_long (struct x86emu *, uint32_t d);
static uint8_t  or_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t or_word (struct x86emu *, uint16_t d, uint16_t s);
static uint32_t or_long (struct x86emu *, uint32_t d, uint32_t s);
static uint8_t  neg_byte (struct x86emu *, uint8_t s);
static uint16_t neg_word (struct x86emu *, uint16_t s);
static uint32_t neg_long (struct x86emu *, uint32_t s);
static uint8_t  rcl_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t rcl_word (struct x86emu *, uint16_t d, uint8_t s);
static uint32_t rcl_long (struct x86emu *, uint32_t d, uint8_t s);
static uint8_t  rcr_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t rcr_word (struct x86emu *, uint16_t d, uint8_t s);
static uint32_t rcr_long (struct x86emu *, uint32_t d, uint8_t s);
static uint8_t  rol_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t rol_word (struct x86emu *, uint16_t d, uint8_t s);
static uint32_t rol_long (struct x86emu *, uint32_t d, uint8_t s);
static uint8_t  ror_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t ror_word (struct x86emu *, uint16_t d, uint8_t s);
static uint32_t ror_long (struct x86emu *, uint32_t d, uint8_t s);
static uint8_t  shl_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t shl_word (struct x86emu *, uint16_t d, uint8_t s);
static uint32_t shl_long (struct x86emu *, uint32_t d, uint8_t s);
static uint8_t  shr_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t shr_word (struct x86emu *, uint16_t d, uint8_t s);
static uint32_t shr_long (struct x86emu *, uint32_t d, uint8_t s);
static uint8_t  sar_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t sar_word (struct x86emu *, uint16_t d, uint8_t s);
static uint32_t sar_long (struct x86emu *, uint32_t d, uint8_t s);
static uint16_t shld_word (struct x86emu *, uint16_t d, uint16_t fill, uint8_t s);
static uint32_t shld_long (struct x86emu *, uint32_t d, uint32_t fill, uint8_t s);
static uint16_t shrd_word (struct x86emu *, uint16_t d, uint16_t fill, uint8_t s);
static uint32_t shrd_long (struct x86emu *, uint32_t d, uint32_t fill, uint8_t s);
static uint8_t  sbb_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t sbb_word (struct x86emu *, uint16_t d, uint16_t s);
static uint32_t sbb_long (struct x86emu *, uint32_t d, uint32_t s);
static uint8_t  sub_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t sub_word (struct x86emu *, uint16_t d, uint16_t s);
static uint32_t sub_long (struct x86emu *, uint32_t d, uint32_t s);
static void     test_byte (struct x86emu *, uint8_t d, uint8_t s);
static void     test_word (struct x86emu *, uint16_t d, uint16_t s);
static void     test_long (struct x86emu *, uint32_t d, uint32_t s);
static uint8_t  xor_byte (struct x86emu *, uint8_t d, uint8_t s);
static uint16_t xor_word (struct x86emu *, uint16_t d, uint16_t s);
static uint32_t xor_long (struct x86emu *, uint32_t d, uint32_t s);
static void     imul_byte (struct x86emu *, uint8_t s);
static void     imul_word (struct x86emu *, uint16_t s);
static void     imul_long (struct x86emu *, uint32_t s);
static void     mul_byte (struct x86emu *, uint8_t s);
static void     mul_word (struct x86emu *, uint16_t s);
static void     mul_long (struct x86emu *, uint32_t s);
static void     idiv_byte (struct x86emu *, uint8_t s);
static void     idiv_word (struct x86emu *, uint16_t s);
static void     idiv_long (struct x86emu *, uint32_t s);
static void     div_byte (struct x86emu *, uint8_t s);
static void     div_word (struct x86emu *, uint16_t s);
static void     div_long (struct x86emu *, uint32_t s);
static void     ins (struct x86emu *, int size);
static void     outs (struct x86emu *, int size);
static void     push_word (struct x86emu *, uint16_t w);
static void     push_long (struct x86emu *, uint32_t w);
static uint16_t pop_word (struct x86emu *);
static uint32_t pop_long (struct x86emu *);

/*
 * REMARKS:
 * Handles any pending asynchronous interrupts.
 */
static void
x86emu_intr_dispatch(struct x86emu *emu, uint8_t intno)
{
        if (emu->_x86emu_intrTab[intno]) {
                (*emu->_x86emu_intrTab[intno]) (emu, intno);
        } else {
                push_word(emu, (uint16_t) emu->x86.R_FLG);
                CLEAR_FLAG(F_IF);
                CLEAR_FLAG(F_TF);
                push_word(emu, emu->x86.R_CS);
                emu->x86.R_CS = fetch_word(emu, 0, intno * 4 + 2);
                push_word(emu, emu->x86.R_IP);
                emu->x86.R_IP = fetch_word(emu, 0, intno * 4);
        }
}

static void 
x86emu_intr_handle(struct x86emu *emu)
{
        uint8_t intno;

        if (emu->x86.intr & INTR_SYNCH) {
                intno = emu->x86.intno;
                emu->x86.intr = 0;
                x86emu_intr_dispatch(emu, intno);
        }
}

/*
 * PARAMETERS:
 * intrnum - Interrupt number to raise
 * 
 * REMARKS:
 * Raise the specified interrupt to be handled before the execution of the
 * next instruction.
 */
void 
x86emu_intr_raise(struct x86emu *emu, uint8_t intrnum)
{
        emu->x86.intno = intrnum;
        emu->x86.intr |= INTR_SYNCH;
}

/*
 * REMARKS:
 * Main execution loop for the emulator. We return from here when the system
 * halts, which is normally caused by a stack fault when we return from the
 * original real mode call.
 */
void 
x86emu_exec(struct x86emu *emu)
{
        emu->x86.intr = 0;

#ifdef _KERNEL
        if (setjmp(&emu->exec_state))
                return;
#else
        if (setjmp(emu->exec_state))
                return;
#endif

        for (;;) {
                if (emu->x86.intr) {
                        if (((emu->x86.intr & INTR_SYNCH) &&
                            (emu->x86.intno == 0 || emu->x86.intno == 2)) ||
                            !ACCESS_FLAG(F_IF)) {
                                x86emu_intr_handle(emu);
                        }
                }
                if (emu->x86.R_CS == 0 && emu->x86.R_IP == 0)
                        return;
                x86emu_exec_one_byte(emu);
                ++emu->cur_cycles;
        }
}

void
x86emu_exec_call(struct x86emu *emu, uint16_t seg, uint16_t off)
{
        push_word(emu, 0);
        push_word(emu, 0);
        emu->x86.R_CS = seg;
        emu->x86.R_IP = off;

        x86emu_exec(emu);
}

void
x86emu_exec_intr(struct x86emu *emu, uint8_t intr)
{
        push_word(emu, emu->x86.R_FLG);
        CLEAR_FLAG(F_IF);
        CLEAR_FLAG(F_TF);
        push_word(emu, 0);
        push_word(emu, 0);
        emu->x86.R_CS = (*emu->emu_rdw)(emu, intr * 4 + 2);
        emu->x86.R_IP = (*emu->emu_rdw)(emu, intr * 4);
        emu->x86.intr = 0;

        x86emu_exec(emu);
}

/*
 * REMARKS:
 * Halts the system by setting the halted system flag.
 */
void 
x86emu_halt_sys(struct x86emu *emu)
{
#ifdef _KERNEL
        longjmp(&emu->exec_state);
#else
        longjmp(emu->exec_state, 1);
#endif
}

/*
 * PARAMETERS:
 * mod          - Mod value from decoded byte
 * regh - Reg h value from decoded byte
 * regl - Reg l value from decoded byte
 * 
 * REMARKS:
 * Raise the specified interrupt to be handled before the execution of the
 * next instruction.
 * 
 * NOTE: Do not inline this function, as (*emu->emu_rdb) is already inline!
 */
static void 
fetch_decode_modrm(struct x86emu *emu)
{
        int fetched;

        fetched = fetch_byte_imm(emu);
        emu->cur_mod = (fetched >> 6) & 0x03;
        emu->cur_rh = (fetched >> 3) & 0x07;
        emu->cur_rl = (fetched >> 0) & 0x07;
}

/*
 * RETURNS:
 * Immediate byte value read from instruction queue
 * 
 * REMARKS:
 * This function returns the immediate byte from the instruction queue, and
 * moves the instruction pointer to the next value.
 * 
 * NOTE: Do not inline this function, as (*emu->emu_rdb) is already inline!
 */
static uint8_t 
fetch_byte_imm(struct x86emu *emu)
{
        uint8_t fetched;

        fetched = fetch_byte(emu, emu->x86.R_CS, emu->x86.R_IP);
        emu->x86.R_IP++;
        return fetched;
}

/*
 * RETURNS:
 * Immediate word value read from instruction queue
 * 
 * REMARKS:
 * This function returns the immediate byte from the instruction queue, and
 * moves the instruction pointer to the next value.
 * 
 * NOTE: Do not inline this function, as (*emu->emu_rdw) is already inline!
 */
static uint16_t 
fetch_word_imm(struct x86emu *emu)
{
        uint16_t fetched;

        fetched = fetch_word(emu, emu->x86.R_CS, emu->x86.R_IP);
        emu->x86.R_IP += 2;
        return fetched;
}

/*
 * RETURNS:
 * Immediate lone value read from instruction queue
 * 
 * REMARKS:
 * This function returns the immediate byte from the instruction queue, and
 * moves the instruction pointer to the next value.
 * 
 * NOTE: Do not inline this function, as (*emu->emu_rdw) is already inline!
 */
static uint32_t 
fetch_long_imm(struct x86emu *emu)
{
        uint32_t fetched;

        fetched = fetch_long(emu, emu->x86.R_CS, emu->x86.R_IP);
        emu->x86.R_IP += 4;
        return fetched;
}

/*
 * RETURNS:
 * Value of the default data segment
 * 
 * REMARKS:
 * Inline function that returns the default data segment for the current
 * instruction.
 * 
 * On the x86 processor, the default segment is not always DS if there is
 * no segment override. Address modes such as -3[BP] or 10[BP+SI] all refer to
 * addresses relative to SS (ie: on the stack). So, at the minimum, all
 * decodings of addressing modes would have to set/clear a bit describing
 * whether the access is relative to DS or SS.  That is the function of the
 * cpu-state-variable emu->x86.mode. There are several potential states:
 * 
 *      repe prefix seen  (handled elsewhere)
 *      repne prefix seen  (ditto)
 * 
 *      cs segment override
 *      ds segment override
 *      es segment override
 *      fs segment override
 *      gs segment override
 *      ss segment override
 * 
 *      ds/ss select (in absence of override)
 * 
 * Each of the above 7 items are handled with a bit in the mode field.
 */
static uint32_t 
get_data_segment(struct x86emu *emu)
{
        switch (emu->x86.mode & SYSMODE_SEGMASK) {
        case 0:         /* default case: use ds register */
        case SYSMODE_SEGOVR_DS:
        case SYSMODE_SEGOVR_DS | SYSMODE_SEG_DS_SS:
                return emu->x86.R_DS;
        case SYSMODE_SEG_DS_SS:/* non-overridden, use ss register */
                return emu->x86.R_SS;
        case SYSMODE_SEGOVR_CS:
        case SYSMODE_SEGOVR_CS | SYSMODE_SEG_DS_SS:
                return emu->x86.R_CS;
        case SYSMODE_SEGOVR_ES:
        case SYSMODE_SEGOVR_ES | SYSMODE_SEG_DS_SS:
                return emu->x86.R_ES;
        case SYSMODE_SEGOVR_FS:
        case SYSMODE_SEGOVR_FS | SYSMODE_SEG_DS_SS:
                return emu->x86.R_FS;
        case SYSMODE_SEGOVR_GS:
        case SYSMODE_SEGOVR_GS | SYSMODE_SEG_DS_SS:
                return emu->x86.R_GS;
        case SYSMODE_SEGOVR_SS:
        case SYSMODE_SEGOVR_SS | SYSMODE_SEG_DS_SS:
                return emu->x86.R_SS;
        }
        x86emu_halt_sys(emu);
}

/*
 * PARAMETERS:
 * offset       - Offset to load data from
 * 
 * RETURNS:
 * Byte value read from the absolute memory location.
 * 
 * NOTE: Do not inline this function as (*emu->emu_rdX) is already inline!
 */
static uint8_t 
fetch_data_byte(struct x86emu *emu, uint32_t offset)
{
        return fetch_byte(emu, get_data_segment(emu), offset);
}

/*
 * PARAMETERS:
 * offset       - Offset to load data from
 * 
 * RETURNS:
 * Word value read from the absolute memory location.
 * 
 * NOTE: Do not inline this function as (*emu->emu_rdX) is already inline!
 */
static uint16_t 
fetch_data_word(struct x86emu *emu, uint32_t offset)
{
        return fetch_word(emu, get_data_segment(emu), offset);
}

/*
 * PARAMETERS:
 * offset       - Offset to load data from
 * 
 * RETURNS:
 * Long value read from the absolute memory location.
 * 
 * NOTE: Do not inline this function as (*emu->emu_rdX) is already inline!
 */
static uint32_t 
fetch_data_long(struct x86emu *emu, uint32_t offset)
{
        return fetch_long(emu, get_data_segment(emu), offset);
}

/*
 * PARAMETERS:
 * segment      - Segment to load data from
 * offset       - Offset to load data from
 * 
 * RETURNS:
 * Byte value read from the absolute memory location.
 * 
 * NOTE: Do not inline this function as (*emu->emu_rdX) is already inline!
 */
static uint8_t 
fetch_byte(struct x86emu *emu, uint32_t segment, uint32_t offset)
{
        return (*emu->emu_rdb) (emu, ((uint32_t) segment << 4) + offset);
}

/*
 * PARAMETERS:
 * segment      - Segment to load data from
 * offset       - Offset to load data from
 * 
 * RETURNS:
 * Word value read from the absolute memory location.
 * 
 * NOTE: Do not inline this function as (*emu->emu_rdX) is already inline!
 */
static uint16_t 
fetch_word(struct x86emu *emu, uint32_t segment, uint32_t offset)
{
        return (*emu->emu_rdw) (emu, ((uint32_t) segment << 4) + offset);
}

/*
 * PARAMETERS:
 * segment      - Segment to load data from
 * offset       - Offset to load data from
 * 
 * RETURNS:
 * Long value read from the absolute memory location.
 * 
 * NOTE: Do not inline this function as (*emu->emu_rdX) is already inline!
 */
static uint32_t 
fetch_long(struct x86emu *emu, uint32_t segment, uint32_t offset)
{
        return (*emu->emu_rdl) (emu, ((uint32_t) segment << 4) + offset);
}

/*
 * PARAMETERS:
 * offset       - Offset to store data at
 * val          - Value to store
 * 
 * REMARKS:
 * Writes a word value to an segmented memory location. The segment used is
 * the current 'default' segment, which may have been overridden.
 * 
 * NOTE: Do not inline this function as (*emu->emu_wrX) is already inline!
 */
static void 
store_data_byte(struct x86emu *emu, uint32_t offset, uint8_t val)
{
        store_byte(emu, get_data_segment(emu), offset, val);
}

/*
 * PARAMETERS:
 * offset       - Offset to store data at
 * val          - Value to store
 * 
 * REMARKS:
 * Writes a word value to an segmented memory location. The segment used is
 * the current 'default' segment, which may have been overridden.
 * 
 * NOTE: Do not inline this function as (*emu->emu_wrX) is already inline!
 */
static void 
store_data_word(struct x86emu *emu, uint32_t offset, uint16_t val)
{
        store_word(emu, get_data_segment(emu), offset, val);
}

/*
 * PARAMETERS:
 * offset       - Offset to store data at
 * val          - Value to store
 * 
 * REMARKS:
 * Writes a long value to an segmented memory location. The segment used is
 * the current 'default' segment, which may have been overridden.
 * 
 * NOTE: Do not inline this function as (*emu->emu_wrX) is already inline!
 */
static void 
store_data_long(struct x86emu *emu, uint32_t offset, uint32_t val)
{
        store_long(emu, get_data_segment(emu), offset, val);
}

/*
 * PARAMETERS:
 * segment      - Segment to store data at
 * offset       - Offset to store data at
 * val          - Value to store
 * 
 * REMARKS:
 * Writes a byte value to an absolute memory location.
 * 
 * NOTE: Do not inline this function as (*emu->emu_wrX) is already inline!
 */
static void 
store_byte(struct x86emu *emu, uint32_t segment, uint32_t offset, uint8_t val)
{
        (*emu->emu_wrb) (emu, ((uint32_t) segment << 4) + offset, val);
}

/*
 * PARAMETERS:
 * segment      - Segment to store data at
 * offset       - Offset to store data at
 * val          - Value to store
 * 
 * REMARKS:
 * Writes a word value to an absolute memory location.
 * 
 * NOTE: Do not inline this function as (*emu->emu_wrX) is already inline!
 */
static void 
store_word(struct x86emu *emu, uint32_t segment, uint32_t offset, uint16_t val)
{
        (*emu->emu_wrw) (emu, ((uint32_t) segment << 4) + offset, val);
}

/*
 * PARAMETERS:
 * segment      - Segment to store data at
 * offset       - Offset to store data at
 * val          - Value to store
 * 
 * REMARKS:
 * Writes a long value to an absolute memory location.
 * 
 * NOTE: Do not inline this function as (*emu->emu_wrX) is already inline!
 */
static void 
store_long(struct x86emu *emu, uint32_t segment, uint32_t offset, uint32_t val)
{
        (*emu->emu_wrl) (emu, ((uint32_t) segment << 4) + offset, val);
}

/*
 * PARAMETERS:
 * reg  - Register to decode
 * 
 * RETURNS:
 * Pointer to the appropriate register
 * 
 * REMARKS:
 * Return a pointer to the register given by the R/RM field of the
 * modrm byte, for byte operands. Also enables the decoding of instructions.
 */
static uint8_t *
decode_rm_byte_register(struct x86emu *emu, int reg)
{
        switch (reg) {
        case 0:
                return &emu->x86.R_AL;
        case 1:
                return &emu->x86.R_CL;
        case 2:
                return &emu->x86.R_DL;
        case 3:
                return &emu->x86.R_BL;
        case 4:
                return &emu->x86.R_AH;
        case 5:
                return &emu->x86.R_CH;
        case 6:
                return &emu->x86.R_DH;
        case 7:
                return &emu->x86.R_BH;
        default:
                x86emu_halt_sys(emu);
        }
}

static uint8_t *
decode_rl_byte_register(struct x86emu *emu)
{
        return decode_rm_byte_register(emu, emu->cur_rl);
}

static uint8_t *
decode_rh_byte_register(struct x86emu *emu)
{
        return decode_rm_byte_register(emu, emu->cur_rh);
}

/*
 * PARAMETERS:
 * reg  - Register to decode
 * 
 * RETURNS:
 * Pointer to the appropriate register
 * 
 * REMARKS:
 * Return a pointer to the register given by the R/RM field of the
 * modrm byte, for word operands.  Also enables the decoding of instructions.
 */
static uint16_t *
decode_rm_word_register(struct x86emu *emu, int reg)
{
        switch (reg) {
        case 0:
                return &emu->x86.R_AX;
        case 1:
                return &emu->x86.R_CX;
        case 2:
                return &emu->x86.R_DX;
        case 3:
                return &emu->x86.R_BX;
        case 4:
                return &emu->x86.R_SP;
        case 5:
                return &emu->x86.R_BP;
        case 6:
                return &emu->x86.R_SI;
        case 7:
                return &emu->x86.R_DI;
        default:
                x86emu_halt_sys(emu);
        }
}

static uint16_t *
decode_rl_word_register(struct x86emu *emu)
{
        return decode_rm_word_register(emu, emu->cur_rl);
}

static uint16_t *
decode_rh_word_register(struct x86emu *emu)
{
        return decode_rm_word_register(emu, emu->cur_rh);
}

/*
 * PARAMETERS:
 * reg  - Register to decode
 * 
 * RETURNS:
 * Pointer to the appropriate register
 * 
 * REMARKS:
 * Return a pointer to the register given by the R/RM field of the
 * modrm byte, for dword operands.  Also enables the decoding of instructions.
 */
static uint32_t *
decode_rm_long_register(struct x86emu *emu, int reg)
{
        switch (reg) {
        case 0:
                return &emu->x86.R_EAX;
        case 1:
                return &emu->x86.R_ECX;
        case 2:
                return &emu->x86.R_EDX;
        case 3:
                return &emu->x86.R_EBX;
        case 4:
                return &emu->x86.R_ESP;
        case 5:
                return &emu->x86.R_EBP;
        case 6:
                return &emu->x86.R_ESI;
        case 7:
                return &emu->x86.R_EDI;
        default:
                x86emu_halt_sys(emu);
        }
}

static uint32_t *
decode_rl_long_register(struct x86emu *emu)
{
        return decode_rm_long_register(emu, emu->cur_rl);
}

static uint32_t *
decode_rh_long_register(struct x86emu *emu)
{
        return decode_rm_long_register(emu, emu->cur_rh);
}


/*
 * PARAMETERS:
 * reg  - Register to decode
 * 
 * RETURNS:
 * Pointer to the appropriate register
 * 
 * REMARKS:
 * Return a pointer to the register given by the R/RM field of the
 * modrm byte, for word operands, modified from above for the weirdo
 * special case of segreg operands.  Also enables the decoding of instructions.
 */
static uint16_t *
decode_rh_seg_register(struct x86emu *emu)
{
        switch (emu->cur_rh) {
        case 0:
                return &emu->x86.R_ES;
        case 1:
                return &emu->x86.R_CS;
        case 2:
                return &emu->x86.R_SS;
        case 3:
                return &emu->x86.R_DS;
        case 4:
                return &emu->x86.R_FS;
        case 5:
                return &emu->x86.R_GS;
        default:
                x86emu_halt_sys(emu);
        }
}

/*
 * Return offset from the SIB Byte.
 */
static uint32_t 
decode_sib_address(struct x86emu *emu, int sib, int mod)
{
        uint32_t base = 0, i = 0, scale = 1;

        switch (sib & 0x07) {
        case 0:
                base = emu->x86.R_EAX;
                break;
        case 1:
                base = emu->x86.R_ECX;

                break;
        case 2:
                base = emu->x86.R_EDX;
                break;
        case 3:
                base = emu->x86.R_EBX;
                break;
        case 4:
                base = emu->x86.R_ESP;
                emu->x86.mode |= SYSMODE_SEG_DS_SS;
                break;
        case 5:
                if (mod == 0) {
                        base = fetch_long_imm(emu);
                } else {
                        base = emu->x86.R_EBP;
                        emu->x86.mode |= SYSMODE_SEG_DS_SS;
                }
                break;
        case 6:
                base = emu->x86.R_ESI;
                break;
        case 7:
                base = emu->x86.R_EDI;
                break;
        }
        switch ((sib >> 3) & 0x07) {
        case 0:
                i = emu->x86.R_EAX;
                break;
        case 1:
                i = emu->x86.R_ECX;
                break;
        case 2:
                i = emu->x86.R_EDX;
                break;
        case 3:
                i = emu->x86.R_EBX;
                break;
        case 4:
                i = 0;
                break;
        case 5:
                i = emu->x86.R_EBP;
                break;
        case 6:
                i = emu->x86.R_ESI;
                break;
        case 7:
                i = emu->x86.R_EDI;
                break;
        }
        scale = 1 << ((sib >> 6) & 0x03);
        return base + (i * scale);
}

/*
 * PARAMETERS:
 * rm   - RM value to decode
 * 
 * RETURNS:
 * Offset in memory for the address decoding
 * 
 * REMARKS:
 * Return the offset given by mod=00, mod=01 or mod=10 addressing.
 * Also enables the decoding of instructions.
 */
static uint32_t
decode_rl_address(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_ADDR) {
                uint32_t offset, sib;
                /* 32-bit addressing */
                switch (emu->cur_rl) {
                case 0:
                        offset = emu->x86.R_EAX;
                        break;
                case 1:
                        offset = emu->x86.R_ECX;
                        break;
                case 2:
                        offset = emu->x86.R_EDX;
                        break;
                case 3:
                        offset = emu->x86.R_EBX;
                        break;
                case 4:
                        sib = fetch_byte_imm(emu);
                        offset = decode_sib_address(emu, sib, 0);
                        break;
                case 5:
                        if (emu->cur_mod == 0) {
                                offset = fetch_long_imm(emu);
                        } else {
                                emu->x86.mode |= SYSMODE_SEG_DS_SS;
                                offset = emu->x86.R_EBP;
                        }
                        break;
                case 6:
                        offset = emu->x86.R_ESI;
                        break;
                case 7:
                        offset = emu->x86.R_EDI;
                        break;
                default:
                        x86emu_halt_sys(emu);
                }
                if (emu->cur_mod == 1)
                        offset += (int8_t)fetch_byte_imm(emu);
                else if (emu->cur_mod == 2)
                        offset += fetch_long_imm(emu);
                return offset;
        } else {
                uint16_t offset;

                /* 16-bit addressing */
                switch (emu->cur_rl) {
                case 0:
                        offset = emu->x86.R_BX + emu->x86.R_SI;
                        break;
                case 1:
                        offset = emu->x86.R_BX + emu->x86.R_DI;
                        break;
                case 2:
                        emu->x86.mode |= SYSMODE_SEG_DS_SS;
                        offset = emu->x86.R_BP + emu->x86.R_SI;
                        break;
                case 3:
                        emu->x86.mode |= SYSMODE_SEG_DS_SS;
                        offset = emu->x86.R_BP + emu->x86.R_DI;
                        break;
                case 4:
                        offset = emu->x86.R_SI;
                        break;
                case 5:
                        offset = emu->x86.R_DI;
                        break;
                case 6:
                        if (emu->cur_mod == 0) {
                                offset = fetch_word_imm(emu);
                        } else {
                                emu->x86.mode |= SYSMODE_SEG_DS_SS;
                                offset = emu->x86.R_BP;
                        }
                        break;
                case 7:
                        offset = emu->x86.R_BX;
                        break;
                default:
                        x86emu_halt_sys(emu);
                }
                if (emu->cur_mod == 1)
                        offset += (int8_t)fetch_byte_imm(emu);
                else if (emu->cur_mod == 2)
                        offset += fetch_word_imm(emu);
                return offset;
        }
}

static uint8_t
decode_and_fetch_byte(struct x86emu *emu)
{
        if (emu->cur_mod != 3) {
                emu->cur_offset = decode_rl_address(emu);
                return fetch_data_byte(emu, emu->cur_offset);
        } else {
                return *decode_rl_byte_register(emu);
        }
}

static uint16_t
decode_and_fetch_word_disp(struct x86emu *emu, int16_t disp)
{
        if (emu->cur_mod != 3) {
                /* TODO: A20 gate emulation */
                emu->cur_offset = decode_rl_address(emu) + disp;
                if ((emu->x86.mode & SYSMODE_PREFIX_ADDR) == 0)
                        emu->cur_offset &= 0xffff;
                return fetch_data_word(emu, emu->cur_offset);
        } else {
                return *decode_rl_word_register(emu);
        }
}

static uint32_t
decode_and_fetch_long_disp(struct x86emu *emu, int16_t disp)
{
        if (emu->cur_mod != 3) {
                /* TODO: A20 gate emulation */
                emu->cur_offset = decode_rl_address(emu) + disp;
                if ((emu->x86.mode & SYSMODE_PREFIX_ADDR) == 0)
                        emu->cur_offset &= 0xffff;
                return fetch_data_long(emu, emu->cur_offset);
        } else {
                return *decode_rl_long_register(emu);
        }
}

uint16_t
decode_and_fetch_word(struct x86emu *emu)
{
        return decode_and_fetch_word_disp(emu, 0);
}

uint32_t
decode_and_fetch_long(struct x86emu *emu)
{
        return decode_and_fetch_long_disp(emu, 0);
}

uint8_t
decode_and_fetch_byte_imm8(struct x86emu *emu, uint8_t *imm)
{
        if (emu->cur_mod != 3) {
                emu->cur_offset = decode_rl_address(emu);
                *imm = fetch_byte_imm(emu);
                return fetch_data_byte(emu, emu->cur_offset);
        } else {
                *imm = fetch_byte_imm(emu);
                return *decode_rl_byte_register(emu);
        }
}

static uint16_t
decode_and_fetch_word_imm8(struct x86emu *emu, uint8_t *imm)
{
        if (emu->cur_mod != 3) {
                emu->cur_offset = decode_rl_address(emu);
                *imm = fetch_byte_imm(emu);
                return fetch_data_word(emu, emu->cur_offset);
        } else {
                *imm = fetch_byte_imm(emu);
                return *decode_rl_word_register(emu);
        }
}

static uint32_t
decode_and_fetch_long_imm8(struct x86emu *emu, uint8_t *imm)
{
        if (emu->cur_mod != 3) {
                emu->cur_offset = decode_rl_address(emu);
                *imm = fetch_byte_imm(emu);
                return fetch_data_long(emu, emu->cur_offset);
        } else {
                *imm = fetch_byte_imm(emu);
                return *decode_rl_long_register(emu);
        }
}

static void
write_back_byte(struct x86emu *emu, uint8_t val)
{
        if (emu->cur_mod != 3)
                store_data_byte(emu, emu->cur_offset, val);
        else
                *decode_rl_byte_register(emu) = val;
}

static void
write_back_word(struct x86emu *emu, uint16_t val)
{
        if (emu->cur_mod != 3)
                store_data_word(emu, emu->cur_offset, val);
        else
                *decode_rl_word_register(emu) = val;
}

static void
write_back_long(struct x86emu *emu, uint32_t val)
{
        if (emu->cur_mod != 3)
                store_data_long(emu, emu->cur_offset, val);
        else
                *decode_rl_long_register(emu) = val;
}

static void
common_inc_word_long(struct x86emu *emu, union x86emu_register *reg)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                reg->I32_reg.e_reg = inc_long(emu, reg->I32_reg.e_reg);
        else
                reg->I16_reg.x_reg = inc_word(emu, reg->I16_reg.x_reg);
}

static void
common_dec_word_long(struct x86emu *emu, union x86emu_register *reg)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                reg->I32_reg.e_reg = dec_long(emu, reg->I32_reg.e_reg);
        else
                reg->I16_reg.x_reg = dec_word(emu, reg->I16_reg.x_reg);
}

static void
common_binop_byte_rm_r(struct x86emu *emu, 
    uint8_t (*binop)(struct x86emu *, uint8_t, uint8_t))
{
        uint32_t destoffset;
        uint8_t *destreg, srcval;
        uint8_t destval;

        fetch_decode_modrm(emu);
        srcval = *decode_rh_byte_register(emu);
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                destval = fetch_data_byte(emu, destoffset);
                destval = (*binop)(emu, destval, srcval);
                store_data_byte(emu, destoffset, destval);
        } else {
                destreg = decode_rl_byte_register(emu);
                *destreg = (*binop)(emu, *destreg, srcval);
        }
}

static void
common_binop_ns_byte_rm_r(struct x86emu *emu, 
    void (*binop)(struct x86emu *, uint8_t, uint8_t))
{
        uint32_t destoffset;
        uint8_t destval, srcval;

        fetch_decode_modrm(emu);
        srcval = *decode_rh_byte_register(emu);
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                destval = fetch_data_byte(emu, destoffset);
        } else {
                destval = *decode_rl_byte_register(emu);
        }
        (*binop)(emu, destval, srcval);
}

static void
common_binop_word_rm_r(struct x86emu *emu,
    uint16_t (*binop)(struct x86emu *, uint16_t, uint16_t))
{
        uint32_t destoffset;
        uint16_t destval, *destreg, srcval;

        fetch_decode_modrm(emu);
        srcval = *decode_rh_word_register(emu);
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                destval = fetch_data_word(emu, destoffset);
                destval = (*binop)(emu, destval, srcval);
                store_data_word(emu, destoffset, destval);
        } else {
                destreg = decode_rl_word_register(emu);
                *destreg = (*binop)(emu, *destreg, srcval);
        }
}

static void
common_binop_byte_r_rm(struct x86emu *emu,
    uint8_t (*binop)(struct x86emu *, uint8_t, uint8_t))
{
        uint8_t *destreg, srcval;
        uint32_t srcoffset;

        fetch_decode_modrm(emu);
        destreg = decode_rh_byte_register(emu);
        if (emu->cur_mod != 3) {
                srcoffset = decode_rl_address(emu);
                srcval = fetch_data_byte(emu, srcoffset);
        } else {
                srcval = *decode_rl_byte_register(emu);
        }
        *destreg = (*binop)(emu, *destreg, srcval);
}

static void
common_binop_long_rm_r(struct x86emu *emu,
    uint32_t (*binop)(struct x86emu *, uint32_t, uint32_t))
{
        uint32_t destoffset;
        uint32_t destval, *destreg, srcval;

        fetch_decode_modrm(emu);
        srcval = *decode_rh_long_register(emu);
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                destval = fetch_data_long(emu, destoffset);
                destval = (*binop)(emu, destval, srcval);
                store_data_long(emu, destoffset, destval);
        } else {
                destreg = decode_rl_long_register(emu);
                *destreg = (*binop)(emu, *destreg, srcval);
        }
}

static void
common_binop_word_long_rm_r(struct x86emu *emu,
    uint16_t (*binop16)(struct x86emu *, uint16_t, uint16_t),
    uint32_t (*binop32)(struct x86emu *, uint32_t, uint32_t))
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                common_binop_long_rm_r(emu, binop32);
        else
                common_binop_word_rm_r(emu, binop16);
}

static void
common_binop_ns_word_rm_r(struct x86emu *emu,
    void (*binop)(struct x86emu *, uint16_t, uint16_t))
{
        uint32_t destoffset;
        uint16_t destval, srcval;

        fetch_decode_modrm(emu);
        srcval = *decode_rh_word_register(emu);
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                destval = fetch_data_word(emu, destoffset);
        } else {
                destval = *decode_rl_word_register(emu);
        }
        (*binop)(emu, destval, srcval);
}


static void
common_binop_ns_long_rm_r(struct x86emu *emu,
    void (*binop)(struct x86emu *, uint32_t, uint32_t))
{
        uint32_t destoffset;
        uint32_t destval, srcval;

        fetch_decode_modrm(emu);
        srcval = *decode_rh_long_register(emu);
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                destval = fetch_data_long(emu, destoffset);
        } else {
                destval = *decode_rl_long_register(emu);
        }
        (*binop)(emu, destval, srcval);
}

static void
common_binop_ns_word_long_rm_r(struct x86emu *emu,
    void (*binop16)(struct x86emu *, uint16_t, uint16_t),
    void (*binop32)(struct x86emu *, uint32_t, uint32_t))
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                common_binop_ns_long_rm_r(emu, binop32);
        else
                common_binop_ns_word_rm_r(emu, binop16);
}

static void
common_binop_long_r_rm(struct x86emu *emu,
    uint32_t (*binop)(struct x86emu *, uint32_t, uint32_t))
{
        uint32_t srcoffset;
        uint32_t *destreg, srcval;

        fetch_decode_modrm(emu);
        destreg = decode_rh_long_register(emu);
        if (emu->cur_mod != 3) {
                srcoffset = decode_rl_address(emu);
                srcval = fetch_data_long(emu, srcoffset);
        } else {
                srcval = *decode_rl_long_register(emu);
        }
        *destreg = (*binop)(emu, *destreg, srcval);
}

static void
common_binop_word_r_rm(struct x86emu *emu,
    uint16_t (*binop)(struct x86emu *, uint16_t, uint16_t))
{
        uint32_t srcoffset;
        uint16_t *destreg, srcval;

        fetch_decode_modrm(emu);
        destreg = decode_rh_word_register(emu);
        if (emu->cur_mod != 3) {
                srcoffset = decode_rl_address(emu);
                srcval = fetch_data_word(emu, srcoffset);
        } else {
                srcval = *decode_rl_word_register(emu);
        }
        *destreg = (*binop)(emu, *destreg, srcval);
}

static void
common_binop_word_long_r_rm(struct x86emu *emu,
    uint16_t (*binop16)(struct x86emu *, uint16_t, uint16_t),
    uint32_t (*binop32)(struct x86emu *, uint32_t, uint32_t))
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                common_binop_long_r_rm(emu, binop32);
        else
                common_binop_word_r_rm(emu, binop16);
}

static void
common_binop_byte_imm(struct x86emu *emu,
    uint8_t (*binop)(struct x86emu *, uint8_t, uint8_t))
{
        uint8_t srcval;

        srcval = fetch_byte_imm(emu);
        emu->x86.R_AL = (*binop)(emu, emu->x86.R_AL, srcval);
}

static void
common_binop_word_long_imm(struct x86emu *emu,
    uint16_t (*binop16)(struct x86emu *, uint16_t, uint16_t),
    uint32_t (*binop32)(struct x86emu *, uint32_t, uint32_t))
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                uint32_t srcval;

                srcval = fetch_long_imm(emu);
                emu->x86.R_EAX = (*binop32)(emu, emu->x86.R_EAX, srcval);
        } else {
                uint16_t srcval;

                srcval = fetch_word_imm(emu);
                emu->x86.R_AX = (*binop16)(emu, emu->x86.R_AX, srcval);
        }
}

static void
common_push_word_long(struct x86emu *emu, union x86emu_register *reg)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                push_long(emu, reg->I32_reg.e_reg);
        else
                push_word(emu, reg->I16_reg.x_reg);
}

static void
common_pop_word_long(struct x86emu *emu, union x86emu_register *reg)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                reg->I32_reg.e_reg = pop_long(emu);
        else
                reg->I16_reg.x_reg = pop_word(emu);
}

static void
common_imul_long_IMM(struct x86emu *emu, int byte_imm)
{
        uint32_t srcoffset;
        uint32_t *destreg, srcval;
        int32_t imm;
        uint64_t res;

        fetch_decode_modrm(emu);
        destreg = decode_rh_long_register(emu);
        if (emu->cur_mod != 3) {
                srcoffset = decode_rl_address(emu);
                srcval = fetch_data_long(emu, srcoffset);
        } else {
                srcval = *decode_rl_long_register(emu);
        }

        if (byte_imm)
                imm = (int8_t)fetch_byte_imm(emu);
        else
                imm = fetch_long_imm(emu);
        res = (int32_t)srcval * imm;

        if (res > 0xffffffff) {
                SET_FLAG(F_CF);
                SET_FLAG(F_OF);
        } else {
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_OF);
        }
        *destreg = (uint32_t)res;
}

static void
common_imul_word_IMM(struct x86emu *emu, int byte_imm)
{
        uint32_t srcoffset;
        uint16_t *destreg, srcval;
        int16_t imm;
        uint32_t res;

        fetch_decode_modrm(emu);
        destreg = decode_rh_word_register(emu);
        if (emu->cur_mod != 3) {
                srcoffset = decode_rl_address(emu);
                srcval = fetch_data_word(emu, srcoffset);
        } else {
                srcval = *decode_rl_word_register(emu);
        }

        if (byte_imm)
                imm = (int8_t)fetch_byte_imm(emu);
        else
                imm = fetch_word_imm(emu);
        res = (int16_t)srcval * imm;

        if (res > 0xffff) {
                SET_FLAG(F_CF);
                SET_FLAG(F_OF);
        } else {
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_OF);
        }
        *destreg = (uint16_t) res;
}

static void
common_imul_imm(struct x86emu *emu, int byte_imm)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                common_imul_long_IMM(emu, byte_imm);
        else
                common_imul_word_IMM(emu, byte_imm);
}

static void
common_jmp_near(struct x86emu *emu, int cond)
{
        int8_t offset;
        uint16_t target;

        offset = (int8_t) fetch_byte_imm(emu);
        target = (uint16_t) (emu->x86.R_IP + (int16_t) offset);
        if (cond)
                emu->x86.R_IP = target;
}

static void
common_load_far_pointer(struct x86emu *emu, uint16_t *seg)
{
        uint16_t *dstreg;
        uint32_t srcoffset;

        fetch_decode_modrm(emu);
        if (emu->cur_mod == 3)
                x86emu_halt_sys(emu);

        dstreg = decode_rh_word_register(emu);
        srcoffset = decode_rl_address(emu);
        *dstreg = fetch_data_word(emu, srcoffset);
        *seg = fetch_data_word(emu, srcoffset + 2);
}

/* Implementation */

/*
 * REMARKS:
 * Handles opcode 0x3a
 */
static void
x86emuOp_cmp_byte_R_RM(struct x86emu *emu)
{
        uint8_t *destreg, srcval;

        fetch_decode_modrm(emu);
        destreg = decode_rh_byte_register(emu);
        srcval = decode_and_fetch_byte(emu);
        cmp_byte(emu, *destreg, srcval);
}

/*
 * REMARKS:
 * 
 * Handles opcode 0x3b
 */
static void
x86emuOp32_cmp_word_R_RM(struct x86emu *emu)
{
        uint32_t srcval, *destreg;

        fetch_decode_modrm(emu);
        destreg = decode_rh_long_register(emu);
        srcval = decode_and_fetch_long(emu);
        cmp_long(emu, *destreg, srcval);
}

static void
x86emuOp16_cmp_word_R_RM(struct x86emu *emu)
{
        uint16_t srcval, *destreg;

        fetch_decode_modrm(emu);
        destreg = decode_rh_word_register(emu);
        srcval = decode_and_fetch_word(emu);
        cmp_word(emu, *destreg, srcval);
}

static void
x86emuOp_cmp_word_R_RM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                x86emuOp32_cmp_word_R_RM(emu);
        else
                x86emuOp16_cmp_word_R_RM(emu);
}

/*
 * REMARKS:
 * Handles opcode 0x3c
 */
static void
x86emuOp_cmp_byte_AL_IMM(struct x86emu *emu)
{
        uint8_t srcval;

        srcval = fetch_byte_imm(emu);
        cmp_byte(emu, emu->x86.R_AL, srcval);
}

/*
 * REMARKS:
 * Handles opcode 0x3d
 */
static void
x86emuOp32_cmp_word_AX_IMM(struct x86emu *emu)
{
        uint32_t srcval;

        srcval = fetch_long_imm(emu);
        cmp_long(emu, emu->x86.R_EAX, srcval);
}

static void
x86emuOp16_cmp_word_AX_IMM(struct x86emu *emu)
{
        uint16_t srcval;

        srcval = fetch_word_imm(emu);
        cmp_word(emu, emu->x86.R_AX, srcval);
}

static void
x86emuOp_cmp_word_AX_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                x86emuOp32_cmp_word_AX_IMM(emu);
        else
                x86emuOp16_cmp_word_AX_IMM(emu);
}

/*
 * REMARKS:
 * Handles opcode 0x60
 */
static void
x86emuOp_push_all(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                uint32_t old_sp = emu->x86.R_ESP;

                push_long(emu, emu->x86.R_EAX);
                push_long(emu, emu->x86.R_ECX);
                push_long(emu, emu->x86.R_EDX);
                push_long(emu, emu->x86.R_EBX);
                push_long(emu, old_sp);
                push_long(emu, emu->x86.R_EBP);
                push_long(emu, emu->x86.R_ESI);
                push_long(emu, emu->x86.R_EDI);
        } else {
                uint16_t old_sp = emu->x86.R_SP;

                push_word(emu, emu->x86.R_AX);
                push_word(emu, emu->x86.R_CX);
                push_word(emu, emu->x86.R_DX);
                push_word(emu, emu->x86.R_BX);
                push_word(emu, old_sp);
                push_word(emu, emu->x86.R_BP);
                push_word(emu, emu->x86.R_SI);
                push_word(emu, emu->x86.R_DI);
        }
}

/*
 * REMARKS:
 * Handles opcode 0x61
 */
static void
x86emuOp_pop_all(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                emu->x86.R_EDI = pop_long(emu);
                emu->x86.R_ESI = pop_long(emu);
                emu->x86.R_EBP = pop_long(emu);
                emu->x86.R_ESP += 4;    /* skip ESP */
                emu->x86.R_EBX = pop_long(emu);
                emu->x86.R_EDX = pop_long(emu);
                emu->x86.R_ECX = pop_long(emu);
                emu->x86.R_EAX = pop_long(emu);
        } else {
                emu->x86.R_DI = pop_word(emu);
                emu->x86.R_SI = pop_word(emu);
                emu->x86.R_BP = pop_word(emu);
                emu->x86.R_SP += 2;/* skip SP */
                emu->x86.R_BX = pop_word(emu);
                emu->x86.R_DX = pop_word(emu);
                emu->x86.R_CX = pop_word(emu);
                emu->x86.R_AX = pop_word(emu);
        }
}
/*opcode 0x62   ILLEGAL OP, calls x86emuOp_illegal_op() */
/*opcode 0x63   ILLEGAL OP, calls x86emuOp_illegal_op() */


/*
 * REMARKS:
 * Handles opcode 0x68
 */
static void
x86emuOp_push_word_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                uint32_t imm;

                imm = fetch_long_imm(emu);
                push_long(emu, imm);
        } else {
                uint16_t imm;

                imm = fetch_word_imm(emu);
                push_word(emu, imm);
        }
}

/*
 * REMARKS:
 * Handles opcode 0x6a
 */
static void
x86emuOp_push_byte_IMM(struct x86emu *emu)
{
        int16_t imm;

        imm = (int8_t) fetch_byte_imm(emu);
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                push_long(emu, (int32_t) imm);
        } else {
                push_word(emu, imm);
        }
}

/*
 * REMARKS:
 * Handles opcode 0x6c and 0x6d
 */
static void
x86emuOp_ins_word(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                ins(emu, 4);
        } else {
                ins(emu, 2);
        }
}

/*
 * REMARKS:
 * Handles opcode 0x6f
 */
static void
x86emuOp_outs_word(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                outs(emu, 4);
        } else {
                outs(emu, 2);
        }
}

/*
 * REMARKS:
 * Handles opcode 0x7c
 */
static void
x86emuOp_jump_near_L(struct x86emu *emu)
{
        int sf, of;

        sf = ACCESS_FLAG(F_SF) != 0;
        of = ACCESS_FLAG(F_OF) != 0;

        common_jmp_near(emu, sf != of);
}

/*
 * REMARKS:
 * Handles opcode 0x7d
 */
static void
x86emuOp_jump_near_NL(struct x86emu *emu)
{
        int sf, of;

        sf = ACCESS_FLAG(F_SF) != 0;
        of = ACCESS_FLAG(F_OF) != 0;

        common_jmp_near(emu, sf == of);
}

/*
 * REMARKS:
 * Handles opcode 0x7e
 */
static void
x86emuOp_jump_near_LE(struct x86emu *emu)
{
        int sf, of;

        sf = ACCESS_FLAG(F_SF) != 0;
        of = ACCESS_FLAG(F_OF) != 0;

        common_jmp_near(emu, sf != of || ACCESS_FLAG(F_ZF));
}

/*
 * REMARKS:
 * Handles opcode 0x7f
 */
static void
x86emuOp_jump_near_NLE(struct x86emu *emu)
{
        int sf, of;

        sf = ACCESS_FLAG(F_SF) != 0;
        of = ACCESS_FLAG(F_OF) != 0;

        common_jmp_near(emu, sf == of && !ACCESS_FLAG(F_ZF));
}

static
uint8_t(*const opc80_byte_operation[]) (struct x86emu *, uint8_t d, uint8_t s) =
{
        add_byte,               /* 00 */
        or_byte,                /* 01 */
        adc_byte,               /* 02 */
        sbb_byte,               /* 03 */
        and_byte,               /* 04 */
        sub_byte,               /* 05 */
        xor_byte,               /* 06 */
        cmp_byte,               /* 07 */
};

/*
 * REMARKS:
 * Handles opcode 0x80
 */
static void
x86emuOp_opc80_byte_RM_IMM(struct x86emu *emu)
{
        uint8_t imm, destval;

        /*
         * Weirdo special case instruction format.  Part of the opcode
         * held below in "RH".  Doubly nested case would result, except
         * that the decoded instruction
         */
        fetch_decode_modrm(emu);
        destval = decode_and_fetch_byte(emu);
        imm = fetch_byte_imm(emu);
        destval = (*opc80_byte_operation[emu->cur_rh]) (emu, destval, imm);
        if (emu->cur_rh != 7)
                write_back_byte(emu, destval);
}

static
uint16_t(* const opc81_word_operation[])
    (struct x86emu *, uint16_t d, uint16_t s) =
{
        add_word,               /* 00 */
        or_word,                /* 01 */
        adc_word,               /* 02 */
        sbb_word,               /* 03 */
        and_word,               /* 04 */
        sub_word,               /* 05 */
        xor_word,               /* 06 */
        cmp_word,               /* 07 */
};

static
uint32_t(* const opc81_long_operation[])
    (struct x86emu *, uint32_t d, uint32_t s) =
{
        add_long,               /* 00 */
        or_long,                /* 01 */
        adc_long,               /* 02 */
        sbb_long,               /* 03 */
        and_long,               /* 04 */
        sub_long,               /* 05 */
        xor_long,               /* 06 */
        cmp_long,               /* 07 */
};

/*
 * REMARKS:
 * Handles opcode 0x81
 */
static void
x86emuOp32_opc81_word_RM_IMM(struct x86emu *emu)
{
        uint32_t destval, imm;

        /*
         * Weirdo special case instruction format.  Part of the opcode
         * held below in "RH".  Doubly nested case would result, except
         * that the decoded instruction
         */
        fetch_decode_modrm(emu);
        destval = decode_and_fetch_long(emu);
        imm = fetch_long_imm(emu);
        destval = (*opc81_long_operation[emu->cur_rh]) (emu, destval, imm);
        if (emu->cur_rh != 7)
                write_back_long(emu, destval);
}

static void
x86emuOp16_opc81_word_RM_IMM(struct x86emu *emu)
{
        uint16_t destval, imm;

        /*
         * Weirdo special case instruction format.  Part of the opcode
         * held below in "RH".  Doubly nested case would result, except
         * that the decoded instruction
         */
        fetch_decode_modrm(emu);
        destval = decode_and_fetch_word(emu);
        imm = fetch_word_imm(emu);
        destval = (*opc81_word_operation[emu->cur_rh]) (emu, destval, imm);
        if (emu->cur_rh != 7)
                write_back_word(emu, destval);
}

static void
x86emuOp_opc81_word_RM_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                x86emuOp32_opc81_word_RM_IMM(emu);
        else
                x86emuOp16_opc81_word_RM_IMM(emu);
}

static
uint8_t(* const opc82_byte_operation[])
    (struct x86emu *, uint8_t s, uint8_t d) =
{
        add_byte,               /* 00 */
        or_byte,                /* 01 *//* YYY UNUSED ???? */
        adc_byte,               /* 02 */
        sbb_byte,               /* 03 */
        and_byte,               /* 04 *//* YYY UNUSED ???? */
        sub_byte,               /* 05 */
        xor_byte,               /* 06 *//* YYY UNUSED ???? */
        cmp_byte,               /* 07 */
};

/*
 * REMARKS:
 * Handles opcode 0x82
 */
static void
x86emuOp_opc82_byte_RM_IMM(struct x86emu *emu)
{
        uint8_t imm, destval;

        /*
         * Weirdo special case instruction format.  Part of the opcode
         * held below in "RH".  Doubly nested case would result, except
         * that the decoded instruction Similar to opcode 81, except that
         * the immediate byte is sign extended to a word length.
         */
        fetch_decode_modrm(emu);
        destval = decode_and_fetch_byte(emu);
        imm = fetch_byte_imm(emu);
        destval = (*opc82_byte_operation[emu->cur_rh]) (emu, destval, imm);
        if (emu->cur_rh != 7)
                write_back_byte(emu, destval);
}

static
uint16_t(* const opc83_word_operation[])
    (struct x86emu *, uint16_t s, uint16_t d) =
{
        add_word,               /* 00 */
        or_word,                /* 01 *//* YYY UNUSED ???? */
        adc_word,               /* 02 */
        sbb_word,               /* 03 */
        and_word,               /* 04 *//* YYY UNUSED ???? */
        sub_word,               /* 05 */
        xor_word,               /* 06 *//* YYY UNUSED ???? */
        cmp_word,               /* 07 */
};

static
uint32_t(* const opc83_long_operation[])
    (struct x86emu *, uint32_t s, uint32_t d) =
{
        add_long,               /* 00 */
        or_long,                /* 01 *//* YYY UNUSED ???? */
        adc_long,               /* 02 */
        sbb_long,               /* 03 */
        and_long,               /* 04 *//* YYY UNUSED ???? */
        sub_long,               /* 05 */
        xor_long,               /* 06 *//* YYY UNUSED ???? */
        cmp_long,               /* 07 */
};

/*
 * REMARKS:
 * Handles opcode 0x83
 */
static void
x86emuOp32_opc83_word_RM_IMM(struct x86emu *emu)
{
        uint32_t destval, imm;

        fetch_decode_modrm(emu);
        destval = decode_and_fetch_long(emu);
        imm = (int8_t) fetch_byte_imm(emu);
        destval = (*opc83_long_operation[emu->cur_rh]) (emu, destval, imm);
        if (emu->cur_rh != 7)
                write_back_long(emu, destval);
}

static void
x86emuOp16_opc83_word_RM_IMM(struct x86emu *emu)
{
        uint16_t destval, imm;

        fetch_decode_modrm(emu);
        destval = decode_and_fetch_word(emu);
        imm = (int8_t) fetch_byte_imm(emu);
        destval = (*opc83_word_operation[emu->cur_rh]) (emu, destval, imm);
        if (emu->cur_rh != 7)
                write_back_word(emu, destval);
}

static void
x86emuOp_opc83_word_RM_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                x86emuOp32_opc83_word_RM_IMM(emu);
        else
                x86emuOp16_opc83_word_RM_IMM(emu);
}

/*
 * REMARKS:
 * Handles opcode 0x86
 */
static void
x86emuOp_xchg_byte_RM_R(struct x86emu *emu)
{
        uint8_t *srcreg, destval, tmp;

        fetch_decode_modrm(emu);
        destval = decode_and_fetch_byte(emu);
        srcreg = decode_rh_byte_register(emu);
        tmp = destval;
        destval = *srcreg;
        *srcreg = tmp;
        write_back_byte(emu, destval);
}

/*
 * REMARKS:
 * Handles opcode 0x87
 */
static void
x86emuOp32_xchg_word_RM_R(struct x86emu *emu)
{
        uint32_t *srcreg, destval, tmp;

        fetch_decode_modrm(emu);
        destval = decode_and_fetch_long(emu);
        srcreg = decode_rh_long_register(emu);
        tmp = destval;
        destval = *srcreg;
        *srcreg = tmp;
        write_back_long(emu, destval);
}

static void
x86emuOp16_xchg_word_RM_R(struct x86emu *emu)
{
        uint16_t *srcreg, destval, tmp;

        fetch_decode_modrm(emu);
        destval = decode_and_fetch_word(emu);
        srcreg = decode_rh_word_register(emu);
        tmp = destval;
        destval = *srcreg;
        *srcreg = tmp;
        write_back_word(emu, destval);
}

static void
x86emuOp_xchg_word_RM_R(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                x86emuOp32_xchg_word_RM_R(emu);
        else
                x86emuOp16_xchg_word_RM_R(emu);
}

/*
 * REMARKS:
 * Handles opcode 0x88
 */
static void
x86emuOp_mov_byte_RM_R(struct x86emu *emu)
{
        uint8_t *destreg, *srcreg;
        uint32_t destoffset;

        fetch_decode_modrm(emu);
        srcreg = decode_rh_byte_register(emu);
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                store_data_byte(emu, destoffset, *srcreg);
        } else {
                destreg = decode_rl_byte_register(emu);
                *destreg = *srcreg;
        }
}

/*
 * REMARKS:
 * Handles opcode 0x89
 */
static void
x86emuOp32_mov_word_RM_R(struct x86emu *emu)
{
        uint32_t destoffset;
        uint32_t *destreg, srcval;

        fetch_decode_modrm(emu);
        srcval = *decode_rh_long_register(emu);
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                store_data_long(emu, destoffset, srcval);
        } else {
                destreg = decode_rl_long_register(emu);
                *destreg = srcval;
        }
}

static void
x86emuOp16_mov_word_RM_R(struct x86emu *emu)
{
        uint32_t destoffset;
        uint16_t *destreg, srcval;

        fetch_decode_modrm(emu);
        srcval = *decode_rh_word_register(emu);
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                store_data_word(emu, destoffset, srcval);
        } else {
                destreg = decode_rl_word_register(emu);
                *destreg = srcval;
        }
}

static void
x86emuOp_mov_word_RM_R(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                x86emuOp32_mov_word_RM_R(emu);
        else
                x86emuOp16_mov_word_RM_R(emu);
}

/*
 * REMARKS:
 * Handles opcode 0x8a
 */
static void
x86emuOp_mov_byte_R_RM(struct x86emu *emu)
{
        uint8_t *destreg;

        fetch_decode_modrm(emu);
        destreg = decode_rh_byte_register(emu);
        *destreg = decode_and_fetch_byte(emu);
}

/*
 * REMARKS:
 * Handles opcode 0x8b
 */
static void
x86emuOp_mov_word_R_RM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                uint32_t *destreg;

                fetch_decode_modrm(emu);
                destreg = decode_rh_long_register(emu);
                *destreg = decode_and_fetch_long(emu);
        } else {
                uint16_t *destreg;

                fetch_decode_modrm(emu);
                destreg = decode_rh_word_register(emu);
                *destreg = decode_and_fetch_word(emu);
        }
}

/*
 * REMARKS:
 * Handles opcode 0x8c
 */
static void
x86emuOp_mov_word_RM_SR(struct x86emu *emu)
{
        uint16_t *destreg, srcval;
        uint32_t destoffset;

        fetch_decode_modrm(emu);
        srcval = *decode_rh_seg_register(emu);
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                store_data_word(emu, destoffset, srcval);
        } else {
                destreg = decode_rl_word_register(emu);
                *destreg = srcval;
        }
}

/*
 * REMARKS:
 * Handles opcode 0x8d
 */
static void
x86emuOp_lea_word_R_M(struct x86emu *emu)
{
        uint32_t destoffset;

        fetch_decode_modrm(emu);
        if (emu->cur_mod == 3)
                x86emu_halt_sys(emu);

        destoffset = decode_rl_address(emu);
        if (emu->x86.mode & SYSMODE_PREFIX_ADDR) {
                uint32_t *srcreg;

                srcreg = decode_rh_long_register(emu);
                *srcreg = (uint32_t) destoffset;
        } else {
                uint16_t *srcreg;

                srcreg = decode_rh_word_register(emu);
                *srcreg = (uint16_t) destoffset;
        }
}

/*
 * REMARKS:
 * Handles opcode 0x8e
 */
static void
x86emuOp_mov_word_SR_RM(struct x86emu *emu)
{
        uint16_t *destreg;

        fetch_decode_modrm(emu);
        destreg = decode_rh_seg_register(emu);
        *destreg = decode_and_fetch_word(emu);
        /*
         * Clean up, and reset all the R_xSP pointers to the correct
         * locations.  This is about 3x too much overhead (doing all the
         * segreg ptrs when only one is needed, but this instruction
         * *cannot* be that common, and this isn't too much work anyway.
         */
}

/*
 * REMARKS:
 * Handles opcode 0x8f
 */
static void
x86emuOp32_pop_RM(struct x86emu *emu)
{
        uint32_t destoffset;
        uint32_t destval, *destreg;

        fetch_decode_modrm(emu);
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                destval = pop_long(emu);
                store_data_long(emu, destoffset, destval);
        } else {
                destreg = decode_rl_long_register(emu);
                *destreg = pop_long(emu);
        }
}

static void
x86emuOp16_pop_RM(struct x86emu *emu)
{
        uint32_t destoffset;
        uint16_t destval, *destreg;

        fetch_decode_modrm(emu);
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                destval = pop_word(emu);
                store_data_word(emu, destoffset, destval);
        } else {
                destreg = decode_rl_word_register(emu);
                *destreg = pop_word(emu);
        }
}

static void
x86emuOp_pop_RM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                x86emuOp32_pop_RM(emu);
        else
                x86emuOp16_pop_RM(emu);
}

/*
 * REMARKS:
 * Handles opcode 0x91
 */
static void
x86emuOp_xchg_word_AX_CX(struct x86emu *emu)
{
        uint32_t tmp;

        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                tmp = emu->x86.R_EAX;
                emu->x86.R_EAX = emu->x86.R_ECX;
                emu->x86.R_ECX = tmp;
        } else {
                tmp = emu->x86.R_AX;
                emu->x86.R_AX = emu->x86.R_CX;
                emu->x86.R_CX = (uint16_t) tmp;
        }
}

/*
 * REMARKS:
 * Handles opcode 0x92
 */
static void
x86emuOp_xchg_word_AX_DX(struct x86emu *emu)
{
        uint32_t tmp;

        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                tmp = emu->x86.R_EAX;
                emu->x86.R_EAX = emu->x86.R_EDX;
                emu->x86.R_EDX = tmp;
        } else {
                tmp = emu->x86.R_AX;
                emu->x86.R_AX = emu->x86.R_DX;
                emu->x86.R_DX = (uint16_t) tmp;
        }
}

/*
 * REMARKS:
 * Handles opcode 0x93
 */
static void
x86emuOp_xchg_word_AX_BX(struct x86emu *emu)
{
        uint32_t tmp;

        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                tmp = emu->x86.R_EAX;
                emu->x86.R_EAX = emu->x86.R_EBX;
                emu->x86.R_EBX = tmp;
        } else {
                tmp = emu->x86.R_AX;
                emu->x86.R_AX = emu->x86.R_BX;
                emu->x86.R_BX = (uint16_t) tmp;
        }
}

/*
 * REMARKS:
 * Handles opcode 0x94
 */
static void
x86emuOp_xchg_word_AX_SP(struct x86emu *emu)
{
        uint32_t tmp;

        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                tmp = emu->x86.R_EAX;
                emu->x86.R_EAX = emu->x86.R_ESP;
                emu->x86.R_ESP = tmp;
        } else {
                tmp = emu->x86.R_AX;
                emu->x86.R_AX = emu->x86.R_SP;
                emu->x86.R_SP = (uint16_t) tmp;
        }
}

/*
 * REMARKS:
 * Handles opcode 0x95
 */
static void
x86emuOp_xchg_word_AX_BP(struct x86emu *emu)
{
        uint32_t tmp;

        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                tmp = emu->x86.R_EAX;
                emu->x86.R_EAX = emu->x86.R_EBP;
                emu->x86.R_EBP = tmp;
        } else {
                tmp = emu->x86.R_AX;
                emu->x86.R_AX = emu->x86.R_BP;
                emu->x86.R_BP = (uint16_t) tmp;
        }
}

/*
 * REMARKS:
 * Handles opcode 0x96
 */
static void
x86emuOp_xchg_word_AX_SI(struct x86emu *emu)
{
        uint32_t tmp;

        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                tmp = emu->x86.R_EAX;
                emu->x86.R_EAX = emu->x86.R_ESI;
                emu->x86.R_ESI = tmp;
        } else {
                tmp = emu->x86.R_AX;
                emu->x86.R_AX = emu->x86.R_SI;
                emu->x86.R_SI = (uint16_t) tmp;
        }
}

/*
 * REMARKS:
 * Handles opcode 0x97
 */
static void
x86emuOp_xchg_word_AX_DI(struct x86emu *emu)
{
        uint32_t tmp;

        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                tmp = emu->x86.R_EAX;
                emu->x86.R_EAX = emu->x86.R_EDI;
                emu->x86.R_EDI = tmp;
        } else {
                tmp = emu->x86.R_AX;
                emu->x86.R_AX = emu->x86.R_DI;
                emu->x86.R_DI = (uint16_t) tmp;
        }
}

/*
 * REMARKS:
 * Handles opcode 0x98
 */
static void
x86emuOp_cbw(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                if (emu->x86.R_AX & 0x8000) {
                        emu->x86.R_EAX |= 0xffff0000;
                } else {
                        emu->x86.R_EAX &= 0x0000ffff;
                }
        } else {
                if (emu->x86.R_AL & 0x80) {
                        emu->x86.R_AH = 0xff;
                } else {
                        emu->x86.R_AH = 0x0;
                }
        }
}

/*
 * REMARKS:
 * Handles opcode 0x99
 */
static void
x86emuOp_cwd(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                if (emu->x86.R_EAX & 0x80000000) {
                        emu->x86.R_EDX = 0xffffffff;
                } else {
                        emu->x86.R_EDX = 0x0;
                }
        } else {
                if (emu->x86.R_AX & 0x8000) {
                        emu->x86.R_DX = 0xffff;
                } else {
                        emu->x86.R_DX = 0x0;
                }
        }
}

/*
 * REMARKS:
 * Handles opcode 0x9a
 */
static void
x86emuOp_call_far_IMM(struct x86emu *emu)
{
        uint16_t farseg, faroff;

        faroff = fetch_word_imm(emu);
        farseg = fetch_word_imm(emu);
        /* XXX
         * 
         * Hooked interrupt vectors calling into our "BIOS" will cause problems
         * unless all intersegment stuff is checked for BIOS access.  Check
         * needed here.  For moment, let it alone. */
        push_word(emu, emu->x86.R_CS);
        emu->x86.R_CS = farseg;
        push_word(emu, emu->x86.R_IP);
        emu->x86.R_IP = faroff;
}

/*
 * REMARKS:
 * Handles opcode 0x9c
 */
static void
x86emuOp_pushf_word(struct x86emu *emu)
{
        uint32_t flags;

        /* clear out *all* bits not representing flags, and turn on real bits */
        flags = (emu->x86.R_EFLG & F_MSK) | F_ALWAYS_ON;
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                push_long(emu, flags);
        } else {
                push_word(emu, (uint16_t) flags);
        }
}

/*
 * REMARKS:
 * Handles opcode 0x9d
 */
static void
x86emuOp_popf_word(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                emu->x86.R_EFLG = pop_long(emu);
        } else {
                emu->x86.R_FLG = pop_word(emu);
        }
}

/*
 * REMARKS:
 * Handles opcode 0x9e
 */
static void
x86emuOp_sahf(struct x86emu *emu)
{
        /* clear the lower bits of the flag register */
        emu->x86.R_FLG &= 0xffffff00;
        /* or in the AH register into the flags register */
        emu->x86.R_FLG |= emu->x86.R_AH;
}

/*
 * REMARKS:
 * Handles opcode 0x9f
 */
static void
x86emuOp_lahf(struct x86emu *emu)
{
        emu->x86.R_AH = (uint8_t) (emu->x86.R_FLG & 0xff);
        /* undocumented TC++ behavior??? Nope.  It's documented, but you have
         * too look real hard to notice it. */
        emu->x86.R_AH |= 0x2;
}

/*
 * REMARKS:
 * Handles opcode 0xa0
 */
static void
x86emuOp_mov_AL_M_IMM(struct x86emu *emu)
{
        uint16_t offset;

        offset = fetch_word_imm(emu);
        emu->x86.R_AL = fetch_data_byte(emu, offset);
}

/*
 * REMARKS:
 * Handles opcode 0xa1
 */
static void
x86emuOp_mov_AX_M_IMM(struct x86emu *emu)
{
        uint16_t offset;

        offset = fetch_word_imm(emu);
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                emu->x86.R_EAX = fetch_data_long(emu, offset);
        } else {
                emu->x86.R_AX = fetch_data_word(emu, offset);
        }
}

/*
 * REMARKS:
 * Handles opcode 0xa2
 */
static void
x86emuOp_mov_M_AL_IMM(struct x86emu *emu)
{
        uint16_t offset;

        offset = fetch_word_imm(emu);
        store_data_byte(emu, offset, emu->x86.R_AL);
}

/*
 * REMARKS:
 * Handles opcode 0xa3
 */
static void
x86emuOp_mov_M_AX_IMM(struct x86emu *emu)
{
        uint16_t offset;

        offset = fetch_word_imm(emu);
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                store_data_long(emu, offset, emu->x86.R_EAX);
        } else {
                store_data_word(emu, offset, emu->x86.R_AX);
        }
}

/*
 * REMARKS:
 * Handles opcode 0xa4
 */
static void
x86emuOp_movs_byte(struct x86emu *emu)
{
        uint8_t val;
        uint32_t count;
        int inc;

        if (ACCESS_FLAG(F_DF))  /* down */
                inc = -1;
        else
                inc = 1;
        count = 1;
        if (emu->x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) {
                /* dont care whether REPE or REPNE */
                /* move them until CX is ZERO. */
                count = emu->x86.R_CX;
                emu->x86.R_CX = 0;
                emu->x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE);
        }
        while (count--) {
                val = fetch_data_byte(emu, emu->x86.R_SI);
                store_byte(emu, emu->x86.R_ES, emu->x86.R_DI, val);
                emu->x86.R_SI += inc;
                emu->x86.R_DI += inc;
        }
}

/*
 * REMARKS:
 * Handles opcode 0xa5
 */
static void
x86emuOp_movs_word(struct x86emu *emu)
{
        uint32_t val;
        int inc;
        uint32_t count;

        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                inc = 4;
        else
                inc = 2;

        if (ACCESS_FLAG(F_DF))  /* down */
                inc = -inc;

        count = 1;
        if (emu->x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) {
                /* dont care whether REPE or REPNE */
                /* move them until CX is ZERO. */
                count = emu->x86.R_CX;
                emu->x86.R_CX = 0;
                emu->x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE);
        }
        while (count--) {
                if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                        val = fetch_data_long(emu, emu->x86.R_SI);
                        store_long(emu, emu->x86.R_ES, emu->x86.R_DI, val);
                } else {
                        val = fetch_data_word(emu, emu->x86.R_SI);
                        store_word(emu, emu->x86.R_ES, emu->x86.R_DI,
                            (uint16_t) val);
                }
                emu->x86.R_SI += inc;
                emu->x86.R_DI += inc;
        }
}

/*
 * REMARKS:
 * Handles opcode 0xa6
 */
static void
x86emuOp_cmps_byte(struct x86emu *emu)
{
        int8_t val1, val2;
        int inc;

        if (ACCESS_FLAG(F_DF))  /* down */
                inc = -1;
        else
                inc = 1;

        if (emu->x86.mode & SYSMODE_PREFIX_REPE) {
                /* REPE  */
                /* move them until CX is ZERO. */
                while (emu->x86.R_CX != 0) {
                        val1 = fetch_data_byte(emu, emu->x86.R_SI);
                        val2 = fetch_byte(emu, emu->x86.R_ES, emu->x86.R_DI);
                        cmp_byte(emu, val1, val2);
                        emu->x86.R_CX -= 1;
                        emu->x86.R_SI += inc;
                        emu->x86.R_DI += inc;
                        if (ACCESS_FLAG(F_ZF) == 0)
                                break;
                }
                emu->x86.mode &= ~SYSMODE_PREFIX_REPE;
        } else if (emu->x86.mode & SYSMODE_PREFIX_REPNE) {
                /* REPNE  */
                /* move them until CX is ZERO. */
                while (emu->x86.R_CX != 0) {
                        val1 = fetch_data_byte(emu, emu->x86.R_SI);
                        val2 = fetch_byte(emu, emu->x86.R_ES, emu->x86.R_DI);
                        cmp_byte(emu, val1, val2);
                        emu->x86.R_CX -= 1;
                        emu->x86.R_SI += inc;
                        emu->x86.R_DI += inc;
                        if (ACCESS_FLAG(F_ZF))
                                break;  /* zero flag set means equal */
                }
                emu->x86.mode &= ~SYSMODE_PREFIX_REPNE;
        } else {
                val1 = fetch_data_byte(emu, emu->x86.R_SI);
                val2 = fetch_byte(emu, emu->x86.R_ES, emu->x86.R_DI);
                cmp_byte(emu, val1, val2);
                emu->x86.R_SI += inc;
                emu->x86.R_DI += inc;
        }
}

/*
 * REMARKS:
 * Handles opcode 0xa7
 */
static void
x86emuOp_cmps_word(struct x86emu *emu)
{
        uint32_t val1, val2;
        int inc;

        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                if (ACCESS_FLAG(F_DF))  /* down */
                        inc = -4;
                else
                        inc = 4;
        } else {
                if (ACCESS_FLAG(F_DF))  /* down */
                        inc = -2;
                else
                        inc = 2;
        }
        if (emu->x86.mode & SYSMODE_PREFIX_REPE) {
                /* REPE  */
                /* move them until CX is ZERO. */
                while (emu->x86.R_CX != 0) {
                        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                                val1 = fetch_data_long(emu, emu->x86.R_SI);
                                val2 = fetch_long(emu, emu->x86.R_ES,
                                    emu->x86.R_DI);
                                cmp_long(emu, val1, val2);
                        } else {
                                val1 = fetch_data_word(emu, emu->x86.R_SI);
                                val2 = fetch_word(emu, emu->x86.R_ES,
                                    emu->x86.R_DI);
                                cmp_word(emu, (uint16_t) val1, (uint16_t) val2);
                        }
                        emu->x86.R_CX -= 1;
                        emu->x86.R_SI += inc;
                        emu->x86.R_DI += inc;
                        if (ACCESS_FLAG(F_ZF) == 0)
                                break;
                }
                emu->x86.mode &= ~SYSMODE_PREFIX_REPE;
        } else if (emu->x86.mode & SYSMODE_PREFIX_REPNE) {
                /* REPNE  */
                /* move them until CX is ZERO. */
                while (emu->x86.R_CX != 0) {
                        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                                val1 = fetch_data_long(emu, emu->x86.R_SI);
                                val2 = fetch_long(emu, emu->x86.R_ES,
                                    emu->x86.R_DI);
                                cmp_long(emu, val1, val2);
                        } else {
                                val1 = fetch_data_word(emu, emu->x86.R_SI);
                                val2 = fetch_word(emu, emu->x86.R_ES,
                                    emu->x86.R_DI);
                                cmp_word(emu, (uint16_t) val1, (uint16_t) val2);
                        }
                        emu->x86.R_CX -= 1;
                        emu->x86.R_SI += inc;
                        emu->x86.R_DI += inc;
                        if (ACCESS_FLAG(F_ZF))
                                break;  /* zero flag set means equal */
                }
                emu->x86.mode &= ~SYSMODE_PREFIX_REPNE;
        } else {
                if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                        val1 = fetch_data_long(emu, emu->x86.R_SI);
                        val2 = fetch_long(emu, emu->x86.R_ES, emu->x86.R_DI);
                        cmp_long(emu, val1, val2);
                } else {
                        val1 = fetch_data_word(emu, emu->x86.R_SI);
                        val2 = fetch_word(emu, emu->x86.R_ES, emu->x86.R_DI);
                        cmp_word(emu, (uint16_t) val1, (uint16_t) val2);
                }
                emu->x86.R_SI += inc;
                emu->x86.R_DI += inc;
        }
}

/*
 * REMARKS:
 * Handles opcode 0xa9
 */
static void
x86emuOp_test_AX_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                test_long(emu, emu->x86.R_EAX, fetch_long_imm(emu));
        } else {
                test_word(emu, emu->x86.R_AX, fetch_word_imm(emu));
        }
}

/*
 * REMARKS:
 * Handles opcode 0xaa
 */
static void
x86emuOp_stos_byte(struct x86emu *emu)
{
        int inc;

        if (ACCESS_FLAG(F_DF))  /* down */
                inc = -1;
        else
                inc = 1;
        if (emu->x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) {
                /* dont care whether REPE or REPNE */
                /* move them until CX is ZERO. */
                while (emu->x86.R_CX != 0) {
                        store_byte(emu, emu->x86.R_ES, emu->x86.R_DI,
                            emu->x86.R_AL);
                        emu->x86.R_CX -= 1;
                        emu->x86.R_DI += inc;
                }
                emu->x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE);
        } else {
                store_byte(emu, emu->x86.R_ES, emu->x86.R_DI, emu->x86.R_AL);
                emu->x86.R_DI += inc;
        }
}

/*
 * REMARKS:
 * Handles opcode 0xab
 */
static void
x86emuOp_stos_word(struct x86emu *emu)
{
        int inc;
        uint32_t count;

        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                inc = 4;
        else
                inc = 2;
        
        if (ACCESS_FLAG(F_DF))  /* down */
                inc = -inc;

        count = 1;
        if (emu->x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) {
                /* dont care whether REPE or REPNE */
                /* move them until CX is ZERO. */
                count = emu->x86.R_CX;
                emu->x86.R_CX = 0;
                emu->x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE);
        }
        while (count--) {
                if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                        store_long(emu, emu->x86.R_ES, emu->x86.R_DI,
                            emu->x86.R_EAX);
                } else {
                        store_word(emu, emu->x86.R_ES, emu->x86.R_DI,
                            emu->x86.R_AX);
                }
                emu->x86.R_DI += inc;
        }
}

/*
 * REMARKS:
 * Handles opcode 0xac
 */
static void
x86emuOp_lods_byte(struct x86emu *emu)
{
        int inc;

        if (ACCESS_FLAG(F_DF))  /* down */
                inc = -1;
        else
                inc = 1;
        if (emu->x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) {
                /* dont care whether REPE or REPNE */
                /* move them until CX is ZERO. */
                while (emu->x86.R_CX != 0) {
                        emu->x86.R_AL = fetch_data_byte(emu, emu->x86.R_SI);
                        emu->x86.R_CX -= 1;
                        emu->x86.R_SI += inc;
                }
                emu->x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE);
        } else {
                emu->x86.R_AL = fetch_data_byte(emu, emu->x86.R_SI);
                emu->x86.R_SI += inc;
        }
}

/*
 * REMARKS:
 * Handles opcode 0xad
 */
static void
x86emuOp_lods_word(struct x86emu *emu)
{
        int inc;
        uint32_t count;

        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                inc = 4;
        else
                inc = 2;

        if (ACCESS_FLAG(F_DF))  /* down */
                inc = -inc;

        count = 1;
        if (emu->x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) {
                /* dont care whether REPE or REPNE */
                /* move them until CX is ZERO. */
                count = emu->x86.R_CX;
                emu->x86.R_CX = 0;
                emu->x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE);
        }
        while (count--) {
                if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                        emu->x86.R_EAX = fetch_data_long(emu, emu->x86.R_SI);
                } else {
                        emu->x86.R_AX = fetch_data_word(emu, emu->x86.R_SI);
                }
                emu->x86.R_SI += inc;
        }
}

/*
 * REMARKS:
 * Handles opcode 0xae
 */
static void
x86emuOp_scas_byte(struct x86emu *emu)
{
        int8_t val2;
        int inc;

        if (ACCESS_FLAG(F_DF))  /* down */
                inc = -1;
        else
                inc = 1;
        if (emu->x86.mode & SYSMODE_PREFIX_REPE) {
                /* REPE  */
                /* move them until CX is ZERO. */
                while (emu->x86.R_CX != 0) {
                        val2 = fetch_byte(emu, emu->x86.R_ES, emu->x86.R_DI);
                        cmp_byte(emu, emu->x86.R_AL, val2);
                        emu->x86.R_CX -= 1;
                        emu->x86.R_DI += inc;
                        if (ACCESS_FLAG(F_ZF) == 0)
                                break;
                }
                emu->x86.mode &= ~SYSMODE_PREFIX_REPE;
        } else if (emu->x86.mode & SYSMODE_PREFIX_REPNE) {
                /* REPNE  */
                /* move them until CX is ZERO. */
                while (emu->x86.R_CX != 0) {
                        val2 = fetch_byte(emu, emu->x86.R_ES, emu->x86.R_DI);
                        cmp_byte(emu, emu->x86.R_AL, val2);
                        emu->x86.R_CX -= 1;
                        emu->x86.R_DI += inc;
                        if (ACCESS_FLAG(F_ZF))
                                break;  /* zero flag set means equal */
                }
                emu->x86.mode &= ~SYSMODE_PREFIX_REPNE;
        } else {
                val2 = fetch_byte(emu, emu->x86.R_ES, emu->x86.R_DI);
                cmp_byte(emu, emu->x86.R_AL, val2);
                emu->x86.R_DI += inc;
        }
}

/*
 * REMARKS:
 * Handles opcode 0xaf
 */
static void
x86emuOp_scas_word(struct x86emu *emu)
{
        int inc;
        uint32_t val;

        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                inc = 4;
        else
                inc = 2;

        if (ACCESS_FLAG(F_DF))  /* down */
                inc = -inc;

        if (emu->x86.mode & SYSMODE_PREFIX_REPE) {
                /* REPE  */
                /* move them until CX is ZERO. */
                while (emu->x86.R_CX != 0) {
                        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                                val = fetch_long(emu, emu->x86.R_ES,
                                    emu->x86.R_DI);
                                cmp_long(emu, emu->x86.R_EAX, val);
                        } else {
                                val = fetch_word(emu, emu->x86.R_ES,
                                    emu->x86.R_DI);
                                cmp_word(emu, emu->x86.R_AX, (uint16_t) val);
                        }
                        emu->x86.R_CX -= 1;
                        emu->x86.R_DI += inc;
                        if (ACCESS_FLAG(F_ZF) == 0)
                                break;
                }
                emu->x86.mode &= ~SYSMODE_PREFIX_REPE;
        } else if (emu->x86.mode & SYSMODE_PREFIX_REPNE) {
                /* REPNE  */
                /* move them until CX is ZERO. */
                while (emu->x86.R_CX != 0) {
                        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                                val = fetch_long(emu, emu->x86.R_ES,
                                    emu->x86.R_DI);
                                cmp_long(emu, emu->x86.R_EAX, val);
                        } else {
                                val = fetch_word(emu, emu->x86.R_ES,
                                    emu->x86.R_DI);
                                cmp_word(emu, emu->x86.R_AX, (uint16_t) val);
                        }
                        emu->x86.R_CX -= 1;
                        emu->x86.R_DI += inc;
                        if (ACCESS_FLAG(F_ZF))
                                break;  /* zero flag set means equal */
                }
                emu->x86.mode &= ~SYSMODE_PREFIX_REPNE;
        } else {
                if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                        val = fetch_long(emu, emu->x86.R_ES, emu->x86.R_DI);
                        cmp_long(emu, emu->x86.R_EAX, val);
                } else {
                        val = fetch_word(emu, emu->x86.R_ES, emu->x86.R_DI);
                        cmp_word(emu, emu->x86.R_AX, (uint16_t) val);
                }
                emu->x86.R_DI += inc;
        }
}

/*
 * REMARKS:
 * Handles opcode 0xb8
 */
static void
x86emuOp_mov_word_AX_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                emu->x86.R_EAX = fetch_long_imm(emu);
        else
                emu->x86.R_AX = fetch_word_imm(emu);
}

/*
 * REMARKS:
 * Handles opcode 0xb9
 */
static void
x86emuOp_mov_word_CX_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                emu->x86.R_ECX = fetch_long_imm(emu);
        else
                emu->x86.R_CX = fetch_word_imm(emu);
}

/*
 * REMARKS:
 * Handles opcode 0xba
 */
static void
x86emuOp_mov_word_DX_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                emu->x86.R_EDX = fetch_long_imm(emu);
        else
                emu->x86.R_DX = fetch_word_imm(emu);
}

/*
 * REMARKS:
 * Handles opcode 0xbb
 */
static void
x86emuOp_mov_word_BX_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                emu->x86.R_EBX = fetch_long_imm(emu);
        else
                emu->x86.R_BX = fetch_word_imm(emu);
}

/*
 * REMARKS:
 * Handles opcode 0xbc
 */
static void
x86emuOp_mov_word_SP_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                emu->x86.R_ESP = fetch_long_imm(emu);
        else
                emu->x86.R_SP = fetch_word_imm(emu);
}

/*
 * REMARKS:
 * Handles opcode 0xbd
 */
static void
x86emuOp_mov_word_BP_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                emu->x86.R_EBP = fetch_long_imm(emu);
        else
                emu->x86.R_BP = fetch_word_imm(emu);
}

/*
 * REMARKS:
 * Handles opcode 0xbe
 */
static void
x86emuOp_mov_word_SI_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                emu->x86.R_ESI = fetch_long_imm(emu);
        else
                emu->x86.R_SI = fetch_word_imm(emu);
}

/*
 * REMARKS:
 * Handles opcode 0xbf
 */
static void
x86emuOp_mov_word_DI_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                emu->x86.R_EDI = fetch_long_imm(emu);
        else
                emu->x86.R_DI = fetch_word_imm(emu);
}
/* used by opcodes c0, d0, and d2. */
static
uint8_t(* const opcD0_byte_operation[])
    (struct x86emu *, uint8_t d, uint8_t s) =
{
        rol_byte,
        ror_byte,
        rcl_byte,
        rcr_byte,
        shl_byte,
        shr_byte,
        shl_byte,               /* sal_byte === shl_byte  by definition */
        sar_byte,
};

/*
 * REMARKS:
 * Handles opcode 0xc0
 */
static void
x86emuOp_opcC0_byte_RM_MEM(struct x86emu *emu)
{
        uint8_t destval, amt;

        /*
         * Yet another weirdo special case instruction format.  Part of
         * the opcode held below in "RH".  Doubly nested case would
         * result, except that the decoded instruction
         */
        fetch_decode_modrm(emu);
        /* know operation, decode the mod byte to find the addressing mode. */
        destval = decode_and_fetch_byte_imm8(emu, &amt);
        destval = (*opcD0_byte_operation[emu->cur_rh]) (emu, destval, amt);
        write_back_byte(emu, destval);
}
/* used by opcodes c1, d1, and d3. */
static
uint16_t(* const opcD1_word_operation[])
    (struct x86emu *, uint16_t s, uint8_t d) =
{
        rol_word,
        ror_word,
        rcl_word,
        rcr_word,
        shl_word,
        shr_word,
        shl_word,               /* sal_byte === shl_byte  by definition */
        sar_word,
};
/* used by opcodes c1, d1, and d3. */
static
uint32_t(* const opcD1_long_operation[])
    (struct x86emu *, uint32_t s, uint8_t d) =
{
        rol_long,
        ror_long,
        rcl_long,
        rcr_long,
        shl_long,
        shr_long,
        shl_long,               /* sal_byte === shl_byte  by definition */
        sar_long,
};

/*
 * REMARKS:
 * Handles opcode 0xc1
 */
static void
x86emuOp_opcC1_word_RM_MEM(struct x86emu *emu)
{
        uint8_t amt;

        /*
         * Yet another weirdo special case instruction format.  Part of
         * the opcode held below in "RH".  Doubly nested case would
         * result, except that the decoded instruction
         */
        fetch_decode_modrm(emu);
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                uint32_t destval;

                destval = decode_and_fetch_long_imm8(emu, &amt);
                destval = (*opcD1_long_operation[emu->cur_rh])
                    (emu, destval, amt);
                write_back_long(emu, destval);
        } else {
                uint16_t destval;

                destval = decode_and_fetch_word_imm8(emu, &amt);
                destval = (*opcD1_word_operation[emu->cur_rh])
                    (emu, destval, amt);
                write_back_word(emu, destval);
        }
}

/*
 * REMARKS:
 * Handles opcode 0xc2
 */
static void
x86emuOp_ret_near_IMM(struct x86emu *emu)
{
        uint16_t imm;

        imm = fetch_word_imm(emu);
        emu->x86.R_IP = pop_word(emu);
        emu->x86.R_SP += imm;
}

/*
 * REMARKS:
 * Handles opcode 0xc6
 */
static void
x86emuOp_mov_byte_RM_IMM(struct x86emu *emu)
{
        uint8_t *destreg;
        uint32_t destoffset;
        uint8_t imm;

        fetch_decode_modrm(emu);
        if (emu->cur_rh != 0)
                x86emu_halt_sys(emu);
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                imm = fetch_byte_imm(emu);
                store_data_byte(emu, destoffset, imm);
        } else {
                destreg = decode_rl_byte_register(emu);
                imm = fetch_byte_imm(emu);
                *destreg = imm;
        }
}

/*
 * REMARKS:
 * Handles opcode 0xc7
 */
static void
x86emuOp32_mov_word_RM_IMM(struct x86emu *emu)
{
        uint32_t destoffset;
        uint32_t imm, *destreg;

        fetch_decode_modrm(emu);
        if (emu->cur_rh != 0)
                x86emu_halt_sys(emu);

        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                imm = fetch_long_imm(emu);
                store_data_long(emu, destoffset, imm);
        } else {
                destreg = decode_rl_long_register(emu);
                imm = fetch_long_imm(emu);
                *destreg = imm;
        }
}

static void
x86emuOp16_mov_word_RM_IMM(struct x86emu *emu)
{
        uint32_t destoffset;
        uint16_t imm, *destreg;

        fetch_decode_modrm(emu);
        if (emu->cur_rh != 0)
                x86emu_halt_sys(emu);

        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                imm = fetch_word_imm(emu);
                store_data_word(emu, destoffset, imm);
        } else {
                destreg = decode_rl_word_register(emu);
                imm = fetch_word_imm(emu);
                *destreg = imm;
        }
}

static void
x86emuOp_mov_word_RM_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                x86emuOp32_mov_word_RM_IMM(emu);
        else
                x86emuOp16_mov_word_RM_IMM(emu);
}

/*
 * REMARKS:
 * Handles opcode 0xc8
 */
static void
x86emuOp_enter(struct x86emu *emu)
{
        uint16_t local, frame_pointer;
        uint8_t nesting;
        int i;

        local = fetch_word_imm(emu);
        nesting = fetch_byte_imm(emu);
        push_word(emu, emu->x86.R_BP);
        frame_pointer = emu->x86.R_SP;
        if (nesting > 0) {
                for (i = 1; i < nesting; i++) {
                        emu->x86.R_BP -= 2;
                        push_word(emu, fetch_word(emu, emu->x86.R_SS,
                            emu->x86.R_BP));
                }
                push_word(emu, frame_pointer);
        }
        emu->x86.R_BP = frame_pointer;
        emu->x86.R_SP = (uint16_t) (emu->x86.R_SP - local);
}

/*
 * REMARKS:
 * Handles opcode 0xc9
 */
static void
x86emuOp_leave(struct x86emu *emu)
{
        emu->x86.R_SP = emu->x86.R_BP;
        emu->x86.R_BP = pop_word(emu);
}

/*
 * REMARKS:
 * Handles opcode 0xca
 */
static void
x86emuOp_ret_far_IMM(struct x86emu *emu)
{
        uint16_t imm;

        imm = fetch_word_imm(emu);
        emu->x86.R_IP = pop_word(emu);
        emu->x86.R_CS = pop_word(emu);
        emu->x86.R_SP += imm;
}

/*
 * REMARKS:
 * Handles opcode 0xcb
 */
static void
x86emuOp_ret_far(struct x86emu *emu)
{
        emu->x86.R_IP = pop_word(emu);
        emu->x86.R_CS = pop_word(emu);
}

/*
 * REMARKS:
 * Handles opcode 0xcc
 */
static void
x86emuOp_int3(struct x86emu *emu)
{
        x86emu_intr_dispatch(emu, 3);
}

/*
 * REMARKS:
 * Handles opcode 0xcd
 */
static void
x86emuOp_int_IMM(struct x86emu *emu)
{
        uint8_t intnum;

        intnum = fetch_byte_imm(emu);
        x86emu_intr_dispatch(emu, intnum);
}

/*
 * REMARKS:
 * Handles opcode 0xce
 */
static void
x86emuOp_into(struct x86emu *emu)
{
        if (ACCESS_FLAG(F_OF))
                x86emu_intr_dispatch(emu, 4);
}

/*
 * REMARKS:
 * Handles opcode 0xcf
 */
static void
x86emuOp_iret(struct x86emu *emu)
{
        emu->x86.R_IP = pop_word(emu);
        emu->x86.R_CS = pop_word(emu);
        emu->x86.R_FLG = pop_word(emu);
}

/*
 * REMARKS:
 * Handles opcode 0xd0
 */
static void
x86emuOp_opcD0_byte_RM_1(struct x86emu *emu)
{
        uint8_t destval;

        fetch_decode_modrm(emu);
        destval = decode_and_fetch_byte(emu);
        destval = (*opcD0_byte_operation[emu->cur_rh]) (emu, destval, 1);
        write_back_byte(emu, destval);
}

/*
 * REMARKS:
 * Handles opcode 0xd1
 */
static void
x86emuOp_opcD1_word_RM_1(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                uint32_t destval;

                fetch_decode_modrm(emu);
                destval = decode_and_fetch_long(emu);
                destval = (*opcD1_long_operation[emu->cur_rh])(emu, destval, 1);
                write_back_long(emu, destval);
        } else {
                uint16_t destval;

                fetch_decode_modrm(emu);
                destval = decode_and_fetch_word(emu);
                destval = (*opcD1_word_operation[emu->cur_rh])(emu, destval, 1);
                write_back_word(emu, destval);
        }
}

/*
 * REMARKS:
 * Handles opcode 0xd2
 */
static void
x86emuOp_opcD2_byte_RM_CL(struct x86emu *emu)
{
        uint8_t destval;

        fetch_decode_modrm(emu);
        destval = decode_and_fetch_byte(emu);
        destval = (*opcD0_byte_operation[emu->cur_rh])
            (emu, destval, emu->x86.R_CL);
        write_back_byte(emu, destval);
}

/*
 * REMARKS:
 * Handles opcode 0xd3
 */
static void
x86emuOp_opcD3_word_RM_CL(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                uint32_t destval;

                fetch_decode_modrm(emu);
                destval = decode_and_fetch_long(emu);
                destval = (*opcD1_long_operation[emu->cur_rh])
                    (emu, destval, emu->x86.R_CL);
                write_back_long(emu, destval);
        } else {
                uint16_t destval;

                fetch_decode_modrm(emu);
                destval = decode_and_fetch_word(emu);
                destval = (*opcD1_word_operation[emu->cur_rh])
                    (emu, destval, emu->x86.R_CL);
                write_back_word(emu, destval);
        }
}

/*
 * REMARKS:
 * Handles opcode 0xd4
 */
static void
x86emuOp_aam(struct x86emu *emu)
{
        uint8_t a;

        a = fetch_byte_imm(emu);        /* this is a stupid encoding. */
        if (a != 10) {
                /* fix: add base decoding aam_word(uint8_t val, int base a) */
                x86emu_halt_sys(emu);
        }
        /* note the type change here --- returning AL and AH in AX. */
        emu->x86.R_AX = aam_word(emu, emu->x86.R_AL);
}

/*
 * REMARKS:
 * Handles opcode 0xd5
 */
static void
x86emuOp_aad(struct x86emu *emu)
{
        uint8_t a;

        a = fetch_byte_imm(emu);
        if (a != 10) {
                /* fix: add base decoding aad_word(uint16_t val, int base a) */
                x86emu_halt_sys(emu);
        }
        emu->x86.R_AX = aad_word(emu, emu->x86.R_AX);
}
/* opcode 0xd6 ILLEGAL OPCODE */


/*
 * REMARKS:
 * Handles opcode 0xd7
 */
static void
x86emuOp_xlat(struct x86emu *emu)
{
        uint16_t addr;

        addr = (uint16_t) (emu->x86.R_BX + (uint8_t) emu->x86.R_AL);
        emu->x86.R_AL = fetch_data_byte(emu, addr);
}

/* opcode=0xd8 */
static void 
x86emuOp_esc_coprocess_d8(struct x86emu *emu)
{
}
/* opcode=0xd9 */
static void 
x86emuOp_esc_coprocess_d9(struct x86emu *emu)
{
        fetch_decode_modrm(emu);
        if (emu->cur_mod != 3)
                decode_rl_address(emu);
}
/* opcode=0xda */
static void 
x86emuOp_esc_coprocess_da(struct x86emu *emu)
{
        fetch_decode_modrm(emu);
        if (emu->cur_mod != 3)
                decode_rl_address(emu);
}
/* opcode=0xdb */
static void 
x86emuOp_esc_coprocess_db(struct x86emu *emu)
{
        fetch_decode_modrm(emu);
        if (emu->cur_mod != 3)
                decode_rl_address(emu);
}
/* opcode=0xdc */
static void 
x86emuOp_esc_coprocess_dc(struct x86emu *emu)
{
        fetch_decode_modrm(emu);
        if (emu->cur_mod != 3)
                decode_rl_address(emu);
}
/* opcode=0xdd */
static void 
x86emuOp_esc_coprocess_dd(struct x86emu *emu)
{
        fetch_decode_modrm(emu);
        if (emu->cur_mod != 3)
                decode_rl_address(emu);
}
/* opcode=0xde */
static void 
x86emuOp_esc_coprocess_de(struct x86emu *emu)
{
        fetch_decode_modrm(emu);
        if (emu->cur_mod != 3)
                decode_rl_address(emu);
}
/* opcode=0xdf */
static void 
x86emuOp_esc_coprocess_df(struct x86emu *emu)
{
        fetch_decode_modrm(emu);
        if (emu->cur_mod != 3)
                decode_rl_address(emu);
}


/*
 * REMARKS:
 * Handles opcode 0xe0
 */
static void
x86emuOp_loopne(struct x86emu *emu)
{
        int16_t ip;

        ip = (int8_t) fetch_byte_imm(emu);
        ip += (int16_t) emu->x86.R_IP;
        emu->x86.R_CX -= 1;
        if (emu->x86.R_CX != 0 && !ACCESS_FLAG(F_ZF))   /* CX != 0 and !ZF */
                emu->x86.R_IP = ip;
}

/*
 * REMARKS:
 * Handles opcode 0xe1
 */
static void
x86emuOp_loope(struct x86emu *emu)
{
        int16_t ip;

        ip = (int8_t) fetch_byte_imm(emu);
        ip += (int16_t) emu->x86.R_IP;
        emu->x86.R_CX -= 1;
        if (emu->x86.R_CX != 0 && ACCESS_FLAG(F_ZF))    /* CX != 0 and ZF */
                emu->x86.R_IP = ip;
}

/*
 * REMARKS:
 * Handles opcode 0xe2
 */
static void
x86emuOp_loop(struct x86emu *emu)
{
        int16_t ip;

        ip = (int8_t) fetch_byte_imm(emu);
        ip += (int16_t) emu->x86.R_IP;
        emu->x86.R_CX -= 1;
        if (emu->x86.R_CX != 0)
                emu->x86.R_IP = ip;
}

/*
 * REMARKS:
 * Handles opcode 0xe3
 */
static void
x86emuOp_jcxz(struct x86emu *emu)
{
        uint16_t target;
        int8_t offset;

        /* jump to byte offset if overflow flag is set */
        offset = (int8_t) fetch_byte_imm(emu);
        target = (uint16_t) (emu->x86.R_IP + offset);
        if (emu->x86.R_CX == 0)
                emu->x86.R_IP = target;
}

/*
 * REMARKS:
 * Handles opcode 0xe4
 */
static void
x86emuOp_in_byte_AL_IMM(struct x86emu *emu)
{
        uint8_t port;

        port = (uint8_t) fetch_byte_imm(emu);
        emu->x86.R_AL = (*emu->emu_inb) (emu, port);
}

/*
 * REMARKS:
 * Handles opcode 0xe5
 */
static void
x86emuOp_in_word_AX_IMM(struct x86emu *emu)
{
        uint8_t port;

        port = (uint8_t) fetch_byte_imm(emu);
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                emu->x86.R_EAX = (*emu->emu_inl) (emu, port);
        } else {
                emu->x86.R_AX = (*emu->emu_inw) (emu, port);
        }
}

/*
 * REMARKS:
 * Handles opcode 0xe6
 */
static void
x86emuOp_out_byte_IMM_AL(struct x86emu *emu)
{
        uint8_t port;

        port = (uint8_t) fetch_byte_imm(emu);
        (*emu->emu_outb) (emu, port, emu->x86.R_AL);
}

/*
 * REMARKS:
 * Handles opcode 0xe7
 */
static void
x86emuOp_out_word_IMM_AX(struct x86emu *emu)
{
        uint8_t port;

        port = (uint8_t) fetch_byte_imm(emu);
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                (*emu->emu_outl) (emu, port, emu->x86.R_EAX);
        } else {
                (*emu->emu_outw) (emu, port, emu->x86.R_AX);
        }
}

/*
 * REMARKS:
 * Handles opcode 0xe8
 */
static void
x86emuOp_call_near_IMM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                int32_t ip;
                ip = (int32_t) fetch_long_imm(emu);
                ip += (int32_t) emu->x86.R_EIP;
                push_long(emu, emu->x86.R_EIP);
                emu->x86.R_EIP = ip;
        } else {
                int16_t ip;
                ip = (int16_t) fetch_word_imm(emu);
                ip += (int16_t) emu->x86.R_IP;  /* CHECK SIGN */
                push_word(emu, emu->x86.R_IP);
                emu->x86.R_IP = ip;
        }
}

/*
 * REMARKS:
 * Handles opcode 0xe9
 */
static void
x86emuOp_jump_near_IMM(struct x86emu *emu)
{
        int ip;

        ip = (int16_t) fetch_word_imm(emu);
        ip += (int16_t) emu->x86.R_IP;
        emu->x86.R_IP = (uint16_t) ip;
}

/*
 * REMARKS:
 * Handles opcode 0xea
 */
static void
x86emuOp_jump_far_IMM(struct x86emu *emu)
{
        uint16_t cs, ip;

        ip = fetch_word_imm(emu);
        cs = fetch_word_imm(emu);
        emu->x86.R_IP = ip;
        emu->x86.R_CS = cs;
}

/*
 * REMARKS:
 * Handles opcode 0xeb
 */
static void
x86emuOp_jump_byte_IMM(struct x86emu *emu)
{
        uint16_t target;
        int8_t offset;

        offset = (int8_t) fetch_byte_imm(emu);
        target = (uint16_t) (emu->x86.R_IP + offset);
        emu->x86.R_IP = target;
}

/*
 * REMARKS:
 * Handles opcode 0xec
 */
static void
x86emuOp_in_byte_AL_DX(struct x86emu *emu)
{
        emu->x86.R_AL = (*emu->emu_inb) (emu, emu->x86.R_DX);
}

/*
 * REMARKS:
 * Handles opcode 0xed
 */
static void
x86emuOp_in_word_AX_DX(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                emu->x86.R_EAX = (*emu->emu_inl) (emu, emu->x86.R_DX);
        } else {
                emu->x86.R_AX = (*emu->emu_inw) (emu, emu->x86.R_DX);
        }
}

/*
 * REMARKS:
 * Handles opcode 0xee
 */
static void
x86emuOp_out_byte_DX_AL(struct x86emu *emu)
{
        (*emu->emu_outb) (emu, emu->x86.R_DX, emu->x86.R_AL);
}

/*
 * REMARKS:
 * Handles opcode 0xef
 */
static void
x86emuOp_out_word_DX_AX(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                (*emu->emu_outl) (emu, emu->x86.R_DX, emu->x86.R_EAX);
        } else {
                (*emu->emu_outw) (emu, emu->x86.R_DX, emu->x86.R_AX);
        }
}

/*
 * REMARKS:
 * Handles opcode 0xf0
 */
static void
x86emuOp_lock(struct x86emu *emu)
{
}
/*opcode 0xf1 ILLEGAL OPERATION */


/*
 * REMARKS:
 * Handles opcode 0xf5
 */
static void
x86emuOp_cmc(struct x86emu *emu)
{
        if (ACCESS_FLAG(F_CF))
                CLEAR_FLAG(F_CF);
        else
                SET_FLAG(F_CF);
}

/*
 * REMARKS:
 * Handles opcode 0xf6
 */
static void
x86emuOp_opcF6_byte_RM(struct x86emu *emu)
{
        uint8_t destval, srcval;

        /* long, drawn out code follows.  Double switch for a total of 32
         * cases.  */
        fetch_decode_modrm(emu);
        if (emu->cur_rh == 1)
                x86emu_halt_sys(emu);

        if (emu->cur_rh == 0) {
                destval = decode_and_fetch_byte_imm8(emu, &srcval);
                test_byte(emu, destval, srcval);
                return;
        }
        destval = decode_and_fetch_byte(emu);
        switch (emu->cur_rh) {
        case 2:
                destval = ~destval;
                write_back_byte(emu, destval);
                break;
        case 3:
                destval = neg_byte(emu, destval);
                write_back_byte(emu, destval);
                break;
        case 4:
                mul_byte(emu, destval);
                break;
        case 5:
                imul_byte(emu, destval);
                break;
        case 6:
                div_byte(emu, destval);
                break;
        case 7:
                idiv_byte(emu, destval);
                break;
        }
}

/*
 * REMARKS:
 * Handles opcode 0xf7
 */
static void
x86emuOp32_opcF7_word_RM(struct x86emu *emu)
{
        uint32_t destval, srcval;

        /* long, drawn out code follows.  Double switch for a total of 32
         * cases.  */
        fetch_decode_modrm(emu);
        if (emu->cur_rh == 1)
                x86emu_halt_sys(emu);

        if (emu->cur_rh == 0) {
                if (emu->cur_mod != 3) {
                        uint32_t destoffset;

                        destoffset = decode_rl_address(emu);
                        srcval = fetch_long_imm(emu);
                        destval = fetch_data_long(emu, destoffset);
                } else {
                        srcval = fetch_long_imm(emu);
                        destval = *decode_rl_long_register(emu);
                }
                test_long(emu, destval, srcval);
                return;
        }
        destval = decode_and_fetch_long(emu);
        switch (emu->cur_rh) {
        case 2:
                destval = ~destval;
                write_back_long(emu, destval);
                break;
        case 3:
                destval = neg_long(emu, destval);
                write_back_long(emu, destval);
                break;
        case 4:
                mul_long(emu, destval);
                break;
        case 5:
                imul_long(emu, destval);
                break;
        case 6:
                div_long(emu, destval);
                break;
        case 7:
                idiv_long(emu, destval);
                break;
        }
}
static void
x86emuOp16_opcF7_word_RM(struct x86emu *emu)
{
        uint16_t destval, srcval;

        /* long, drawn out code follows.  Double switch for a total of 32
         * cases.  */
        fetch_decode_modrm(emu);
        if (emu->cur_rh == 1)
                x86emu_halt_sys(emu);

        if (emu->cur_rh == 0) {
                if (emu->cur_mod != 3) {
                        uint32_t destoffset;

                        destoffset = decode_rl_address(emu);
                        srcval = fetch_word_imm(emu);
                        destval = fetch_data_word(emu, destoffset);
                } else {
                        srcval = fetch_word_imm(emu);
                        destval = *decode_rl_word_register(emu);
                }
                test_word(emu, destval, srcval);
                return;
        }
        destval = decode_and_fetch_word(emu);
        switch (emu->cur_rh) {
        case 2:
                destval = ~destval;
                write_back_word(emu, destval);
                break;
        case 3:
                destval = neg_word(emu, destval);
                write_back_word(emu, destval);
                break;
        case 4:
                mul_word(emu, destval);
                break;
        case 5:
                imul_word(emu, destval);
                break;
        case 6:
                div_word(emu, destval);
                break;
        case 7:
                idiv_word(emu, destval);
                break;
        }
}
static void
x86emuOp_opcF7_word_RM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                x86emuOp32_opcF7_word_RM(emu);
        else
                x86emuOp16_opcF7_word_RM(emu);
}

/*
 * REMARKS:
 * Handles opcode 0xfe
 */
static void
x86emuOp_opcFE_byte_RM(struct x86emu *emu)
{
        uint8_t destval;
        uint32_t destoffset;
        uint8_t *destreg;

        /* Yet another special case instruction. */
        fetch_decode_modrm(emu);
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                switch (emu->cur_rh) {
                case 0: /* inc word ptr ... */
                        destval = fetch_data_byte(emu, destoffset);
                        destval = inc_byte(emu, destval);
                        store_data_byte(emu, destoffset, destval);
                        break;
                case 1: /* dec word ptr ... */
                        destval = fetch_data_byte(emu, destoffset);
                        destval = dec_byte(emu, destval);
                        store_data_byte(emu, destoffset, destval);
                        break;
                }
        } else {
                destreg = decode_rl_byte_register(emu);
                switch (emu->cur_rh) {
                case 0:
                        *destreg = inc_byte(emu, *destreg);
                        break;
                case 1:
                        *destreg = dec_byte(emu, *destreg);
                        break;
                }
        }
}

/*
 * REMARKS:
 * Handles opcode 0xff
 */
static void
x86emuOp32_opcFF_word_RM(struct x86emu *emu)
{
        uint32_t destoffset = 0;
        uint32_t destval, *destreg;

        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                destval = fetch_data_long(emu, destoffset);
                switch (emu->cur_rh) {
                case 0: /* inc word ptr ... */
                        destval = inc_long(emu, destval);
                        store_data_long(emu, destoffset, destval);
                        break;
                case 1: /* dec word ptr ... */
                        destval = dec_long(emu, destval);
                        store_data_long(emu, destoffset, destval);
                        break;
                case 6: /* push word ptr ... */
                        push_long(emu, destval);
                        break;
                }
        } else {
                destreg = decode_rl_long_register(emu);
                switch (emu->cur_rh) {
                case 0:
                        *destreg = inc_long(emu, *destreg);
                        break;
                case 1:
                        *destreg = dec_long(emu, *destreg);
                        break;
                case 6:
                        push_long(emu, *destreg);
                        break;
                }
        }
}

static void
x86emuOp16_opcFF_word_RM(struct x86emu *emu)
{
        uint32_t destoffset = 0;
        uint16_t *destreg;
        uint16_t destval;

        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                destval = fetch_data_word(emu, destoffset);
                switch (emu->cur_rh) {
                case 0:
                        destval = inc_word(emu, destval);
                        store_data_word(emu, destoffset, destval);
                        break;
                case 1: /* dec word ptr ... */
                        destval = dec_word(emu, destval);
                        store_data_word(emu, destoffset, destval);
                        break;
                case 6: /* push word ptr ... */
                        push_word(emu, destval);
                        break;
                }
        } else {
                destreg = decode_rl_word_register(emu);
                switch (emu->cur_rh) {
                case 0:
                        *destreg = inc_word(emu, *destreg);
                        break;
                case 1:
                        *destreg = dec_word(emu, *destreg);
                        break;
                case 6:
                        push_word(emu, *destreg);
                        break;
                }
        }
}

static void
x86emuOp_opcFF_word_RM(struct x86emu *emu)
{
        uint32_t destoffset = 0;
        uint16_t destval, destval2;

        /* Yet another special case instruction. */
        fetch_decode_modrm(emu);
        if ((emu->cur_mod == 3 && (emu->cur_rh == 3 || emu->cur_rh == 5)) ||
            emu->cur_rh == 7)
                x86emu_halt_sys(emu);
        if (emu->cur_rh == 0 || emu->cur_rh == 1 || emu->cur_rh == 6) {
                if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                        x86emuOp32_opcFF_word_RM(emu);
                else
                        x86emuOp16_opcFF_word_RM(emu);
                return;
        }

        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                destval = fetch_data_word(emu, destoffset);
                switch (emu->cur_rh) {
                case 3: /* call far ptr ... */
                        destval2 = fetch_data_word(emu, destoffset + 2);
                        push_word(emu, emu->x86.R_CS);
                        emu->x86.R_CS = destval2;
                        push_word(emu, emu->x86.R_IP);
                        emu->x86.R_IP = destval;
                        break;
                case 5: /* jmp far ptr ... */
                        destval2 = fetch_data_word(emu, destoffset + 2);
                        emu->x86.R_IP = destval;
                        emu->x86.R_CS = destval2;
                        break;
                }
        } else {
                destval = *decode_rl_word_register(emu);
        }

        switch (emu->cur_rh) {
        case 2: /* call word ptr */
                push_word(emu, emu->x86.R_IP);
                emu->x86.R_IP = destval;
                break;
        case 4: /* jmp */
                emu->x86.R_IP = destval;
                break;
        }
}

/*
 *  * Single byte operation code table:
 */
static void
x86emu_exec_one_byte(struct x86emu * emu)
{
        uint8_t op1;

        op1 = fetch_byte_imm(emu);

        switch (op1) {
        case 0x00:
                common_binop_byte_rm_r(emu, add_byte);
                break;
        case 0x01:
                common_binop_word_long_rm_r(emu, add_word, add_long);
                break;
        case 0x02:
                common_binop_byte_r_rm(emu, add_byte);
                break;
        case 0x03:
                common_binop_word_long_r_rm(emu, add_word, add_long);
                break;
        case 0x04:
                common_binop_byte_imm(emu, add_byte);
                break;
        case 0x05:
                common_binop_word_long_imm(emu, add_word, add_long);
                break;
        case 0x06:
                push_word(emu, emu->x86.R_ES);
                break;
        case 0x07:
                emu->x86.R_ES = pop_word(emu);
                break;

        case 0x08:
                common_binop_byte_rm_r(emu, or_byte);
                break;
        case 0x09:
                common_binop_word_long_rm_r(emu, or_word, or_long);
                break;
        case 0x0a:
                common_binop_byte_r_rm(emu, or_byte);
                break;
        case 0x0b:
                common_binop_word_long_r_rm(emu, or_word, or_long);
                break;
        case 0x0c:
                common_binop_byte_imm(emu, or_byte);
                break;
        case 0x0d:
                common_binop_word_long_imm(emu, or_word, or_long);
                break;
        case 0x0e:
                push_word(emu, emu->x86.R_CS);
                break;
        case 0x0f:
                x86emu_exec_two_byte(emu);
                break;

        case 0x10:
                common_binop_byte_rm_r(emu, adc_byte);
                break;
        case 0x11:
                common_binop_word_long_rm_r(emu, adc_word, adc_long);
                break;
        case 0x12:
                common_binop_byte_r_rm(emu, adc_byte);
                break;
        case 0x13:
                common_binop_word_long_r_rm(emu, adc_word, adc_long);
                break;
        case 0x14:
                common_binop_byte_imm(emu, adc_byte);
                break;
        case 0x15:
                common_binop_word_long_imm(emu, adc_word, adc_long);
                break;
        case 0x16:
                push_word(emu, emu->x86.R_SS);
                break;
        case 0x17:
                emu->x86.R_SS = pop_word(emu);
                break;

        case 0x18:
                common_binop_byte_rm_r(emu, sbb_byte);
                break;
        case 0x19:
                common_binop_word_long_rm_r(emu, sbb_word, sbb_long);
                break;
        case 0x1a:
                common_binop_byte_r_rm(emu, sbb_byte);
                break;
        case 0x1b:
                common_binop_word_long_r_rm(emu, sbb_word, sbb_long);
                break;
        case 0x1c:
                common_binop_byte_imm(emu, sbb_byte);
                break;
        case 0x1d:
                common_binop_word_long_imm(emu, sbb_word, sbb_long);
                break;
        case 0x1e:
                push_word(emu, emu->x86.R_DS);
                break;
        case 0x1f:
                emu->x86.R_DS = pop_word(emu);
                break;

        case 0x20:
                common_binop_byte_rm_r(emu, and_byte);
                break;
        case 0x21:
                common_binop_word_long_rm_r(emu, and_word, and_long);
                break;
        case 0x22:
                common_binop_byte_r_rm(emu, and_byte);
                break;
        case 0x23:
                common_binop_word_long_r_rm(emu, and_word, and_long);
                break;
        case 0x24:
                common_binop_byte_imm(emu, and_byte);
                break;
        case 0x25:
                common_binop_word_long_imm(emu, and_word, and_long);
                break;
        case 0x26:
                emu->x86.mode |= SYSMODE_SEGOVR_ES;
                break;
        case 0x27:
                emu->x86.R_AL = daa_byte(emu, emu->x86.R_AL);
                break;

        case 0x28:
                common_binop_byte_rm_r(emu, sub_byte);
                break;
        case 0x29:
                common_binop_word_long_rm_r(emu, sub_word, sub_long);
                break;
        case 0x2a:
                common_binop_byte_r_rm(emu, sub_byte);
                break;
        case 0x2b:
                common_binop_word_long_r_rm(emu, sub_word, sub_long);
                break;
        case 0x2c:
                common_binop_byte_imm(emu, sub_byte);
                break;
        case 0x2d:
                common_binop_word_long_imm(emu, sub_word, sub_long);
                break;
        case 0x2e:
                emu->x86.mode |= SYSMODE_SEGOVR_CS;
                break;
        case 0x2f:
                emu->x86.R_AL = das_byte(emu, emu->x86.R_AL);
                break;

        case 0x30:
                common_binop_byte_rm_r(emu, xor_byte);
                break;
        case 0x31:
                common_binop_word_long_rm_r(emu, xor_word, xor_long);
                break;
        case 0x32:
                common_binop_byte_r_rm(emu, xor_byte);
                break;
        case 0x33:
                common_binop_word_long_r_rm(emu, xor_word, xor_long);
                break;
        case 0x34:
                common_binop_byte_imm(emu, xor_byte);
                break;
        case 0x35:
                common_binop_word_long_imm(emu, xor_word, xor_long);
                break;
        case 0x36:
                emu->x86.mode |= SYSMODE_SEGOVR_SS;
                break;
        case 0x37:
                emu->x86.R_AX = aaa_word(emu, emu->x86.R_AX);
                break;

        case 0x38:
                common_binop_ns_byte_rm_r(emu, cmp_byte_no_return);
                break;
        case 0x39:
                common_binop_ns_word_long_rm_r(emu, cmp_word_no_return,
                    cmp_long_no_return);
                break;
        case 0x3a:
                x86emuOp_cmp_byte_R_RM(emu);
                break;
        case 0x3b:
                x86emuOp_cmp_word_R_RM(emu);
                break;
        case 0x3c:
                x86emuOp_cmp_byte_AL_IMM(emu);
                break;
        case 0x3d:
                x86emuOp_cmp_word_AX_IMM(emu);
                break;
        case 0x3e:
                emu->x86.mode |= SYSMODE_SEGOVR_DS;
                break;
        case 0x3f:
                emu->x86.R_AX = aas_word(emu, emu->x86.R_AX);
                break;

        case 0x40:
                common_inc_word_long(emu, &emu->x86.register_a);
                break;
        case 0x41:
                common_inc_word_long(emu, &emu->x86.register_c);
                break;
        case 0x42:
                common_inc_word_long(emu, &emu->x86.register_d);
                break;
        case 0x43:
                common_inc_word_long(emu, &emu->x86.register_b);
                break;
        case 0x44:
                common_inc_word_long(emu, &emu->x86.register_sp);
                break;
        case 0x45:
                common_inc_word_long(emu, &emu->x86.register_bp);
                break;
        case 0x46:
                common_inc_word_long(emu, &emu->x86.register_si);
                break;
        case 0x47:
                common_inc_word_long(emu, &emu->x86.register_di);
                break;

        case 0x48:
                common_dec_word_long(emu, &emu->x86.register_a);
                break;
        case 0x49:
                common_dec_word_long(emu, &emu->x86.register_c);
                break;
        case 0x4a:
                common_dec_word_long(emu, &emu->x86.register_d);
                break;
        case 0x4b:
                common_dec_word_long(emu, &emu->x86.register_b);
                break;
        case 0x4c:
                common_dec_word_long(emu, &emu->x86.register_sp);
                break;
        case 0x4d:
                common_dec_word_long(emu, &emu->x86.register_bp);
                break;
        case 0x4e:
                common_dec_word_long(emu, &emu->x86.register_si);
                break;
        case 0x4f:
                common_dec_word_long(emu, &emu->x86.register_di);
                break;

        case 0x50:
                common_push_word_long(emu, &emu->x86.register_a);
                break;
        case 0x51:
                common_push_word_long(emu, &emu->x86.register_c);
                break;
        case 0x52:
                common_push_word_long(emu, &emu->x86.register_d);
                break;
        case 0x53:
                common_push_word_long(emu, &emu->x86.register_b);
                break;
        case 0x54:
                common_push_word_long(emu, &emu->x86.register_sp);
                break;
        case 0x55:
                common_push_word_long(emu, &emu->x86.register_bp);
                break;
        case 0x56:
                common_push_word_long(emu, &emu->x86.register_si);
                break;
        case 0x57:
                common_push_word_long(emu, &emu->x86.register_di);
                break;

        case 0x58:
                common_pop_word_long(emu, &emu->x86.register_a);
                break;
        case 0x59:
                common_pop_word_long(emu, &emu->x86.register_c);
                break;
        case 0x5a:
                common_pop_word_long(emu, &emu->x86.register_d);
                break;
        case 0x5b:
                common_pop_word_long(emu, &emu->x86.register_b);
                break;
        case 0x5c:
                common_pop_word_long(emu, &emu->x86.register_sp);
                break;
        case 0x5d:
                common_pop_word_long(emu, &emu->x86.register_bp);
                break;
        case 0x5e:
                common_pop_word_long(emu, &emu->x86.register_si);
                break;
        case 0x5f:
                common_pop_word_long(emu, &emu->x86.register_di);
                break;

        case 0x60:
                x86emuOp_push_all(emu);
                break;
        case 0x61:
                x86emuOp_pop_all(emu);
                break;
        /* 0x62 bound */
        /* 0x63 arpl */
        case 0x64:
                emu->x86.mode |= SYSMODE_SEGOVR_FS;
                break;
        case 0x65:
                emu->x86.mode |= SYSMODE_SEGOVR_GS;
                break;
        case 0x66:
                emu->x86.mode |= SYSMODE_PREFIX_DATA;
                break;
        case 0x67:
                emu->x86.mode |= SYSMODE_PREFIX_ADDR;
                break;

        case 0x68:
                x86emuOp_push_word_IMM(emu);
                break;
        case 0x69:
                common_imul_imm(emu, 0);
                break;
        case 0x6a:
                x86emuOp_push_byte_IMM(emu);
                break;
        case 0x6b:
                common_imul_imm(emu, 1);
                break;
        case 0x6c:
                ins(emu, 1);
                break;
        case 0x6d:
                x86emuOp_ins_word(emu);
                break;
        case 0x6e:
                outs(emu, 1);
                break;
        case 0x6f:
                x86emuOp_outs_word(emu);
                break;

        case 0x70:
                common_jmp_near(emu, ACCESS_FLAG(F_OF));
                break;
        case 0x71:
                common_jmp_near(emu, !ACCESS_FLAG(F_OF));
                break;
        case 0x72:
                common_jmp_near(emu, ACCESS_FLAG(F_CF));
                break;
        case 0x73:
                common_jmp_near(emu, !ACCESS_FLAG(F_CF));
                break;
        case 0x74:
                common_jmp_near(emu, ACCESS_FLAG(F_ZF));
                break;
        case 0x75:
                common_jmp_near(emu, !ACCESS_FLAG(F_ZF));
                break;
        case 0x76:
                common_jmp_near(emu, ACCESS_FLAG(F_CF) || ACCESS_FLAG(F_ZF));
                break;
        case 0x77:
                common_jmp_near(emu, !ACCESS_FLAG(F_CF) && !ACCESS_FLAG(F_ZF));
                break;

        case 0x78:
                common_jmp_near(emu, ACCESS_FLAG(F_SF));
                break;
        case 0x79:
                common_jmp_near(emu, !ACCESS_FLAG(F_SF));
                break;
        case 0x7a:
                common_jmp_near(emu, ACCESS_FLAG(F_PF));
                break;
        case 0x7b:
                common_jmp_near(emu, !ACCESS_FLAG(F_PF));
                break;
        case 0x7c:
                x86emuOp_jump_near_L(emu);
                break;
        case 0x7d:
                x86emuOp_jump_near_NL(emu);
                break;
        case 0x7e:
                x86emuOp_jump_near_LE(emu);
                break;
        case 0x7f:
                x86emuOp_jump_near_NLE(emu);
                break;

        case 0x80:
                x86emuOp_opc80_byte_RM_IMM(emu);
                break;
        case 0x81:
                x86emuOp_opc81_word_RM_IMM(emu);
                break;
        case 0x82:
                x86emuOp_opc82_byte_RM_IMM(emu);
                break;
        case 0x83:
                x86emuOp_opc83_word_RM_IMM(emu);
                break;
        case 0x84:
                common_binop_ns_byte_rm_r(emu, test_byte);
                break;
        case 0x85:
                common_binop_ns_word_long_rm_r(emu, test_word, test_long);
                break;
        case 0x86:
                x86emuOp_xchg_byte_RM_R(emu);
                break;
        case 0x87:
                x86emuOp_xchg_word_RM_R(emu);
                break;

        case 0x88:
                x86emuOp_mov_byte_RM_R(emu);
                break;
        case 0x89:
                x86emuOp_mov_word_RM_R(emu);
                break;
        case 0x8a:
                x86emuOp_mov_byte_R_RM(emu);
                break;
        case 0x8b:
                x86emuOp_mov_word_R_RM(emu);
                break;
        case 0x8c:
                x86emuOp_mov_word_RM_SR(emu);
                break;
        case 0x8d:
                x86emuOp_lea_word_R_M(emu);
                break;
        case 0x8e:
                x86emuOp_mov_word_SR_RM(emu);
                break;
        case 0x8f:
                x86emuOp_pop_RM(emu);
                break;

        case 0x90:
                /* nop */
                break;
        case 0x91:
                x86emuOp_xchg_word_AX_CX(emu);
                break;
        case 0x92:
                x86emuOp_xchg_word_AX_DX(emu);
                break;
        case 0x93:
                x86emuOp_xchg_word_AX_BX(emu);
                break;
        case 0x94:
                x86emuOp_xchg_word_AX_SP(emu);
                break;
        case 0x95:
                x86emuOp_xchg_word_AX_BP(emu);
                break;
        case 0x96:
                x86emuOp_xchg_word_AX_SI(emu);
                break;
        case 0x97:
                x86emuOp_xchg_word_AX_DI(emu);
                break;

        case 0x98:
                x86emuOp_cbw(emu);
                break;
        case 0x99:
                x86emuOp_cwd(emu);
                break;
        case 0x9a:
                x86emuOp_call_far_IMM(emu);
                break;
        case 0x9b:
                /* wait */
                break;
        case 0x9c:
                x86emuOp_pushf_word(emu);
                break;
        case 0x9d:
                x86emuOp_popf_word(emu);
                break;
        case 0x9e:
                x86emuOp_sahf(emu);
                break;
        case 0x9f:
                x86emuOp_lahf(emu);
                break;

        case 0xa0:
                x86emuOp_mov_AL_M_IMM(emu);
                break;
        case 0xa1:
                x86emuOp_mov_AX_M_IMM(emu);
                break;
        case 0xa2:
                x86emuOp_mov_M_AL_IMM(emu);
                break;
        case 0xa3:
                x86emuOp_mov_M_AX_IMM(emu);
                break;
        case 0xa4:
                x86emuOp_movs_byte(emu);
                break;
        case 0xa5:
                x86emuOp_movs_word(emu);
                break;
        case 0xa6:
                x86emuOp_cmps_byte(emu);
                break;
        case 0xa7:
                x86emuOp_cmps_word(emu);
                break;

        case 0xa8:
                test_byte(emu, emu->x86.R_AL, fetch_byte_imm(emu));
                break;
        case 0xa9:
                x86emuOp_test_AX_IMM(emu);
                break;
        case 0xaa:
                x86emuOp_stos_byte(emu);
                break;
        case 0xab:
                x86emuOp_stos_word(emu);
                break;
        case 0xac:
                x86emuOp_lods_byte(emu);
                break;
        case 0xad:
                x86emuOp_lods_word(emu);
                break;
        case 0xae:
                x86emuOp_scas_byte(emu);
                break;
        case 0xaf:
                x86emuOp_scas_word(emu);
                break;

        case 0xb0:
                emu->x86.R_AL = fetch_byte_imm(emu);
                break;
        case 0xb1:
                emu->x86.R_CL = fetch_byte_imm(emu);
                break;
        case 0xb2:
                emu->x86.R_DL = fetch_byte_imm(emu);
                break;
        case 0xb3:
                emu->x86.R_BL = fetch_byte_imm(emu);
                break;
        case 0xb4:
                emu->x86.R_AH = fetch_byte_imm(emu);
                break;
        case 0xb5:
                emu->x86.R_CH = fetch_byte_imm(emu);
                break;
        case 0xb6:
                emu->x86.R_DH = fetch_byte_imm(emu);
                break;
        case 0xb7:
                emu->x86.R_BH = fetch_byte_imm(emu);
                break;

        case 0xb8:
                x86emuOp_mov_word_AX_IMM(emu);
                break;
        case 0xb9:
                x86emuOp_mov_word_CX_IMM(emu);
                break;
        case 0xba:
                x86emuOp_mov_word_DX_IMM(emu);
                break;
        case 0xbb:
                x86emuOp_mov_word_BX_IMM(emu);
                break;
        case 0xbc:

                x86emuOp_mov_word_SP_IMM(emu);
                break;
        case 0xbd:
                x86emuOp_mov_word_BP_IMM(emu);
                break;
        case 0xbe:
                x86emuOp_mov_word_SI_IMM(emu);
                break;
        case 0xbf:
                x86emuOp_mov_word_DI_IMM(emu);
                break;

        case 0xc0:
                x86emuOp_opcC0_byte_RM_MEM(emu);
                break;
        case 0xc1:
                x86emuOp_opcC1_word_RM_MEM(emu);
                break;
        case 0xc2:
                x86emuOp_ret_near_IMM(emu);
                break;
        case 0xc3:
                emu->x86.R_IP = pop_word(emu);
                break;
        case 0xc4:
                common_load_far_pointer(emu, &emu->x86.R_ES);
                break;
        case 0xc5:
                common_load_far_pointer(emu, &emu->x86.R_DS);
                break;
        case 0xc6:
                x86emuOp_mov_byte_RM_IMM(emu);
                break;
        case 0xc7:
                x86emuOp_mov_word_RM_IMM(emu);
                break;
        case 0xc8:
                x86emuOp_enter(emu);
                break;
        case 0xc9:
                x86emuOp_leave(emu);
                break;
        case 0xca:
                x86emuOp_ret_far_IMM(emu);
                break;
        case 0xcb:
                x86emuOp_ret_far(emu);
                break;
        case 0xcc:
                x86emuOp_int3(emu);
                break;
        case 0xcd:
                x86emuOp_int_IMM(emu);
                break;
        case 0xce:
                x86emuOp_into(emu);
                break;
        case 0xcf:
                x86emuOp_iret(emu);
                break;

        case 0xd0:
                x86emuOp_opcD0_byte_RM_1(emu);
                break;
        case 0xd1:
                x86emuOp_opcD1_word_RM_1(emu);
                break;
        case 0xd2:
                x86emuOp_opcD2_byte_RM_CL(emu);
                break;
        case 0xd3:
                x86emuOp_opcD3_word_RM_CL(emu);
                break;
        case 0xd4:
                x86emuOp_aam(emu);
                break;
        case 0xd5:
                x86emuOp_aad(emu);
                break;
        /* 0xd6 Undocumented SETALC instruction */
        case 0xd7:
                x86emuOp_xlat(emu);
                break;
        case 0xd8:
                x86emuOp_esc_coprocess_d8(emu);
                break;
        case 0xd9:
                x86emuOp_esc_coprocess_d9(emu);
                break;
        case 0xda:
                x86emuOp_esc_coprocess_da(emu);
                break;
        case 0xdb:
                x86emuOp_esc_coprocess_db(emu);
                break;
        case 0xdc:
                x86emuOp_esc_coprocess_dc(emu);
                break;
        case 0xdd:
                x86emuOp_esc_coprocess_dd(emu);
                break;
        case 0xde:
                x86emuOp_esc_coprocess_de(emu);
                break;
        case 0xdf:
                x86emuOp_esc_coprocess_df(emu);
                break;

        case 0xe0:
                x86emuOp_loopne(emu);
                break;
        case 0xe1:
                x86emuOp_loope(emu);
                break;
        case 0xe2:
                x86emuOp_loop(emu);
                break;
        case 0xe3:
                x86emuOp_jcxz(emu);
                break;
        case 0xe4:
                x86emuOp_in_byte_AL_IMM(emu);
                break;
        case 0xe5:
                x86emuOp_in_word_AX_IMM(emu);
                break;
        case 0xe6:
                x86emuOp_out_byte_IMM_AL(emu);
                break;
        case 0xe7:
                x86emuOp_out_word_IMM_AX(emu);
                break;

        case 0xe8:
                x86emuOp_call_near_IMM(emu);
                break;
        case 0xe9:
                x86emuOp_jump_near_IMM(emu);
                break;
        case 0xea:
                x86emuOp_jump_far_IMM(emu);
                break;
        case 0xeb:
                x86emuOp_jump_byte_IMM(emu);
                break;
        case 0xec:
                x86emuOp_in_byte_AL_DX(emu);
                break;
        case 0xed:
                x86emuOp_in_word_AX_DX(emu);
                break;
        case 0xee:
                x86emuOp_out_byte_DX_AL(emu);
                break;
        case 0xef:
                x86emuOp_out_word_DX_AX(emu);
                break;

        case 0xf0:
                x86emuOp_lock(emu);
                break;
        case 0xf2:
                emu->x86.mode |= SYSMODE_PREFIX_REPNE;
                break;
        case 0xf3:
                emu->x86.mode |= SYSMODE_PREFIX_REPE;
                break;
        case 0xf4:
                x86emu_halt_sys(emu);
                break;
        case 0xf5:
                x86emuOp_cmc(emu);
                break;
        case 0xf6:
                x86emuOp_opcF6_byte_RM(emu);
                break;
        case 0xf7:
                x86emuOp_opcF7_word_RM(emu);
                break;

        case 0xf8:
                CLEAR_FLAG(F_CF);
                break;
        case 0xf9:
                SET_FLAG(F_CF);
                break;
        case 0xfa:
                CLEAR_FLAG(F_IF);
                break;
        case 0xfb:
                SET_FLAG(F_IF);
                break;
        case 0xfc:
                CLEAR_FLAG(F_DF);
                break;
        case 0xfd:
                SET_FLAG(F_DF);
                break;
        case 0xfe:
                x86emuOp_opcFE_byte_RM(emu);
                break;
        case 0xff:
                x86emuOp_opcFF_word_RM(emu);
                break;
        default:
                x86emu_halt_sys(emu);
                break;
        }
        if (op1 != 0x26 && op1 != 0x2e && op1 != 0x36 && op1 != 0x3e &&
            (op1 | 3) != 0x67)
                emu->x86.mode &= ~SYSMODE_CLRMASK;
}

static void
common_jmp_long(struct x86emu *emu, int cond)
{
        int16_t target;

        target = (int16_t) fetch_word_imm(emu);
        target += (int16_t) emu->x86.R_IP;
        if (cond)
                emu->x86.R_IP = (uint16_t) target;
}

static void
common_set_byte(struct x86emu *emu, int cond)
{
        uint32_t destoffset;
        uint8_t *destreg, destval;

        fetch_decode_modrm(emu);
        destval = cond ? 0x01 : 0x00;
        if (emu->cur_mod != 3) {
                destoffset = decode_rl_address(emu);
                store_data_byte(emu, destoffset, destval);
        } else {
                destreg = decode_rl_byte_register(emu);
                *destreg = destval;
        }
}

static void
common_bitstring32(struct x86emu *emu, int op)
{
        int bit;
        uint32_t srcval, *shiftreg, mask;

        fetch_decode_modrm(emu);
        shiftreg = decode_rh_long_register(emu);
        srcval = decode_and_fetch_long_disp(emu, (int16_t) *shiftreg >> 5);
        bit = *shiftreg & 0x1F;
        mask =  0x1 << bit;
        CONDITIONAL_SET_FLAG(srcval & mask, F_CF);

        switch (op) {
        case 0:
                break;
        case 1:
                write_back_long(emu, srcval | mask);
                break;
        case 2:
                write_back_long(emu, srcval & ~mask);
                break;
        case 3:
                write_back_long(emu, srcval ^ mask);
                break;
        }
}

static void
common_bitstring16(struct x86emu *emu, int op)
{
        int bit;
        uint16_t srcval, *shiftreg, mask;

        fetch_decode_modrm(emu);
        shiftreg = decode_rh_word_register(emu);
        srcval = decode_and_fetch_word_disp(emu, (int16_t) *shiftreg >> 4);
        bit = *shiftreg & 0xF;
        mask =  0x1 << bit;
        CONDITIONAL_SET_FLAG(srcval & mask, F_CF);

        switch (op) {
        case 0:
                break;
        case 1:
                write_back_word(emu, srcval | mask);
                break;
        case 2:
                write_back_word(emu, srcval & ~mask);
                break;
        case 3:
                write_back_word(emu, srcval ^ mask);
                break;
        }
}

static void
common_bitstring(struct x86emu *emu, int op)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                common_bitstring32(emu, op);
        else
                common_bitstring16(emu, op);
}

static void
common_bitsearch32(struct x86emu *emu, int diff)
{
        uint32_t srcval, *dstreg;

        fetch_decode_modrm(emu);
        dstreg = decode_rh_long_register(emu);
        srcval = decode_and_fetch_long(emu);
        CONDITIONAL_SET_FLAG(srcval == 0, F_ZF);
        for (*dstreg = 0; *dstreg < 32; *dstreg += diff) {
                if ((srcval >> *dstreg) & 1)
                        break;
        }
}

static void
common_bitsearch16(struct x86emu *emu, int diff)
{
        uint16_t srcval, *dstreg;

        fetch_decode_modrm(emu);
        dstreg = decode_rh_word_register(emu);
        srcval = decode_and_fetch_word(emu);
        CONDITIONAL_SET_FLAG(srcval == 0, F_ZF);
        for (*dstreg = 0; *dstreg < 16; *dstreg += diff) {
                if ((srcval >> *dstreg) & 1)
                        break;
        }
}

static void
common_bitsearch(struct x86emu *emu, int diff)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                common_bitsearch32(emu, diff);
        else
                common_bitsearch16(emu, diff);
}

static void
common_shift32(struct x86emu *emu, int shift_left, int use_cl)
{
        uint8_t shift;
        uint32_t destval, *shiftreg;

        fetch_decode_modrm(emu);
        shiftreg = decode_rh_long_register(emu);
        if (use_cl) {
                destval = decode_and_fetch_long(emu);
                shift = emu->x86.R_CL;
        } else {
                destval = decode_and_fetch_long_imm8(emu, &shift);
        }
        if (shift_left)
                destval = shld_long(emu, destval, *shiftreg, shift);
        else
                destval = shrd_long(emu, destval, *shiftreg, shift);
        write_back_long(emu, destval);
}

static void
common_shift16(struct x86emu *emu, int shift_left, int use_cl)
{
        uint8_t shift;
        uint16_t destval, *shiftreg;

        fetch_decode_modrm(emu);
        shiftreg = decode_rh_word_register(emu);
        if (use_cl) {
                destval = decode_and_fetch_word(emu);
                shift = emu->x86.R_CL;
        } else {
                destval = decode_and_fetch_word_imm8(emu, &shift);
        }
        if (shift_left)
                destval = shld_word(emu, destval, *shiftreg, shift);
        else
                destval = shrd_word(emu, destval, *shiftreg, shift);
        write_back_word(emu, destval);
}

static void
common_shift(struct x86emu *emu, int shift_left, int use_cl)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                common_shift32(emu, shift_left, use_cl);
        else
                common_shift16(emu, shift_left, use_cl);
}

/*
 * Implementation
 */
#define xorl(a,b)   ((a) && !(b)) || (!(a) && (b))


/*
 * REMARKS:
 * Handles opcode 0x0f,0x31
 */
static void
x86emuOp2_rdtsc(struct x86emu *emu)
{
        emu->x86.R_EAX = emu->cur_cycles & 0xffffffff;
        emu->x86.R_EDX = emu->cur_cycles >> 32;
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xa0
 */
static void
x86emuOp2_push_FS(struct x86emu *emu)
{
        push_word(emu, emu->x86.R_FS);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xa1
 */
static void
x86emuOp2_pop_FS(struct x86emu *emu)
{
        emu->x86.R_FS = pop_word(emu);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xa1
 */
#if defined(__i386__) || defined(__amd64__)
static void
hw_cpuid(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d)
{
        __asm__ volatile("cpuid"
                             : "=a" (*a), "=b" (*b),
                               "=c" (*c), "=d" (*d)
                             : "a" (*a), "c" (*c)
                             : "cc");
}
#endif
static void
x86emuOp2_cpuid(struct x86emu *emu)
{
#if defined(__i386__) || defined(__amd64__)
        hw_cpuid(&emu->x86.R_EAX, &emu->x86.R_EBX, &emu->x86.R_ECX,
            &emu->x86.R_EDX);
#endif
        switch (emu->x86.R_EAX) {
        case 0:
                emu->x86.R_EAX = 1;
#if !defined(__i386__) && !defined(__amd64__)
                /* "GenuineIntel" */
                emu->x86.R_EBX = 0x756e6547;
                emu->x86.R_EDX = 0x49656e69;
                emu->x86.R_ECX = 0x6c65746e;
#endif
                break;
        case 1:
#if !defined(__i386__) && !defined(__amd64__)
                emu->x86.R_EAX = 0x00000480;
                emu->x86.R_EBX = emu->x86.R_ECX = 0;
                emu->x86.R_EDX = 0x00000002;
#else
                emu->x86.R_EDX &= 0x00000012;
#endif
                break;
        default:
                emu->x86.R_EAX = emu->x86.R_EBX = emu->x86.R_ECX =
                    emu->x86.R_EDX = 0;
                break;
        }
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xa3
 */
static void
x86emuOp2_bt_R(struct x86emu *emu)
{
        common_bitstring(emu, 0);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xa4
 */
static void
x86emuOp2_shld_IMM(struct x86emu *emu)
{
        common_shift(emu, 1, 0);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xa5
 */
static void
x86emuOp2_shld_CL(struct x86emu *emu)
{
        common_shift(emu, 1, 1);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xa8
 */
static void
x86emuOp2_push_GS(struct x86emu *emu)
{
        push_word(emu, emu->x86.R_GS);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xa9
 */
static void
x86emuOp2_pop_GS(struct x86emu *emu)
{
        emu->x86.R_GS = pop_word(emu);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xab
 */
static void
x86emuOp2_bts_R(struct x86emu *emu)
{
        common_bitstring(emu, 1);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xac
 */
static void
x86emuOp2_shrd_IMM(struct x86emu *emu)
{
        common_shift(emu, 0, 0);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xad
 */
static void
x86emuOp2_shrd_CL(struct x86emu *emu)
{
        common_shift(emu, 0, 1);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xaf
 */
static void
x86emuOp2_32_imul_R_RM(struct x86emu *emu)
{
        uint32_t *destreg, srcval;
        uint64_t res;

        fetch_decode_modrm(emu);
        destreg = decode_rh_long_register(emu);
        srcval = decode_and_fetch_long(emu);
        res = (int32_t) *destreg * (int32_t)srcval;
        if (res > 0xffffffff) {
                SET_FLAG(F_CF);
                SET_FLAG(F_OF);
        } else {
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_OF);
        }
        *destreg = (uint32_t) res;
}

static void
x86emuOp2_16_imul_R_RM(struct x86emu *emu)
{
        uint16_t *destreg, srcval;
        uint32_t res;

        fetch_decode_modrm(emu);
        destreg = decode_rh_word_register(emu);
        srcval = decode_and_fetch_word(emu);
        res = (int16_t) * destreg * (int16_t)srcval;
        if (res > 0xFFFF) {
                SET_FLAG(F_CF);
                SET_FLAG(F_OF);
        } else {
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_OF);
        }
        *destreg = (uint16_t) res;
}

static void
x86emuOp2_imul_R_RM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                x86emuOp2_32_imul_R_RM(emu);
        else
                x86emuOp2_16_imul_R_RM(emu);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xb2
 */
static void
x86emuOp2_lss_R_IMM(struct x86emu *emu)
{
        common_load_far_pointer(emu, &emu->x86.R_SS);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xb3
 */
static void
x86emuOp2_btr_R(struct x86emu *emu)
{
        common_bitstring(emu, 2);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xb4
 */
static void
x86emuOp2_lfs_R_IMM(struct x86emu *emu)
{
        common_load_far_pointer(emu, &emu->x86.R_FS);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xb5
 */
static void
x86emuOp2_lgs_R_IMM(struct x86emu *emu)
{
        common_load_far_pointer(emu, &emu->x86.R_GS);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xb6
 */
static void
x86emuOp2_32_movzx_byte_R_RM(struct x86emu *emu)
{
        uint32_t *destreg;

        fetch_decode_modrm(emu);
        destreg = decode_rh_long_register(emu);
        *destreg = decode_and_fetch_byte(emu);
}

static void
x86emuOp2_16_movzx_byte_R_RM(struct x86emu *emu)
{
        uint16_t *destreg;

        fetch_decode_modrm(emu);
        destreg = decode_rh_word_register(emu);
        *destreg = decode_and_fetch_byte(emu);
}

static void
x86emuOp2_movzx_byte_R_RM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                x86emuOp2_32_movzx_byte_R_RM(emu);
        else
                x86emuOp2_16_movzx_byte_R_RM(emu);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xb7
 */
static void
x86emuOp2_movzx_word_R_RM(struct x86emu *emu)
{
        uint32_t *destreg;

        fetch_decode_modrm(emu);
        destreg = decode_rh_long_register(emu);
        *destreg = decode_and_fetch_word(emu);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xba
 */
static void
x86emuOp2_32_btX_I(struct x86emu *emu)
{
        int bit;
        uint32_t srcval, mask;
        uint8_t shift;

        fetch_decode_modrm(emu);
        if (emu->cur_rh < 4)
                x86emu_halt_sys(emu);

        srcval = decode_and_fetch_long_imm8(emu, &shift);
        bit = shift & 0x1F;
        mask = (0x1 << bit);

        switch (emu->cur_rh) {
        case 5:
                write_back_long(emu, srcval | mask);
                break;
        case 6:
                write_back_long(emu, srcval & ~mask);
                break;
        case 7:
                write_back_long(emu, srcval ^ mask);
                break;
        }
        CONDITIONAL_SET_FLAG(srcval & mask, F_CF);
}

static void
x86emuOp2_16_btX_I(struct x86emu *emu)
{
        int bit;

        uint16_t srcval, mask;
        uint8_t shift;

        fetch_decode_modrm(emu);
        if (emu->cur_rh < 4)
                x86emu_halt_sys(emu);

        srcval = decode_and_fetch_word_imm8(emu, &shift);
        bit = shift & 0xF;
        mask = (0x1 << bit);
        switch (emu->cur_rh) {
        case 5:
                write_back_word(emu, srcval | mask);
                break;
        case 6:
                write_back_word(emu, srcval & ~mask);
                break;
        case 7:
                write_back_word(emu, srcval ^ mask);
                break;
        }
        CONDITIONAL_SET_FLAG(srcval & mask, F_CF);
}

static void
x86emuOp2_btX_I(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                x86emuOp2_32_btX_I(emu);
        else
                x86emuOp2_16_btX_I(emu);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xbb
 */
static void
x86emuOp2_btc_R(struct x86emu *emu)
{
        common_bitstring(emu, 3);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xbc
 */
static void
x86emuOp2_bsf(struct x86emu *emu)
{
        common_bitsearch(emu, +1);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xbd
 */
static void
x86emuOp2_bsr(struct x86emu *emu)
{
        common_bitsearch(emu, -1);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xbe
 */
static void
x86emuOp2_32_movsx_byte_R_RM(struct x86emu *emu)
{
        uint32_t *destreg;

        fetch_decode_modrm(emu);
        destreg = decode_rh_long_register(emu);
        *destreg = (int32_t)(int8_t)decode_and_fetch_byte(emu);
}

static void
x86emuOp2_16_movsx_byte_R_RM(struct x86emu *emu)
{
        uint16_t *destreg;

        fetch_decode_modrm(emu);
        destreg = decode_rh_word_register(emu);
        *destreg = (int16_t)(int8_t)decode_and_fetch_byte(emu);
}

static void
x86emuOp2_movsx_byte_R_RM(struct x86emu *emu)
{
        if (emu->x86.mode & SYSMODE_PREFIX_DATA)
                x86emuOp2_32_movsx_byte_R_RM(emu);
        else
                x86emuOp2_16_movsx_byte_R_RM(emu);
}

/*
 * REMARKS:
 * Handles opcode 0x0f,0xbf
 */
static void
x86emuOp2_movsx_word_R_RM(struct x86emu *emu)
{
        uint32_t *destreg;

        fetch_decode_modrm(emu);
        destreg = decode_rh_long_register(emu);
        *destreg = (int32_t)(int16_t)decode_and_fetch_word(emu);
}

static void
x86emu_exec_two_byte(struct x86emu * emu)
{
        uint8_t op2;

        op2 = fetch_byte_imm(emu);

        switch (op2) {
        /* 0x00 Group F (ring 0 PM)      */
        /* 0x01 Group G (ring 0 PM)      */
        /* 0x02 lar (ring 0 PM)          */
        /* 0x03 lsl (ring 0 PM)          */
        /* 0x05 loadall (undocumented)   */
        /* 0x06 clts (ring 0 PM)         */
        /* 0x07 loadall (undocumented)   */
        /* 0x08 invd (ring 0 PM)         */
        /* 0x09 wbinvd (ring 0 PM)       */

        /* 0x20 mov reg32(op2); break;creg (ring 0 PM) */
        /* 0x21 mov reg32(op2); break;dreg (ring 0 PM) */
        /* 0x22 mov creg(op2); break;reg32 (ring 0 PM) */
        /* 0x23 mov dreg(op2); break;reg32 (ring 0 PM) */
        /* 0x24 mov reg32(op2); break;treg (ring 0 PM) */
        /* 0x26 mov treg(op2); break;reg32 (ring 0 PM) */

        case 0x31:
                x86emuOp2_rdtsc(emu);
                break;

        case 0x80:
                common_jmp_long(emu, ACCESS_FLAG(F_OF));
                break;
        case 0x81:
                common_jmp_long(emu, !ACCESS_FLAG(F_OF));
                break;
        case 0x82:
                common_jmp_long(emu, ACCESS_FLAG(F_CF));
                break;
        case 0x83:
                common_jmp_long(emu, !ACCESS_FLAG(F_CF));
                break;
        case 0x84:
                common_jmp_long(emu, ACCESS_FLAG(F_ZF));
                break;
        case 0x85:
                common_jmp_long(emu, !ACCESS_FLAG(F_ZF));
                break;
        case 0x86:
                common_jmp_long(emu, ACCESS_FLAG(F_CF) || ACCESS_FLAG(F_ZF));
                break;
        case 0x87:
                common_jmp_long(emu, !(ACCESS_FLAG(F_CF) || ACCESS_FLAG(F_ZF)));
                break;
        case 0x88:
                common_jmp_long(emu, ACCESS_FLAG(F_SF));
                break;
        case 0x89:
                common_jmp_long(emu, !ACCESS_FLAG(F_SF));
                break;
        case 0x8a:
                common_jmp_long(emu, ACCESS_FLAG(F_PF));
                break;
        case 0x8b:
                common_jmp_long(emu, !ACCESS_FLAG(F_PF));
                break;
        case 0x8c:
                common_jmp_long(emu, xorl(ACCESS_FLAG(F_SF),
                    ACCESS_FLAG(F_OF)));
                break;
        case 0x8d:
                common_jmp_long(emu, !(xorl(ACCESS_FLAG(F_SF),
                    ACCESS_FLAG(F_OF))));
                break;
        case 0x8e:
                common_jmp_long(emu, (xorl(ACCESS_FLAG(F_SF), ACCESS_FLAG(F_OF))
                    || ACCESS_FLAG(F_ZF)));
                break;
        case 0x8f:
                common_jmp_long(emu, 
                    !(xorl(ACCESS_FLAG(F_SF), ACCESS_FLAG(F_OF)) ||
                    ACCESS_FLAG(F_ZF)));
                break;

        case 0x90:
                common_set_byte(emu, ACCESS_FLAG(F_OF));
                break;
        case 0x91:
                common_set_byte(emu, !ACCESS_FLAG(F_OF));
                break;
        case 0x92:
                common_set_byte(emu, ACCESS_FLAG(F_CF));
                break;
        case 0x93:
                common_set_byte(emu, !ACCESS_FLAG(F_CF));
                break;
        case 0x94:
                common_set_byte(emu, ACCESS_FLAG(F_ZF));
                break;
        case 0x95:
                common_set_byte(emu, !ACCESS_FLAG(F_ZF));
                break;
        case 0x96:
                common_set_byte(emu, ACCESS_FLAG(F_CF) || ACCESS_FLAG(F_ZF));
                break;
        case 0x97:
                common_set_byte(emu, !(ACCESS_FLAG(F_CF) || ACCESS_FLAG(F_ZF)));
                break;
        case 0x98:
                common_set_byte(emu, ACCESS_FLAG(F_SF));
                break;
        case 0x99:
                common_set_byte(emu, !ACCESS_FLAG(F_SF));
                break;
        case 0x9a:
                common_set_byte(emu, ACCESS_FLAG(F_PF));
                break;
        case 0x9b:
                common_set_byte(emu, !ACCESS_FLAG(F_PF));
                break;
        case 0x9c:
                common_set_byte(emu, xorl(ACCESS_FLAG(F_SF),
                    ACCESS_FLAG(F_OF)));
                break;
        case 0x9d:
                common_set_byte(emu, xorl(ACCESS_FLAG(F_SF),
                    ACCESS_FLAG(F_OF)));
                break;
        case 0x9e:
                common_set_byte(emu,
                    (xorl(ACCESS_FLAG(F_SF), ACCESS_FLAG(F_OF)) ||
                    ACCESS_FLAG(F_ZF)));
                break;
        case 0x9f:
                common_set_byte(emu,
                    !(xorl(ACCESS_FLAG(F_SF), ACCESS_FLAG(F_OF)) ||
                    ACCESS_FLAG(F_ZF)));
                break;

        case 0xa0:
                x86emuOp2_push_FS(emu);
                break;
        case 0xa1:
                x86emuOp2_pop_FS(emu);
                break;
        case 0xa2:
                x86emuOp2_cpuid(emu);
                break;
        case 0xa3:
                x86emuOp2_bt_R(emu);
                break;
        case 0xa4:
                x86emuOp2_shld_IMM(emu);
                break;
        case 0xa5:
                x86emuOp2_shld_CL(emu);
                break;
        case 0xa8:
                x86emuOp2_push_GS(emu);
                break;
        case 0xa9:
                x86emuOp2_pop_GS(emu);
                break;
        case 0xab:
                x86emuOp2_bts_R(emu);
                break;
        case 0xac:
                x86emuOp2_shrd_IMM(emu);
                break;
        case 0xad:
                x86emuOp2_shrd_CL(emu);
                break;
        case 0xaf:
                x86emuOp2_imul_R_RM(emu);
                break;

        /* 0xb0 TODO: cmpxchg */
        /* 0xb1 TODO: cmpxchg */
        case 0xb2:
                x86emuOp2_lss_R_IMM(emu);
                break;
        case 0xb3:
                x86emuOp2_btr_R(emu);
                break;
        case 0xb4:
                x86emuOp2_lfs_R_IMM(emu);
                break;
        case 0xb5:
                x86emuOp2_lgs_R_IMM(emu);
                break;
        case 0xb6:
                x86emuOp2_movzx_byte_R_RM(emu);
                break;
        case 0xb7:
                x86emuOp2_movzx_word_R_RM(emu);
                break;
        case 0xba:
                x86emuOp2_btX_I(emu);
                break;
        case 0xbb:
                x86emuOp2_btc_R(emu);
                break;
        case 0xbc:
                x86emuOp2_bsf(emu);
                break;
        case 0xbd:
                x86emuOp2_bsr(emu);
                break;
        case 0xbe:
                x86emuOp2_movsx_byte_R_RM(emu);
                break;
        case 0xbf:
                x86emuOp2_movsx_word_R_RM(emu);
                break;

        /* 0xc0 TODO: xadd */
        /* 0xc1 TODO: xadd */
        /* 0xc8 TODO: bswap */
        /* 0xc9 TODO: bswap */
        /* 0xca TODO: bswap */
        /* 0xcb TODO: bswap */
        /* 0xcc TODO: bswap */
        /* 0xcd TODO: bswap */
        /* 0xce TODO: bswap */
        /* 0xcf TODO: bswap */

        default:
                x86emu_halt_sys(emu);
                break;
        }
}

/*
 * Carry Chain Calculation
 *
 * This represents a somewhat expensive calculation which is
 * apparently required to emulate the setting of the OF and AF flag.
 * The latter is not so important, but the former is.  The overflow
 * flag is the XOR of the top two bits of the carry chain for an
 * addition (similar for subtraction).  Since we do not want to
 * simulate the addition in a bitwise manner, we try to calculate the
 * carry chain given the two operands and the result.
 *
 * So, given the following table, which represents the addition of two
 * bits, we can derive a formula for the carry chain.
 *
 * a   b   cin   r     cout
 * 0   0   0     0     0
 * 0   0   1     1     0
 * 0   1   0     1     0
 * 0   1   1     0     1
 * 1   0   0     1     0
 * 1   0   1     0     1
 * 1   1   0     0     1
 * 1   1   1     1     1
 *
 * Construction of table for cout:
 *
 * ab
 * r  \  00   01   11  10
 * |------------------
 * 0  |   0    1    1   1
 * 1  |   0    0    1   0
 *
 * By inspection, one gets:  cc = ab +  r'(a + b)
 *
 * That represents alot of operations, but NO CHOICE....
 *
 * Borrow Chain Calculation.
 *
 * The following table represents the subtraction of two bits, from
 * which we can derive a formula for the borrow chain.
 *
 * a   b   bin   r     bout
 * 0   0   0     0     0
 * 0   0   1     1     1
 * 0   1   0     1     1
 * 0   1   1     0     1
 * 1   0   0     1     0
 * 1   0   1     0     0
 * 1   1   0     0     0
 * 1   1   1     1     1
 *
 * Construction of table for cout:
 *
 * ab
 * r  \  00   01   11  10
 * |------------------
 * 0  |   0    1    0   0
 * 1  |   1    1    1   0
 *
 * By inspection, one gets:  bc = a'b +  r(a' + b)
 *
 */

/*
 * Global Variables
 */

static uint32_t x86emu_parity_tab[8] =
{
        0x96696996,
        0x69969669,
        0x69969669,
        0x96696996,
        0x69969669,
        0x96696996,
        0x96696996,
        0x69969669,
};
#define PARITY(x)   (((x86emu_parity_tab[(x) / 32] >> ((x) % 32)) & 1) == 0)
#define XOR2(x)         (((x) ^ ((x)>>1)) & 0x1)


/*
 * REMARKS:
 * Implements the AAA instruction and side effects.
 */
static uint16_t 
aaa_word(struct x86emu *emu, uint16_t d)
{
        uint16_t res;
        if ((d & 0xf) > 0x9 || ACCESS_FLAG(F_AF)) {
                d += 0x6;
                d += 0x100;
                SET_FLAG(F_AF);
                SET_FLAG(F_CF);
        } else {
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_AF);
        }
        res = (uint16_t) (d & 0xFF0F);
        CLEAR_FLAG(F_SF);
        CONDITIONAL_SET_FLAG(res == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
        return res;
}

/*
 * REMARKS:
 * Implements the AAA instruction and side effects.
 */
static uint16_t 
aas_word(struct x86emu *emu, uint16_t d)
{
        uint16_t res;
        if ((d & 0xf) > 0x9 || ACCESS_FLAG(F_AF)) {
                d -= 0x6;
                d -= 0x100;
                SET_FLAG(F_AF);
                SET_FLAG(F_CF);
        } else {
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_AF);
        }
        res = (uint16_t) (d & 0xFF0F);
        CLEAR_FLAG(F_SF);
        CONDITIONAL_SET_FLAG(res == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
        return res;
}

/*
 * REMARKS:
 * Implements the AAD instruction and side effects.
 */
static uint16_t 
aad_word(struct x86emu *emu, uint16_t d)
{
        uint16_t l;
        uint8_t hb, lb;

        hb = (uint8_t) ((d >> 8) & 0xff);
        lb = (uint8_t) ((d & 0xff));
        l = (uint16_t) ((lb + 10 * hb) & 0xFF);

        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_AF);
        CLEAR_FLAG(F_OF);
        CONDITIONAL_SET_FLAG(l & 0x80, F_SF);
        CONDITIONAL_SET_FLAG(l == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(l & 0xff), F_PF);
        return l;
}

/*
 * REMARKS:
 * Implements the AAM instruction and side effects.
 */
static uint16_t 
aam_word(struct x86emu *emu, uint8_t d)
{
        uint16_t h, l;

        h = (uint16_t) (d / 10);
        l = (uint16_t) (d % 10);
        l |= (uint16_t) (h << 8);

        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_AF);
        CLEAR_FLAG(F_OF);
        CONDITIONAL_SET_FLAG(l & 0x80, F_SF);
        CONDITIONAL_SET_FLAG(l == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(l & 0xff), F_PF);
        return l;
}

/*
 * REMARKS:
 * Implements the ADC instruction and side effects.
 */
static uint8_t 
adc_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t cc;

        if (ACCESS_FLAG(F_CF))
                res = 1 + d + s;
        else
                res = d + s;

        CONDITIONAL_SET_FLAG(res & 0x100, F_CF);
        CONDITIONAL_SET_FLAG((res & 0xff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the carry chain  SEE NOTE AT TOP. */
        cc = (s & d) | ((~res) & (s | d));
        CONDITIONAL_SET_FLAG(XOR2(cc >> 6), F_OF);
        CONDITIONAL_SET_FLAG(cc & 0x8, F_AF);
        return (uint8_t) res;
}

/*
 * REMARKS:
 * Implements the ADC instruction and side effects.
 */
static uint16_t 
adc_word(struct x86emu *emu, uint16_t d, uint16_t s)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t cc;

        if (ACCESS_FLAG(F_CF))
                res = 1 + d + s;
        else
                res = d + s;

        CONDITIONAL_SET_FLAG(res & 0x10000, F_CF);
        CONDITIONAL_SET_FLAG((res & 0xffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the carry chain  SEE NOTE AT TOP. */
        cc = (s & d) | ((~res) & (s | d));
        CONDITIONAL_SET_FLAG(XOR2(cc >> 14), F_OF);
        CONDITIONAL_SET_FLAG(cc & 0x8, F_AF);
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the ADC instruction and side effects.
 */
static uint32_t 
adc_long(struct x86emu *emu, uint32_t d, uint32_t s)
{
        uint32_t lo;    /* all operands in native machine order */
        uint32_t hi;
        uint32_t res;
        uint32_t cc;

        if (ACCESS_FLAG(F_CF)) {
                lo = 1 + (d & 0xFFFF) + (s & 0xFFFF);
                res = 1 + d + s;
        } else {
                lo = (d & 0xFFFF) + (s & 0xFFFF);
                res = d + s;
        }
        hi = (lo >> 16) + (d >> 16) + (s >> 16);

        CONDITIONAL_SET_FLAG(hi & 0x10000, F_CF);
        CONDITIONAL_SET_FLAG((res & 0xffffffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the carry chain  SEE NOTE AT TOP. */
        cc = (s & d) | ((~res) & (s | d));
        CONDITIONAL_SET_FLAG(XOR2(cc >> 30), F_OF);
        CONDITIONAL_SET_FLAG(cc & 0x8, F_AF);
        return res;
}

/*
 * REMARKS:
 * Implements the ADD instruction and side effects.
 */
static uint8_t 
add_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t cc;

        res = d + s;
        CONDITIONAL_SET_FLAG(res & 0x100, F_CF);
        CONDITIONAL_SET_FLAG((res & 0xff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the carry chain  SEE NOTE AT TOP. */
        cc = (s & d) | ((~res) & (s | d));
        CONDITIONAL_SET_FLAG(XOR2(cc >> 6), F_OF);
        CONDITIONAL_SET_FLAG(cc & 0x8, F_AF);
        return (uint8_t) res;
}

/*
 * REMARKS:
 * Implements the ADD instruction and side effects.
 */
static uint16_t 
add_word(struct x86emu *emu, uint16_t d, uint16_t s)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t cc;

        res = d + s;
        CONDITIONAL_SET_FLAG(res & 0x10000, F_CF);
        CONDITIONAL_SET_FLAG((res & 0xffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the carry chain  SEE NOTE AT TOP. */
        cc = (s & d) | ((~res) & (s | d));
        CONDITIONAL_SET_FLAG(XOR2(cc >> 14), F_OF);
        CONDITIONAL_SET_FLAG(cc & 0x8, F_AF);
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the ADD instruction and side effects.
 */
static uint32_t 
add_long(struct x86emu *emu, uint32_t d, uint32_t s)
{
        uint32_t lo;    /* all operands in native machine order */
        uint32_t hi;
        uint32_t res;
        uint32_t cc;

        lo = (d & 0xFFFF) + (s & 0xFFFF);
        res = d + s;
        hi = (lo >> 16) + (d >> 16) + (s >> 16);

        CONDITIONAL_SET_FLAG(hi & 0x10000, F_CF);
        CONDITIONAL_SET_FLAG((res & 0xffffffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the carry chain  SEE NOTE AT TOP. */
        cc = (s & d) | ((~res) & (s | d));
        CONDITIONAL_SET_FLAG(XOR2(cc >> 30), F_OF);
        CONDITIONAL_SET_FLAG(cc & 0x8, F_AF);

        return res;
}

/*
 * REMARKS:
 * Implements the AND instruction and side effects.
 */
static uint8_t 
and_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        uint8_t res;    /* all operands in native machine order */

        res = d & s;

        /* set the flags  */
        CLEAR_FLAG(F_OF);
        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_AF);
        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
        CONDITIONAL_SET_FLAG(res == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res), F_PF);
        return res;
}

/*
 * REMARKS:
 * Implements the AND instruction and side effects.
 */
static uint16_t 
and_word(struct x86emu *emu, uint16_t d, uint16_t s)
{
        uint16_t res;   /* all operands in native machine order */

        res = d & s;

        /* set the flags  */
        CLEAR_FLAG(F_OF);
        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_AF);
        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
        CONDITIONAL_SET_FLAG(res == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
        return res;
}

/*
 * REMARKS:
 * Implements the AND instruction and side effects.
 */
static uint32_t 
and_long(struct x86emu *emu, uint32_t d, uint32_t s)
{
        uint32_t res;   /* all operands in native machine order */

        res = d & s;

        /* set the flags  */
        CLEAR_FLAG(F_OF);
        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_AF);
        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
        CONDITIONAL_SET_FLAG(res == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
        return res;
}

/*
 * REMARKS:
 * Implements the CMP instruction and side effects.
 */
static uint8_t 
cmp_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t bc;

        res = d - s;
        CLEAR_FLAG(F_CF);
        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
        CONDITIONAL_SET_FLAG((res & 0xff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the borrow chain.  See note at top */
        bc = (res & (~d | s)) | (~d & s);
        CONDITIONAL_SET_FLAG(bc & 0x80, F_CF);
        CONDITIONAL_SET_FLAG(XOR2(bc >> 6), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return d;
}

static void 
cmp_byte_no_return(struct x86emu *emu, uint8_t d, uint8_t s)
{
        cmp_byte(emu, d, s);
}

/*
 * REMARKS:
 * Implements the CMP instruction and side effects.
 */
static uint16_t 
cmp_word(struct x86emu *emu, uint16_t d, uint16_t s)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t bc;

        res = d - s;
        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
        CONDITIONAL_SET_FLAG((res & 0xffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the borrow chain.  See note at top */
        bc = (res & (~d | s)) | (~d & s);
        CONDITIONAL_SET_FLAG(bc & 0x8000, F_CF);
        CONDITIONAL_SET_FLAG(XOR2(bc >> 14), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return d;
}

static void 
cmp_word_no_return(struct x86emu *emu, uint16_t d, uint16_t s)
{
        cmp_word(emu, d, s);
}

/*
 * REMARKS:
 * Implements the CMP instruction and side effects.
 */
static uint32_t 
cmp_long(struct x86emu *emu, uint32_t d, uint32_t s)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t bc;

        res = d - s;
        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
        CONDITIONAL_SET_FLAG((res & 0xffffffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the borrow chain.  See note at top */
        bc = (res & (~d | s)) | (~d & s);
        CONDITIONAL_SET_FLAG(bc & 0x80000000, F_CF);
        CONDITIONAL_SET_FLAG(XOR2(bc >> 30), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return d;
}

static void 
cmp_long_no_return(struct x86emu *emu, uint32_t d, uint32_t s)
{
        cmp_long(emu, d, s);
}

/*
 * REMARKS:
 * Implements the DAA instruction and side effects.
 */
static uint8_t 
daa_byte(struct x86emu *emu, uint8_t d)
{
        uint32_t res = d;
        if ((d & 0xf) > 9 || ACCESS_FLAG(F_AF)) {
                res += 6;
                SET_FLAG(F_AF);
        }
        if (res > 0x9F || ACCESS_FLAG(F_CF)) {
                res += 0x60;
                SET_FLAG(F_CF);
        }
        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
        CONDITIONAL_SET_FLAG((res & 0xFF) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
        return (uint8_t) res;
}

/*
 * REMARKS:
 * Implements the DAS instruction and side effects.
 */
static uint8_t 
das_byte(struct x86emu *emu, uint8_t d)
{
        if ((d & 0xf) > 9 || ACCESS_FLAG(F_AF)) {
                d -= 6;
                SET_FLAG(F_AF);
        }
        if (d > 0x9F || ACCESS_FLAG(F_CF)) {
                d -= 0x60;
                SET_FLAG(F_CF);
        }
        CONDITIONAL_SET_FLAG(d & 0x80, F_SF);
        CONDITIONAL_SET_FLAG(d == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(d & 0xff), F_PF);
        return d;
}

/*
 * REMARKS:
 * Implements the DEC instruction and side effects.
 */
static uint8_t 
dec_byte(struct x86emu *emu, uint8_t d)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t bc;

        res = d - 1;
        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
        CONDITIONAL_SET_FLAG((res & 0xff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the borrow chain.  See note at top */
        /* based on sub_byte, uses s==1.  */
        bc = (res & (~d | 1)) | (~d & 1);
        /* carry flag unchanged */
        CONDITIONAL_SET_FLAG(XOR2(bc >> 6), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return (uint8_t) res;
}

/*
 * REMARKS:
 * Implements the DEC instruction and side effects.
 */
static uint16_t 
dec_word(struct x86emu *emu, uint16_t d)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t bc;

        res = d - 1;
        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
        CONDITIONAL_SET_FLAG((res & 0xffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the borrow chain.  See note at top */
        /* based on the sub_byte routine, with s==1 */
        bc = (res & (~d | 1)) | (~d & 1);
        /* carry flag unchanged */
        CONDITIONAL_SET_FLAG(XOR2(bc >> 14), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the DEC instruction and side effects.
 */
static uint32_t 
dec_long(struct x86emu *emu, uint32_t d)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t bc;

        res = d - 1;

        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
        CONDITIONAL_SET_FLAG((res & 0xffffffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the borrow chain.  See note at top */
        bc = (res & (~d | 1)) | (~d & 1);
        /* carry flag unchanged */
        CONDITIONAL_SET_FLAG(XOR2(bc >> 30), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return res;
}

/*
 * REMARKS:
 * Implements the INC instruction and side effects.
 */
static uint8_t 
inc_byte(struct x86emu *emu, uint8_t d)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t cc;

        res = d + 1;
        CONDITIONAL_SET_FLAG((res & 0xff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the carry chain  SEE NOTE AT TOP. */
        cc = ((1 & d) | (~res)) & (1 | d);
        CONDITIONAL_SET_FLAG(XOR2(cc >> 6), F_OF);
        CONDITIONAL_SET_FLAG(cc & 0x8, F_AF);
        return (uint8_t) res;
}

/*
 * REMARKS:
 * Implements the INC instruction and side effects.
 */
static uint16_t 
inc_word(struct x86emu *emu, uint16_t d)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t cc;

        res = d + 1;
        CONDITIONAL_SET_FLAG((res & 0xffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the carry chain  SEE NOTE AT TOP. */
        cc = (1 & d) | ((~res) & (1 | d));
        CONDITIONAL_SET_FLAG(XOR2(cc >> 14), F_OF);
        CONDITIONAL_SET_FLAG(cc & 0x8, F_AF);
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the INC instruction and side effects.
 */
static uint32_t 
inc_long(struct x86emu *emu, uint32_t d)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t cc;

        res = d + 1;
        CONDITIONAL_SET_FLAG((res & 0xffffffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the carry chain  SEE NOTE AT TOP. */
        cc = (1 & d) | ((~res) & (1 | d));
        CONDITIONAL_SET_FLAG(XOR2(cc >> 30), F_OF);
        CONDITIONAL_SET_FLAG(cc & 0x8, F_AF);
        return res;
}

/*
 * REMARKS:
 * Implements the OR instruction and side effects.
 */
static uint8_t 
or_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        uint8_t res;    /* all operands in native machine order */

        res = d | s;
        CLEAR_FLAG(F_OF);
        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_AF);
        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
        CONDITIONAL_SET_FLAG(res == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res), F_PF);
        return res;
}

/*
 * REMARKS:
 * Implements the OR instruction and side effects.
 */
static uint16_t 
or_word(struct x86emu *emu, uint16_t d, uint16_t s)
{
        uint16_t res;   /* all operands in native machine order */

        res = d | s;
        /* set the carry flag to be bit 8 */
        CLEAR_FLAG(F_OF);
        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_AF);
        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
        CONDITIONAL_SET_FLAG(res == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
        return res;
}

/*
 * REMARKS:
 * Implements the OR instruction and side effects.
 */
static uint32_t 
or_long(struct x86emu *emu, uint32_t d, uint32_t s)
{
        uint32_t res;   /* all operands in native machine order */

        res = d | s;

        /* set the carry flag to be bit 8 */
        CLEAR_FLAG(F_OF);
        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_AF);
        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
        CONDITIONAL_SET_FLAG(res == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
        return res;
}

/*
 * REMARKS:
 * Implements the OR instruction and side effects.
 */
static uint8_t 
neg_byte(struct x86emu *emu, uint8_t s)
{
        uint8_t res;
        uint8_t bc;

        CONDITIONAL_SET_FLAG(s != 0, F_CF);
        res = (uint8_t) - s;
        CONDITIONAL_SET_FLAG((res & 0xff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
        CONDITIONAL_SET_FLAG(PARITY(res), F_PF);
        /* calculate the borrow chain --- modified such that d=0.
         * substituting d=0 into     bc= res&(~d|s)|(~d&s); (the one used for
         * sub) and simplifying, since ~d=0xff..., ~d|s == 0xffff..., and
         * res&0xfff... == res.  Similarly ~d&s == s.  So the simplified
         * result is: */
        bc = res | s;
        CONDITIONAL_SET_FLAG(XOR2(bc >> 6), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return res;
}

/*
 * REMARKS:
 * Implements the OR instruction and side effects.
 */
static uint16_t 
neg_word(struct x86emu *emu, uint16_t s)
{
        uint16_t res;
        uint16_t bc;

        CONDITIONAL_SET_FLAG(s != 0, F_CF);
        res = (uint16_t) - s;
        CONDITIONAL_SET_FLAG((res & 0xffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the borrow chain --- modified such that d=0.
         * substituting d=0 into     bc= res&(~d|s)|(~d&s); (the one used for
         * sub) and simplifying, since ~d=0xff..., ~d|s == 0xffff..., and
         * res&0xfff... == res.  Similarly ~d&s == s.  So the simplified
         * result is: */
        bc = res | s;
        CONDITIONAL_SET_FLAG(XOR2(bc >> 14), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return res;
}

/*
 * REMARKS:
 * Implements the OR instruction and side effects.
 */
static uint32_t 
neg_long(struct x86emu *emu, uint32_t s)
{
        uint32_t res;
        uint32_t bc;

        CONDITIONAL_SET_FLAG(s != 0, F_CF);
        res = (uint32_t) - s;
        CONDITIONAL_SET_FLAG((res & 0xffffffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the borrow chain --- modified such that d=0.
         * substituting d=0 into     bc= res&(~d|s)|(~d&s); (the one used for
         * sub) and simplifying, since ~d=0xff..., ~d|s == 0xffff..., and
         * res&0xfff... == res.  Similarly ~d&s == s.  So the simplified
         * result is: */
        bc = res | s;
        CONDITIONAL_SET_FLAG(XOR2(bc >> 30), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return res;
}

/*
 * REMARKS:
 * Implements the RCL instruction and side effects.
 */
static uint8_t 
rcl_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        unsigned int res, cnt, mask, cf;

        /* s is the rotate distance.  It varies from 0 - 8. */
        /* have
         * 
         * CF  B_7 B_6 B_5 B_4 B_3 B_2 B_1 B_0
         * 
         * want to rotate through the carry by "s" bits.  We could loop, but
         * that's inefficient.  So the width is 9, and we split into three
         * parts:
         * 
         * The new carry flag   (was B_n) the stuff in B_n-1 .. B_0 the stuff
         * in B_7 .. B_n+1
         * 
         * The new rotate is done mod 9, and given this, for a rotation of n
         * bits (mod 9) the new carry flag is then located n bits from the MSB.
         * The low part is then shifted up cnt bits, and the high part is or'd
         * in.  Using CAPS for new values, and lowercase for the original
         * values, this can be expressed as:
         * 
         * IF n > 0 1) CF <-  b_(8-n) 2) B_(7) .. B_(n)  <-  b_(8-(n+1)) .. b_0
         * 3) B_(n-1) <- cf 4) B_(n-2) .. B_0 <-  b_7 .. b_(8-(n-1))
         */
        res = d;
        if ((cnt = s % 9) != 0) {
                /* extract the new CARRY FLAG. */
                /* CF <-  b_(8-n)             */
                cf = (d >> (8 - cnt)) & 0x1;

                /* 
                 * Get the low stuff which rotated into the range B_7 .. B_cnt
                 * B_(7) .. B_(n)  <-  b_(8-(n+1)) .. b_0
                 * note that the right hand side done by the mask.
                 */
                res = (d << cnt) & 0xff;

                /* 
                 * now the high stuff which rotated around into the positions
                 * B_cnt-2 .. B_0
                 * B_(n-2) .. B_0 <-  b_7 .. b_(8-(n-1))
                 * shift it downward, 7-(n-2) = 9-n positions. and mask off
                 * the result before or'ing in.
                 */
                mask = (1 << (cnt - 1)) - 1;
                res |= (d >> (9 - cnt)) & mask;

                /* if the carry flag was set, or it in.  */
                if (ACCESS_FLAG(F_CF)) {        /* carry flag is set */
                        /* B_(n-1) <- cf */
                        res |= 1 << (cnt - 1);
                }
                /* set the new carry flag, based on the variable "cf" */
                CONDITIONAL_SET_FLAG(cf, F_CF);
                /* OVERFLOW is set *IFF* cnt==1, then it is the xor of CF and
                 * the most significant bit.  Blecck. */
                /* parenthesized this expression since it appears to be
                 * causing OF to be misset */
                CONDITIONAL_SET_FLAG(cnt == 1 && XOR2(cf + ((res >> 6) & 0x2)),
                    F_OF);

        }
        return (uint8_t) res;
}

/*
 * REMARKS:
 * Implements the RCL instruction and side effects.
 */
static uint16_t 
rcl_word(struct x86emu *emu, uint16_t d, uint8_t s)
{
        unsigned int res, cnt, mask, cf;

        res = d;
        if ((cnt = s % 17) != 0) {
                cf = (d >> (16 - cnt)) & 0x1;
                res = (d << cnt) & 0xffff;
                mask = (1 << (cnt - 1)) - 1;
                res |= (d >> (17 - cnt)) & mask;
                if (ACCESS_FLAG(F_CF)) {
                        res |= 1 << (cnt - 1);
                }
                CONDITIONAL_SET_FLAG(cf, F_CF);
                CONDITIONAL_SET_FLAG(cnt == 1 && XOR2(cf + ((res >> 14) & 0x2)),
                    F_OF);
        }
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the RCL instruction and side effects.
 */
static uint32_t 
rcl_long(struct x86emu *emu, uint32_t d, uint8_t s)
{
        uint32_t res, cnt, mask, cf;

        res = d;
        if ((cnt = s % 33) != 0) {
                cf = (d >> (32 - cnt)) & 0x1;
                res = (d << cnt) & 0xffffffff;
                mask = (1 << (cnt - 1)) - 1;
                res |= (d >> (33 - cnt)) & mask;
                if (ACCESS_FLAG(F_CF)) {        /* carry flag is set */
                        res |= 1 << (cnt - 1);
                }
                CONDITIONAL_SET_FLAG(cf, F_CF);
                CONDITIONAL_SET_FLAG(cnt == 1 && XOR2(cf + ((res >> 30) & 0x2)),
                    F_OF);
        }
        return res;
}

/*
 * REMARKS:
 * Implements the RCR instruction and side effects.
 */
static uint8_t 
rcr_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        uint32_t res, cnt;
        uint32_t mask, cf, ocf = 0;

        /* rotate right through carry */
        /* s is the rotate distance.  It varies from 0 - 8. d is the byte
         * object rotated.
         * 
         * have
         * 
         * CF  B_7 B_6 B_5 B_4 B_3 B_2 B_1 B_0
         * 
         * The new rotate is done mod 9, and given this, for a rotation of n
         * bits (mod 9) the new carry flag is then located n bits from the LSB.
         * The low part is then shifted up cnt bits, and the high part is or'd
         * in.  Using CAPS for new values, and lowercase for the original
         * values, this can be expressed as:
         * 
         * IF n > 0 
         *      1) CF <-  b_(n-1) 
         *      2) B_(8-(n+1)) .. B_(0)  <-  b_(7) .. b_(n)
         *      3) B_(8-n) <- cf 4) B_(7) .. B_(8-(n-1)) <-  b_(n-2) .. b_(0)
         */
        res = d;
        if ((cnt = s % 9) != 0) {
                /* extract the new CARRY FLAG. */
                /* CF <-  b_(n-1)              */
                if (cnt == 1) {
                        cf = d & 0x1;
                        /* note hackery here.  Access_flag(..) evaluates to
                         * either 0 if flag not set non-zero if flag is set.
                         * doing access_flag(..) != 0 casts that into either
                         * 0..1 in any representation of the flags register
                         * (i.e. packed bit array or unpacked.) */
                        ocf = ACCESS_FLAG(F_CF) != 0;
                } else
                        cf = (d >> (cnt - 1)) & 0x1;

                /* B_(8-(n+1)) .. B_(0)  <-  b_(7) .. b_n  */
                /* note that the right hand side done by the mask This is
                 * effectively done by shifting the object to the right.  The
                 * result must be masked, in case the object came in and was
                 * treated as a negative number.  Needed??? */

                mask = (1 << (8 - cnt)) - 1;
                res = (d >> cnt) & mask;

                /* now the high stuff which rotated around into the positions
                 * B_cnt-2 .. B_0 */
                /* B_(7) .. B_(8-(n-1)) <-  b_(n-2) .. b_(0) */
                /* shift it downward, 7-(n-2) = 9-n positions. and mask off
                 * the result before or'ing in. */
                res |= (d << (9 - cnt));

                /* if the carry flag was set, or it in.  */
                if (ACCESS_FLAG(F_CF)) {        /* carry flag is set */
                        /* B_(8-n) <- cf */
                        res |= 1 << (8 - cnt);
                }
                /* set the new carry flag, based on the variable "cf" */
                CONDITIONAL_SET_FLAG(cf, F_CF);
                /* OVERFLOW is set *IFF* cnt==1, then it is the xor of CF and
                 * the most significant bit.  Blecck. */
                /* parenthesized... */
                if (cnt == 1) {
                        CONDITIONAL_SET_FLAG(XOR2(ocf + ((d >> 6) & 0x2)),
                            F_OF);
                }
        }
        return (uint8_t) res;
}

/*
 * REMARKS:
 * Implements the RCR instruction and side effects.
 */
static uint16_t 
rcr_word(struct x86emu *emu, uint16_t d, uint8_t s)
{
        uint32_t res, cnt;
        uint32_t mask, cf, ocf = 0;

        /* rotate right through carry */
        res = d;
        if ((cnt = s % 17) != 0) {
                if (cnt == 1) {
                        cf = d & 0x1;
                        ocf = ACCESS_FLAG(F_CF) != 0;
                } else
                        cf = (d >> (cnt - 1)) & 0x1;
                mask = (1 << (16 - cnt)) - 1;
                res = (d >> cnt) & mask;
                res |= (d << (17 - cnt));
                if (ACCESS_FLAG(F_CF)) {
                        res |= 1 << (16 - cnt);
                }
                CONDITIONAL_SET_FLAG(cf, F_CF);
                if (cnt == 1) {
                        CONDITIONAL_SET_FLAG(XOR2(ocf + ((d >> 14) & 0x2)),
                            F_OF);
                }
        }
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the RCR instruction and side effects.
 */
static uint32_t 
rcr_long(struct x86emu *emu, uint32_t d, uint8_t s)
{
        uint32_t res, cnt;
        uint32_t mask, cf, ocf = 0;

        /* rotate right through carry */
        res = d;
        if ((cnt = s % 33) != 0) {
                if (cnt == 1) {
                        cf = d & 0x1;
                        ocf = ACCESS_FLAG(F_CF) != 0;
                } else
                        cf = (d >> (cnt - 1)) & 0x1;
                mask = (1 << (32 - cnt)) - 1;
                res = (d >> cnt) & mask;
                if (cnt != 1)
                        res |= (d << (33 - cnt));
                if (ACCESS_FLAG(F_CF)) {        /* carry flag is set */
                        res |= 1 << (32 - cnt);
                }
                CONDITIONAL_SET_FLAG(cf, F_CF);
                if (cnt == 1) {
                        CONDITIONAL_SET_FLAG(XOR2(ocf + ((d >> 30) & 0x2)),
                            F_OF);
                }
        }
        return res;
}

/*
 * REMARKS:
 * Implements the ROL instruction and side effects.
 */
static uint8_t 
rol_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        unsigned int res, cnt, mask;

        /* rotate left */
        /* s is the rotate distance.  It varies from 0 - 8. d is the byte
         * object rotated.
         * 
         * have
         * 
         * CF  B_7 ... B_0
         * 
         * The new rotate is done mod 8. Much simpler than the "rcl" or "rcr"
         * operations.
         * 
         * IF n > 0 1) B_(7) .. B_(n)  <-  b_(8-(n+1)) .. b_(0) 2) B_(n-1) ..
         * B_(0) <-  b_(7) .. b_(8-n) */
        res = d;
        if ((cnt = s % 8) != 0) {
                /* B_(7) .. B_(n)  <-  b_(8-(n+1)) .. b_(0) */
                res = (d << cnt);

                /* B_(n-1) .. B_(0) <-  b_(7) .. b_(8-n) */
                mask = (1 << cnt) - 1;
                res |= (d >> (8 - cnt)) & mask;

                /* set the new carry flag, Note that it is the low order bit
                 * of the result!!!                               */
                CONDITIONAL_SET_FLAG(res & 0x1, F_CF);
                /* OVERFLOW is set *IFF* s==1, then it is the xor of CF and
                 * the most significant bit.  Blecck. */
                CONDITIONAL_SET_FLAG(s == 1 &&
                    XOR2((res & 0x1) + ((res >> 6) & 0x2)),
                    F_OF);
        } else if (s != 0) {
                /* set the new carry flag, Note that it is the low order bit
                 * of the result!!!                               */
                CONDITIONAL_SET_FLAG(res & 0x1, F_CF);
        }
        return (uint8_t) res;
}

/*
 * REMARKS:
 * Implements the ROL instruction and side effects.
 */
static uint16_t 
rol_word(struct x86emu *emu, uint16_t d, uint8_t s)
{
        unsigned int res, cnt, mask;

        res = d;
        if ((cnt = s % 16) != 0) {
                res = (d << cnt);
                mask = (1 << cnt) - 1;
                res |= (d >> (16 - cnt)) & mask;
                CONDITIONAL_SET_FLAG(res & 0x1, F_CF);
                CONDITIONAL_SET_FLAG(s == 1 &&
                    XOR2((res & 0x1) + ((res >> 14) & 0x2)),
                    F_OF);
        } else if (s != 0) {
                /* set the new carry flag, Note that it is the low order bit
                 * of the result!!!                               */
                CONDITIONAL_SET_FLAG(res & 0x1, F_CF);
        }
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the ROL instruction and side effects.
 */
static uint32_t 
rol_long(struct x86emu *emu, uint32_t d, uint8_t s)
{
        uint32_t res, cnt, mask;

        res = d;
        if ((cnt = s % 32) != 0) {
                res = (d << cnt);
                mask = (1 << cnt) - 1;
                res |= (d >> (32 - cnt)) & mask;
                CONDITIONAL_SET_FLAG(res & 0x1, F_CF);
                CONDITIONAL_SET_FLAG(s == 1 &&
                    XOR2((res & 0x1) + ((res >> 30) & 0x2)),
                    F_OF);
        } else if (s != 0) {
                /* set the new carry flag, Note that it is the low order bit
                 * of the result!!!                               */
                CONDITIONAL_SET_FLAG(res & 0x1, F_CF);
        }
        return res;
}

/*
 * REMARKS:
 * Implements the ROR instruction and side effects.
 */
static uint8_t 
ror_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        unsigned int res, cnt, mask;

        /* rotate right */
        /* s is the rotate distance.  It varies from 0 - 8. d is the byte
         * object rotated.
         * 
         * have
         * 
         * B_7 ... B_0
         * 
         * The rotate is done mod 8.
         * 
         * IF n > 0 1) B_(8-(n+1)) .. B_(0)  <-  b_(7) .. b_(n) 2) B_(7) ..
         * B_(8-n) <-  b_(n-1) .. b_(0) */
        res = d;
        if ((cnt = s % 8) != 0) {       /* not a typo, do nada if cnt==0 */
                /* B_(7) .. B_(8-n) <-  b_(n-1) .. b_(0) */
                res = (d << (8 - cnt));

                /* B_(8-(n+1)) .. B_(0)  <-  b_(7) .. b_(n) */
                mask = (1 << (8 - cnt)) - 1;
                res |= (d >> (cnt)) & mask;

                /* set the new carry flag, Note that it is the low order bit
                 * of the result!!!                               */
                CONDITIONAL_SET_FLAG(res & 0x80, F_CF);
                /* OVERFLOW is set *IFF* s==1, then it is the xor of the two
                 * most significant bits.  Blecck. */
                CONDITIONAL_SET_FLAG(s == 1 && XOR2(res >> 6), F_OF);
        } else if (s != 0) {
                /* set the new carry flag, Note that it is the low order bit
                 * of the result!!!                               */
                CONDITIONAL_SET_FLAG(res & 0x80, F_CF);
        }
        return (uint8_t) res;
}

/*
 * REMARKS:
 * Implements the ROR instruction and side effects.
 */
static uint16_t 
ror_word(struct x86emu *emu, uint16_t d, uint8_t s)
{
        unsigned int res, cnt, mask;

        res = d;
        if ((cnt = s % 16) != 0) {
                res = (d << (16 - cnt));
                mask = (1 << (16 - cnt)) - 1;
                res |= (d >> (cnt)) & mask;
                CONDITIONAL_SET_FLAG(res & 0x8000, F_CF);
                CONDITIONAL_SET_FLAG(s == 1 && XOR2(res >> 14), F_OF);
        } else if (s != 0) {
                /* set the new carry flag, Note that it is the low order bit
                 * of the result!!!                               */
                CONDITIONAL_SET_FLAG(res & 0x8000, F_CF);
        }
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the ROR instruction and side effects.
 */
static uint32_t 
ror_long(struct x86emu *emu, uint32_t d, uint8_t s)
{
        uint32_t res, cnt, mask;

        res = d;
        if ((cnt = s % 32) != 0) {
                res = (d << (32 - cnt));
                mask = (1 << (32 - cnt)) - 1;
                res |= (d >> (cnt)) & mask;
                CONDITIONAL_SET_FLAG(res & 0x80000000, F_CF);
                CONDITIONAL_SET_FLAG(s == 1 && XOR2(res >> 30), F_OF);
        } else if (s != 0) {
                /* set the new carry flag, Note that it is the low order bit
                 * of the result!!!                               */
                CONDITIONAL_SET_FLAG(res & 0x80000000, F_CF);
        }
        return res;
}

/*
 * REMARKS:
 * Implements the SHL instruction and side effects.
 */
static uint8_t 
shl_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        unsigned int cnt, res, cf;

        if (s < 8) {
                cnt = s % 8;

                /* last bit shifted out goes into carry flag */
                if (cnt > 0) {
                        res = d << cnt;
                        cf = d & (1 << (8 - cnt));
                        CONDITIONAL_SET_FLAG(cf, F_CF);
                        CONDITIONAL_SET_FLAG((res & 0xff) == 0, F_ZF);
                        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
                        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
                } else {
                        res = (uint8_t) d;
                }

                if (cnt == 1) {
                        /* Needs simplification. */
                        CONDITIONAL_SET_FLAG(
                            (((res & 0x80) == 0x80) ^
                                (ACCESS_FLAG(F_CF) != 0)),
                        /* was (emu->x86.R_FLG&F_CF)==F_CF)), */
                            F_OF);
                } else {
                        CLEAR_FLAG(F_OF);
                }
        } else {
                res = 0;
                CONDITIONAL_SET_FLAG((d << (s - 1)) & 0x80, F_CF);
                CLEAR_FLAG(F_OF);
                CLEAR_FLAG(F_SF);
                SET_FLAG(F_PF);
                SET_FLAG(F_ZF);
        }
        return (uint8_t) res;
}

/*
 * REMARKS:
 * Implements the SHL instruction and side effects.
 */
static uint16_t 
shl_word(struct x86emu *emu, uint16_t d, uint8_t s)
{
        unsigned int cnt, res, cf;

        if (s < 16) {
                cnt = s % 16;
                if (cnt > 0) {
                        res = d << cnt;
                        cf = d & (1 << (16 - cnt));
                        CONDITIONAL_SET_FLAG(cf, F_CF);
                        CONDITIONAL_SET_FLAG((res & 0xffff) == 0, F_ZF);
                        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
                        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
                } else {
                        res = (uint16_t) d;
                }

                if (cnt == 1) {
                        CONDITIONAL_SET_FLAG(
                            (((res & 0x8000) == 0x8000) ^
                                (ACCESS_FLAG(F_CF) != 0)),
                            F_OF);
                } else {
                        CLEAR_FLAG(F_OF);
                }
        } else {
                res = 0;
                CONDITIONAL_SET_FLAG((d << (s - 1)) & 0x8000, F_CF);
                CLEAR_FLAG(F_OF);
                CLEAR_FLAG(F_SF);
                SET_FLAG(F_PF);
                SET_FLAG(F_ZF);
        }
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the SHL instruction and side effects.
 */
static uint32_t 
shl_long(struct x86emu *emu, uint32_t d, uint8_t s)
{
        unsigned int cnt, res, cf;

        if (s < 32) {
                cnt = s % 32;
                if (cnt > 0) {
                        res = d << cnt;
                        cf = d & (1 << (32 - cnt));
                        CONDITIONAL_SET_FLAG(cf, F_CF);
                        CONDITIONAL_SET_FLAG((res & 0xffffffff) == 0, F_ZF);
                        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
                        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
                } else {
                        res = d;
                }
                if (cnt == 1) {
                        CONDITIONAL_SET_FLAG((((res & 0x80000000) == 0x80000000)
                            ^ (ACCESS_FLAG(F_CF) != 0)), F_OF);
                } else {
                        CLEAR_FLAG(F_OF);
                }
        } else {
                res = 0;
                CONDITIONAL_SET_FLAG((d << (s - 1)) & 0x80000000, F_CF);
                CLEAR_FLAG(F_OF);
                CLEAR_FLAG(F_SF);
                SET_FLAG(F_PF);
                SET_FLAG(F_ZF);
        }
        return res;
}

/*
 * REMARKS:
 * Implements the SHR instruction and side effects.
 */
static uint8_t 
shr_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        unsigned int cnt, res, cf;

        if (s < 8) {
                cnt = s % 8;
                if (cnt > 0) {
                        cf = d & (1 << (cnt - 1));
                        res = d >> cnt;
                        CONDITIONAL_SET_FLAG(cf, F_CF);
                        CONDITIONAL_SET_FLAG((res & 0xff) == 0, F_ZF);
                        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
                        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
                } else {
                        res = (uint8_t) d;
                }

                if (cnt == 1) {
                        CONDITIONAL_SET_FLAG(XOR2(res >> 6), F_OF);
                } else {
                        CLEAR_FLAG(F_OF);
                }
        } else {
                res = 0;
                CONDITIONAL_SET_FLAG((d >> (s - 1)) & 0x1, F_CF);
                CLEAR_FLAG(F_OF);
                CLEAR_FLAG(F_SF);
                SET_FLAG(F_PF);
                SET_FLAG(F_ZF);
        }
        return (uint8_t) res;
}

/*
 * REMARKS:
 * Implements the SHR instruction and side effects.
 */
static uint16_t 
shr_word(struct x86emu *emu, uint16_t d, uint8_t s)
{
        unsigned int cnt, res, cf;

        if (s < 16) {
                cnt = s % 16;
                if (cnt > 0) {
                        cf = d & (1 << (cnt - 1));
                        res = d >> cnt;
                        CONDITIONAL_SET_FLAG(cf, F_CF);
                        CONDITIONAL_SET_FLAG((res & 0xffff) == 0, F_ZF);
                        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
                        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
                } else {
                        res = d;
                }

                if (cnt == 1) {
                        CONDITIONAL_SET_FLAG(XOR2(res >> 14), F_OF);
                } else {
                        CLEAR_FLAG(F_OF);
                }
        } else {
                res = 0;
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_OF);
                SET_FLAG(F_ZF);
                CLEAR_FLAG(F_SF);
                CLEAR_FLAG(F_PF);
        }
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the SHR instruction and side effects.
 */
static uint32_t 
shr_long(struct x86emu *emu, uint32_t d, uint8_t s)
{
        unsigned int cnt, res, cf;

        if (s < 32) {
                cnt = s % 32;
                if (cnt > 0) {
                        cf = d & (1 << (cnt - 1));
                        res = d >> cnt;
                        CONDITIONAL_SET_FLAG(cf, F_CF);
                        CONDITIONAL_SET_FLAG((res & 0xffffffff) == 0, F_ZF);
                        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
                        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
                } else {
                        res = d;
                }
                if (cnt == 1) {
                        CONDITIONAL_SET_FLAG(XOR2(res >> 30), F_OF);
                } else {
                        CLEAR_FLAG(F_OF);
                }
        } else {
                res = 0;
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_OF);
                SET_FLAG(F_ZF);
                CLEAR_FLAG(F_SF);
                CLEAR_FLAG(F_PF);
        }
        return res;
}

/*
 * REMARKS:
 * Implements the SAR instruction and side effects.
 */
static uint8_t 
sar_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        unsigned int cnt, res, cf, mask, sf;

        res = d;
        sf = d & 0x80;
        cnt = s % 8;
        if (cnt > 0 && cnt < 8) {
                mask = (1 << (8 - cnt)) - 1;
                cf = d & (1 << (cnt - 1));
                res = (d >> cnt) & mask;
                CONDITIONAL_SET_FLAG(cf, F_CF);
                if (sf) {
                        res |= ~mask;
                }
                CONDITIONAL_SET_FLAG((res & 0xff) == 0, F_ZF);
                CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
                CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
        } else if (cnt >= 8) {
                if (sf) {
                        res = 0xff;
                        SET_FLAG(F_CF);
                        CLEAR_FLAG(F_ZF);
                        SET_FLAG(F_SF);
                        SET_FLAG(F_PF);
                } else {
                        res = 0;
                        CLEAR_FLAG(F_CF);
                        SET_FLAG(F_ZF);
                        CLEAR_FLAG(F_SF);
                        CLEAR_FLAG(F_PF);
                }
        }
        return (uint8_t) res;
}

/*
 * REMARKS:
 * Implements the SAR instruction and side effects.
 */
static uint16_t 
sar_word(struct x86emu *emu, uint16_t d, uint8_t s)
{
        unsigned int cnt, res, cf, mask, sf;

        sf = d & 0x8000;
        cnt = s % 16;
        res = d;
        if (cnt > 0 && cnt < 16) {
                mask = (1 << (16 - cnt)) - 1;
                cf = d & (1 << (cnt - 1));
                res = (d >> cnt) & mask;
                CONDITIONAL_SET_FLAG(cf, F_CF);
                if (sf) {
                        res |= ~mask;
                }
                CONDITIONAL_SET_FLAG((res & 0xffff) == 0, F_ZF);
                CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
                CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
        } else if (cnt >= 16) {
                if (sf) {
                        res = 0xffff;
                        SET_FLAG(F_CF);
                        CLEAR_FLAG(F_ZF);
                        SET_FLAG(F_SF);
                        SET_FLAG(F_PF);
                } else {
                        res = 0;
                        CLEAR_FLAG(F_CF);
                        SET_FLAG(F_ZF);
                        CLEAR_FLAG(F_SF);
                        CLEAR_FLAG(F_PF);
                }
        }
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the SAR instruction and side effects.
 */
static uint32_t 
sar_long(struct x86emu *emu, uint32_t d, uint8_t s)
{
        uint32_t cnt, res, cf, mask, sf;

        sf = d & 0x80000000;
        cnt = s % 32;
        res = d;
        if (cnt > 0 && cnt < 32) {
                mask = (1 << (32 - cnt)) - 1;
                cf = d & (1 << (cnt - 1));
                res = (d >> cnt) & mask;
                CONDITIONAL_SET_FLAG(cf, F_CF);
                if (sf) {
                        res |= ~mask;
                }
                CONDITIONAL_SET_FLAG((res & 0xffffffff) == 0, F_ZF);
                CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
                CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
        } else if (cnt >= 32) {
                if (sf) {
                        res = 0xffffffff;
                        SET_FLAG(F_CF);
                        CLEAR_FLAG(F_ZF);
                        SET_FLAG(F_SF);
                        SET_FLAG(F_PF);
                } else {
                        res = 0;
                        CLEAR_FLAG(F_CF);
                        SET_FLAG(F_ZF);
                        CLEAR_FLAG(F_SF);
                        CLEAR_FLAG(F_PF);
                }
        }
        return res;
}

/*
 * REMARKS:
 * Implements the SHLD instruction and side effects.
 */
static uint16_t 
shld_word(struct x86emu *emu, uint16_t d, uint16_t fill, uint8_t s)
{
        unsigned int cnt, res, cf;

        if (s < 16) {
                cnt = s % 16;
                if (cnt > 0) {
                        res = (d << cnt) | (fill >> (16 - cnt));
                        cf = d & (1 << (16 - cnt));
                        CONDITIONAL_SET_FLAG(cf, F_CF);
                        CONDITIONAL_SET_FLAG((res & 0xffff) == 0, F_ZF);
                        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
                        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
                } else {
                        res = d;
                }
                if (cnt == 1) {
                        CONDITIONAL_SET_FLAG((((res & 0x8000) == 0x8000) ^
                                (ACCESS_FLAG(F_CF) != 0)), F_OF);
                } else {
                        CLEAR_FLAG(F_OF);
                }
        } else {
                res = 0;
                CONDITIONAL_SET_FLAG((d << (s - 1)) & 0x8000, F_CF);
                CLEAR_FLAG(F_OF);
                CLEAR_FLAG(F_SF);
                SET_FLAG(F_PF);
                SET_FLAG(F_ZF);
        }
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the SHLD instruction and side effects.
 */
static uint32_t 
shld_long(struct x86emu *emu, uint32_t d, uint32_t fill, uint8_t s)
{
        unsigned int cnt, res, cf;

        if (s < 32) {
                cnt = s % 32;
                if (cnt > 0) {
                        res = (d << cnt) | (fill >> (32 - cnt));
                        cf = d & (1 << (32 - cnt));
                        CONDITIONAL_SET_FLAG(cf, F_CF);
                        CONDITIONAL_SET_FLAG((res & 0xffffffff) == 0, F_ZF);
                        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
                        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
                } else {
                        res = d;
                }
                if (cnt == 1) {
                        CONDITIONAL_SET_FLAG((((res & 0x80000000) == 0x80000000)
                            ^ (ACCESS_FLAG(F_CF) != 0)), F_OF);
                } else {
                        CLEAR_FLAG(F_OF);
                }
        } else {
                res = 0;
                CONDITIONAL_SET_FLAG((d << (s - 1)) & 0x80000000, F_CF);
                CLEAR_FLAG(F_OF);
                CLEAR_FLAG(F_SF);
                SET_FLAG(F_PF);
                SET_FLAG(F_ZF);
        }
        return res;
}

/*
 * REMARKS:
 * Implements the SHRD instruction and side effects.
 */
static uint16_t 
shrd_word(struct x86emu *emu, uint16_t d, uint16_t fill, uint8_t s)
{
        unsigned int cnt, res, cf;

        if (s < 16) {
                cnt = s % 16;
                if (cnt > 0) {
                        cf = d & (1 << (cnt - 1));
                        res = (d >> cnt) | (fill << (16 - cnt));
                        CONDITIONAL_SET_FLAG(cf, F_CF);
                        CONDITIONAL_SET_FLAG((res & 0xffff) == 0, F_ZF);
                        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
                        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
                } else {
                        res = d;
                }

                if (cnt == 1) {
                        CONDITIONAL_SET_FLAG(XOR2(res >> 14), F_OF);
                } else {
                        CLEAR_FLAG(F_OF);
                }
        } else {
                res = 0;
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_OF);
                SET_FLAG(F_ZF);
                CLEAR_FLAG(F_SF);
                CLEAR_FLAG(F_PF);
        }
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the SHRD instruction and side effects.
 */
static uint32_t 
shrd_long(struct x86emu *emu, uint32_t d, uint32_t fill, uint8_t s)
{
        unsigned int cnt, res, cf;

        if (s < 32) {
                cnt = s % 32;
                if (cnt > 0) {
                        cf = d & (1 << (cnt - 1));
                        res = (d >> cnt) | (fill << (32 - cnt));
                        CONDITIONAL_SET_FLAG(cf, F_CF);
                        CONDITIONAL_SET_FLAG((res & 0xffffffff) == 0, F_ZF);
                        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
                        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
                } else {
                        res = d;
                }
                if (cnt == 1) {
                        CONDITIONAL_SET_FLAG(XOR2(res >> 30), F_OF);
                } else {
                        CLEAR_FLAG(F_OF);
                }
        } else {
                res = 0;
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_OF);
                SET_FLAG(F_ZF);
                CLEAR_FLAG(F_SF);
                CLEAR_FLAG(F_PF);
        }
        return res;
}

/*
 * REMARKS:
 * Implements the SBB instruction and side effects.
 */
static uint8_t 
sbb_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t bc;

        if (ACCESS_FLAG(F_CF))
                res = d - s - 1;
        else
                res = d - s;
        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
        CONDITIONAL_SET_FLAG((res & 0xff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the borrow chain.  See note at top */
        bc = (res & (~d | s)) | (~d & s);
        CONDITIONAL_SET_FLAG(bc & 0x80, F_CF);
        CONDITIONAL_SET_FLAG(XOR2(bc >> 6), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return (uint8_t) res;
}

/*
 * REMARKS:
 * Implements the SBB instruction and side effects.
 */
static uint16_t 
sbb_word(struct x86emu *emu, uint16_t d, uint16_t s)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t bc;

        if (ACCESS_FLAG(F_CF))
                res = d - s - 1;
        else
                res = d - s;
        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
        CONDITIONAL_SET_FLAG((res & 0xffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the borrow chain.  See note at top */
        bc = (res & (~d | s)) | (~d & s);
        CONDITIONAL_SET_FLAG(bc & 0x8000, F_CF);
        CONDITIONAL_SET_FLAG(XOR2(bc >> 14), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the SBB instruction and side effects.
 */
static uint32_t 
sbb_long(struct x86emu *emu, uint32_t d, uint32_t s)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t bc;

        if (ACCESS_FLAG(F_CF))
                res = d - s - 1;
        else
                res = d - s;
        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
        CONDITIONAL_SET_FLAG((res & 0xffffffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the borrow chain.  See note at top */
        bc = (res & (~d | s)) | (~d & s);
        CONDITIONAL_SET_FLAG(bc & 0x80000000, F_CF);
        CONDITIONAL_SET_FLAG(XOR2(bc >> 30), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return res;
}

/*
 * REMARKS:
 * Implements the SUB instruction and side effects.
 */
static uint8_t 
sub_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t bc;

        res = d - s;
        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
        CONDITIONAL_SET_FLAG((res & 0xff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the borrow chain.  See note at top */
        bc = (res & (~d | s)) | (~d & s);
        CONDITIONAL_SET_FLAG(bc & 0x80, F_CF);
        CONDITIONAL_SET_FLAG(XOR2(bc >> 6), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return (uint8_t) res;
}

/*
 * REMARKS:
 * Implements the SUB instruction and side effects.
 */
static uint16_t 
sub_word(struct x86emu *emu, uint16_t d, uint16_t s)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t bc;

        res = d - s;
        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
        CONDITIONAL_SET_FLAG((res & 0xffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the borrow chain.  See note at top */
        bc = (res & (~d | s)) | (~d & s);
        CONDITIONAL_SET_FLAG(bc & 0x8000, F_CF);
        CONDITIONAL_SET_FLAG(XOR2(bc >> 14), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return (uint16_t) res;
}

/*
 * REMARKS:
 * Implements the SUB instruction and side effects.
 */
static uint32_t 
sub_long(struct x86emu *emu, uint32_t d, uint32_t s)
{
        uint32_t res;   /* all operands in native machine order */
        uint32_t bc;

        res = d - s;
        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
        CONDITIONAL_SET_FLAG((res & 0xffffffff) == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);

        /* calculate the borrow chain.  See note at top */
        bc = (res & (~d | s)) | (~d & s);
        CONDITIONAL_SET_FLAG(bc & 0x80000000, F_CF);
        CONDITIONAL_SET_FLAG(XOR2(bc >> 30), F_OF);
        CONDITIONAL_SET_FLAG(bc & 0x8, F_AF);
        return res;
}

/*
 * REMARKS:
 * Implements the TEST instruction and side effects.
 */
static void 
test_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        uint32_t res;   /* all operands in native machine order */

        res = d & s;

        CLEAR_FLAG(F_OF);
        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
        CONDITIONAL_SET_FLAG(res == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
        /* AF == dont care */
        CLEAR_FLAG(F_CF);
}

/*
 * REMARKS:
 * Implements the TEST instruction and side effects.
 */
static void 
test_word(struct x86emu *emu, uint16_t d, uint16_t s)
{
        uint32_t res;   /* all operands in native machine order */

        res = d & s;

        CLEAR_FLAG(F_OF);
        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
        CONDITIONAL_SET_FLAG(res == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
        /* AF == dont care */
        CLEAR_FLAG(F_CF);
}

/*
 * REMARKS:
 * Implements the TEST instruction and side effects.
 */
static void 
test_long(struct x86emu *emu, uint32_t d, uint32_t s)
{
        uint32_t res;   /* all operands in native machine order */

        res = d & s;

        CLEAR_FLAG(F_OF);
        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
        CONDITIONAL_SET_FLAG(res == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
        /* AF == dont care */
        CLEAR_FLAG(F_CF);
}

/*
 * REMARKS:
 * Implements the XOR instruction and side effects.
 */
static uint8_t 
xor_byte(struct x86emu *emu, uint8_t d, uint8_t s)
{
        uint8_t res;    /* all operands in native machine order */

        res = d ^ s;
        CLEAR_FLAG(F_OF);
        CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
        CONDITIONAL_SET_FLAG(res == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res), F_PF);
        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_AF);
        return res;
}

/*
 * REMARKS:
 * Implements the XOR instruction and side effects.
 */
static uint16_t 
xor_word(struct x86emu *emu, uint16_t d, uint16_t s)
{
        uint16_t res;   /* all operands in native machine order */

        res = d ^ s;
        CLEAR_FLAG(F_OF);
        CONDITIONAL_SET_FLAG(res & 0x8000, F_SF);
        CONDITIONAL_SET_FLAG(res == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_AF);
        return res;
}

/*
 * REMARKS:
 * Implements the XOR instruction and side effects.
 */
static uint32_t 
xor_long(struct x86emu *emu, uint32_t d, uint32_t s)
{
        uint32_t res;   /* all operands in native machine order */

        res = d ^ s;
        CLEAR_FLAG(F_OF);
        CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF);
        CONDITIONAL_SET_FLAG(res == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_AF);
        return res;
}

/*
 * REMARKS:
 * Implements the IMUL instruction and side effects.
 */
static void 
imul_byte(struct x86emu *emu, uint8_t s)
{
        int16_t res = (int16_t) ((int8_t) emu->x86.R_AL * (int8_t) s);

        emu->x86.R_AX = res;
        if (((emu->x86.R_AL & 0x80) == 0 && emu->x86.R_AH == 0x00) ||
            ((emu->x86.R_AL & 0x80) != 0 && emu->x86.R_AH == 0xFF)) {
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_OF);
        } else {
                SET_FLAG(F_CF);
                SET_FLAG(F_OF);
        }
}

/*
 * REMARKS:
 * Implements the IMUL instruction and side effects.
 */
static void 
imul_word(struct x86emu *emu, uint16_t s)
{
        int32_t res = (int16_t) emu->x86.R_AX * (int16_t) s;

        emu->x86.R_AX = (uint16_t) res;
        emu->x86.R_DX = (uint16_t) (res >> 16);
        if (((emu->x86.R_AX & 0x8000) == 0 && emu->x86.R_DX == 0x00) ||
            ((emu->x86.R_AX & 0x8000) != 0 && emu->x86.R_DX == 0xFF)) {
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_OF);
        } else {
                SET_FLAG(F_CF);
                SET_FLAG(F_OF);
        }
}

/*
 * REMARKS:
 * Implements the IMUL instruction and side effects.
 */
static void 
imul_long(struct x86emu *emu, uint32_t s)
{
        int64_t res;
        
        res = (int64_t)(int32_t)emu->x86.R_EAX * (int32_t)s;
        emu->x86.R_EAX = (uint32_t)res;
        emu->x86.R_EDX = ((uint64_t)res) >> 32;
        if (((emu->x86.R_EAX & 0x80000000) == 0 && emu->x86.R_EDX == 0x00) ||
            ((emu->x86.R_EAX & 0x80000000) != 0 && emu->x86.R_EDX == 0xFF)) {
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_OF);
        } else {
                SET_FLAG(F_CF);
                SET_FLAG(F_OF);
        }
}

/*
 * REMARKS:
 * Implements the MUL instruction and side effects.
 */
static void 
mul_byte(struct x86emu *emu, uint8_t s)
{
        uint16_t res = (uint16_t) (emu->x86.R_AL * s);

        emu->x86.R_AX = res;
        if (emu->x86.R_AH == 0) {
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_OF);
        } else {
                SET_FLAG(F_CF);
                SET_FLAG(F_OF);
        }
}

/*
 * REMARKS:
 * Implements the MUL instruction and side effects.
 */
static void 
mul_word(struct x86emu *emu, uint16_t s)
{
        uint32_t res = emu->x86.R_AX * s;

        emu->x86.R_AX = (uint16_t) res;
        emu->x86.R_DX = (uint16_t) (res >> 16);
        if (emu->x86.R_DX == 0) {
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_OF);
        } else {
                SET_FLAG(F_CF);
                SET_FLAG(F_OF);
        }
}

/*
 * REMARKS:
 * Implements the MUL instruction and side effects.
 */
static void 
mul_long(struct x86emu *emu, uint32_t s)
{
        uint64_t res = (uint64_t) emu->x86.R_EAX * s;

        emu->x86.R_EAX = (uint32_t) res;
        emu->x86.R_EDX = (uint32_t) (res >> 32);

        if (emu->x86.R_EDX == 0) {
                CLEAR_FLAG(F_CF);
                CLEAR_FLAG(F_OF);
        } else {
                SET_FLAG(F_CF);
                SET_FLAG(F_OF);
        }
}

/*
 * REMARKS:
 * Implements the IDIV instruction and side effects.
 */
static void 
idiv_byte(struct x86emu *emu, uint8_t s)
{
        int32_t dvd, div, mod;

        dvd = (int16_t) emu->x86.R_AX;
        if (s == 0) {
                x86emu_intr_raise(emu, 8);
                return;
        }
        div = dvd / (int8_t) s;
        mod = dvd % (int8_t) s;
        if (div > 0x7f || div < -0x7f) {
                x86emu_intr_raise(emu, 8);
                return;
        }
        emu->x86.R_AL = (int8_t) div;
        emu->x86.R_AH = (int8_t) mod;
}

/*
 * REMARKS:
 * Implements the IDIV instruction and side effects.
 */
static void 
idiv_word(struct x86emu *emu, uint16_t s)
{
        int32_t dvd, div, mod;

        dvd = (((int32_t) emu->x86.R_DX) << 16) | emu->x86.R_AX;
        if (s == 0) {
                x86emu_intr_raise(emu, 8);
                return;
        }
        div = dvd / (int16_t) s;
        mod = dvd % (int16_t) s;
        if (div > 0x7fff || div < -0x7fff) {
                x86emu_intr_raise(emu, 8);
                return;
        }
        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_SF);
        CONDITIONAL_SET_FLAG(div == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(mod & 0xff), F_PF);

        emu->x86.R_AX = (uint16_t) div;
        emu->x86.R_DX = (uint16_t) mod;
}

/*
 * REMARKS:
 * Implements the IDIV instruction and side effects.
 */
static void 
idiv_long(struct x86emu *emu, uint32_t s)
{
        int64_t dvd, div, mod;

        dvd = (((int64_t) emu->x86.R_EDX) << 32) | emu->x86.R_EAX;
        if (s == 0) {
                x86emu_intr_raise(emu, 8);
                return;
        }
        div = dvd / (int32_t) s;
        mod = dvd % (int32_t) s;
        if (div > 0x7fffffff || div < -0x7fffffff) {
                x86emu_intr_raise(emu, 8);
                return;
        }
        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_AF);
        CLEAR_FLAG(F_SF);
        SET_FLAG(F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(mod & 0xff), F_PF);

        emu->x86.R_EAX = (uint32_t) div;
        emu->x86.R_EDX = (uint32_t) mod;
}

/*
 * REMARKS:
 * Implements the DIV instruction and side effects.
 */
static void 
div_byte(struct x86emu *emu, uint8_t s)
{
        uint32_t dvd, div, mod;

        dvd = emu->x86.R_AX;
        if (s == 0) {
                x86emu_intr_raise(emu, 8);
                return;
        }
        div = dvd / (uint8_t) s;
        mod = dvd % (uint8_t) s;
        if (div > 0xff) {
                x86emu_intr_raise(emu, 8);
                return;
        }
        emu->x86.R_AL = (uint8_t) div;
        emu->x86.R_AH = (uint8_t) mod;
}

/*
 * REMARKS:
 * Implements the DIV instruction and side effects.
 */
static void 
div_word(struct x86emu *emu, uint16_t s)
{
        uint32_t dvd, div, mod;

        dvd = (((uint32_t) emu->x86.R_DX) << 16) | emu->x86.R_AX;
        if (s == 0) {
                x86emu_intr_raise(emu, 8);
                return;
        }
        div = dvd / (uint16_t) s;
        mod = dvd % (uint16_t) s;
        if (div > 0xffff) {
                x86emu_intr_raise(emu, 8);
                return;
        }
        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_SF);
        CONDITIONAL_SET_FLAG(div == 0, F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(mod & 0xff), F_PF);

        emu->x86.R_AX = (uint16_t) div;
        emu->x86.R_DX = (uint16_t) mod;
}

/*
 * REMARKS:
 * Implements the DIV instruction and side effects.
 */
static void 
div_long(struct x86emu *emu, uint32_t s)
{
        uint64_t dvd, div, mod;

        dvd = (((uint64_t) emu->x86.R_EDX) << 32) | emu->x86.R_EAX;
        if (s == 0) {
                x86emu_intr_raise(emu, 8);
                return;
        }
        div = dvd / (uint32_t) s;
        mod = dvd % (uint32_t) s;
        if (div > 0xffffffff) {
                x86emu_intr_raise(emu, 8);
                return;
        }
        CLEAR_FLAG(F_CF);
        CLEAR_FLAG(F_AF);
        CLEAR_FLAG(F_SF);
        SET_FLAG(F_ZF);
        CONDITIONAL_SET_FLAG(PARITY(mod & 0xff), F_PF);

        emu->x86.R_EAX = (uint32_t) div;
        emu->x86.R_EDX = (uint32_t) mod;
}

/*
 * REMARKS:
 * Implements the IN string instruction and side effects.
 */
static void 
ins(struct x86emu *emu, int size)
{
        int inc = size;

        if (ACCESS_FLAG(F_DF)) {
                inc = -size;
        }
        if (emu->x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) {
                /* dont care whether REPE or REPNE */
                /* in until CX is ZERO. */
                uint32_t count = ((emu->x86.mode & SYSMODE_PREFIX_DATA) ?
                    emu->x86.R_ECX : emu->x86.R_CX);
                switch (size) {
                case 1:
                        while (count--) {
                                store_byte(emu, emu->x86.R_ES, emu->x86.R_DI,
                                    (*emu->emu_inb) (emu, emu->x86.R_DX));
                                emu->x86.R_DI += inc;
                        }
                        break;

                case 2:
                        while (count--) {
                                store_word(emu, emu->x86.R_ES, emu->x86.R_DI,
                                    (*emu->emu_inw) (emu, emu->x86.R_DX));
                                emu->x86.R_DI += inc;
                        }
                        break;
                case 4:
                        while (count--) {
                                store_long(emu, emu->x86.R_ES, emu->x86.R_DI,
                                    (*emu->emu_inl) (emu, emu->x86.R_DX));
                                emu->x86.R_DI += inc;
                                break;
                        }
                }
                emu->x86.R_CX = 0;
                if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                        emu->x86.R_ECX = 0;
                }
                emu->x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE);
        } else {
                switch (size) {
                case 1:
                        store_byte(emu, emu->x86.R_ES, emu->x86.R_DI,
                            (*emu->emu_inb) (emu, emu->x86.R_DX));
                        break;
                case 2:
                        store_word(emu, emu->x86.R_ES, emu->x86.R_DI,
                            (*emu->emu_inw) (emu, emu->x86.R_DX));
                        break;
                case 4:
                        store_long(emu, emu->x86.R_ES, emu->x86.R_DI,
                            (*emu->emu_inl) (emu, emu->x86.R_DX));
                        break;
                }
                emu->x86.R_DI += inc;
        }
}

/*
 * REMARKS:
 * Implements the OUT string instruction and side effects.
 */
static void 
outs(struct x86emu *emu, int size)
{
        int inc = size;

        if (ACCESS_FLAG(F_DF)) {
                inc = -size;
        }
        if (emu->x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) {
                /* dont care whether REPE or REPNE */
                /* out until CX is ZERO. */
                uint32_t count = ((emu->x86.mode & SYSMODE_PREFIX_DATA) ?
                    emu->x86.R_ECX : emu->x86.R_CX);
                switch (size) {
                case 1:
                        while (count--) {
                                (*emu->emu_outb) (emu, emu->x86.R_DX,
                                    fetch_byte(emu, emu->x86.R_ES,
                                    emu->x86.R_SI));
                                emu->x86.R_SI += inc;
                        }
                        break;

                case 2:
                        while (count--) {
                                (*emu->emu_outw) (emu, emu->x86.R_DX,
                                    fetch_word(emu, emu->x86.R_ES,
                                    emu->x86.R_SI));
                                emu->x86.R_SI += inc;
                        }
                        break;
                case 4:
                        while (count--) {
                                (*emu->emu_outl) (emu, emu->x86.R_DX,
                                    fetch_long(emu, emu->x86.R_ES,
                                    emu->x86.R_SI));
                                emu->x86.R_SI += inc;
                                break;
                        }
                }
                emu->x86.R_CX = 0;
                if (emu->x86.mode & SYSMODE_PREFIX_DATA) {
                        emu->x86.R_ECX = 0;
                }
                emu->x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE);
        } else {
                switch (size) {
                case 1:
                        (*emu->emu_outb) (emu, emu->x86.R_DX,
                            fetch_byte(emu, emu->x86.R_ES, emu->x86.R_SI));
                        break;
                case 2:
                        (*emu->emu_outw) (emu, emu->x86.R_DX,
                            fetch_word(emu, emu->x86.R_ES, emu->x86.R_SI));
                        break;
                case 4:
                        (*emu->emu_outl) (emu, emu->x86.R_DX,
                            fetch_long(emu, emu->x86.R_ES, emu->x86.R_SI));
                        break;
                }
                emu->x86.R_SI += inc;
        }
}

/*
 * REMARKS:
 * Pushes a word onto the stack.
 * 
 * NOTE: Do not inline this, as (*emu->emu_wrX) is already inline!
 */
static void 
push_word(struct x86emu *emu, uint16_t w)
{
        emu->x86.R_SP -= 2;
        store_word(emu, emu->x86.R_SS, emu->x86.R_SP, w);
}

/*
 * REMARKS:
 * Pushes a long onto the stack.
 * 
 * NOTE: Do not inline this, as (*emu->emu_wrX) is already inline!
 */
static void 
push_long(struct x86emu *emu, uint32_t w)
{
        emu->x86.R_SP -= 4;
        store_long(emu, emu->x86.R_SS, emu->x86.R_SP, w);
}

/*
 * REMARKS:
 * Pops a word from the stack.
 * 
 * NOTE: Do not inline this, as (*emu->emu_rdX) is already inline!
 */
static uint16_t 
pop_word(struct x86emu *emu)
{
        uint16_t res;

        res = fetch_word(emu, emu->x86.R_SS, emu->x86.R_SP);
        emu->x86.R_SP += 2;
        return res;
}

/*
 * REMARKS:
 * Pops a long from the stack.
 * 
 * NOTE: Do not inline this, as (*emu->emu_rdX) is already inline!
 */
static uint32_t 
pop_long(struct x86emu *emu)
{
        uint32_t res;

        res = fetch_long(emu, emu->x86.R_SS, emu->x86.R_SP);
        emu->x86.R_SP += 4;
        return res;
}