/*
 * Fast486 386/486 CPU Emulation Library
 * opcodes.c
 *
 * Copyright (C) 2015 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/* INCLUDES *******************************************************************/

#include <windef.h>

// #define NDEBUG
#include <debug.h>

#include <fast486.h>
#include "opcodes.h"
#include "opgroups.h"
#include "extraops.h"
#include "common.h"
#include "fpu.h"

/* PUBLIC VARIABLES ***********************************************************/

FAST486_OPCODE_HANDLER_PROC
Fast486OpcodeHandlers[FAST486_NUM_OPCODE_HANDLERS] =
{
    Fast486OpcodeAddByteModrm,          /* 0x00 - 0x03 */
    Fast486OpcodeAddModrm,
    Fast486OpcodeAddByteModrm,
    Fast486OpcodeAddModrm,
    Fast486OpcodeAddAl,                 /* 0x04 */
    Fast486OpcodeAddEax,                /* 0x05 */
    Fast486OpcodePushEs,                /* 0x06 */
    Fast486OpcodePopEs,                 /* 0x07 */
    Fast486OpcodeOrByteModrm,           /* 0x08 - 0x0B */
    Fast486OpcodeOrModrm,
    Fast486OpcodeOrByteModrm,
    Fast486OpcodeOrModrm,
    Fast486OpcodeOrAl,                  /* 0x0C */
    Fast486OpcodeOrEax,                 /* 0x0D */
    Fast486OpcodePushCs,                /* 0x0E */
    Fast486OpcodeExtended,              /* 0x0F */
    Fast486OpcodeAdcByteModrm,          /* 0x10 - 0x13 */
    Fast486OpcodeAdcModrm,
    Fast486OpcodeAdcByteModrm,
    Fast486OpcodeAdcModrm,
    Fast486OpcodeAdcAl,                 /* 0x14 */
    Fast486OpcodeAdcEax,                /* 0x15 */
    Fast486OpcodePushSs,                /* 0x16 */
    Fast486OpcodePopSs,                 /* 0x17 */
    Fast486OpcodeSbbByteModrm,          /* 0x18 - 0x1B */
    Fast486OpcodeSbbModrm,
    Fast486OpcodeSbbByteModrm,
    Fast486OpcodeSbbModrm,
    Fast486OpcodeSbbAl,                 /* 0x1C */
    Fast486OpcodeSbbEax,                /* 0x1D */
    Fast486OpcodePushDs,                /* 0x1E */
    Fast486OpcodePopDs,                 /* 0x1F */
    Fast486OpcodeAndByteModrm,          /* 0x20 - 0x23 */
    Fast486OpcodeAndModrm,
    Fast486OpcodeAndByteModrm,
    Fast486OpcodeAndModrm,
    Fast486OpcodeAndAl,                 /* 0x24 */
    Fast486OpcodeAndEax,                /* 0x25 */
    Fast486OpcodePrefix,                /* 0x26 */
    Fast486OpcodeDaa,                   /* 0x27 */
    Fast486OpcodeCmpSubByteModrm,       /* 0x28 - 0x2B */
    Fast486OpcodeCmpSubModrm,
    Fast486OpcodeCmpSubByteModrm,
    Fast486OpcodeCmpSubModrm,
    Fast486OpcodeCmpSubAl,              /* 0x2C */
    Fast486OpcodeCmpSubEax,             /* 0x2D */
    Fast486OpcodePrefix,                /* 0x2E */
    Fast486OpcodeDas,                   /* 0x2F */
    Fast486OpcodeXorByteModrm,          /* 0x30 - 0x33 */
    Fast486OpcodeXorModrm,
    Fast486OpcodeXorByteModrm,
    Fast486OpcodeXorModrm,
    Fast486OpcodeXorAl,                 /* 0x34 */
    Fast486OpcodeXorEax,                /* 0x35 */
    Fast486OpcodePrefix,                /* 0x36 */
    Fast486OpcodeAaa,                   /* 0x37 */
    Fast486OpcodeCmpSubByteModrm,       /* 0x38 - 0x3B */
    Fast486OpcodeCmpSubModrm,
    Fast486OpcodeCmpSubByteModrm,
    Fast486OpcodeCmpSubModrm,
    Fast486OpcodeCmpSubAl,              /* 0x3C */
    Fast486OpcodeCmpSubEax,             /* 0x3D */
    Fast486OpcodePrefix,                /* 0x3E */
    Fast486OpcodeAas,                   /* 0x3F */
    Fast486OpcodeIncrement,             /* 0x40 - 0x47 */
    Fast486OpcodeIncrement,
    Fast486OpcodeIncrement,
    Fast486OpcodeIncrement,
    Fast486OpcodeIncrement,
    Fast486OpcodeIncrement,
    Fast486OpcodeIncrement,
    Fast486OpcodeIncrement,
    Fast486OpcodeDecrement,             /* 0x48 - 0x4F */
    Fast486OpcodeDecrement,
    Fast486OpcodeDecrement,
    Fast486OpcodeDecrement,
    Fast486OpcodeDecrement,
    Fast486OpcodeDecrement,
    Fast486OpcodeDecrement,
    Fast486OpcodeDecrement,
    Fast486OpcodePushReg,               /* 0x50 - 0x57 */
    Fast486OpcodePushReg,
    Fast486OpcodePushReg,
    Fast486OpcodePushReg,
    Fast486OpcodePushReg,
    Fast486OpcodePushReg,
    Fast486OpcodePushReg,
    Fast486OpcodePushReg,
    Fast486OpcodePopReg,                /* 0x58 - 0x5F */
    Fast486OpcodePopReg,
    Fast486OpcodePopReg,
    Fast486OpcodePopReg,
    Fast486OpcodePopReg,
    Fast486OpcodePopReg,
    Fast486OpcodePopReg,
    Fast486OpcodePopReg,
    Fast486OpcodePushAll,               /* 0x60 */
    Fast486OpcodePopAll,                /* 0x61 */
    Fast486OpcodeBound,                 /* 0x62 */
    Fast486OpcodeArpl,                  /* 0x63 */
    Fast486OpcodePrefix,                /* 0x64 - 0x67 */
    Fast486OpcodePrefix,
    Fast486OpcodePrefix,
    Fast486OpcodePrefix,
    Fast486OpcodePushImm,               /* 0x68 */
    Fast486OpcodeImulModrmImm,          /* 0x69 */
    Fast486OpcodePushByteImm,           /* 0x6A */
    Fast486OpcodeImulModrmImm,          /* 0x6B */
    Fast486OpcodeIns,                   /* 0x6C */
    Fast486OpcodeIns,                   /* 0x6D */
    Fast486OpcodeOuts,                  /* 0x6E */
    Fast486OpcodeOuts,                  /* 0x6F */
    Fast486OpcodeShortConditionalJmp,   /* 0x70 - 0x7F */
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeShortConditionalJmp,
    Fast486OpcodeGroup8082,             /* 0x80 */
    Fast486OpcodeGroup81,               /* 0x81 */
    Fast486OpcodeGroup8082,             /* 0x82 */
    Fast486OpcodeGroup83,               /* 0x83 */
    Fast486OpcodeTestByteModrm,         /* 0x84 */
    Fast486OpcodeTestModrm,             /* 0x85 */
    Fast486OpcodeXchgByteModrm,         /* 0x86 */
    Fast486OpcodeXchgModrm,             /* 0x87 */
    Fast486OpcodeMovByteModrm,          /* 0x88 */
    Fast486OpcodeMovModrm,              /* 0x89 */
    Fast486OpcodeMovByteModrm,          /* 0x8A */
    Fast486OpcodeMovModrm,              /* 0x8B */
    Fast486OpcodeMovStoreSeg,           /* 0x8C */
    Fast486OpcodeLea,                   /* 0x8D */
    Fast486OpcodeMovLoadSeg,            /* 0x8E */
    Fast486OpcodeGroup8F,               /* 0x8F */
    Fast486OpcodeNop,                   /* 0x90 */
    Fast486OpcodeExchangeEax,           /* 0x91 - 0x97 */
    Fast486OpcodeExchangeEax,
    Fast486OpcodeExchangeEax,
    Fast486OpcodeExchangeEax,
    Fast486OpcodeExchangeEax,
    Fast486OpcodeExchangeEax,
    Fast486OpcodeExchangeEax,
    Fast486OpcodeCwde,                  /* 0x98 */
    Fast486OpcodeCdq,                   /* 0x99 */
    Fast486OpcodeCallAbs,               /* 0x9A */
    Fast486OpcodeWait,                  /* 0x9B */
    Fast486OpcodePushFlags,             /* 0x9C */
    Fast486OpcodePopFlags,              /* 0x9D */
    Fast486OpcodeSahf,                  /* 0x9E */
    Fast486OpcodeLahf,                  /* 0x9F */
    Fast486OpcodeMovAlOffset,           /* 0xA0 */
    Fast486OpcodeMovEaxOffset,          /* 0xA1 */
    Fast486OpcodeMovOffsetAl,           /* 0xA2 */
    Fast486OpcodeMovOffsetEax,          /* 0xA3 */
    Fast486OpcodeMovs,                  /* 0xA4 */
    Fast486OpcodeMovs,                  /* 0xA5 */
    Fast486OpcodeCmps,                  /* 0xA6 */
    Fast486OpcodeCmps,                  /* 0xA7 */
    Fast486OpcodeTestAl,                /* 0xA8 */
    Fast486OpcodeTestEax,               /* 0xA9 */
    Fast486OpcodeStos,                  /* 0xAA */
    Fast486OpcodeStos,                  /* 0xAB */
    Fast486OpcodeLods,                  /* 0xAC */
    Fast486OpcodeLods,                  /* 0xAD */
    Fast486OpcodeScas,                  /* 0xAE */
    Fast486OpcodeScas,                  /* 0xAF */
    Fast486OpcodeMovByteRegImm,         /* 0xB0 - 0xB7 */
    Fast486OpcodeMovByteRegImm,
    Fast486OpcodeMovByteRegImm,
    Fast486OpcodeMovByteRegImm,
    Fast486OpcodeMovByteRegImm,
    Fast486OpcodeMovByteRegImm,
    Fast486OpcodeMovByteRegImm,
    Fast486OpcodeMovByteRegImm,
    Fast486OpcodeMovRegImm,             /* 0xB8 - 0xBF */
    Fast486OpcodeMovRegImm,
    Fast486OpcodeMovRegImm,
    Fast486OpcodeMovRegImm,
    Fast486OpcodeMovRegImm,
    Fast486OpcodeMovRegImm,
    Fast486OpcodeMovRegImm,
    Fast486OpcodeMovRegImm,
    Fast486OpcodeGroupC0,               /* 0xC0 */
    Fast486OpcodeGroupC1,               /* 0xC1 */
    Fast486OpcodeRet,                   /* 0xC2 */
    Fast486OpcodeRet,                   /* 0xC3 */
    Fast486OpcodeLdsLes,                /* 0xC4 */
    Fast486OpcodeLdsLes,                /* 0xC5 */
    Fast486OpcodeGroupC6,               /* 0xC6 */
    Fast486OpcodeGroupC7,               /* 0xC7 */
    Fast486OpcodeEnter,                 /* 0xC8 */
    Fast486OpcodeLeave,                 /* 0xC9 */
    Fast486OpcodeRetFar,                /* 0xCA */
    Fast486OpcodeRetFar,                /* 0xCB */
    Fast486OpcodeInt,                   /* 0xCC */
    Fast486OpcodeInt,                   /* 0xCD */
    Fast486OpcodeInt,                   /* 0xCE */
    Fast486OpcodeIret,                  /* 0xCF */
    Fast486OpcodeGroupD0,               /* 0xD0 - 0xD3 */
    Fast486OpcodeGroupD1,
    Fast486OpcodeGroupD2,
    Fast486OpcodeGroupD3,
    Fast486OpcodeAam,                   /* 0xD4 */
    Fast486OpcodeAad,                   /* 0xD5 */
    Fast486OpcodeSalc,                  /* 0xD6 */
    Fast486OpcodeXlat,                  /* 0xD7 */
    Fast486FpuOpcodeD8,                 /* 0xD8 - 0xDF */
    Fast486FpuOpcodeD9,
    Fast486FpuOpcodeDA,
    Fast486FpuOpcodeDB,
    Fast486FpuOpcodeDC,
    Fast486FpuOpcodeDD,
    Fast486FpuOpcodeDE,
    Fast486FpuOpcodeDF,
    Fast486OpcodeLoop,                  /* 0xE0 - 0xE2 */
    Fast486OpcodeLoop,
    Fast486OpcodeLoop,
    Fast486OpcodeJecxz,                 /* 0xE3 */
    Fast486OpcodeInByte,                /* 0xE4 */
    Fast486OpcodeIn,                    /* 0xE5 */
    Fast486OpcodeOutByte,               /* 0xE6 */
    Fast486OpcodeOut,                   /* 0xE7 */
    Fast486OpcodeCall,                  /* 0xE8 */
    Fast486OpcodeJmp,                   /* 0xE9 */
    Fast486OpcodeJmpAbs,                /* 0xEA */
    Fast486OpcodeShortJump,             /* 0xEB */
    Fast486OpcodeInByte,                /* 0xEC */
    Fast486OpcodeIn,                    /* 0xED */
    Fast486OpcodeOutByte,               /* 0xEE */
    Fast486OpcodeOut,                   /* 0xEF */
    Fast486OpcodePrefix,                /* 0xF0 */
    Fast486OpcodeInvalid,               /* 0xF1 */  // Invalid opcode -- ICEBP/INT01 opcode
    Fast486OpcodePrefix,                /* 0xF2 */
    Fast486OpcodePrefix,                /* 0xF3 */
    Fast486OpcodeHalt,                  /* 0xF4 */
    Fast486OpcodeComplCarry,            /* 0xF5 */
    Fast486OpcodeGroupF6,               /* 0xF6 */
    Fast486OpcodeGroupF7,               /* 0xF7 */
    Fast486OpcodeClearCarry,            /* 0xF8 */
    Fast486OpcodeSetCarry,              /* 0xF9 */
    Fast486OpcodeClearInt,              /* 0xFA */
    Fast486OpcodeSetInt,                /* 0xFB */
    Fast486OpcodeClearDir,              /* 0xFC */
    Fast486OpcodeSetDir,                /* 0xFD */
    Fast486OpcodeGroupFE,               /* 0xFE */
    Fast486OpcodeGroupFF,               /* 0xFF */
};

/* PUBLIC FUNCTIONS ***********************************************************/

FAST486_OPCODE_HANDLER(Fast486OpcodeInvalid)
{
    /*
     * This is not a valid opcode.
     * Well, not totally: see http://www.rcollins.org/secrets/opcodes/ICEBP.html
     * for more details.
     */
    DPRINT1("FAST486 -- Calling ICEBP opcode\n");
    Fast486Exception(State, FAST486_EXCEPTION_UD);
}

FAST486_OPCODE_HANDLER(Fast486OpcodePrefix)
{
    switch (Opcode)
    {
        /* ES: */
        case 0x26:
        {
            State->PrefixFlags |= FAST486_PREFIX_SEG;
            State->SegmentOverride = FAST486_REG_ES;
            break;
        }

        /* CS: */
        case 0x2E:
        {
            State->PrefixFlags |= FAST486_PREFIX_SEG;
            State->SegmentOverride = FAST486_REG_CS;
            break;
        }

        /* SS: */
        case 0x36:
        {
            State->PrefixFlags |= FAST486_PREFIX_SEG;
            State->SegmentOverride = FAST486_REG_SS;
            break;
        }

        /* DS: */
        case 0x3E:
        {
            State->PrefixFlags |= FAST486_PREFIX_SEG;
            State->SegmentOverride = FAST486_REG_DS;
            break;
        }

        /* FS: */
        case 0x64:
        {
            State->PrefixFlags |= FAST486_PREFIX_SEG;
            State->SegmentOverride = FAST486_REG_FS;
            break;
        }

        /* GS: */
        case 0x65:
        {
            State->PrefixFlags |= FAST486_PREFIX_SEG;
            State->SegmentOverride = FAST486_REG_GS;
            break;
        }

        /* OPSIZE */
        case 0x66:
        {
            State->PrefixFlags |= FAST486_PREFIX_OPSIZE;
            break;
        }

        /* ADSIZE */
        case 0x67:
        {
            State->PrefixFlags |= FAST486_PREFIX_ADSIZE;
            break;
        }

        /* LOCK */
        case 0xF0:
        {
            State->PrefixFlags |= FAST486_PREFIX_LOCK;
            break;
        }

        /* REPNZ */
        case 0xF2:
        {
            /* Mutually exclusive with REP */
            State->PrefixFlags |= FAST486_PREFIX_REPNZ;
            State->PrefixFlags &= ~FAST486_PREFIX_REP;
            break;
        }

        /* REP / REPZ */
        case 0xF3:
        {
            /* Mutually exclusive with REPNZ */
            State->PrefixFlags |= FAST486_PREFIX_REP;
            State->PrefixFlags &= ~FAST486_PREFIX_REPNZ;
            break;
        }

        default:
        {
            /* Shouldn't happen */
            ASSERT(FALSE);
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeIncrement)
{
    ULONG Value;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xF8) == 0x40);

    if (Size)
    {
        Value = ++State->GeneralRegs[Opcode & 0x07].Long;

        State->Flags.Of = (Value == SIGN_FLAG_LONG);
        State->Flags.Sf = ((Value & SIGN_FLAG_LONG) != 0);
    }
    else
    {
        Value = ++State->GeneralRegs[Opcode & 0x07].LowWord;

        State->Flags.Of = (Value == SIGN_FLAG_WORD);
        State->Flags.Sf = ((Value & SIGN_FLAG_WORD) != 0);
    }

    State->Flags.Zf = (Value == 0);
    State->Flags.Af = ((Value & 0x0F) == 0);
    State->Flags.Pf = Fast486CalculateParity(LOBYTE(Value));
}

FAST486_OPCODE_HANDLER(Fast486OpcodeDecrement)
{
    ULONG Value;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xF8) == 0x48);

    if (Size)
    {
        Value = --State->GeneralRegs[Opcode & 0x07].Long;

        State->Flags.Of = (Value == (SIGN_FLAG_LONG - 1));
        State->Flags.Sf = ((Value & SIGN_FLAG_LONG) != 0);
    }
    else
    {
        Value = --State->GeneralRegs[Opcode & 0x07].LowWord;

        State->Flags.Of = (Value == (SIGN_FLAG_WORD - 1));
        State->Flags.Sf = ((Value & SIGN_FLAG_WORD) != 0);
    }

    State->Flags.Zf = (Value == 0);
    State->Flags.Af = ((Value & 0x0F) == 0x0F);
    State->Flags.Pf = Fast486CalculateParity(LOBYTE(Value));
}

FAST486_OPCODE_HANDLER(Fast486OpcodePushReg)
{
    NO_LOCK_PREFIX();

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xF8) == 0x50);

    /* Call the internal function */
    Fast486StackPush(State, State->GeneralRegs[Opcode & 0x07].Long);
}

FAST486_OPCODE_HANDLER(Fast486OpcodePopReg)
{
    ULONG Value;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xF8) == 0x58);

    /* Call the internal function */
    if (!Fast486StackPop(State, &Value)) return;

    /* Store the value */
    if (Size) State->GeneralRegs[Opcode & 0x07].Long = Value;
    else State->GeneralRegs[Opcode & 0x07].LowWord = Value;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeNop)
{
}

FAST486_OPCODE_HANDLER(Fast486OpcodeExchangeEax)
{
    INT Reg = Opcode & 0x07;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xF8) == 0x90);

    /* Exchange the values */
    if (Size)
    {
        ULONG Value;

        Value = State->GeneralRegs[Reg].Long;
        State->GeneralRegs[Reg].Long = State->GeneralRegs[FAST486_REG_EAX].Long;
        State->GeneralRegs[FAST486_REG_EAX].Long = Value;
    }
    else
    {
        USHORT Value;

        Value = State->GeneralRegs[Reg].LowWord;
        State->GeneralRegs[Reg].LowWord = State->GeneralRegs[FAST486_REG_EAX].LowWord;
        State->GeneralRegs[FAST486_REG_EAX].LowWord = Value;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeShortConditionalJmp)
{
    BOOLEAN Jump = FALSE;
    CHAR Offset = 0;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xF0) == 0x70);

    TOGGLE_OPSIZE(Size);

    /* Fetch the offset */
    if (!Fast486FetchByte(State, (PUCHAR)&Offset))
    {
        /* An exception occurred */
        return;
    }

    switch ((Opcode & 0x0F) >> 1)
    {
        /* JO / JNO */
        case 0:
        {
            Jump = State->Flags.Of;
            break;
        }

        /* JC / JNC */
        case 1:
        {
            Jump = State->Flags.Cf;
            break;
        }

        /* JZ / JNZ */
        case 2:
        {
            Jump = State->Flags.Zf;
            break;
        }

        /* JBE / JNBE */
        case 3:
        {
            Jump = State->Flags.Cf || State->Flags.Zf;
            break;
        }

        /* JS / JNS */
        case 4:
        {
            Jump = State->Flags.Sf;
            break;
        }

        /* JP / JNP */
        case 5:
        {
            Jump = State->Flags.Pf;
            break;
        }

        /* JL / JNL */
        case 6:
        {
            Jump = State->Flags.Sf != State->Flags.Of;
            break;
        }

        /* JLE / JNLE */
        case 7:
        {
            Jump = (State->Flags.Sf != State->Flags.Of) || State->Flags.Zf;
            break;
        }
    }

    if (Opcode & 1)
    {
        /* Invert the result */
        Jump = !Jump;
    }

    if (Jump)
    {
        /* Move the instruction pointer */
        State->InstPtr.Long += Offset;

        if (!Size)
        {
            /* Clear the top half of EIP */
            State->InstPtr.Long &= 0xFFFF;
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeClearCarry)
{
    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xF8);

    NO_LOCK_PREFIX();

    /* Clear CF and return success */
    State->Flags.Cf = FALSE;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeSetCarry)
{
    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xF9);

    NO_LOCK_PREFIX();

    /* Set CF and return success*/
    State->Flags.Cf = TRUE;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeComplCarry)
{
    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xF5);

    NO_LOCK_PREFIX();

    /* Toggle CF and return success */
    State->Flags.Cf = !State->Flags.Cf;
    return;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeClearInt)
{
    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xFA);

    NO_LOCK_PREFIX();

    /* Check for protected mode */
    if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
    {
        /* Check IOPL */
        if (State->Flags.Iopl >= Fast486GetCurrentPrivLevel(State))
        {
            /* Clear the interrupt flag */
            State->Flags.If = FALSE;
        }
        else
        {
            /* General Protection Fault */
            Fast486Exception(State, FAST486_EXCEPTION_GP);
            return;
        }
    }
    else
    {
        /* Just clear the interrupt flag */
        State->Flags.If = FALSE;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeSetInt)
{
    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xFB);

    NO_LOCK_PREFIX();

    /* Check for protected mode */
    if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
    {
        /* Check IOPL */
        if (State->Flags.Iopl >= Fast486GetCurrentPrivLevel(State))
        {
            /* Set the interrupt flag */
            State->Flags.If = TRUE;
        }
        else
        {
            /* General Protection Fault */
            Fast486Exception(State, FAST486_EXCEPTION_GP);
            return;
        }
    }
    else
    {
        /* Just set the interrupt flag */
        State->Flags.If = TRUE;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeClearDir)
{
    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xFC);

    NO_LOCK_PREFIX();

    /* Clear DF */
    State->Flags.Df = FALSE;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeSetDir)
{
    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xFD);

    NO_LOCK_PREFIX();

    /* Set DF */
    State->Flags.Df = TRUE;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeHalt)
{
    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xF4);

    NO_LOCK_PREFIX();

    /* Privileged instructions can only be executed under CPL = 0 */
    if (Fast486GetCurrentPrivLevel(State) != 0)
    {
        Fast486Exception(State, FAST486_EXCEPTION_GP);
        return;
    }

    /* Halt */
    State->Halted = TRUE;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeInByte)
{
    UCHAR Data;
    ULONG Port;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xF7) == 0xE4);

    if (Opcode == 0xE4)
    {
        /* Fetch the parameter */
        if (!Fast486FetchByte(State, &Data))
        {
            /* Exception occurred */
            return;
        }

        /* Set the port number to the parameter */
        Port = Data;
    }
    else
    {
        /* The port number is in DX */
        Port = State->GeneralRegs[FAST486_REG_EDX].LowWord;
    }

    if (!Fast486IoPrivilegeCheck(State, Port)) return;

    /* Read a byte from the I/O port */
    State->IoReadCallback(State, Port, &Data, 1, sizeof(UCHAR));

    /* Store the result in AL */
    State->GeneralRegs[FAST486_REG_EAX].LowByte = Data;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeIn)
{
    ULONG Port;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xF7) == 0xE5);

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    if (Opcode == 0xE5)
    {
        UCHAR Data;

        /* Fetch the parameter */
        if (!Fast486FetchByte(State, &Data))
        {
            /* Exception occurred */
            return;
        }

        /* Set the port number to the parameter */
        Port = Data;
    }
    else
    {
        /* The port number is in DX */
        Port = State->GeneralRegs[FAST486_REG_EDX].LowWord;
    }

    if (!Fast486IoPrivilegeCheck(State, Port)) return;

    if (Size)
    {
        ULONG Data;

        /* Read a dword from the I/O port */
        State->IoReadCallback(State, Port, &Data, 1, sizeof(ULONG));

        /* Store the value in EAX */
        State->GeneralRegs[FAST486_REG_EAX].Long = Data;
    }
    else
    {
        USHORT Data;

        /* Read a word from the I/O port */
        State->IoReadCallback(State, Port, &Data, 1, sizeof(USHORT));

        /* Store the value in AX */
        State->GeneralRegs[FAST486_REG_EAX].LowWord = Data;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeOutByte)
{
    UCHAR Data;
    ULONG Port;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xF7) == 0xE6);

    if (Opcode == 0xE6)
    {
        /* Fetch the parameter */
        if (!Fast486FetchByte(State, &Data))
        {
            /* Exception occurred */
            return;
        }

        /* Set the port number to the parameter */
        Port = Data;
    }
    else
    {
        /* The port number is in DX */
        Port = State->GeneralRegs[FAST486_REG_EDX].LowWord;
    }

    if (!Fast486IoPrivilegeCheck(State, Port)) return;

    /* Read the value from AL */
    Data = State->GeneralRegs[FAST486_REG_EAX].LowByte;

    /* Write the byte to the I/O port */
    State->IoWriteCallback(State, Port, &Data, 1, sizeof(UCHAR));
}

FAST486_OPCODE_HANDLER(Fast486OpcodeOut)
{
    ULONG Port;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xF7) == 0xE7);

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    if (Opcode == 0xE7)
    {
        UCHAR Data;

        /* Fetch the parameter */
        if (!Fast486FetchByte(State, &Data))
        {
            /* Exception occurred */
            return;
        }

        /* Set the port number to the parameter */
        Port = Data;
    }
    else
    {
        /* The port number is in DX */
        Port = State->GeneralRegs[FAST486_REG_EDX].LowWord;
    }

    if (!Fast486IoPrivilegeCheck(State, Port)) return;

    if (Size)
    {
        /* Get the value from EAX */
        ULONG Data = State->GeneralRegs[FAST486_REG_EAX].Long;

        /* Write a dword to the I/O port */
        State->IoWriteCallback(State, Port, &Data, 1, sizeof(ULONG));
    }
    else
    {
        /* Get the value from AX */
        USHORT Data = State->GeneralRegs[FAST486_REG_EAX].LowWord;

        /* Write a word to the I/O port */
        State->IoWriteCallback(State, Port, &Data, 1, sizeof(USHORT));
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeShortJump)
{
    CHAR Offset = 0;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_OPSIZE(Size);

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xEB);

    /* Fetch the offset */
    if (!Fast486FetchByte(State, (PUCHAR)&Offset))
    {
        /* An exception occurred */
        return;
    }

    /* Move the instruction pointer */
    State->InstPtr.Long += Offset;

    if (!Size)
    {
        /* Clear the top half of EIP */
        State->InstPtr.Long &= 0xFFFF;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeMovRegImm)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xF8) == 0xB8);

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    if (Size)
    {
        ULONG Value;

        /* Fetch the dword */
        if (!Fast486FetchDword(State, &Value))
        {
            /* Exception occurred */
            return;
        }

        /* Store the value in the register */
        State->GeneralRegs[Opcode & 0x07].Long = Value;
    }
    else
    {
        USHORT Value;

        /* Fetch the word */
        if (!Fast486FetchWord(State, &Value))
        {
            /* Exception occurred */
            return;
        }

        /* Store the value in the register */
        State->GeneralRegs[Opcode & 0x07].LowWord = Value;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeMovByteRegImm)
{
    UCHAR Value;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xF8) == 0xB0);

    NO_LOCK_PREFIX();

    /* Fetch the byte */
    if (!Fast486FetchByte(State, &Value))
    {
        /* Exception occurred */
        return;
    }

    if (Opcode & 0x04)
    {
        /* AH, CH, DH or BH */
        State->GeneralRegs[Opcode & 0x03].HighByte = Value;
    }
    else
    {
        /* AL, CL, DL or BL */
        State->GeneralRegs[Opcode & 0x03].LowByte = Value;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAddByteModrm)
{
    UCHAR FirstValue, SecondValue, Result;
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x00);

    TOGGLE_ADSIZE(AddressSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if (!Fast486ReadModrmByteOperands(State,
                                      &ModRegRm,
                                      &FirstValue,
                                      &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    Result = FirstValue + SecondValue;

    /* Update the flags */
    State->Flags.Cf = (Result < FirstValue) && (Result < SecondValue);
    State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) == (SecondValue & SIGN_FLAG_BYTE))
                      && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE));
    State->Flags.Af = ((((FirstValue & 0x0F) + (SecondValue & 0x0F)) & 0x10) != 0);
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Write back the result */
    Fast486WriteModrmByteOperands(State,
                                  &ModRegRm,
                                  Opcode & FAST486_OPCODE_WRITE_REG,
                                  Result);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAddModrm)
{
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN OperandSize, AddressSize;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x01);

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_ADSIZE(AddressSize);
    TOGGLE_OPSIZE(OperandSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    /* Check the operand size */
    if (OperandSize)
    {
        ULONG FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmDwordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue + SecondValue;

        /* Update the flags */
        State->Flags.Cf = (Result < FirstValue) && (Result < SecondValue);
        State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) == (SecondValue & SIGN_FLAG_LONG))
                          && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG));
        State->Flags.Af = ((((FirstValue & 0x0F) + (SecondValue & 0x0F)) & 0x10) != 0);
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        Fast486WriteModrmDwordOperands(State,
                                       &ModRegRm,
                                       Opcode & FAST486_OPCODE_WRITE_REG,
                                       Result);
    }
    else
    {
        USHORT FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmWordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue + SecondValue;

        /* Update the flags */
        State->Flags.Cf = (Result < FirstValue) && (Result < SecondValue);
        State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) == (SecondValue & SIGN_FLAG_WORD))
                          && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD));
        State->Flags.Af = ((((FirstValue & 0x0F) + (SecondValue & 0x0F)) & 0x10) != 0);
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        Fast486WriteModrmWordOperands(State,
                                       &ModRegRm,
                                       Opcode & FAST486_OPCODE_WRITE_REG,
                                       Result);
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAddAl)
{
    UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte;
    UCHAR SecondValue, Result;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x04);

    NO_LOCK_PREFIX();

    if (!Fast486FetchByte(State, &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    Result = FirstValue + SecondValue;

    /* Update the flags */
    State->Flags.Cf = (Result < FirstValue) && (Result < SecondValue);
    State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) == (SecondValue & SIGN_FLAG_BYTE))
                      && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE));
    State->Flags.Af = ((((FirstValue & 0x0F) + (SecondValue & 0x0F)) & 0x10) != 0);
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Write back the result */
    State->GeneralRegs[FAST486_REG_EAX].LowByte = Result;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAddEax)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x05);

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    if (Size)
    {
        ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long;
        ULONG SecondValue, Result;

        if (!Fast486FetchDword(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue + SecondValue;

        /* Update the flags */
        State->Flags.Cf = (Result < FirstValue) && (Result < SecondValue);
        State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) == (SecondValue & SIGN_FLAG_LONG))
                          && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG));
        State->Flags.Af = ((((FirstValue & 0x0F) + (SecondValue & 0x0F)) & 0x10) != 0);
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        State->GeneralRegs[FAST486_REG_EAX].Long = Result;
    }
    else
    {
        USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord;
        USHORT SecondValue, Result;

        if (!Fast486FetchWord(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue + SecondValue;

        /* Update the flags */
        State->Flags.Cf = (Result < FirstValue) && (Result < SecondValue);
        State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) == (SecondValue & SIGN_FLAG_WORD))
                          && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD));
        State->Flags.Af = ((((FirstValue & 0x0F) + (SecondValue & 0x0F)) & 0x10) != 0);
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        State->GeneralRegs[FAST486_REG_EAX].LowWord = Result;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeOrByteModrm)
{
    UCHAR FirstValue, SecondValue, Result;
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x08);

    TOGGLE_ADSIZE(AddressSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if (!Fast486ReadModrmByteOperands(State,
                                      &ModRegRm,
                                      &FirstValue,
                                      &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    Result = FirstValue | SecondValue;

    /* Update the flags */
    State->Flags.Cf = FALSE;
    State->Flags.Of = FALSE;
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Write back the result */
    Fast486WriteModrmByteOperands(State,
                                  &ModRegRm,
                                  Opcode & FAST486_OPCODE_WRITE_REG,
                                  Result);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeOrModrm)
{
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN OperandSize, AddressSize;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x09);

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_ADSIZE(AddressSize);
    TOGGLE_OPSIZE(OperandSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    /* Check the operand size */
    if (OperandSize)
    {
        ULONG FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmDwordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue | SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        Fast486WriteModrmDwordOperands(State,
                                       &ModRegRm,
                                       Opcode & FAST486_OPCODE_WRITE_REG,
                                       Result);
    }
    else
    {
        USHORT FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmWordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue | SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        Fast486WriteModrmWordOperands(State,
                                       &ModRegRm,
                                       Opcode & FAST486_OPCODE_WRITE_REG,
                                       Result);
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeOrAl)
{
    UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte;
    UCHAR SecondValue, Result;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x0C);

    NO_LOCK_PREFIX();

    if (!Fast486FetchByte(State, &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    Result = FirstValue | SecondValue;

    /* Update the flags */
    State->Flags.Cf = FALSE;
    State->Flags.Of = FALSE;
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Write back the result */
    State->GeneralRegs[FAST486_REG_EAX].LowByte = Result;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeOrEax)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x0D);

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    if (Size)
    {
        ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long;
        ULONG SecondValue, Result;

        if (!Fast486FetchDword(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue | SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        State->GeneralRegs[FAST486_REG_EAX].Long = Result;
    }
    else
    {
        USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord;
        USHORT SecondValue, Result;

        if (!Fast486FetchWord(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue | SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        State->GeneralRegs[FAST486_REG_EAX].LowWord = Result;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAndByteModrm)
{
    UCHAR FirstValue, SecondValue, Result;
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x20);

    TOGGLE_ADSIZE(AddressSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if (!Fast486ReadModrmByteOperands(State,
                                      &ModRegRm,
                                      &FirstValue,
                                      &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    Result = FirstValue & SecondValue;

    /* Update the flags */
    State->Flags.Cf = FALSE;
    State->Flags.Of = FALSE;
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Write back the result */
    Fast486WriteModrmByteOperands(State,
                                  &ModRegRm,
                                  Opcode & FAST486_OPCODE_WRITE_REG,
                                  Result);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAndModrm)
{
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN OperandSize, AddressSize;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x21);

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_ADSIZE(AddressSize);
    TOGGLE_OPSIZE(OperandSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    /* Check the operand size */
    if (OperandSize)
    {
        ULONG FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmDwordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue & SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        Fast486WriteModrmDwordOperands(State,
                                       &ModRegRm,
                                       Opcode & FAST486_OPCODE_WRITE_REG,
                                       Result);
    }
    else
    {
        USHORT FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmWordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue & SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        Fast486WriteModrmWordOperands(State,
                                       &ModRegRm,
                                       Opcode & FAST486_OPCODE_WRITE_REG,
                                       Result);
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAndAl)
{
    UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte;
    UCHAR SecondValue, Result;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x24);

    NO_LOCK_PREFIX();

    if (!Fast486FetchByte(State, &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    Result = FirstValue & SecondValue;

    /* Update the flags */
    State->Flags.Cf = FALSE;
    State->Flags.Of = FALSE;
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Write back the result */
    State->GeneralRegs[FAST486_REG_EAX].LowByte = Result;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAndEax)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x25);

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    if (Size)
    {
        ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long;
        ULONG SecondValue, Result;

        if (!Fast486FetchDword(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue & SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        State->GeneralRegs[FAST486_REG_EAX].Long = Result;
    }
    else
    {
        USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord;
        USHORT SecondValue, Result;

        if (!Fast486FetchWord(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue & SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        State->GeneralRegs[FAST486_REG_EAX].LowWord = Result;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeXorByteModrm)
{
    UCHAR FirstValue, SecondValue, Result;
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x30);

    TOGGLE_ADSIZE(AddressSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if (!Fast486ReadModrmByteOperands(State,
                                      &ModRegRm,
                                      &FirstValue,
                                      &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    Result = FirstValue ^ SecondValue;

    /* Update the flags */
    State->Flags.Cf = FALSE;
    State->Flags.Of = FALSE;
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Write back the result */
    Fast486WriteModrmByteOperands(State,
                                  &ModRegRm,
                                  Opcode & FAST486_OPCODE_WRITE_REG,
                                  Result);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeXorModrm)
{
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN OperandSize, AddressSize;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x31);

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_ADSIZE(AddressSize);
    TOGGLE_OPSIZE(OperandSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    /* Check the operand size */
    if (OperandSize)
    {
        ULONG FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmDwordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue ^ SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        Fast486WriteModrmDwordOperands(State,
                                       &ModRegRm,
                                       Opcode & FAST486_OPCODE_WRITE_REG,
                                       Result);
    }
    else
    {
        USHORT FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmWordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue ^ SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        Fast486WriteModrmWordOperands(State,
                                      &ModRegRm,
                                      Opcode & FAST486_OPCODE_WRITE_REG,
                                      Result);
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeXorAl)
{
    UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte;
    UCHAR SecondValue, Result;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x34);

    NO_LOCK_PREFIX();

    if (!Fast486FetchByte(State, &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    Result = FirstValue ^ SecondValue;

    /* Update the flags */
    State->Flags.Cf = FALSE;
    State->Flags.Of = FALSE;
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Write back the result */
    State->GeneralRegs[FAST486_REG_EAX].LowByte = Result;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeXorEax)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x35);

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    if (Size)
    {
        ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long;
        ULONG SecondValue, Result;

        if (!Fast486FetchDword(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue ^ SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        State->GeneralRegs[FAST486_REG_EAX].Long = Result;
    }
    else
    {
        USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord;
        USHORT SecondValue, Result;

        if (!Fast486FetchWord(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue ^ SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        State->GeneralRegs[FAST486_REG_EAX].LowWord = Result;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeTestByteModrm)
{
    UCHAR FirstValue, SecondValue, Result;
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x84);

    TOGGLE_ADSIZE(AddressSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if (!Fast486ReadModrmByteOperands(State,
                                      &ModRegRm,
                                      &FirstValue,
                                      &SecondValue))
    {
        /* Exception occurred */
        return;
    }
    /* Calculate the result */
    Result = FirstValue & SecondValue;

    /* Update the flags */
    State->Flags.Cf = FALSE;
    State->Flags.Of = FALSE;
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeTestModrm)
{
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN OperandSize, AddressSize;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x85);

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_ADSIZE(AddressSize);
    TOGGLE_OPSIZE(OperandSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    /* Check the operand size */
    if (OperandSize)
    {
        ULONG FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmDwordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue & SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);
    }
    else
    {
        USHORT FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmWordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue & SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeTestAl)
{
    UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte;
    UCHAR SecondValue, Result;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xA8);

    NO_LOCK_PREFIX();

    if (!Fast486FetchByte(State, &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    Result = FirstValue & SecondValue;

    /* Update the flags */
    State->Flags.Cf = FALSE;
    State->Flags.Of = FALSE;
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeTestEax)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xA9);

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    if (Size)
    {
        ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long;
        ULONG SecondValue, Result;

        if (!Fast486FetchDword(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue & SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);
    }
    else
    {
        USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord;
        USHORT SecondValue, Result;

        if (!Fast486FetchWord(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue & SecondValue;

        /* Update the flags */
        State->Flags.Cf = FALSE;
        State->Flags.Of = FALSE;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeXchgByteModrm)
{
    UCHAR FirstValue, SecondValue;
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x86);

    TOGGLE_ADSIZE(AddressSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if (!Fast486ReadModrmByteOperands(State,
                                      &ModRegRm,
                                      &FirstValue,
                                      &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Write the value from the register to the R/M */
    if (!Fast486WriteModrmByteOperands(State,
                                       &ModRegRm,
                                       FALSE,
                                       FirstValue))
    {
        /* Exception occurred */
        return;
    }

    /* Write the value from the R/M to the register */
    Fast486WriteModrmByteOperands(State,
                                  &ModRegRm,
                                  TRUE,
                                  SecondValue);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeXchgModrm)
{
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN OperandSize, AddressSize;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x87);

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_ADSIZE(AddressSize);
    TOGGLE_OPSIZE(OperandSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    /* Check the operand size */
    if (OperandSize)
    {
        ULONG FirstValue, SecondValue;

        if (!Fast486ReadModrmDwordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Write the value from the register to the R/M */
        if (!Fast486WriteModrmDwordOperands(State,
                                            &ModRegRm,
                                            FALSE,
                                            FirstValue))
        {
            /* Exception occurred */
            return;
        }

        /* Write the value from the R/M to the register */
        Fast486WriteModrmDwordOperands(State,
                                       &ModRegRm,
                                       TRUE,
                                       SecondValue);
    }
    else
    {
        USHORT FirstValue, SecondValue;

        if (!Fast486ReadModrmWordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Write the value from the register to the R/M */
        if (!Fast486WriteModrmWordOperands(State,
                                           &ModRegRm,
                                           FALSE,
                                           FirstValue))
        {
            /* Exception occurred */
            return;
        }

        /* Write the value from the R/M to the register */
        Fast486WriteModrmWordOperands(State,
                                      &ModRegRm,
                                      TRUE,
                                      SecondValue);
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodePushEs)
{
    /* Call the internal API */
    Fast486StackPush(State, State->SegmentRegs[FAST486_REG_ES].Selector);
}

FAST486_OPCODE_HANDLER(Fast486OpcodePopEs)
{
    ULONG NewSelector;

    if (!Fast486StackPop(State, &NewSelector))
    {
        /* Exception occurred */
        return;
    }

    /* Call the internal API */
    Fast486LoadSegment(State, FAST486_REG_ES, LOWORD(NewSelector));
}

FAST486_OPCODE_HANDLER(Fast486OpcodePushCs)
{
    /* Call the internal API */
    Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAdcByteModrm)
{
    UCHAR FirstValue, SecondValue, Result;
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x10);

    TOGGLE_ADSIZE(AddressSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if (!Fast486ReadModrmByteOperands(State,
                                      &ModRegRm,
                                      &FirstValue,
                                      &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    Result = FirstValue + SecondValue + State->Flags.Cf;

    /* Special exception for CF */
    State->Flags.Cf = State->Flags.Cf
                      && ((FirstValue == 0xFF) || (SecondValue == 0xFF));

    /* Update the flags */
    State->Flags.Cf = State->Flags.Cf || ((Result < FirstValue) && (Result < SecondValue));
    State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) == (SecondValue & SIGN_FLAG_BYTE))
                      && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE));
    State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0;
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Write back the result */
    Fast486WriteModrmByteOperands(State,
                                  &ModRegRm,
                                  Opcode & FAST486_OPCODE_WRITE_REG,
                                  Result);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAdcModrm)
{
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN OperandSize, AddressSize;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x11);

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_ADSIZE(AddressSize);
    TOGGLE_OPSIZE(OperandSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    /* Check the operand size */
    if (OperandSize)
    {
        ULONG FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmDwordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue + SecondValue + State->Flags.Cf;

        /* Special exception for CF */
        State->Flags.Cf = State->Flags.Cf
                          && ((FirstValue == 0xFFFFFFFF) || (SecondValue == 0xFFFFFFFF));

        /* Update the flags */
        State->Flags.Cf = State->Flags.Cf || ((Result < FirstValue) && (Result < SecondValue));
        State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) == (SecondValue & SIGN_FLAG_LONG))
                          && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG));
        State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        Fast486WriteModrmDwordOperands(State,
                                       &ModRegRm,
                                       Opcode & FAST486_OPCODE_WRITE_REG,
                                       Result);
    }
    else
    {
        USHORT FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmWordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue + SecondValue + State->Flags.Cf;

        /* Special exception for CF */
        State->Flags.Cf = State->Flags.Cf
                          && ((FirstValue == 0xFFFF) || (SecondValue == 0xFFFF));

        /* Update the flags */
        State->Flags.Cf = State->Flags.Cf || ((Result < FirstValue) && (Result < SecondValue));
        State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) == (SecondValue & SIGN_FLAG_WORD))
                          && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD));
        State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        Fast486WriteModrmWordOperands(State,
                                       &ModRegRm,
                                       Opcode & FAST486_OPCODE_WRITE_REG,
                                       Result);
    }

}

FAST486_OPCODE_HANDLER(Fast486OpcodeAdcAl)
{
    UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte;
    UCHAR SecondValue, Result;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x14);

    NO_LOCK_PREFIX();

    if (!Fast486FetchByte(State, &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    Result = FirstValue + SecondValue + State->Flags.Cf;

    /* Special exception for CF */
    State->Flags.Cf = State->Flags.Cf &&
                      ((FirstValue == 0xFF) || (SecondValue == 0xFF));

    /* Update the flags */
    State->Flags.Cf = State->Flags.Cf || ((Result < FirstValue) && (Result < SecondValue));
    State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) == (SecondValue & SIGN_FLAG_BYTE))
                      && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE));
    State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0;
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Write back the result */
    State->GeneralRegs[FAST486_REG_EAX].LowByte = Result;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAdcEax)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x15);

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    if (Size)
    {
        ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long;
        ULONG SecondValue, Result;

        if (!Fast486FetchDword(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue + SecondValue + State->Flags.Cf;

        /* Special exception for CF */
        State->Flags.Cf = State->Flags.Cf &&
                          ((FirstValue == 0xFFFFFFFF) || (SecondValue == 0xFFFFFFFF));

        /* Update the flags */
        State->Flags.Cf = State->Flags.Cf || ((Result < FirstValue) && (Result < SecondValue));
        State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) == (SecondValue & SIGN_FLAG_LONG))
                          && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG));
        State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        State->GeneralRegs[FAST486_REG_EAX].Long = Result;
    }
    else
    {
        USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord;
        USHORT SecondValue, Result;

        if (!Fast486FetchWord(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue + SecondValue + State->Flags.Cf;

        /* Special exception for CF */
        State->Flags.Cf = State->Flags.Cf &&
                          ((FirstValue == 0xFFFF) || (SecondValue == 0xFFFF));

        /* Update the flags */
        State->Flags.Cf = State->Flags.Cf || ((Result < FirstValue) && (Result < SecondValue));
        State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) == (SecondValue & SIGN_FLAG_WORD))
                          && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD));
        State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        State->GeneralRegs[FAST486_REG_EAX].LowWord = Result;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodePushSs)
{
    /* Call the internal API */
    Fast486StackPush(State, State->SegmentRegs[FAST486_REG_SS].Selector);
}

FAST486_OPCODE_HANDLER(Fast486OpcodePopSs)
{
    ULONG NewSelector;

    if (!Fast486StackPop(State, &NewSelector))
    {
        /* Exception occurred */
        return;
    }

    /* Call the internal API */
    if (Fast486LoadSegment(State, FAST486_REG_SS, LOWORD(NewSelector)))
    {
        /* Inhibit all interrupts until the next instruction */
        State->DoNotInterrupt = TRUE;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeSbbByteModrm)
{
    UCHAR FirstValue, SecondValue, Result;
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
    INT Carry = State->Flags.Cf ? 1 : 0;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x18);

    TOGGLE_ADSIZE(AddressSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if (!Fast486ReadModrmByteOperands(State,
                                      &ModRegRm,
                                      &FirstValue,
                                      &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Check if this is the instruction that writes to R/M */
    if (!(Opcode & FAST486_OPCODE_WRITE_REG))
    {
        /* Swap the order */
        SWAP(FirstValue, SecondValue);
    }

    /* Calculate the result */
    Result = FirstValue - SecondValue - Carry;

    /* Update the flags */
    State->Flags.Cf = Carry ? (FirstValue <= SecondValue) : (FirstValue < SecondValue);
    State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) != (SecondValue & SIGN_FLAG_BYTE))
                      && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE));
    State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0;
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Write back the result */
    Fast486WriteModrmByteOperands(State,
                                  &ModRegRm,
                                  Opcode & FAST486_OPCODE_WRITE_REG,
                                  Result);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeSbbModrm)
{
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN OperandSize, AddressSize;
    INT Carry = State->Flags.Cf ? 1 : 0;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x19);

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_ADSIZE(AddressSize);
    TOGGLE_OPSIZE(OperandSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    /* Check the operand size */
    if (OperandSize)
    {
        ULONG FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmDwordOperands(State,
                                           &ModRegRm,
                                           &FirstValue,
                                           &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Check if this is the instruction that writes to R/M */
        if (!(Opcode & FAST486_OPCODE_WRITE_REG))
        {
            /* Swap the order */
            SWAP(FirstValue, SecondValue);
        }

        /* Calculate the result */
        Result = FirstValue - SecondValue - Carry;

        /* Update the flags */
        State->Flags.Cf = Carry ? (FirstValue <= SecondValue) : (FirstValue < SecondValue);
        State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) != (SecondValue & SIGN_FLAG_LONG))
                          && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG));
        State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        Fast486WriteModrmDwordOperands(State,
                                       &ModRegRm,
                                       Opcode & FAST486_OPCODE_WRITE_REG,
                                       Result);
    }
    else
    {
        USHORT FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmWordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Check if this is the instruction that writes to R/M */
        if (!(Opcode & FAST486_OPCODE_WRITE_REG))
        {
            /* Swap the order */
            SWAP(FirstValue, SecondValue);
        }

        /* Calculate the result */
        Result = FirstValue - SecondValue - Carry;

        /* Update the flags */
        State->Flags.Cf = Carry ? (FirstValue <= SecondValue) : (FirstValue < SecondValue);
        State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) != (SecondValue & SIGN_FLAG_WORD))
                          && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD));
        State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        Fast486WriteModrmWordOperands(State,
                                      &ModRegRm,
                                      Opcode & FAST486_OPCODE_WRITE_REG,
                                      Result);
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeSbbAl)
{
    UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte;
    UCHAR SecondValue, Result;
    INT Carry = State->Flags.Cf ? 1 : 0;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x1C);

    NO_LOCK_PREFIX();

    if (!Fast486FetchByte(State, &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    Result = FirstValue - SecondValue - Carry;

    /* Update the flags */
    State->Flags.Cf = Carry ? (FirstValue <= SecondValue) : (FirstValue < SecondValue);
    State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) != (SecondValue & SIGN_FLAG_BYTE))
                      && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE));
    State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0;
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Write back the result */
    State->GeneralRegs[FAST486_REG_EAX].LowByte = Result;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeSbbEax)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
    INT Carry = State->Flags.Cf ? 1 : 0;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x1D);

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    if (Size)
    {
        ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long;
        ULONG SecondValue, Result;

        if (!Fast486FetchDword(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue - SecondValue - Carry;

        /* Update the flags */
        State->Flags.Cf = Carry ? (FirstValue <= SecondValue) : (FirstValue < SecondValue);
        State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) != (SecondValue & SIGN_FLAG_LONG))
                          && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG));
        State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        State->GeneralRegs[FAST486_REG_EAX].Long = Result;
    }
    else
    {
        USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord;
        USHORT SecondValue, Result;

        if (!Fast486FetchWord(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue - SecondValue - Carry;

        /* Update the flags */
        State->Flags.Cf = Carry ? (FirstValue <= SecondValue) : (FirstValue < SecondValue);
        State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) != (SecondValue & SIGN_FLAG_WORD))
                          && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD));
        State->Flags.Af = ((FirstValue ^ SecondValue ^ Result) & 0x10) != 0;
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Write back the result */
        State->GeneralRegs[FAST486_REG_EAX].LowWord = Result;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodePushDs)
{
    /* Call the internal API */
    Fast486StackPush(State, State->SegmentRegs[FAST486_REG_DS].Selector);
}

FAST486_OPCODE_HANDLER(Fast486OpcodePopDs)
{
    ULONG NewSelector;

    if (!Fast486StackPop(State, &NewSelector))
    {
        /* Exception occurred */
        return;
    }

    /* Call the internal API */
    Fast486LoadSegment(State, FAST486_REG_DS, LOWORD(NewSelector));
}

FAST486_OPCODE_HANDLER(Fast486OpcodeDaa)
{
    UCHAR Value = State->GeneralRegs[FAST486_REG_EAX].LowByte;
    BOOLEAN Carry = State->Flags.Cf;

    /* Clear the carry flag */
    State->Flags.Cf = FALSE;

    /* Check if the first BCD digit is invalid or there was a carry from it */
    if (((Value & 0x0F) > 9) || State->Flags.Af)
    {
        /* Correct it */
        State->GeneralRegs[FAST486_REG_EAX].LowByte += 0x06;
        if (State->GeneralRegs[FAST486_REG_EAX].LowByte < 0x06)
        {
            /* A carry occurred */
            State->Flags.Cf = TRUE;
        }

        /* Set the adjust flag */
        State->Flags.Af = TRUE;
    }

    /* Check if the second BCD digit is invalid or there was a carry from it */
    if ((Value > 0x99) || Carry)
    {
        /* Correct it */
        State->GeneralRegs[FAST486_REG_EAX].LowByte += 0x60;

        /* There was a carry */
        State->Flags.Cf = TRUE;
    }

    Value = State->GeneralRegs[FAST486_REG_EAX].LowByte;

    /* Update the flags */
    State->Flags.Sf = (Value & SIGN_FLAG_BYTE) != 0;
    State->Flags.Zf = (Value == 0);
    State->Flags.Pf = Fast486CalculateParity(Value);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeCmpSubByteModrm)
{
    UCHAR FirstValue, SecondValue, Result;
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xED) == 0x28);

    TOGGLE_ADSIZE(AddressSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if (!Fast486ReadModrmByteOperands(State,
                                      &ModRegRm,
                                      &FirstValue,
                                      &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Check if this is the instruction that writes to R/M */
    if (!(Opcode & FAST486_OPCODE_WRITE_REG))
    {
        /* Swap the order */
        SWAP(FirstValue, SecondValue);
    }

    /* Calculate the result */
    Result = FirstValue - SecondValue;

    /* Update the flags */
    State->Flags.Cf = (FirstValue < SecondValue);
    State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) != (SecondValue & SIGN_FLAG_BYTE))
                      && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE));
    State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F);
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Check if this is not a CMP */
    if (!(Opcode & 0x10))
    {
        /* Write back the result */
        Fast486WriteModrmByteOperands(State,
                                      &ModRegRm,
                                      Opcode & FAST486_OPCODE_WRITE_REG,
                                      Result);
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeCmpSubModrm)
{
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN OperandSize, AddressSize;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xED) == 0x29);

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_ADSIZE(AddressSize);
    TOGGLE_OPSIZE(OperandSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    /* Check the operand size */
    if (OperandSize)
    {
        ULONG FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmDwordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Check if this is the instruction that writes to R/M */
        if (!(Opcode & FAST486_OPCODE_WRITE_REG))
        {
            /* Swap the order */
            SWAP(FirstValue, SecondValue);
        }

        /* Calculate the result */
        Result = FirstValue - SecondValue;

        /* Update the flags */
        State->Flags.Cf = (FirstValue < SecondValue);
        State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) != (SecondValue & SIGN_FLAG_LONG))
                          && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG));
        State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F);
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Check if this is not a CMP */
        if (!(Opcode & 0x10))
        {
            /* Write back the result */
            Fast486WriteModrmDwordOperands(State,
                                           &ModRegRm,
                                           Opcode & FAST486_OPCODE_WRITE_REG,
                                           Result);
        }
    }
    else
    {
        USHORT FirstValue, SecondValue, Result;

        if (!Fast486ReadModrmWordOperands(State,
                                          &ModRegRm,
                                          &FirstValue,
                                          &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Check if this is the instruction that writes to R/M */
        if (!(Opcode & FAST486_OPCODE_WRITE_REG))
        {
            /* Swap the order */
            SWAP(FirstValue, SecondValue);
        }

        /* Calculate the result */
        Result = FirstValue - SecondValue;

        /* Update the flags */
        State->Flags.Cf = (FirstValue < SecondValue);
        State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) != (SecondValue & SIGN_FLAG_WORD))
                          && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD));
        State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F);
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Check if this is not a CMP */
        if (!(Opcode & 0x10))
        {
            /* Write back the result */
            Fast486WriteModrmWordOperands(State,
                                          &ModRegRm,
                                          Opcode & FAST486_OPCODE_WRITE_REG,
                                          Result);
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeCmpSubAl)
{
    UCHAR FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowByte;
    UCHAR SecondValue, Result;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xEF) == 0x2C);

    NO_LOCK_PREFIX();

    if (!Fast486FetchByte(State, &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    Result = FirstValue - SecondValue;

    /* Update the flags */
    State->Flags.Cf = (FirstValue < SecondValue);
    State->Flags.Of = ((FirstValue & SIGN_FLAG_BYTE) != (SecondValue & SIGN_FLAG_BYTE))
                      && ((FirstValue & SIGN_FLAG_BYTE) != (Result & SIGN_FLAG_BYTE));
    State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F);
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Check if this is not a CMP */
    if (!(Opcode & 0x10))
    {
        /* Write back the result */
        State->GeneralRegs[FAST486_REG_EAX].LowByte = Result;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeCmpSubEax)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xEF) == 0x2D);

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    if (Size)
    {
        ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long;
        ULONG SecondValue, Result;

        if (!Fast486FetchDword(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue - SecondValue;

        /* Update the flags */
        State->Flags.Cf = (FirstValue < SecondValue);
        State->Flags.Of = ((FirstValue & SIGN_FLAG_LONG) != (SecondValue & SIGN_FLAG_LONG))
                          && ((FirstValue & SIGN_FLAG_LONG) != (Result & SIGN_FLAG_LONG));
        State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F);
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_LONG) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Check if this is not a CMP */
        if (!(Opcode & 0x10))
        {
            /* Write back the result */
            State->GeneralRegs[FAST486_REG_EAX].Long = Result;
        }
    }
    else
    {
        USHORT FirstValue = State->GeneralRegs[FAST486_REG_EAX].LowWord;
        USHORT SecondValue, Result;

        if (!Fast486FetchWord(State, &SecondValue))
        {
            /* Exception occurred */
            return;
        }

        /* Calculate the result */
        Result = FirstValue - SecondValue;

        /* Update the flags */
        State->Flags.Cf = (FirstValue < SecondValue);
        State->Flags.Of = ((FirstValue & SIGN_FLAG_WORD) != (SecondValue & SIGN_FLAG_WORD))
                          && ((FirstValue & SIGN_FLAG_WORD) != (Result & SIGN_FLAG_WORD));
        State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F);
        State->Flags.Zf = (Result == 0);
        State->Flags.Sf = ((Result & SIGN_FLAG_WORD) != 0);
        State->Flags.Pf = Fast486CalculateParity(Result);

        /* Check if this is not a CMP */
        if (!(Opcode & 0x10))
        {
            /* Write back the result */
            State->GeneralRegs[FAST486_REG_EAX].LowWord = Result;
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeDas)
{
    UCHAR Value = State->GeneralRegs[FAST486_REG_EAX].LowByte;
    BOOLEAN Carry = State->Flags.Cf;

    /* Clear the carry flag */
    State->Flags.Cf = FALSE;

    /* Check if the first BCD digit is invalid or there was a borrow */
    if (((Value & 0x0F) > 9) || State->Flags.Af)
    {
        /* Correct it */
        State->GeneralRegs[FAST486_REG_EAX].LowByte -= 0x06;
        if (State->GeneralRegs[FAST486_REG_EAX].LowByte > 0xFB)
        {
            /* A borrow occurred */
            State->Flags.Cf = TRUE;
        }

        /* Set the adjust flag */
        State->Flags.Af = TRUE;
    }

    /* Check if the second BCD digit is invalid or there was a borrow */
    if ((Value > 0x99) || Carry)
    {
        /* Correct it */
        State->GeneralRegs[FAST486_REG_EAX].LowByte -= 0x60;

        /* There was a borrow */
        State->Flags.Cf = TRUE;
    }

    Value = State->GeneralRegs[FAST486_REG_EAX].LowByte;

    /* Update the flags */
    State->Flags.Sf = (Value & SIGN_FLAG_BYTE) != 0;
    State->Flags.Zf = (Value == 0);
    State->Flags.Pf = Fast486CalculateParity(Value);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAaa)
{
    UCHAR Value = State->GeneralRegs[FAST486_REG_EAX].LowByte;

    /*
     * Check if the value in AL is not a valid BCD digit,
     * or there was a carry from the lowest 4 bits of AL
     */
    if (((Value & 0x0F) > 9) || State->Flags.Af)
    {
        /* Correct it */
        State->GeneralRegs[FAST486_REG_EAX].LowWord += 0x06;
        State->GeneralRegs[FAST486_REG_EAX].HighByte++;

        /* Set CF and AF */
        State->Flags.Cf = State->Flags.Af = TRUE;
    }
    else
    {
        /* Clear CF and AF */
        State->Flags.Cf = State->Flags.Af = FALSE;
    }

    /* Keep only the lowest 4 bits of AL */
    State->GeneralRegs[FAST486_REG_EAX].LowByte &= 0x0F;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAas)
{
    UCHAR Value = State->GeneralRegs[FAST486_REG_EAX].LowByte;

    /*
     * Check if the value in AL is not a valid BCD digit,
     * or there was a borrow from the lowest 4 bits of AL
     */
    if (((Value & 0x0F) > 9) || State->Flags.Af)
    {
        /* Correct it */
        State->GeneralRegs[FAST486_REG_EAX].LowWord -= 0x06;
        State->GeneralRegs[FAST486_REG_EAX].HighByte--;

        /* Set CF and AF */
        State->Flags.Cf = State->Flags.Af = TRUE;
    }
    else
    {
        /* Clear CF and AF */
        State->Flags.Cf = State->Flags.Af = FALSE;
    }

    /* Keep only the lowest 4 bits of AL */
    State->GeneralRegs[FAST486_REG_EAX].LowByte &= 0x0F;
}

FAST486_OPCODE_HANDLER(Fast486OpcodePushAll)
{
    INT i;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
    FAST486_REG SavedEsp = State->GeneralRegs[FAST486_REG_ESP];

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x60);

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    /* Push all the registers in order */
    for (i = 0; i < FAST486_NUM_GEN_REGS; i++)
    {
        if (i == FAST486_REG_ESP)
        {
            /* Use the saved ESP instead */
            if (!Fast486StackPush(State, Size ? SavedEsp.Long : SavedEsp.LowWord))
            {
                /* Exception occurred */
                return;
            }
        }
        else
        {
            /* Push the register */
            if (!Fast486StackPush(State, Size ? State->GeneralRegs[i].Long
                                              : State->GeneralRegs[i].LowWord))
            {
                /* Exception occurred */
                return;
            }
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodePopAll)
{
    INT i;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
    ULONG Value;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x61);

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    /* Pop all the registers in reverse order */
    for (i = FAST486_NUM_GEN_REGS - 1; i >= 0; i--)
    {
        /* Pop the value */
        if (!Fast486StackPop(State, &Value))
        {
            /* Exception occurred */
            return;
        }

        /* Don't modify ESP */
        if (i != FAST486_REG_ESP)
        {
            if (Size) State->GeneralRegs[i].Long = Value;
            else State->GeneralRegs[i].LowWord = LOWORD(Value);
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeBound)
{
    BOOLEAN OperandSize, AddressSize;
    FAST486_MOD_REG_RM ModRegRm;
    FAST486_SEG_REGS Segment = FAST486_REG_DS;

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(OperandSize);
    TOGGLE_ADSIZE(AddressSize);

    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if (!ModRegRm.Memory)
    {
        /* Invalid */
        Fast486Exception(State, FAST486_EXCEPTION_UD);
        return;
    }

    /* Check for the segment override */
    if (State->PrefixFlags & FAST486_PREFIX_SEG)
    {
        /* Use the override segment instead */
        Segment = State->SegmentOverride;
    }

    if (OperandSize)
    {
        LONG Index, LowerBound, UpperBound;

        /* Read the operands */
        if (!Fast486ReadModrmDwordOperands(State,
                                           &ModRegRm,
                                           (PULONG)&Index,
                                           (PULONG)&LowerBound))
        {
            /* Exception occurred */
            return;
        }

        if (!Fast486ReadMemory(State,
                               Segment,
                               ModRegRm.MemoryAddress + sizeof(ULONG),
                               FALSE,
                               &UpperBound,
                               sizeof(ULONG)))
        {
            /* Exception occurred */
            return;
        }

        if ((Index < LowerBound) || (Index > UpperBound))
        {
            /* Out of bounds */
            Fast486Exception(State, FAST486_EXCEPTION_BR);
        }
    }
    else
    {
        SHORT Index, LowerBound, UpperBound;

        /* Read the operands */
        if (!Fast486ReadModrmWordOperands(State,
                                          &ModRegRm,
                                          (PUSHORT)&Index,
                                          (PUSHORT)&LowerBound))
        {
            /* Exception occurred */
            return;
        }

        if (!Fast486ReadMemory(State,
                               Segment,
                               ModRegRm.MemoryAddress + sizeof(USHORT),
                               FALSE,
                               &UpperBound,
                               sizeof(USHORT)))
        {
            /* Exception occurred */
            return;
        }

        if ((Index < LowerBound) || (Index > UpperBound))
        {
            /* Out of bounds */
            Fast486Exception(State, FAST486_EXCEPTION_BR);
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeArpl)
{
    USHORT FirstValue, SecondValue;
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    if (!(State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
        || State->Flags.Vm
        || (State->PrefixFlags & FAST486_PREFIX_LOCK))
    {
        /* Cannot be used in real mode or with a LOCK prefix */
        Fast486Exception(State, FAST486_EXCEPTION_UD);
        return;
    }

    TOGGLE_ADSIZE(AddressSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    /* Read the operands */
    if (!Fast486ReadModrmWordOperands(State,
                                      &ModRegRm,
                                      &FirstValue,
                                      &SecondValue))
    {
        /* Exception occurred */
        return;
    }

    /* Check if the RPL needs adjusting */
    if ((SecondValue & 3) < (FirstValue & 3))
    {
        /* Adjust the RPL */
        SecondValue &= ~3;
        SecondValue |= FirstValue & 3;

        /* Set ZF */
        State->Flags.Zf = TRUE;

        /* Write back the result */
        Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, SecondValue);
    }
    else
    {
        /* Clear ZF */
        State->Flags.Zf = FALSE;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodePushImm)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x68);

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    if (Size)
    {
        ULONG Data;

        if (!Fast486FetchDword(State, &Data))
        {
            /* Exception occurred */
            return;
        }

        /* Call the internal API */
        Fast486StackPush(State, Data);
    }
    else
    {
        SHORT Data;

        if (!Fast486FetchWord(State, (PUSHORT)&Data))
        {
            /* Exception occurred */
            return;
        }

        /* Call the internal API */
        Fast486StackPush(State, Data);
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeImulModrmImm)
{
    BOOLEAN OperandSize, AddressSize;
    FAST486_MOD_REG_RM ModRegRm;
    LONG Multiplier;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x69);

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_ADSIZE(AddressSize);
    TOGGLE_OPSIZE(OperandSize);

    /* Fetch the parameters */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if (Opcode == 0x6B)
    {
        CHAR Byte;

        /* Fetch the immediate operand */
        if (!Fast486FetchByte(State, (PUCHAR)&Byte))
        {
            /* Exception occurred */
            return;
        }

        Multiplier = (LONG)Byte;
    }
    else
    {
        if (OperandSize)
        {
            LONG Dword;

            /* Fetch the immediate operand */
            if (!Fast486FetchDword(State, (PULONG)&Dword))
            {
                /* Exception occurred */
                return;
            }

            Multiplier = Dword;
        }
        else
        {
            SHORT Word;

            /* Fetch the immediate operand */
            if (!Fast486FetchWord(State, (PUSHORT)&Word))
            {
                /* Exception occurred */
                return;
            }

            Multiplier = (LONG)Word;
        }
    }

    if (OperandSize)
    {
        LONG RegValue, Multiplicand;
        LONGLONG Product;

        /* Read the operands */
        if (!Fast486ReadModrmDwordOperands(State,
                                           &ModRegRm,
                                           (PULONG)&RegValue,
                                           (PULONG)&Multiplicand))
        {
            /* Exception occurred */
            return;
        }

        /* Multiply */
        Product = (LONGLONG)Multiplicand * (LONGLONG)Multiplier;

        /* Check for carry/overflow */
        State->Flags.Cf = State->Flags.Of = ((Product < FAST486_LONG_MIN)
                                            || (Product > FAST486_LONG_MAX));

        /* Write-back the result */
        Fast486WriteModrmDwordOperands(State,
                                       &ModRegRm,
                                       TRUE,
                                       (ULONG)((LONG)Product));
    }
    else
    {
        SHORT RegValue, Multiplicand;
        LONG Product;

        /* Read the operands */
        if (!Fast486ReadModrmWordOperands(State,
                                          &ModRegRm,
                                          (PUSHORT)&RegValue,
                                          (PUSHORT)&Multiplicand))
        {
            /* Exception occurred */
            return;
        }

        /* Multiply */
        Product = (LONG)Multiplicand * (LONG)Multiplier;

        /* Check for carry/overflow */
        State->Flags.Cf = State->Flags.Of = ((Product < FAST486_SHORT_MIN)
                                            || (Product > FAST486_SHORT_MAX));

        /* Write-back the result */
        Fast486WriteModrmWordOperands(State,
                                      &ModRegRm,
                                      TRUE,
                                      (USHORT)((SHORT)Product));
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodePushByteImm)
{
    CHAR Data;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x6A);

    if (!Fast486FetchByte(State, (PUCHAR)&Data))
    {
        /* Exception occurred */
        return;
    }

    /* Call the internal API */
    Fast486StackPush(State, Data);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeMovByteModrm)
{
    UCHAR Result;
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x88);

    TOGGLE_ADSIZE(AddressSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if (Opcode & FAST486_OPCODE_WRITE_REG)
    {
        if (!Fast486ReadModrmByteOperands(State, &ModRegRm, NULL, &Result))
        {
            /* Exception occurred */
            return;
        }
    }
    else
    {
        if (!Fast486ReadModrmByteOperands(State, &ModRegRm, &Result, NULL))
        {
            /* Exception occurred */
            return;
        }
    }

    /* Write back the result */
    Fast486WriteModrmByteOperands(State,
                                  &ModRegRm,
                                  Opcode & FAST486_OPCODE_WRITE_REG,
                                  Result);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeMovModrm)
{
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN OperandSize, AddressSize;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFD) == 0x89);

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_ADSIZE(AddressSize);
    TOGGLE_OPSIZE(OperandSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    /* Check the operand size */
    if (OperandSize)
    {
        ULONG Result;



        if (Opcode & FAST486_OPCODE_WRITE_REG)
        {
            if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Result))
            {
                /* Exception occurred */
                return;
            }
        }
        else
        {
            if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, &Result, NULL))
            {
                /* Exception occurred */
                return;
            }
        }

        /* Write back the result */
        Fast486WriteModrmDwordOperands(State,
                                       &ModRegRm,
                                       Opcode & FAST486_OPCODE_WRITE_REG,
                                       Result);
    }
    else
    {
        USHORT Result;

        if (Opcode & FAST486_OPCODE_WRITE_REG)
        {
            if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Result))
            {
                /* Exception occurred */
                return;
            }
        }
        else
        {
            if (!Fast486ReadModrmWordOperands(State, &ModRegRm, &Result, NULL))
            {
                /* Exception occurred */
                return;
            }
        }

        /* Write back the result */
        Fast486WriteModrmWordOperands(State,
                                      &ModRegRm,
                                      Opcode & FAST486_OPCODE_WRITE_REG,
                                      Result);
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeMovStoreSeg)
{
    BOOLEAN OperandSize, AddressSize;
    FAST486_MOD_REG_RM ModRegRm;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x8C);

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_ADSIZE(AddressSize);
    TOGGLE_OPSIZE(OperandSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if (ModRegRm.Register >= FAST486_NUM_SEG_REGS)
    {
        /* Invalid */
        Fast486Exception(State, FAST486_EXCEPTION_UD);
        return;
    }

    /* When the other operand is a memory location, always use 16-bit */
    if (OperandSize && !ModRegRm.Memory)
    {
        Fast486WriteModrmDwordOperands(State,
                                       &ModRegRm,
                                       FALSE,
                                       State->SegmentRegs[ModRegRm.Register].Selector);
    }
    else
    {
        Fast486WriteModrmWordOperands(State,
                                      &ModRegRm,
                                      FALSE,
                                      State->SegmentRegs[ModRegRm.Register].Selector);
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeLea)
{
    FAST486_MOD_REG_RM ModRegRm;
    BOOLEAN OperandSize, AddressSize;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x8D);

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_ADSIZE(AddressSize);
    TOGGLE_OPSIZE(OperandSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    /* The second operand must be memory */
    if (!ModRegRm.Memory)
    {
        /* Invalid */
        Fast486Exception(State, FAST486_EXCEPTION_UD);
        return;
    }

    /* Write the address to the register */
    if (OperandSize)
    {
        Fast486WriteModrmDwordOperands(State,
                                       &ModRegRm,
                                       TRUE,
                                       ModRegRm.MemoryAddress);
    }
    else
    {
        Fast486WriteModrmWordOperands(State,
                                      &ModRegRm,
                                      TRUE,
                                      ModRegRm.MemoryAddress);

    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeMovLoadSeg)
{
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
    FAST486_MOD_REG_RM ModRegRm;
    USHORT Selector;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x8E);

    TOGGLE_ADSIZE(AddressSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if ((ModRegRm.Register >= FAST486_NUM_SEG_REGS)
        || ((FAST486_SEG_REGS)ModRegRm.Register == FAST486_REG_CS))
    {
        /* Invalid */
        Fast486Exception(State, FAST486_EXCEPTION_UD);
        return;
    }

    if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &Selector))
    {
        /* Exception occurred */
        return;
    }

    if (!Fast486LoadSegment(State, ModRegRm.Register, Selector))
    {
        /* Exception occurred */
        return;
    }

    if ((INT)ModRegRm.Register == FAST486_REG_SS)
    {
        /* Inhibit all interrupts until the next instruction */
        State->DoNotInterrupt = TRUE;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeCwde)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x98);

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    if (Size)
    {
        /* Sign extend AX to EAX */
        State->GeneralRegs[FAST486_REG_EAX].Long = MAKELONG
        (
            State->GeneralRegs[FAST486_REG_EAX].LowWord,
            (State->GeneralRegs[FAST486_REG_EAX].LowWord & SIGN_FLAG_WORD)
            ? 0xFFFF : 0x0000
        );
    }
    else
    {
        /* Sign extend AL to AX */
        State->GeneralRegs[FAST486_REG_EAX].HighByte =
        (State->GeneralRegs[FAST486_REG_EAX].LowByte & SIGN_FLAG_BYTE)
        ? 0xFF : 0x00;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeCdq)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x99);

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    if (Size)
    {
        /* Sign extend EAX to EDX:EAX */
        State->GeneralRegs[FAST486_REG_EDX].Long =
        (State->GeneralRegs[FAST486_REG_EAX].Long & SIGN_FLAG_LONG)
        ? 0xFFFFFFFF : 0x00000000;
    }
    else
    {
        /* Sign extend AX to DX:AX */
        State->GeneralRegs[FAST486_REG_EDX].LowWord =
        (State->GeneralRegs[FAST486_REG_EAX].LowWord & SIGN_FLAG_WORD)
        ? 0xFFFF : 0x0000;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeCallAbs)
{
    USHORT Segment = 0;
    ULONG Offset = 0;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x9A);

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    /* Fetch the offset */
    if (Size)
    {
        if (!Fast486FetchDword(State, &Offset))
        {
            /* Exception occurred */
            return;
        }
    }
    else
    {
        if (!Fast486FetchWord(State, (PUSHORT)&Offset))
        {
            /* Exception occurred */
            return;
        }
    }

    /* Fetch the segment */
    if (!Fast486FetchWord(State, &Segment))
    {
        /* Exception occurred */
        return;
    }

    if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) && !State->Flags.Vm)
    {
        if (!Fast486ProcessGate(State, Segment, Offset, TRUE))
        {
            /* Gate processed or exception occurred */
            return;
        }
    }

    /* Push the current code segment selector */
    if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector))
    {
        /* Exception occurred */
        return;
    }

    /* Push the current value of the instruction pointer */
    if (!Fast486StackPush(State, State->InstPtr.Long))
    {
        /* Exception occurred */
        return;
    }

    /* Load the new CS */
    if (!Fast486LoadSegment(State, FAST486_REG_CS, Segment))
    {
        /* Exception occurred */
        return;
    }

    /* Load new (E)IP */
    if (Size) State->InstPtr.Long = Offset;
    else State->InstPtr.LowWord = LOWORD(Offset);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeWait)
{
#ifndef FAST486_NO_FPU
    Fast486FpuExceptionCheck(State);
#endif
}

FAST486_OPCODE_HANDLER(Fast486OpcodePushFlags)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    /* Check for VM86 mode when IOPL is not 3 */
    if (State->Flags.Vm && (State->Flags.Iopl != 3))
    {
        /* Call the VM86 monitor */
        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, 0);
        return;
    }

    /* Push the flags */
    if (Size) Fast486StackPush(State, State->Flags.Long);
    else Fast486StackPush(State, LOWORD(State->Flags.Long));
}

FAST486_OPCODE_HANDLER(Fast486OpcodePopFlags)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
    UINT Cpl = Fast486GetCurrentPrivLevel(State);
    FAST486_FLAGS_REG NewFlags;

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    /* Pop the new flags */
    if (!Fast486StackPop(State, &NewFlags.Long))
    {
        /* Exception occurred */
        return;
    }

    /* Check for VM86 mode when IOPL is not 3 */
    if (State->Flags.Vm && (State->Flags.Iopl != 3))
    {
        /* Call the VM86 monitor */
        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, 0);
        return;
    }

    State->Flags.Cf = NewFlags.Cf;
    State->Flags.Pf = NewFlags.Pf;
    State->Flags.Af = NewFlags.Af;
    State->Flags.Zf = NewFlags.Zf;
    State->Flags.Sf = NewFlags.Sf;
    State->Flags.Tf = NewFlags.Tf;
    State->Flags.Df = NewFlags.Df;
    State->Flags.Of = NewFlags.Of;
    State->Flags.Nt = NewFlags.Nt;
    State->Flags.Ac = NewFlags.Ac;

    if (Cpl == 0) State->Flags.Iopl = NewFlags.Iopl;
    if (Cpl <= State->Flags.Iopl) State->Flags.If = NewFlags.If;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeSahf)
{
    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x9E);

    /* Set the low-order byte of FLAGS to AH */
    State->Flags.Long &= 0xFFFFFF00;
    State->Flags.Long |= State->GeneralRegs[FAST486_REG_EAX].HighByte;

    /* Restore the reserved bits of FLAGS */
    State->Flags.AlwaysSet = TRUE;
    State->Flags.Reserved0 = State->Flags.Reserved1 = FALSE;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeLahf)
{
    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0x9F);

    /* Set AH to the low-order byte of FLAGS */
    State->GeneralRegs[FAST486_REG_EAX].HighByte = LOBYTE(State->Flags.Long);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeRet)
{
    ULONG ReturnAddress;
    USHORT BytesToPop = 0;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFE) == 0xC2);

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    if (Opcode == 0xC2)
    {
        /* Fetch the number of bytes to pop after the return */
        if (!Fast486FetchWord(State, &BytesToPop)) return;
    }

    /* Pop the return address */
    if (!Fast486StackPop(State, &ReturnAddress)) return;

    /* Return to the calling procedure, and if necessary, pop the parameters */
    if (Size)
    {
        State->InstPtr.Long = ReturnAddress;
        State->GeneralRegs[FAST486_REG_ESP].Long += BytesToPop;
    }
    else
    {
        State->InstPtr.LowWord = LOWORD(ReturnAddress);
        State->GeneralRegs[FAST486_REG_ESP].LowWord += BytesToPop;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeLdsLes)
{
    UCHAR FarPointer[6];
    BOOLEAN OperandSize, AddressSize;
    FAST486_MOD_REG_RM ModRegRm;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFE) == 0xC4);

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_OPSIZE(OperandSize);
    TOGGLE_ADSIZE(AddressSize);

    /* Get the operands */
    if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
    {
        /* Exception occurred */
        return;
    }

    if (!ModRegRm.Memory)
    {
        /* Check if this is a BOP and the host supports BOPs */
        if ((Opcode == 0xC4)
            && (ModRegRm.Register == FAST486_REG_EAX)
            && (ModRegRm.SecondRegister == FAST486_REG_ESP)
            && (State->BopCallback != NULL))
        {
            UCHAR BopCode;

            /* Fetch the BOP code */
            if (!Fast486FetchByte(State, &BopCode))
            {
                /* Exception occurred */
                return;
            }

#ifndef FAST486_NO_PREFETCH
            /* Invalidate the prefetch since BOP handlers can alter the memory */
            State->PrefetchValid = FALSE;
#endif

            /* Call the BOP handler */
            State->BopCallback(State, BopCode);

            /*
             * If an interrupt should occur at this time, delay it.
             * We must do this because if an interrupt begins and the BOP callback
             * changes the CS:IP, the interrupt handler won't execute and the
             * stack pointer will never be restored.
             */
            State->DoNotInterrupt = TRUE;

            return;
        }

        /* Invalid */
        Fast486Exception(State, FAST486_EXCEPTION_UD);
        return;
    }

    if (!Fast486ReadMemory(State,
                           (State->PrefixFlags & FAST486_PREFIX_SEG)
                           ? State->SegmentOverride : FAST486_REG_DS,
                           ModRegRm.MemoryAddress,
                           FALSE,
                           FarPointer,
                           OperandSize ? 6 : 4))
    {
        /* Exception occurred */
        return;
    }

    if (OperandSize)
    {
        ULONG Offset = *((PULONG)FarPointer);
        USHORT Segment = *((PUSHORT)&FarPointer[sizeof(ULONG)]);

        /* Set the register to the offset */
        State->GeneralRegs[ModRegRm.Register].Long = Offset;

        /* Load the segment */
        Fast486LoadSegment(State,
                           (Opcode == 0xC4)
                           ? FAST486_REG_ES : FAST486_REG_DS,
                           Segment);
    }
    else
    {
        USHORT Offset = *((PUSHORT)FarPointer);
        USHORT Segment = *((PUSHORT)&FarPointer[sizeof(USHORT)]);

        /* Set the register to the offset */
        State->GeneralRegs[ModRegRm.Register].LowWord = Offset;

        /* Load the segment */
        Fast486LoadSegment(State,
                           (Opcode == 0xC4)
                           ? FAST486_REG_ES : FAST486_REG_DS,
                           Segment);
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeEnter)
{
    INT i;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
    USHORT FrameSize;
    UCHAR NestingLevel;
    FAST486_REG FramePointer;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xC8);

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    if (!Fast486FetchWord(State, &FrameSize))
    {
        /* Exception occurred */
        return;
    }

    if (!Fast486FetchByte(State, &NestingLevel))
    {
        /* Exception occurred */
        return;
    }

    /* Push EBP */
    if (!Fast486StackPush(State, State->GeneralRegs[FAST486_REG_EBP].Long))
    {
        /* Exception occurred */
        return;
    }

    /* Save ESP */
    FramePointer = State->GeneralRegs[FAST486_REG_ESP];

    /* Set up the nested procedure stacks */
    for (i = 1; i < NestingLevel; i++)
    {
        if (Size)
        {
            State->GeneralRegs[FAST486_REG_EBP].Long -= 4;
            Fast486StackPush(State, State->GeneralRegs[FAST486_REG_EBP].Long);
        }
        else
        {
            State->GeneralRegs[FAST486_REG_EBP].LowWord -= 2;
            Fast486StackPush(State, State->GeneralRegs[FAST486_REG_EBP].LowWord);
        }
    }

    if (NestingLevel > 0) Fast486StackPush(State, FramePointer.Long);

    /* Set EBP to the frame pointer */
    if (Size) State->GeneralRegs[FAST486_REG_EBP].Long = FramePointer.Long;
    else State->GeneralRegs[FAST486_REG_EBP].LowWord = FramePointer.LowWord;

    /* Reserve space for the frame */
    if (State->SegmentRegs[FAST486_REG_SS].Size)
    {
        State->GeneralRegs[FAST486_REG_ESP].Long -= (ULONG)FrameSize;
    }
    else
    {
        State->GeneralRegs[FAST486_REG_ESP].LowWord -= FrameSize;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeLeave)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
    ULONG Value;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xC9);

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    if (State->SegmentRegs[FAST486_REG_SS].Size)
    {
        /* Set the stack pointer (ESP) to the base pointer (EBP) */
        State->GeneralRegs[FAST486_REG_ESP].Long = State->GeneralRegs[FAST486_REG_EBP].Long;
    }
    else
    {
        /* Set the stack pointer (SP) to the base pointer (BP) */
        State->GeneralRegs[FAST486_REG_ESP].LowWord = State->GeneralRegs[FAST486_REG_EBP].LowWord;
    }

    /* Pop the saved base pointer from the stack */
    if (Fast486StackPop(State, &Value))
    {
        if (Size) State->GeneralRegs[FAST486_REG_EBP].Long = Value;
        else State->GeneralRegs[FAST486_REG_EBP].LowWord = LOWORD(Value);
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeRetFar)
{
    ULONG Segment = 0;
    ULONG Offset = 0;
    USHORT BytesToPop = 0;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
    ULONG StackPtr;
    ULONG StackSel;
    UCHAR OldCpl = Fast486GetCurrentPrivLevel(State);

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFE) == 0xCA);

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    if (Opcode == 0xCA)
    {
        /* Fetch the number of bytes to pop after the return */
        if (!Fast486FetchWord(State, &BytesToPop)) return;
    }

    /* Pop the offset */
    if (!Fast486StackPop(State, &Offset))
    {
        /* Exception occurred */
        return;
    }

    /* Pop the segment */
    if (!Fast486StackPop(State, &Segment))
    {
        /* Exception occurred */
        return;
    }

    /* Pop the parameters */
    if (State->SegmentRegs[FAST486_REG_SS].Size)
    {
        State->GeneralRegs[FAST486_REG_ESP].Long += BytesToPop;
    }
    else
    {
        State->GeneralRegs[FAST486_REG_ESP].LowWord += BytesToPop;
    }

    if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) && !State->Flags.Vm)
    {
        if (GET_SEGMENT_RPL(Segment) > OldCpl)
        {
            /* Pop ESP */
            if (!Fast486StackPop(State, &StackPtr))
            {
                /* Exception */
                return;
            }

            /* Pop SS */
            if (!Fast486StackPop(State, &StackSel))
            {
                /* Exception */
                return;
            }
        }
    }

    /* Load the new CS */
    if (!Fast486LoadSegment(State, FAST486_REG_CS, Segment))
    {
        /* Exception occurred */
        return;
    }

    /* Load new (E)IP */
    if (Size) State->InstPtr.Long = Offset;
    else State->InstPtr.LowWord = LOWORD(Offset);

    if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) && !State->Flags.Vm)
    {
        UINT i;

        /* Update the CPL */
        State->Cpl = GET_SEGMENT_RPL(Segment);

        if (State->Cpl > OldCpl)
        {
            /* Load new SS */
            if (!Fast486LoadSegment(State, FAST486_REG_SS, StackSel))
            {
                /* Exception */
                return;
            }

            /* Set ESP */
            if (Size) State->GeneralRegs[FAST486_REG_ESP].Long = StackPtr;
            else State->GeneralRegs[FAST486_REG_ESP].LowWord = LOWORD(StackPtr);

            /* Check segment security */
            for (i = 0; i < FAST486_NUM_SEG_REGS; i++)
            {
                /* Don't check CS or SS */
                if ((i == FAST486_REG_CS) || (i == FAST486_REG_SS)) continue;

                if ((State->Cpl > State->SegmentRegs[i].Dpl)
                    && (!State->SegmentRegs[i].Executable
                    || !State->SegmentRegs[i].DirConf))
                {
                    /* Load the NULL descriptor in the segment */
                    if (!Fast486LoadSegment(State, i, 0)) return;
                }
            }
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeInt)
{
    UCHAR IntNum;

    /* Check for V86 mode */
    if (State->Flags.Vm && (State->Flags.Iopl != 3))
    {
        /* Call the V86 monitor */
        Fast486Exception(State, FAST486_EXCEPTION_GP);
        return;
    }

    switch (Opcode)
    {
        case 0xCC:  // INT 3
        {
            /* This is the INT3 instruction */
            IntNum = 3;
            break;
        }

        case 0xCD:  // INT xx
        {
            /* Fetch the interrupt number */
            if (!Fast486FetchByte(State, &IntNum))
            {
                /* Exception occurred */
                return;
            }

            break;
        }

        case 0xCE:  // INTO
        {
            /* Don't do anything if OF is cleared */
            if (!State->Flags.Of) return;

            /* Exception #OF */
            IntNum = FAST486_EXCEPTION_OF;

            break;
        }

        default:
        {
            /* Should not happen */
            ASSERT(FALSE);
        }
    }

    /* Perform the interrupt */
    Fast486PerformInterrupt(State, IntNum);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeIret)
{
    FAST486_SEG_REGS i;
    ULONG InstPtr, CodeSel, StackPtr, StackSel;
    FAST486_FLAGS_REG NewFlags;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xCF);

    NO_LOCK_PREFIX();
    TOGGLE_OPSIZE(Size);

    /* Check if this is a nested task return */
    if (State->Flags.Nt && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE))
    {
        /* Clear the NT flag of the current task */
        State->Flags.Nt = FALSE;

        /* Switch to the old task */
        Fast486TaskSwitch(State, FAST486_TASK_RETURN, 0);
        return;
    }

    /* Pop EIP */
    if (!Fast486StackPop(State, &InstPtr))
    {
        /* Exception occurred */
        return;
    }

    /* Pop CS */
    if (!Fast486StackPop(State, &CodeSel))
    {
        /* Exception occurred */
        return;
    }

    /* Pop EFLAGS */
    if (!Fast486StackPop(State, &NewFlags.Long))
    {
        /* Exception occurred */
        return;
    }

    /* Check for protected mode */
    if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
    {
        UINT OldCpl = Fast486GetCurrentPrivLevel(State);

        if (State->Flags.Vm)
        {
            /* Return from VM86 mode */

            /* Check the IOPL */
            if (State->Flags.Iopl == 3)
            {
                /* Set new EIP */
                State->InstPtr.Long = LOWORD(InstPtr);

                /* Load new CS */
                if (!Fast486LoadSegment(State, FAST486_REG_CS, CodeSel))
                {
                    /* Exception occurred */
                    return;
                }

                /* Set the new flags */
                if (Size) State->Flags.Long = NewFlags.Long & REAL_MODE_FLAGS_MASK;
                else State->Flags.LowWord = NewFlags.LowWord & REAL_MODE_FLAGS_MASK;
                State->Flags.AlwaysSet = State->Flags.Vm = TRUE;
                State->Flags.Iopl = 3;
            }
            else
            {
                /* Call the VM86 monitor */
                Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, 0);
                return;
            }

            return;
        }

        if (NewFlags.Vm)
        {
            /* Return to VM86 mode */
            ULONG Es, Ds, Fs, Gs;

            /* Pop ESP, SS, ES, DS, FS, GS */
            if (!Fast486StackPop(State, &StackPtr)) return;
            if (!Fast486StackPop(State, &StackSel)) return;
            if (!Fast486StackPop(State, &Es)) return;
            if (!Fast486StackPop(State, &Ds)) return;
            if (!Fast486StackPop(State, &Fs)) return;
            if (!Fast486StackPop(State, &Gs)) return;

            /* Set the new IP */
            State->InstPtr.Long = LOWORD(InstPtr);

            /* Set the new SP */
            State->GeneralRegs[FAST486_REG_ESP].Long = StackPtr;

            /* Set the new flags */
            if (Size) State->Flags.Long = NewFlags.Long & REAL_MODE_FLAGS_MASK;
            else State->Flags.LowWord = NewFlags.LowWord & REAL_MODE_FLAGS_MASK;
            State->Flags.AlwaysSet = State->Flags.Vm = TRUE;

            /* Switch to CPL 3 */
            State->Cpl = 3;

            /* Load the new segments */
            if (!Fast486LoadSegment(State, FAST486_REG_CS, CodeSel)) return;
            if (!Fast486LoadSegment(State, FAST486_REG_SS, StackSel)) return;
            if (!Fast486LoadSegment(State, FAST486_REG_ES, Es)) return;
            if (!Fast486LoadSegment(State, FAST486_REG_DS, Ds)) return;
            if (!Fast486LoadSegment(State, FAST486_REG_FS, Fs)) return;
            if (!Fast486LoadSegment(State, FAST486_REG_GS, Gs)) return;

            return;
        }

        if (GET_SEGMENT_RPL(CodeSel) > OldCpl)
        {
            /* Pop ESP */
            if (!Fast486StackPop(State, &StackPtr))
            {
                /* Exception */
                return;
            }

            /* Pop SS */
            if (!Fast486StackPop(State, &StackSel))
            {
                /* Exception */
                return;
            }
        }

        /* Load the new CS */
        if (!Fast486LoadSegment(State, FAST486_REG_CS, CodeSel))
        {
            /* Exception occurred */
            return;
        }

        /* Set EIP */
        if (Size) State->InstPtr.Long = InstPtr;
        else State->InstPtr.LowWord = LOWORD(InstPtr);

        /* Update the CPL */
        State->Cpl = GET_SEGMENT_RPL(CodeSel);

        /* Set the new flags */
        if (Size)
        {
            State->Flags.Long = (State->Flags.Long & ~PROT_MODE_FLAGS_MASK)
                                | (NewFlags.Long & PROT_MODE_FLAGS_MASK);
        }
        else
        {
            State->Flags.LowWord = (State->Flags.LowWord & ~PROT_MODE_FLAGS_MASK)
                                   | (NewFlags.LowWord & PROT_MODE_FLAGS_MASK);
        }
        State->Flags.AlwaysSet = TRUE;

        /* Set additional flags */
        if (OldCpl <= State->Flags.Iopl) State->Flags.If = NewFlags.If;
        if (OldCpl == 0) State->Flags.Iopl = NewFlags.Iopl;

        if (State->Cpl > OldCpl)
        {
            /* Load new SS */
            if (!Fast486LoadSegment(State, FAST486_REG_SS, StackSel))
            {
                /* Exception */
                return;
            }

            /* Set ESP */
            if (Size) State->GeneralRegs[FAST486_REG_ESP].Long = StackPtr;
            else State->GeneralRegs[FAST486_REG_ESP].LowWord = LOWORD(StackPtr);

            /* Check segment security */
            for (i = 0; i < FAST486_NUM_SEG_REGS; i++)
            {
                /* Don't check CS or SS */
                if ((i == FAST486_REG_CS) || (i == FAST486_REG_SS)) continue;

                if ((State->Cpl > State->SegmentRegs[i].Dpl)
                    && (!State->SegmentRegs[i].Executable
                    || !State->SegmentRegs[i].DirConf))
                {
                    /* Load the NULL descriptor in the segment */
                    if (!Fast486LoadSegment(State, i, 0)) return;
                }
            }
        }
    }
    else
    {
        if (Size && (InstPtr & 0xFFFF0000))
        {
            /* Invalid */
            Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, 0);
            return;
        }

        /* Set new EIP */
        State->InstPtr.Long = InstPtr;

        /* Load new CS */
        if (!Fast486LoadSegment(State, FAST486_REG_CS, CodeSel))
        {
            /* Exception occurred */
            return;
        }

        /* Set the new flags */
        if (Size) State->Flags.Long = NewFlags.Long & REAL_MODE_FLAGS_MASK;
        else State->Flags.LowWord = NewFlags.LowWord & REAL_MODE_FLAGS_MASK;
        State->Flags.AlwaysSet = TRUE;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAam)
{
    UCHAR Base;
    UCHAR Value = State->GeneralRegs[FAST486_REG_EAX].LowByte;

    NO_LOCK_PREFIX();

    /* Fetch the base */
    if (!Fast486FetchByte(State, &Base))
    {
        /* Exception occurred */
        return;
    }

    /* Check if the base is zero */
    if (Base == 0)
    {
        /* Divide error */
        Fast486Exception(State, FAST486_EXCEPTION_DE);
        return;
    }

    /* Adjust */
    State->GeneralRegs[FAST486_REG_EAX].HighByte = Value / Base;
    State->GeneralRegs[FAST486_REG_EAX].LowByte = Value %= Base;

    /* Update flags */
    State->Flags.Af = FALSE;
    State->Flags.Zf = (Value == 0);
    State->Flags.Sf = ((Value & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Value);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeAad)
{
    UCHAR Base;
    UCHAR Value = State->GeneralRegs[FAST486_REG_EAX].LowByte;

    NO_LOCK_PREFIX();

    /* Fetch the base */
    if (!Fast486FetchByte(State, &Base))
    {
        /* Exception occurred */
        return;
    }

    /* Adjust */
    Value += State->GeneralRegs[FAST486_REG_EAX].HighByte * Base;
    State->GeneralRegs[FAST486_REG_EAX].LowWord = Value;

    /* Update flags */
    State->Flags.Af = FALSE;
    State->Flags.Zf = (Value == 0);
    State->Flags.Sf = ((Value & SIGN_FLAG_BYTE) != 0);
    State->Flags.Pf = Fast486CalculateParity(Value);
}

FAST486_OPCODE_HANDLER(Fast486OpcodeXlat)
{
    UCHAR Value;
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    TOGGLE_ADSIZE(AddressSize);

    /* Read a byte from DS:[(E)BX + AL] */
    if (!Fast486ReadMemory(State,
                           (State->PrefixFlags & FAST486_PREFIX_SEG)
                           ? State->SegmentOverride : FAST486_REG_DS,
                           (AddressSize ? State->GeneralRegs[FAST486_REG_EBX].Long
                                        : State->GeneralRegs[FAST486_REG_EBX].LowWord)
                           + State->GeneralRegs[FAST486_REG_EAX].LowByte,
                           FALSE,
                           &Value,
                           sizeof(UCHAR)))
    {
        /* Exception occurred */
        return;
    }

    /* Set AL to the result */
    State->GeneralRegs[FAST486_REG_EAX].LowByte = Value;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeLoop)
{
    BOOLEAN Condition;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
    CHAR Offset = 0;

    /* Make sure this is the right instruction */
    ASSERT((Opcode >= 0xE0) && (Opcode <= 0xE2));

    NO_LOCK_PREFIX();
    TOGGLE_ADSIZE(Size);

    if (Size) Condition = ((--State->GeneralRegs[FAST486_REG_ECX].Long) != 0);
    else Condition = ((--State->GeneralRegs[FAST486_REG_ECX].LowWord) != 0);

    if (Opcode == 0xE0)
    {
        /* Additional rule for LOOPNZ */
        if (State->Flags.Zf) Condition = FALSE;
    }
    else if (Opcode == 0xE1)
    {
        /* Additional rule for LOOPZ */
        if (!State->Flags.Zf) Condition = FALSE;
    }

    /* Fetch the offset */
    if (!Fast486FetchByte(State, (PUCHAR)&Offset))
    {
        /* An exception occurred */
        return;
    }

    if (Condition)
    {
        /* Move the instruction pointer */
        if (Size) State->InstPtr.Long += Offset;
        else State->InstPtr.LowWord += Offset;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeJecxz)
{
    BOOLEAN Condition;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
    CHAR Offset = 0;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xE3);

    NO_LOCK_PREFIX();
    TOGGLE_ADSIZE(Size);

    if (Size) Condition = (State->GeneralRegs[FAST486_REG_ECX].Long == 0);
    else Condition = (State->GeneralRegs[FAST486_REG_ECX].LowWord == 0);

    /* Fetch the offset */
    if (!Fast486FetchByte(State, (PUCHAR)&Offset))
    {
        /* An exception occurred */
        return;
    }

    if (Condition)
    {
        /* Move the instruction pointer */
        if (Size) State->InstPtr.Long += Offset;
        else State->InstPtr.LowWord += Offset;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeCall)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xE8);

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    if (Size)
    {
        LONG Offset = 0;

        /* Fetch the offset */
        if (!Fast486FetchDword(State, (PULONG)&Offset))
        {
            /* An exception occurred */
            return;
        }

        /* Push the current value of the instruction pointer */
        if (!Fast486StackPush(State, State->InstPtr.Long))
        {
            /* Exception occurred */
            return;
        }

        /* Move the instruction pointer */
        State->InstPtr.Long += Offset;
    }
    else
    {
        SHORT Offset = 0;

        /* Fetch the offset */
        if (!Fast486FetchWord(State, (PUSHORT)&Offset))
        {
            /* An exception occurred */
            return;
        }

        /* Push the current value of the instruction pointer */
        if (!Fast486StackPush(State, State->InstPtr.Long))
        {
            /* Exception occurred */
            return;
        }

        /* Move the instruction pointer */
        State->InstPtr.LowWord += Offset;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeJmp)
{
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xE9);

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    if (Size)
    {
        LONG Offset = 0;

        /* Fetch the offset */
        if (!Fast486FetchDword(State, (PULONG)&Offset))
        {
            /* An exception occurred */
            return;
        }

        /* Move the instruction pointer */
        State->InstPtr.Long += Offset;
    }
    else
    {
        SHORT Offset = 0;

        /* Fetch the offset */
        if (!Fast486FetchWord(State, (PUSHORT)&Offset))
        {
            /* An exception occurred */
            return;
        }

        /* Move the instruction pointer */
        State->InstPtr.Long += Offset;

        /* Clear the top half of EIP */
        State->InstPtr.Long &= 0xFFFF;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeJmpAbs)
{
    USHORT Segment = 0;
    ULONG Offset = 0;
    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xEA);

    TOGGLE_OPSIZE(Size);
    NO_LOCK_PREFIX();

    /* Fetch the offset */
    if (Size)
    {
        if (!Fast486FetchDword(State, &Offset))
        {
            /* Exception occurred */
            return;
        }
    }
    else
    {
        if (!Fast486FetchWord(State, (PUSHORT)&Offset))
        {
            /* Exception occurred */
            return;
        }
    }

    /* Fetch the segment */
    if (!Fast486FetchWord(State, &Segment))
    {
        /* Exception occurred */
        return;
    }

    if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) && !State->Flags.Vm)
    {
        if (!Fast486ProcessGate(State, Segment, Offset, FALSE))
        {
            /* Gate processed or exception occurred */
            return;
        }
    }

    /* Load the new CS */
    if (!Fast486LoadSegment(State, FAST486_REG_CS, Segment))
    {
        /* Exception occurred */
        return;
    }

    /* Load new EIP */
    State->InstPtr.Long = Offset;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeMovAlOffset)
{
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
    ULONG Offset;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xA0);

    TOGGLE_ADSIZE(AddressSize);

    if (AddressSize)
    {
        if (!Fast486FetchDword(State, &Offset))
        {
            /* Exception occurred */
            return;
        }
    }
    else
    {
        USHORT WordOffset;

        if (!Fast486FetchWord(State, &WordOffset))
        {
            /* Exception occurred */
            return;
        }

        Offset = (ULONG)WordOffset;
    }

    /* Read from memory */
    Fast486ReadMemory(State,
                      (State->PrefixFlags & FAST486_PREFIX_SEG) ?
                      State->SegmentOverride : FAST486_REG_DS,
                      Offset,
                      FALSE,
                      &State->GeneralRegs[FAST486_REG_EAX].LowByte,
                      sizeof(UCHAR));
}

FAST486_OPCODE_HANDLER(Fast486OpcodeMovEaxOffset)
{
    BOOLEAN OperandSize, AddressSize;

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xA1);

    TOGGLE_OPSIZE(OperandSize);
    TOGGLE_ADSIZE(AddressSize);

    if (AddressSize)
    {
        ULONG Offset;

        if (!Fast486FetchDword(State, &Offset))
        {
            /* Exception occurred */
            return;
        }

        /* Read from memory */
        if (OperandSize)
        {
            Fast486ReadMemory(State,
                              (State->PrefixFlags & FAST486_PREFIX_SEG) ?
                              State->SegmentOverride : FAST486_REG_DS,
                              Offset,
                              FALSE,
                              &State->GeneralRegs[FAST486_REG_EAX].Long,
                              sizeof(ULONG));
        }
        else
        {
            Fast486ReadMemory(State,
                              (State->PrefixFlags & FAST486_PREFIX_SEG) ?
                              State->SegmentOverride : FAST486_REG_DS,
                              Offset,
                              FALSE,
                              &State->GeneralRegs[FAST486_REG_EAX].LowWord,
                              sizeof(USHORT));
        }
    }
    else
    {
        USHORT Offset;

        if (!Fast486FetchWord(State, &Offset))
        {
            /* Exception occurred */
            return;
        }

        /* Read from memory */
        if (OperandSize)
        {
            Fast486ReadMemory(State,
                              (State->PrefixFlags & FAST486_PREFIX_SEG) ?
                              State->SegmentOverride : FAST486_REG_DS,
                              Offset,
                              FALSE,
                              &State->GeneralRegs[FAST486_REG_EAX].Long,
                              sizeof(ULONG));
        }
        else
        {
            Fast486ReadMemory(State,
                              (State->PrefixFlags & FAST486_PREFIX_SEG) ?
                              State->SegmentOverride : FAST486_REG_DS,
                              Offset,
                              FALSE,
                              &State->GeneralRegs[FAST486_REG_EAX].LowWord,
                              sizeof(USHORT));
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeMovOffsetAl)
{
    BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
    ULONG Offset;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xA2);

    TOGGLE_ADSIZE(AddressSize);

    if (AddressSize)
    {
        if (!Fast486FetchDword(State, &Offset))
        {
            /* Exception occurred */
            return;
        }
    }
    else
    {
        USHORT WordOffset;

        if (!Fast486FetchWord(State, &WordOffset))
        {
            /* Exception occurred */
            return;
        }

        Offset = (ULONG)WordOffset;
    }

    /* Write to memory */
    Fast486WriteMemory(State,
                      (State->PrefixFlags & FAST486_PREFIX_SEG) ?
                      State->SegmentOverride : FAST486_REG_DS,
                      Offset,
                      &State->GeneralRegs[FAST486_REG_EAX].LowByte,
                      sizeof(UCHAR));
}

FAST486_OPCODE_HANDLER(Fast486OpcodeMovOffsetEax)
{
    BOOLEAN OperandSize, AddressSize;

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xA3);

    TOGGLE_OPSIZE(OperandSize);
    TOGGLE_ADSIZE(AddressSize);

    if (AddressSize)
    {
        ULONG Offset;

        if (!Fast486FetchDword(State, &Offset))
        {
            /* Exception occurred */
            return;
        }

        /* Write to memory */
        if (OperandSize)
        {
            Fast486WriteMemory(State,
                               (State->PrefixFlags & FAST486_PREFIX_SEG) ?
                               State->SegmentOverride : FAST486_REG_DS,
                               Offset,
                               &State->GeneralRegs[FAST486_REG_EAX].Long,
                               sizeof(ULONG));
        }
        else
        {
            Fast486WriteMemory(State,
                               (State->PrefixFlags & FAST486_PREFIX_SEG) ?
                               State->SegmentOverride : FAST486_REG_DS,
                               Offset,
                               &State->GeneralRegs[FAST486_REG_EAX].LowWord,
                               sizeof(USHORT));
        }
    }
    else
    {
        USHORT Offset;

        if (!Fast486FetchWord(State, &Offset))
        {
            /* Exception occurred */
            return;
        }

        /* Write to memory */
        if (OperandSize)
        {
            Fast486WriteMemory(State,
                               (State->PrefixFlags & FAST486_PREFIX_SEG) ?
                               State->SegmentOverride : FAST486_REG_DS,
                               Offset,
                               &State->GeneralRegs[FAST486_REG_EAX].Long,
                               sizeof(ULONG));
        }
        else
        {
            Fast486WriteMemory(State,
                               (State->PrefixFlags & FAST486_PREFIX_SEG) ?
                               State->SegmentOverride : FAST486_REG_DS,
                               Offset,
                               &State->GeneralRegs[FAST486_REG_EAX].LowWord,
                               sizeof(USHORT));
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeSalc)
{
    /*
     * See: http://www.rcollins.org/secrets/opcodes/SALC.html
     * for more information.
     */

    /* Make sure this is the right instruction */
    ASSERT(Opcode == 0xD6);

    NO_LOCK_PREFIX();

    /* Set all the bits of AL to CF */
    State->GeneralRegs[FAST486_REG_EAX].LowByte = State->Flags.Cf ? 0xFF : 0x00;
}

FAST486_OPCODE_HANDLER(Fast486OpcodeMovs)
{
    ULONG Data, DataSize;
    BOOLEAN OperandSize, AddressSize;
    FAST486_SEG_REGS Segment = FAST486_REG_DS;

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFE) == 0xA4);

    TOGGLE_OPSIZE(OperandSize);
    TOGGLE_ADSIZE(AddressSize);

    if (State->PrefixFlags & FAST486_PREFIX_SEG)
    {
        /* Use the override segment instead of DS */
        Segment = State->SegmentOverride;
    }

    if (State->PrefixFlags & (FAST486_PREFIX_REP | FAST486_PREFIX_REPNZ))
    {
        if ((AddressSize && (State->GeneralRegs[FAST486_REG_ECX].Long == 0))
            || (!AddressSize && (State->GeneralRegs[FAST486_REG_ECX].LowWord == 0)))
        {
            /* Do nothing */
            return;
        }
    }

    /* Calculate the size */
    if (Opcode == 0xA4) DataSize = sizeof(UCHAR);
    else DataSize = OperandSize ? sizeof(ULONG) : sizeof(USHORT);

    /* Read from the source operand */
    if (!Fast486ReadMemory(State,
                           Segment,
                           AddressSize ? State->GeneralRegs[FAST486_REG_ESI].Long
                                       : State->GeneralRegs[FAST486_REG_ESI].LowWord,
                           FALSE,
                           &Data,
                           DataSize))
    {
        /* Exception occurred */
        return;
    }

    /* Write to the destination operand */
    if (!Fast486WriteMemory(State,
                            FAST486_REG_ES,
                            AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long
                                        : State->GeneralRegs[FAST486_REG_EDI].LowWord,
                            &Data,
                            DataSize))
    {
        /* Exception occurred */
        return;
    }

    /* Increment/decrement ESI and EDI */
    if (AddressSize)
    {
        if (!State->Flags.Df)
        {
            State->GeneralRegs[FAST486_REG_ESI].Long += DataSize;
            State->GeneralRegs[FAST486_REG_EDI].Long += DataSize;
        }
        else
        {
            State->GeneralRegs[FAST486_REG_ESI].Long -= DataSize;
            State->GeneralRegs[FAST486_REG_EDI].Long -= DataSize;
        }
    }
    else
    {
        if (!State->Flags.Df)
        {
            State->GeneralRegs[FAST486_REG_ESI].LowWord += DataSize;
            State->GeneralRegs[FAST486_REG_EDI].LowWord += DataSize;
        }
        else
        {
            State->GeneralRegs[FAST486_REG_ESI].LowWord -= DataSize;
            State->GeneralRegs[FAST486_REG_EDI].LowWord -= DataSize;
        }
    }

    // FIXME: This method is slow!
    if (State->PrefixFlags & (FAST486_PREFIX_REP | FAST486_PREFIX_REPNZ))
    {
        if (AddressSize)
        {
            if (--State->GeneralRegs[FAST486_REG_ECX].Long)
            {
                /* Repeat the instruction */
                State->InstPtr = State->SavedInstPtr;
            }
        }
        else
        {
            if (--State->GeneralRegs[FAST486_REG_ECX].LowWord)
            {
                /* Repeat the instruction */
                State->InstPtr = State->SavedInstPtr;
            }
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeCmps)
{
    ULONG FirstValue = 0, SecondValue = 0, Result;
    ULONG DataSize, DataMask, SignFlag;
    BOOLEAN OperandSize, AddressSize;
    FAST486_SEG_REGS Segment = FAST486_REG_DS;

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFE) == 0xA6);

    TOGGLE_OPSIZE(OperandSize);
    TOGGLE_ADSIZE(AddressSize);

    if (State->PrefixFlags & FAST486_PREFIX_SEG)
    {
        /* Use the override segment instead of DS */
        Segment = State->SegmentOverride;
    }

    if ((State->PrefixFlags & FAST486_PREFIX_REP)
        || (State->PrefixFlags & FAST486_PREFIX_REPNZ))
    {
        if ((AddressSize && (State->GeneralRegs[FAST486_REG_ECX].Long == 0))
            || (!AddressSize && (State->GeneralRegs[FAST486_REG_ECX].LowWord == 0)))
        {
            /* Do nothing */
            return;
        }
    }

    /* Calculate the size */
    if (Opcode == 0xA6) DataSize = sizeof(UCHAR);
    else DataSize = OperandSize ? sizeof(ULONG) : sizeof(USHORT);

    /* Calculate the mask and sign flag */
    SignFlag = 1 << ((DataSize * 8) - 1);
    DataMask = SignFlag | (SignFlag - 1);

    /* Read from the first source operand */
    if (!Fast486ReadMemory(State,
                           Segment,
                           AddressSize ? State->GeneralRegs[FAST486_REG_ESI].Long
                                       : State->GeneralRegs[FAST486_REG_ESI].LowWord,
                           FALSE,
                           &FirstValue,
                           DataSize))
    {
        /* Exception occurred */
        return;
    }

    /* Read from the second source operand */
    if (!Fast486ReadMemory(State,
                           FAST486_REG_ES,
                           AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long
                                       : State->GeneralRegs[FAST486_REG_EDI].LowWord,
                           FALSE,
                           &SecondValue,
                           DataSize))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    FirstValue &= DataMask;
    SecondValue &= DataMask;
    Result = (FirstValue - SecondValue) & DataMask;

    /* Update the flags */
    State->Flags.Cf = (FirstValue < SecondValue);
    State->Flags.Of = ((FirstValue & SignFlag) != (SecondValue & SignFlag))
                      && ((FirstValue & SignFlag) != (Result & SignFlag));
    State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F);
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SignFlag) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Increment/decrement ESI and EDI */
    if (AddressSize)
    {
        if (!State->Flags.Df)
        {
            State->GeneralRegs[FAST486_REG_ESI].Long += DataSize;
            State->GeneralRegs[FAST486_REG_EDI].Long += DataSize;
        }
        else
        {
            State->GeneralRegs[FAST486_REG_ESI].Long -= DataSize;
            State->GeneralRegs[FAST486_REG_EDI].Long -= DataSize;
        }
    }
    else
    {
        if (!State->Flags.Df)
        {
            State->GeneralRegs[FAST486_REG_ESI].LowWord += DataSize;
            State->GeneralRegs[FAST486_REG_EDI].LowWord += DataSize;
        }
        else
        {
            State->GeneralRegs[FAST486_REG_ESI].LowWord -= DataSize;
            State->GeneralRegs[FAST486_REG_EDI].LowWord -= DataSize;
        }
    }

    // FIXME: This method is slow!
    if ((State->PrefixFlags & FAST486_PREFIX_REP)
        || (State->PrefixFlags & FAST486_PREFIX_REPNZ))
    {
        BOOLEAN Repeat = TRUE;

        if (AddressSize)
        {
            if ((--State->GeneralRegs[FAST486_REG_ECX].Long) == 0)
            {
                /* ECX is 0 */
                Repeat = FALSE;
            }
        }
        else
        {
            if ((--State->GeneralRegs[FAST486_REG_ECX].LowWord) == 0)
            {
                /* CX is 0 */
                Repeat = FALSE;
            }
        }

        if (((State->PrefixFlags & FAST486_PREFIX_REP) && !State->Flags.Zf)
            || ((State->PrefixFlags & FAST486_PREFIX_REPNZ) && State->Flags.Zf))
        {
            /* REPZ with ZF = 0 or REPNZ with ZF = 1 */
            Repeat = FALSE;
        }

        if (Repeat)
        {
            /* Repeat the instruction */
            State->InstPtr = State->SavedInstPtr;
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeStos)
{
    ULONG DataSize;
    BOOLEAN OperandSize, AddressSize;

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFE) == 0xAA);

    TOGGLE_OPSIZE(OperandSize);
    TOGGLE_ADSIZE(AddressSize);

    /* Calculate the size */
    if (Opcode == 0xAA) DataSize = sizeof(UCHAR);
    else DataSize = OperandSize ? sizeof(ULONG) : sizeof(USHORT);

    if (State->PrefixFlags & (FAST486_PREFIX_REP | FAST486_PREFIX_REPNZ))
    {
        UCHAR Block[STRING_BLOCK_SIZE];
        ULONG Count = AddressSize ? State->GeneralRegs[FAST486_REG_ECX].Long
                                  : State->GeneralRegs[FAST486_REG_ECX].LowWord;

        /* Fill the memory block with the data */
        if (DataSize == sizeof(UCHAR))
        {
            RtlFillMemory(Block, sizeof(Block), State->GeneralRegs[FAST486_REG_EAX].LowByte);
        }
        else
        {
            ULONG i;

            for (i = 0; i < STRING_BLOCK_SIZE / DataSize; i++)
            {
                if (DataSize == sizeof(USHORT))
                {
                    ((PUSHORT)Block)[i] = State->GeneralRegs[FAST486_REG_EAX].LowWord;
                }
                else
                {
                    ((PULONG)Block)[i] = State->GeneralRegs[FAST486_REG_EAX].Long;
                }
            }
        }

        /* Transfer until finished */
        while (Count)
        {
            ULONG Processed = min(Count, STRING_BLOCK_SIZE / DataSize);

            /* Simulate the 16-bit wrap-around of DI in 16-bit address mode */
            if (!AddressSize)
            {
                ULONG MaxBytes = State->Flags.Df
                                 ? (ULONG)State->GeneralRegs[FAST486_REG_EDI].LowWord
                                 : (0x10000 - (ULONG)State->GeneralRegs[FAST486_REG_EDI].LowWord);

                Processed = min(Processed, MaxBytes / DataSize);
                if (Processed == 0) Processed = 1;
            }

            if (State->Flags.Df)
            {
                /* Set EDI to the starting location */
                if (AddressSize) State->GeneralRegs[FAST486_REG_EDI].Long -= (Processed - 1) * DataSize;
                else State->GeneralRegs[FAST486_REG_EDI].LowWord -= (Processed - 1) * DataSize;
            }

            /* Write to memory */
            if (!Fast486WriteMemory(State,
                                    FAST486_REG_ES,
                                    AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long
                                                : State->GeneralRegs[FAST486_REG_EDI].LowWord,
                                    Block,
                                    Processed * DataSize))
            {
                /* Set ECX */
                if (AddressSize) State->GeneralRegs[FAST486_REG_ECX].Long = Count;
                else State->GeneralRegs[FAST486_REG_ECX].LowWord = LOWORD(Count);

                /* Exception occurred */
                return;
            }

            if (!State->Flags.Df)
            {
                /* Increase EDI by the number of bytes transfered */
                if (AddressSize) State->GeneralRegs[FAST486_REG_EDI].Long += Processed * DataSize;
                else State->GeneralRegs[FAST486_REG_EDI].LowWord += Processed * DataSize;
            }
            else
            {
                /* Reduce EDI */
                if (AddressSize) State->GeneralRegs[FAST486_REG_EDI].Long -= DataSize;
                else State->GeneralRegs[FAST486_REG_EDI].LowWord -= DataSize;
            }

            /* Reduce the total count by the number processed in this run */
            Count -= Processed;
        }

        /* Clear ECX */
        if (AddressSize) State->GeneralRegs[FAST486_REG_ECX].Long = 0;
        else State->GeneralRegs[FAST486_REG_ECX].LowWord = 0;
    }
    else
    {
        /* Write to the destination operand */
        if (!Fast486WriteMemory(State,
                                FAST486_REG_ES,
                                AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long
                                            : State->GeneralRegs[FAST486_REG_EDI].LowWord,
                                &State->GeneralRegs[FAST486_REG_EAX].Long,
                                DataSize))
        {
            /* Exception occurred */
            return;
        }

        /* Increment/decrement EDI */
        if (AddressSize)
        {
            if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_EDI].Long += DataSize;
            else State->GeneralRegs[FAST486_REG_EDI].Long -= DataSize;
        }
        else
        {
            if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_EDI].LowWord += DataSize;
            else State->GeneralRegs[FAST486_REG_EDI].LowWord -= DataSize;
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeLods)
{
    ULONG DataSize;
    BOOLEAN OperandSize, AddressSize;
    FAST486_SEG_REGS Segment = FAST486_REG_DS;

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFE) == 0xAC);

    TOGGLE_OPSIZE(OperandSize);
    TOGGLE_ADSIZE(AddressSize);

    if (State->PrefixFlags & FAST486_PREFIX_SEG)
    {
        /* Use the override segment instead of DS */
        Segment = State->SegmentOverride;
    }

    /* Calculate the size */
    if (Opcode == 0xAC) DataSize = sizeof(UCHAR);
    else DataSize = OperandSize ? sizeof(ULONG) : sizeof(USHORT);

    if (State->PrefixFlags & (FAST486_PREFIX_REP | FAST486_PREFIX_REPNZ))
    {
        ULONG Count = AddressSize ? State->GeneralRegs[FAST486_REG_ECX].Long
                                  : State->GeneralRegs[FAST486_REG_ECX].LowWord;

        /* If the count is 0, do nothing */
        if (Count == 0) return;

        /* Only the last entry will be loaded */
        if (!State->Flags.Df)
        {
            if (AddressSize) State->GeneralRegs[FAST486_REG_ESI].Long += (Count - 1) * DataSize;
            else State->GeneralRegs[FAST486_REG_ESI].LowWord += (Count - 1) * DataSize;
        }
        else
        {
            if (AddressSize) State->GeneralRegs[FAST486_REG_ESI].Long -= (Count - 1) * DataSize;
            else State->GeneralRegs[FAST486_REG_ESI].LowWord -= (Count - 1) * DataSize;
        }

        /* Clear ECX */
        if (AddressSize) State->GeneralRegs[FAST486_REG_ECX].Long = 0;
        else State->GeneralRegs[FAST486_REG_ECX].LowWord = 0;
    }

    /* Read from the source operand */
    if (!Fast486ReadMemory(State,
                           Segment,
                           AddressSize ? State->GeneralRegs[FAST486_REG_ESI].Long
                                       : State->GeneralRegs[FAST486_REG_ESI].LowWord,
                           FALSE,
                           &State->GeneralRegs[FAST486_REG_EAX].Long,
                           DataSize))
    {
        /* Exception occurred */
        return;
    }

    /* Increment/decrement ESI */
    if (AddressSize)
    {
        if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_ESI].Long += DataSize;
        else State->GeneralRegs[FAST486_REG_ESI].Long -= DataSize;
    }
    else
    {
        if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_ESI].LowWord += DataSize;
        else State->GeneralRegs[FAST486_REG_ESI].LowWord -= DataSize;
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeScas)
{
    ULONG FirstValue = State->GeneralRegs[FAST486_REG_EAX].Long;
    ULONG SecondValue = 0;
    ULONG Result;
    ULONG DataSize, DataMask, SignFlag;
    BOOLEAN OperandSize, AddressSize;

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFE) == 0xAE);

    TOGGLE_OPSIZE(OperandSize);
    TOGGLE_ADSIZE(AddressSize);

    if ((State->PrefixFlags & FAST486_PREFIX_REP)
        || (State->PrefixFlags & FAST486_PREFIX_REPNZ))
    {
        if ((AddressSize && (State->GeneralRegs[FAST486_REG_ECX].Long == 0))
            || (!AddressSize && (State->GeneralRegs[FAST486_REG_ECX].LowWord == 0)))
        {
            /* Do nothing */
            return;
        }
    }

    /* Calculate the size */
    if (Opcode == 0xAE) DataSize = sizeof(UCHAR);
    else DataSize = OperandSize ? sizeof(ULONG) : sizeof(USHORT);

    /* Calculate the mask and sign flag */
    SignFlag = 1 << ((DataSize * 8) - 1);
    DataMask = SignFlag | (SignFlag - 1);

    /* Read from the source operand */
    if (!Fast486ReadMemory(State,
                           FAST486_REG_ES,
                           AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long
                                       : State->GeneralRegs[FAST486_REG_EDI].LowWord,
                           FALSE,
                           &SecondValue,
                           DataSize))
    {
        /* Exception occurred */
        return;
    }

    /* Calculate the result */
    FirstValue &= DataMask;
    SecondValue &= DataMask;
    Result = (FirstValue - SecondValue) & DataMask;

    /* Update the flags */
    State->Flags.Cf = (FirstValue < SecondValue);
    State->Flags.Of = ((FirstValue & SignFlag) != (SecondValue & SignFlag))
                      && ((FirstValue & SignFlag) != (Result & SignFlag));
    State->Flags.Af = (FirstValue & 0x0F) < (SecondValue & 0x0F);
    State->Flags.Zf = (Result == 0);
    State->Flags.Sf = ((Result & SignFlag) != 0);
    State->Flags.Pf = Fast486CalculateParity(Result);

    /* Increment/decrement EDI */
    if (AddressSize)
    {
        if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_EDI].Long += DataSize;
        else State->GeneralRegs[FAST486_REG_EDI].Long -= DataSize;
    }
    else
    {
        if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_EDI].LowWord += DataSize;
        else State->GeneralRegs[FAST486_REG_EDI].LowWord -= DataSize;
    }

    // FIXME: This method is slow!
    if ((State->PrefixFlags & FAST486_PREFIX_REP)
        || (State->PrefixFlags & FAST486_PREFIX_REPNZ))
    {
        BOOLEAN Repeat = TRUE;

        if (AddressSize)
        {
            if ((--State->GeneralRegs[FAST486_REG_ECX].Long) == 0)
            {
                /* ECX is 0 */
                Repeat = FALSE;
            }
        }
        else
        {
            if ((--State->GeneralRegs[FAST486_REG_ECX].LowWord) == 0)
            {
                /* CX is 0 */
                Repeat = FALSE;
            }
        }

        if (((State->PrefixFlags & FAST486_PREFIX_REP) && !State->Flags.Zf)
            || ((State->PrefixFlags & FAST486_PREFIX_REPNZ) && State->Flags.Zf))
        {
            /* REPZ with ZF = 0 or REPNZ with ZF = 1 */
            Repeat = FALSE;
        }

        if (Repeat)
        {
            /* Repeat the instruction */
            State->InstPtr = State->SavedInstPtr;
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeIns)
{
    ULONG DataSize;
    BOOLEAN OperandSize, AddressSize;

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFE) == 0x6C);

    TOGGLE_OPSIZE(OperandSize);
    TOGGLE_ADSIZE(AddressSize);

    if (!Fast486IoPrivilegeCheck(State, State->GeneralRegs[FAST486_REG_EDX].LowWord)) return;

    /* Calculate the size */
    if (Opcode == 0x6C) DataSize = sizeof(UCHAR);
    else DataSize = OperandSize ? sizeof(ULONG) : sizeof(USHORT);

    if (State->PrefixFlags & (FAST486_PREFIX_REP | FAST486_PREFIX_REPNZ))
    {
        UCHAR Block[STRING_BLOCK_SIZE];
        ULONG Count = AddressSize ? State->GeneralRegs[FAST486_REG_ECX].Long
                                  : State->GeneralRegs[FAST486_REG_ECX].LowWord;

        /* Clear the memory block */
        RtlZeroMemory(Block, sizeof(Block));

        /* Transfer until finished */
        while (Count)
        {
            ULONG Processed = min(Count, STRING_BLOCK_SIZE / DataSize);

            /* Simulate the 16-bit wrap-around of DI in 16-bit address mode */
            if (!AddressSize)
            {
                ULONG MaxBytes = State->Flags.Df
                                 ? (ULONG)State->GeneralRegs[FAST486_REG_EDI].LowWord
                                 : (0x10000 - (ULONG)State->GeneralRegs[FAST486_REG_EDI].LowWord);

                Processed = min(Processed, MaxBytes / DataSize);
                if (Processed == 0) Processed = 1;
            }

            /* Read from the I/O port */
            State->IoReadCallback(State,
                                  State->GeneralRegs[FAST486_REG_EDX].LowWord,
                                  Block,
                                  Processed,
                                  DataSize);

            if (State->Flags.Df)
            {
                ULONG i, j;

                /* Reduce EDI by the number of bytes to transfer */
                if (AddressSize) State->GeneralRegs[FAST486_REG_EDI].Long -= Processed * DataSize;
                else State->GeneralRegs[FAST486_REG_EDI].LowWord -= Processed * DataSize;

                /* Reverse the block data */
                for (i = 0; i < Processed / 2; i++)
                {
                    /* Swap the values */
                    for (j = 0; j < DataSize; j++)
                    {
                        UCHAR Temp = Block[i * DataSize + j];
                        Block[i * DataSize + j] = Block[(Processed - i - 1) * DataSize + j];
                        Block[(Processed - i - 1) * DataSize + j] = Temp;
                    }
                }
            }

            /* Write to memory */
            if (!Fast486WriteMemory(State,
                                    FAST486_REG_ES,
                                    AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long
                                                : State->GeneralRegs[FAST486_REG_EDI].LowWord,
                                    Block,
                                    Processed * DataSize))
            {
                /* Set ECX */
                if (AddressSize) State->GeneralRegs[FAST486_REG_ECX].Long = Count;
                else State->GeneralRegs[FAST486_REG_ECX].LowWord = LOWORD(Count);

                /* Exception occurred */
                return;
            }

            if (!State->Flags.Df)
            {
                /* Increase EDI by the number of bytes transfered */
                if (AddressSize) State->GeneralRegs[FAST486_REG_EDI].Long += Processed * DataSize;
                else State->GeneralRegs[FAST486_REG_EDI].LowWord += Processed * DataSize;
            }

            /* Reduce the total count by the number processed in this run */
            Count -= Processed;
        }

        /* Clear ECX */
        if (AddressSize) State->GeneralRegs[FAST486_REG_ECX].Long = 0;
        else State->GeneralRegs[FAST486_REG_ECX].LowWord = 0;
    }
    else
    {
        ULONG Data = 0;

        /* Read from the I/O port */
        State->IoReadCallback(State,
                              State->GeneralRegs[FAST486_REG_EDX].LowWord,
                              &Data,
                              1,
                              DataSize);

        /* Write to the destination operand */
        if (!Fast486WriteMemory(State,
                                FAST486_REG_ES,
                                AddressSize ? State->GeneralRegs[FAST486_REG_EDI].Long
                                            : State->GeneralRegs[FAST486_REG_EDI].LowWord,
                                &Data,
                                DataSize))
        {
            /* Exception occurred */
            return;
        }

        /* Increment/decrement EDI */
        if (AddressSize)
        {
            if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_EDI].Long += DataSize;
            else State->GeneralRegs[FAST486_REG_EDI].Long -= DataSize;
        }
        else
        {
            if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_EDI].LowWord += DataSize;
            else State->GeneralRegs[FAST486_REG_EDI].LowWord -= DataSize;
        }
    }
}

FAST486_OPCODE_HANDLER(Fast486OpcodeOuts)
{
    ULONG DataSize;
    BOOLEAN OperandSize, AddressSize;

    OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;

    /* Make sure this is the right instruction */
    ASSERT((Opcode & 0xFE) == 0x6E);

    TOGGLE_OPSIZE(OperandSize);
    TOGGLE_ADSIZE(AddressSize);

    if (!Fast486IoPrivilegeCheck(State, State->GeneralRegs[FAST486_REG_EDX].LowWord)) return;

    /* Calculate the size */
    if (Opcode == 0x6E) DataSize = sizeof(UCHAR);
    else DataSize = OperandSize ? sizeof(ULONG) : sizeof(USHORT);

    if (State->PrefixFlags & (FAST486_PREFIX_REP | FAST486_PREFIX_REPNZ))
    {
        UCHAR Block[STRING_BLOCK_SIZE];
        ULONG Count = AddressSize ? State->GeneralRegs[FAST486_REG_ECX].Long
                                  : State->GeneralRegs[FAST486_REG_ECX].LowWord;

        /* Clear the memory block */
        RtlZeroMemory(Block, sizeof(Block));

        /* Transfer until finished */
        while (Count)
        {
            ULONG Processed = min(Count, STRING_BLOCK_SIZE / DataSize);

            /* Simulate the 16-bit wrap-around of DI in 16-bit address mode */
            if (!AddressSize)
            {
                ULONG MaxBytes = State->Flags.Df
                                 ? (ULONG)State->GeneralRegs[FAST486_REG_ESI].LowWord
                                 : (0x10000 - (ULONG)State->GeneralRegs[FAST486_REG_ESI].LowWord);

                Processed = min(Processed, MaxBytes / DataSize);
                if (Processed == 0) Processed = 1;
            }

            /* Read from memory */
            if (!Fast486ReadMemory(State,
                                   (State->PrefixFlags & FAST486_PREFIX_SEG)
                                   ? State->SegmentOverride : FAST486_REG_DS,
                                   AddressSize ? State->GeneralRegs[FAST486_REG_ESI].Long
                                               : State->GeneralRegs[FAST486_REG_ESI].LowWord,
                                   FALSE,
                                   Block,
                                   Processed * DataSize))
            {
                /* Set ECX */
                if (AddressSize) State->GeneralRegs[FAST486_REG_ECX].Long = Count;
                else State->GeneralRegs[FAST486_REG_ECX].LowWord = LOWORD(Count);

                /* Exception occurred */
                return;
            }

            if (State->Flags.Df)
            {
                ULONG i, j;

                /* Reduce ESI by the number of bytes to transfer */
                if (AddressSize) State->GeneralRegs[FAST486_REG_ESI].Long -= Processed * DataSize;
                else State->GeneralRegs[FAST486_REG_ESI].LowWord -= Processed * DataSize;

                /* Reverse the block data */
                for (i = 0; i < Processed / 2; i++)
                {
                    /* Swap the values */
                    for (j = 0; j < DataSize; j++)
                    {
                        UCHAR Temp = Block[i * DataSize + j];
                        Block[i * DataSize + j] = Block[(Processed - i - 1) * DataSize + j];
                        Block[(Processed - i - 1) * DataSize + j] = Temp;
                    }
                }
            }

            /* Write to the I/O port */
            State->IoWriteCallback(State,
                                   State->GeneralRegs[FAST486_REG_EDX].LowWord,
                                   Block,
                                   Processed,
                                   DataSize);

            if (!State->Flags.Df)
            {
                /* Increase ESI by the number of bytes transfered */
                if (AddressSize) State->GeneralRegs[FAST486_REG_ESI].Long += Processed * DataSize;
                else State->GeneralRegs[FAST486_REG_ESI].LowWord += Processed * DataSize;
            }

            /* Reduce the total count by the number processed in this run */
            Count -= Processed;
        }

        /* Clear ECX */
        if (AddressSize) State->GeneralRegs[FAST486_REG_ECX].Long = 0;
        else State->GeneralRegs[FAST486_REG_ECX].LowWord = 0;
    }
    else
    {
        ULONG Data = 0;

        /* Read from the source operand */
        if (!Fast486ReadMemory(State,
                               (State->PrefixFlags & FAST486_PREFIX_SEG)
                               ? State->SegmentOverride : FAST486_REG_DS,
                               AddressSize ? State->GeneralRegs[FAST486_REG_ESI].Long
                                           : State->GeneralRegs[FAST486_REG_ESI].LowWord,
                               FALSE,
                               &Data,
                               DataSize))
        {
            /* Exception occurred */
            return;
        }

        /* Write to the I/O port */
        State->IoWriteCallback(State,
                               State->GeneralRegs[FAST486_REG_EDX].LowWord,
                               &Data,
                               1,
                               DataSize);

        /* Increment/decrement ESI */
        if (AddressSize)
        {
            if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_ESI].Long += DataSize;
            else State->GeneralRegs[FAST486_REG_ESI].Long -= DataSize;
        }
        else
        {
            if (!State->Flags.Df) State->GeneralRegs[FAST486_REG_ESI].LowWord += DataSize;
            else State->GeneralRegs[FAST486_REG_ESI].LowWord -= DataSize;
        }
    }
}

/* EOF */