[SOFT386]
[reactos.git] / lib / soft386 / opcodes.c
index b0dd8f5..45dcf46 100644 (file)
@@ -11,6 +11,7 @@
 // #define WIN32_NO_STATUS
 // #define _INC_WINDOWS
 #include <windef.h>
+#include <limits.h>
 
 // #define NDEBUG
 #include <debug.h>
@@ -131,7 +132,7 @@ Soft386OpcodeHandlers[SOFT386_NUM_OPCODE_HANDLERS] =
     Soft386OpcodePushImm,
     Soft386OpcodeImulModrmImm,
     Soft386OpcodePushByteImm,
-    Soft386OpcodeImulModrmByteImm,
+    Soft386OpcodeImulModrmImm,
     NULL, // TODO: OPCODE 0x6C NOT SUPPORTED
     NULL, // TODO: OPCODE 0x6D NOT SUPPORTED
     NULL, // TODO: OPCODE 0x6E NOT SUPPORTED
@@ -184,10 +185,10 @@ Soft386OpcodeHandlers[SOFT386_NUM_OPCODE_HANDLERS] =
     Soft386OpcodePopFlags,
     Soft386OpcodeSahf,
     Soft386OpcodeLahf,
-    NULL, // TODO: OPCODE 0xA0 NOT SUPPORTED
-    NULL, // TODO: OPCODE 0xA1 NOT SUPPORTED
-    NULL, // TODO: OPCODE 0xA2 NOT SUPPORTED
-    NULL, // TODO: OPCODE 0xA3 NOT SUPPORTED
+    Soft386OpcodeMovAlOffset,
+    Soft386OpcodeMovEaxOffset,
+    Soft386OpcodeMovOffsetAl,
+    Soft386OpcodeMovOffsetEax,
     NULL, // TODO: OPCODE 0xA4 NOT SUPPORTED
     NULL, // TODO: OPCODE 0xA5 NOT SUPPORTED
     NULL, // TODO: OPCODE 0xA6 NOT SUPPORTED
@@ -220,8 +221,8 @@ Soft386OpcodeHandlers[SOFT386_NUM_OPCODE_HANDLERS] =
     NULL, // TODO: OPCODE 0xC1 NOT SUPPORTED
     Soft386OpcodeRet,
     Soft386OpcodeRet,
-    Soft386OpcodeLes,
-    Soft386OpcodeLds,
+    Soft386OpcodeLdsLes,
+    Soft386OpcodeLdsLes,
     NULL, // TODO: OPCODE 0xC6 NOT SUPPORTED
     NULL, // TODO: OPCODE 0xC7 NOT SUPPORTED
     Soft386OpcodeEnter,
@@ -1425,18 +1426,19 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeAddEax)
     /* Make sure this is the right instruction */
     ASSERT(Opcode == 0x05);
 
-    if (State->PrefixFlags == SOFT386_PREFIX_OPSIZE)
-    {
-        /* The OPSIZE prefix toggles the size */
-        Size = !Size;
-    }
-    else
+    if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
     {
         /* Invalid prefix */
         Soft386Exception(State, SOFT386_EXCEPTION_UD);
         return FALSE;
     }
 
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
+
     if (Size)
     {
         ULONG FirstValue = State->GeneralRegs[SOFT386_REG_EAX].Long;
@@ -1695,18 +1697,19 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeOrEax)
     /* Make sure this is the right instruction */
     ASSERT(Opcode == 0x0D);
 
-    if (State->PrefixFlags == SOFT386_PREFIX_OPSIZE)
-    {
-        /* The OPSIZE prefix toggles the size */
-        Size = !Size;
-    }
-    else
+    if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
     {
         /* Invalid prefix */
         Soft386Exception(State, SOFT386_EXCEPTION_UD);
         return FALSE;
     }
 
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
+
     if (Size)
     {
         ULONG FirstValue = State->GeneralRegs[SOFT386_REG_EAX].Long;
@@ -1925,9 +1928,9 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeAndAl)
     /* Make sure this is the right instruction */
     ASSERT(Opcode == 0x24);
 
-    if (State->PrefixFlags)
+    if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
     {
-        /* This opcode doesn't take any prefixes */
+        /* Invalid prefix */
         Soft386Exception(State, SOFT386_EXCEPTION_UD);
         return FALSE;
     }
@@ -1961,18 +1964,19 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeAndEax)
     /* Make sure this is the right instruction */
     ASSERT(Opcode == 0x25);
 
-    if (State->PrefixFlags == SOFT386_PREFIX_OPSIZE)
-    {
-        /* The OPSIZE prefix toggles the size */
-        Size = !Size;
-    }
-    else
+    if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
     {
         /* Invalid prefix */
         Soft386Exception(State, SOFT386_EXCEPTION_UD);
         return FALSE;
     }
 
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
+
     if (Size)
     {
         ULONG FirstValue = State->GeneralRegs[SOFT386_REG_EAX].Long;
@@ -2227,18 +2231,19 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeXorEax)
     /* Make sure this is the right instruction */
     ASSERT(Opcode == 0x35);
 
-    if (State->PrefixFlags == SOFT386_PREFIX_OPSIZE)
-    {
-        /* The OPSIZE prefix toggles the size */
-        Size = !Size;
-    }
-    else
+    if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
     {
         /* Invalid prefix */
         Soft386Exception(State, SOFT386_EXCEPTION_UD);
         return FALSE;
     }
 
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
+
     if (Size)
     {
         ULONG FirstValue = State->GeneralRegs[SOFT386_REG_EAX].Long;
@@ -2478,18 +2483,19 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeTestEax)
     /* Make sure this is the right instruction */
     ASSERT(Opcode == 0xA9);
 
-    if (State->PrefixFlags == SOFT386_PREFIX_OPSIZE)
-    {
-        /* The OPSIZE prefix toggles the size */
-        Size = !Size;
-    }
-    else
+    if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
     {
         /* Invalid prefix */
         Soft386Exception(State, SOFT386_EXCEPTION_UD);
         return FALSE;
     }
 
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
+
     if (Size)
     {
         ULONG FirstValue = State->GeneralRegs[SOFT386_REG_EAX].Long;
@@ -2965,18 +2971,19 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeAdcEax)
     /* Make sure this is the right instruction */
     ASSERT(Opcode == 0x15);
 
-    if (State->PrefixFlags == SOFT386_PREFIX_OPSIZE)
-    {
-        /* The OPSIZE prefix toggles the size */
-        Size = !Size;
-    }
-    else
+    if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
     {
         /* Invalid prefix */
         Soft386Exception(State, SOFT386_EXCEPTION_UD);
         return FALSE;
     }
 
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
+
     if (Size)
     {
         ULONG FirstValue = State->GeneralRegs[SOFT386_REG_EAX].Long;
@@ -3418,18 +3425,19 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeCmpSubEax)
     /* Make sure this is the right instruction */
     ASSERT((Opcode & 0xEF) == 0x2D);
 
-    if (State->PrefixFlags == SOFT386_PREFIX_OPSIZE)
-    {
-        /* The OPSIZE prefix toggles the size */
-        Size = !Size;
-    }
-    else
+    if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
     {
         /* Invalid prefix */
         Soft386Exception(State, SOFT386_EXCEPTION_UD);
         return FALSE;
     }
 
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
+
     if (Size)
     {
         ULONG FirstValue = State->GeneralRegs[SOFT386_REG_EAX].Long;
@@ -3790,10 +3798,125 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodePushImm)
 
 SOFT386_OPCODE_HANDLER(Soft386OpcodeImulModrmImm)
 {
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
+    BOOLEAN OperandSize, AddressSize;
+    SOFT386_MOD_REG_RM ModRegRm;
+    LONG Multiplier;
+    LONGLONG Product;
 
-    return FALSE;
+    /* Make sure this is the right instruction */
+    ASSERT((Opcode & 0xFD) == 0x69);
+
+    OperandSize = AddressSize = State->SegmentRegs[SOFT386_REG_CS].Size;
+
+    if (State->PrefixFlags & SOFT386_PREFIX_ADSIZE)
+    {
+        /* The ADSIZE prefix toggles the address size */
+        AddressSize = !AddressSize;
+    }
+
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the operand size */
+        OperandSize = !OperandSize;
+    }
+
+    /* Fetch the parameters */
+    if (!Soft386ParseModRegRm(State, AddressSize, &ModRegRm))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    if (Opcode == 0x6B)
+    {
+        CHAR Byte;
+
+        /* Fetch the immediate operand */
+        if (!Soft386FetchByte(State, (PUCHAR)&Byte))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+
+        Multiplier = (LONG)Byte;
+    }
+    else
+    {
+        if (OperandSize)
+        {
+            LONG Dword;
+
+            /* Fetch the immediate operand */
+            if (!Soft386FetchDword(State, (PULONG)&Dword))
+            {
+                /* Exception occurred */
+                return FALSE;
+            }
+
+            Multiplier = Dword;
+        }
+        else
+        {
+            SHORT Word;
+
+            /* Fetch the immediate operand */
+            if (!Soft386FetchWord(State, (PUSHORT)&Word))
+            {
+                /* Exception occurred */
+                return FALSE;
+            }
+
+            Multiplier = (LONG)Word;
+        }
+    }
+
+    if (OperandSize)
+    {
+        LONG RegValue, Multiplicand;
+
+        /* Read the operands */
+        if (!Soft386ReadModrmDwordOperands(State,
+                                           &ModRegRm,
+                                           (PULONG)&RegValue,
+                                           (PULONG)&Multiplicand))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+
+        /* Multiply */
+        Product = (LONGLONG)Multiplicand * (LONGLONG)Multiplier;
+    }
+    else
+    {
+        SHORT RegValue, Multiplicand;
+
+        /* Read the operands */
+        if (!Soft386ReadModrmWordOperands(State,
+                                          &ModRegRm,
+                                          (PUSHORT)&RegValue,
+                                          (PUSHORT)&Multiplicand))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+
+        /* Multiply */
+        Product = (LONGLONG)Multiplicand * (LONGLONG)Multiplier;
+    }
+
+    /* Check for carry/overflow */
+    if ((Product < LONG_MIN) || (Product > LONG_MAX))
+    {
+        State->Flags.Cf = State->Flags.Of = TRUE;
+    }
+    else State->Flags.Cf = State->Flags.Of = FALSE;
+
+    /* Write-back the result */
+    return Soft386WriteModrmDwordOperands(State,
+                                          &ModRegRm,
+                                          TRUE,
+                                          (ULONG)((LONG)Product));
 }
 
 SOFT386_OPCODE_HANDLER(Soft386OpcodePushByteImm)
@@ -3813,14 +3936,6 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodePushByteImm)
     return Soft386StackPush(State, Data);
 }
 
-SOFT386_OPCODE_HANDLER(Soft386OpcodeImulModrmByteImm)
-{
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
-
-    return FALSE;
-}
-
 SOFT386_OPCODE_HANDLER(Soft386OpcodeMovByteModrm)
 {
     UCHAR FirstValue, SecondValue, Result;
@@ -4125,55 +4240,39 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeWait)
 
 SOFT386_OPCODE_HANDLER(Soft386OpcodePushFlags)
 {
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
-
-    return FALSE;
-}
-
-SOFT386_OPCODE_HANDLER(Soft386OpcodePopFlags)
-{
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
-
-    return FALSE;
-}
-
-SOFT386_OPCODE_HANDLER(Soft386OpcodeSahf)
-{
-    /* 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[SOFT386_REG_EAX].HighByte;
-
-    /* Restore the reserved bits of FLAGS */
-    State->Flags.AlwaysSet = TRUE;
-    State->Flags.Reserved0 = State->Flags.Reserved1 = FALSE;
+    BOOLEAN Size = State->SegmentRegs[SOFT386_REG_CS].Size;
 
-    return FALSE;
-}
+    if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
+    {
+        /* Invalid prefix */
+        Soft386Exception(State, SOFT386_EXCEPTION_UD);
+        return FALSE;
+    }
 
-SOFT386_OPCODE_HANDLER(Soft386OpcodeLahf)
-{
-    /* Make sure this is the right instruction */
-    ASSERT(Opcode == 0x9F);
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* This OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
 
-    /* Set AH to the low-order byte of FLAGS */
-    State->GeneralRegs[SOFT386_REG_EAX].HighByte = LOBYTE(State->Flags.Long);
+    /* Check for VM86 mode when IOPL is not 3 */
+    if (State->Flags.Vm && (State->Flags.Iopl != 3))
+    {
+        /* Call the VM86 monitor */
+        Soft386ExceptionWithErrorCode(State, SOFT386_EXCEPTION_GP, 0);
+        return FALSE;
+    }
 
-    return FALSE;
+    /* Push the flags */
+    if (Size) return Soft386StackPush(State, State->Flags.Long);
+    else return Soft386StackPush(State, LOWORD(State->Flags.Long));
 }
 
-SOFT386_OPCODE_HANDLER(Soft386OpcodeRet)
+SOFT386_OPCODE_HANDLER(Soft386OpcodePopFlags)
 {
-    ULONG ReturnAddress;
-    USHORT BytesToPop = 0;
     BOOLEAN Size = State->SegmentRegs[SOFT386_REG_CS].Size;
-
-    /* Make sure this is the right instruction */
-    ASSERT((Opcode & 0xFE) == 0xC2);
+    INT Cpl = Soft386GetCurrentPrivLevel(State);
+    ULONG NewFlags;
 
     if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
     {
@@ -4182,58 +4281,365 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeRet)
         return FALSE;
     }
 
-    if (State->PrefixFlags == SOFT386_PREFIX_OPSIZE)
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
     {
-        /* The OPSIZE prefix toggles the size */
+        /* This OPSIZE prefix toggles the size */
         Size = !Size;
     }
 
-    if (Opcode == 0xC2)
+    /* Pop the new flags */
+    if (!Soft386StackPop(State, &NewFlags))
     {
-        /* Fetch the number of bytes to pop after the return */
-        if (!Soft386FetchWord(State, &BytesToPop)) return FALSE;
+        /* Exception occurred */
+        return FALSE;
     }
 
-    /* Pop the return address */
-    if (!Soft386StackPop(State, &ReturnAddress)) return FALSE;
-
-    /* Return to the calling procedure, and if necessary, pop the parameters */
-    if (Size)
+    if (!State->Flags.Vm)
     {
-        State->InstPtr.Long = ReturnAddress;
-        State->GeneralRegs[SOFT386_REG_ESP].Long += BytesToPop;
+        /* Check the current privilege level */
+        if (Cpl == 0)
+        {
+            /* Supervisor */
+
+            /* Set the flags */
+            if (Size)
+            {
+                /* Memorize the old state of RF */
+                BOOLEAN OldRf = State->Flags.Rf;
+
+                State->Flags.Long = NewFlags;
+
+                /* Restore VM and RF */
+                State->Flags.Vm = FALSE;
+                State->Flags.Rf = OldRf;
+
+                /* Clear VIF and VIP */
+                State->Flags.Vif = State->Flags.Vip = FALSE;
+            }
+            else State->Flags.LowWord = LOWORD(NewFlags);
+
+            /* Restore the reserved bits */
+            State->Flags.AlwaysSet = TRUE;
+            State->Flags.Reserved0 = FALSE;
+            State->Flags.Reserved1 = FALSE;
+        }
+        else
+        {
+            /* User */
+
+            /* Memorize the old state of IF and IOPL */
+            BOOLEAN OldIf = State->Flags.If;
+            UINT OldIopl = State->Flags.Iopl;
+
+            /* Set the flags */
+            if (Size)
+            {
+                /* Memorize the old state of RF */
+                BOOLEAN OldRf = State->Flags.Rf;
+
+                State->Flags.Long = NewFlags;
+
+                /* Restore VM and RF */
+                State->Flags.Vm = FALSE;
+                State->Flags.Rf = OldRf;
+
+                /* Clear VIF and VIP */
+                State->Flags.Vif = State->Flags.Vip = FALSE;
+            }
+            else State->Flags.LowWord = LOWORD(NewFlags);
+
+            /* Restore the reserved bits and IOPL */
+            State->Flags.AlwaysSet = TRUE;
+            State->Flags.Reserved0 = FALSE;
+            State->Flags.Reserved1 = FALSE;
+            State->Flags.Iopl = OldIopl;
+
+            /* Check if the user doesn't have the privilege to change IF */
+            if (Cpl > State->Flags.Iopl)
+            {
+                /* Restore IF */
+                State->Flags.If = OldIf;
+            }
+        }
     }
     else
     {
-        State->InstPtr.LowWord = LOWORD(ReturnAddress);
-        State->GeneralRegs[SOFT386_REG_ESP].LowWord += BytesToPop;
+        /* Check the IOPL */
+        if (State->Flags.Iopl == 3)
+        {
+            if (Size)
+            {
+                /* Memorize the old state of RF, VIF and VIP */
+                BOOLEAN OldRf = State->Flags.Rf;
+                BOOLEAN OldVif = State->Flags.Vif;
+                BOOLEAN OldVip = State->Flags.Vip;
+
+                State->Flags.Long = NewFlags;
+
+                /* Restore VM, RF, VIF and VIP */
+                State->Flags.Vm = TRUE;
+                State->Flags.Rf = OldRf;
+                State->Flags.Vif = OldVif;
+                State->Flags.Vip = OldVip;
+            }
+            else State->Flags.LowWord = LOWORD(NewFlags);
+
+            /* Restore the reserved bits and IOPL */
+            State->Flags.AlwaysSet = TRUE;
+            State->Flags.Reserved0 = FALSE;
+            State->Flags.Reserved1 = FALSE;
+            State->Flags.Iopl = 3;
+        }
+        else
+        {
+            /* Call the VM86 monitor */
+            Soft386ExceptionWithErrorCode(State, SOFT386_EXCEPTION_GP, 0);
+        }
+        
     }
 
     return TRUE;
 }
 
-SOFT386_OPCODE_HANDLER(Soft386OpcodeLes)
+SOFT386_OPCODE_HANDLER(Soft386OpcodeSahf)
 {
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
+    /* 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[SOFT386_REG_EAX].HighByte;
+
+    /* Restore the reserved bits of FLAGS */
+    State->Flags.AlwaysSet = TRUE;
+    State->Flags.Reserved0 = State->Flags.Reserved1 = FALSE;
 
     return FALSE;
 }
 
-SOFT386_OPCODE_HANDLER(Soft386OpcodeLds)
+SOFT386_OPCODE_HANDLER(Soft386OpcodeLahf)
 {
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
+    /* Make sure this is the right instruction */
+    ASSERT(Opcode == 0x9F);
+
+    /* Set AH to the low-order byte of FLAGS */
+    State->GeneralRegs[SOFT386_REG_EAX].HighByte = LOBYTE(State->Flags.Long);
 
     return FALSE;
 }
 
+SOFT386_OPCODE_HANDLER(Soft386OpcodeRet)
+{
+    ULONG ReturnAddress;
+    USHORT BytesToPop = 0;
+    BOOLEAN Size = State->SegmentRegs[SOFT386_REG_CS].Size;
+
+    /* Make sure this is the right instruction */
+    ASSERT((Opcode & 0xFE) == 0xC2);
+
+    if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
+    {
+        /* Invalid prefix */
+        Soft386Exception(State, SOFT386_EXCEPTION_UD);
+        return FALSE;
+    }
+
+    if (State->PrefixFlags == SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
+
+    if (Opcode == 0xC2)
+    {
+        /* Fetch the number of bytes to pop after the return */
+        if (!Soft386FetchWord(State, &BytesToPop)) return FALSE;
+    }
+
+    /* Pop the return address */
+    if (!Soft386StackPop(State, &ReturnAddress)) return FALSE;
+
+    /* Return to the calling procedure, and if necessary, pop the parameters */
+    if (Size)
+    {
+        State->InstPtr.Long = ReturnAddress;
+        State->GeneralRegs[SOFT386_REG_ESP].Long += BytesToPop;
+    }
+    else
+    {
+        State->InstPtr.LowWord = LOWORD(ReturnAddress);
+        State->GeneralRegs[SOFT386_REG_ESP].LowWord += BytesToPop;
+    }
+
+    return TRUE;
+}
+
+SOFT386_OPCODE_HANDLER(Soft386OpcodeLdsLes)
+{
+    UCHAR FarPointer[6];
+    BOOLEAN OperandSize, AddressSize;
+    SOFT386_MOD_REG_RM ModRegRm;
+
+    /* Make sure this is the right instruction */
+    ASSERT((Opcode & 0xFE) == 0xC4);
+
+    OperandSize = AddressSize = State->SegmentRegs[SOFT386_REG_CS].Size;
+
+    if (State->PrefixFlags & SOFT386_PREFIX_ADSIZE)
+    {
+        /* The ADSIZE prefix toggles the size */
+        AddressSize = !AddressSize;
+    }
+
+    /* Get the operands */
+    if (!Soft386ParseModRegRm(State, AddressSize, &ModRegRm))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    if (!ModRegRm.Memory)
+    {
+        /* Check if this is a BOP and the host supports BOPs */
+        if ((Opcode == 0xC4)
+            && (ModRegRm.Register == SOFT386_REG_EAX)
+            && (ModRegRm.SecondRegister == SOFT386_REG_EBP)
+            && (State->BopCallback != NULL))
+        {
+            USHORT BopCode;
+
+            /* Fetch the BOP code */
+            if (!Soft386FetchWord(State, &BopCode))
+            {
+                /* Exception occurred */
+                return FALSE;
+            }
+
+            /* Call the BOP handler */
+            State->BopCallback(State, BopCode);
+
+            /* Return success */
+            return TRUE;
+        }
+
+        /* Invalid */
+        Soft386Exception(State, SOFT386_EXCEPTION_UD);
+        return FALSE;
+    }
+
+    if (!Soft386ReadMemory(State,
+                           (State->PrefixFlags & SOFT386_PREFIX_SEG)
+                           ? State->SegmentOverride : SOFT386_REG_DS,
+                           ModRegRm.MemoryAddress,
+                           FALSE,
+                           FarPointer,
+                           OperandSize ? 6 : 4))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    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 */
+        return Soft386LoadSegment(State,
+                                  (Opcode == 0xC4)
+                                  ? SOFT386_REG_ES : SOFT386_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 */
+        return Soft386LoadSegment(State,
+                                  (Opcode == 0xC4)
+                                  ? SOFT386_REG_ES : SOFT386_REG_DS,
+                                  Segment);
+    }
+}
+
 SOFT386_OPCODE_HANDLER(Soft386OpcodeEnter)
 {
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
+    INT i;
+    BOOLEAN Size = State->SegmentRegs[SOFT386_REG_CS].Size;
+    USHORT FrameSize;
+    UCHAR NestingLevel;
+    SOFT386_REG FramePointer;
 
-    return FALSE;
+    /* Make sure this is the right instruction */
+    ASSERT(Opcode == 0xC8);
+
+    if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
+    {
+        /* Invalid prefix */
+        Soft386Exception(State, SOFT386_EXCEPTION_UD);
+        return FALSE;
+    }
+
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
+
+    if (!Soft386FetchWord(State, &FrameSize))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    if (!Soft386FetchByte(State, &NestingLevel))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    /* Push EBP */
+    if (!Soft386StackPush(State, State->GeneralRegs[SOFT386_REG_EBP].Long))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    /* Save ESP */
+    FramePointer = State->GeneralRegs[SOFT386_REG_ESP];
+
+    /* Set up the nested procedure stacks */
+    for (i = 1; i < NestingLevel; i++)
+    {
+        if (Size)
+        {
+            State->GeneralRegs[SOFT386_REG_EBP].Long -= 4;
+            Soft386StackPush(State, State->GeneralRegs[SOFT386_REG_EBP].Long);
+        }
+        else
+        {
+            State->GeneralRegs[SOFT386_REG_EBP].LowWord -= 2;
+            Soft386StackPush(State, State->GeneralRegs[SOFT386_REG_EBP].LowWord);
+        }
+    }
+
+    if (NestingLevel > 0) Soft386StackPush(State, FramePointer.Long);
+
+    /* Set EBP to the frame pointer */
+    State->GeneralRegs[SOFT386_REG_EBP] = FramePointer;
+
+    /* Reserve space for the frame */
+    if (Size) State->GeneralRegs[SOFT386_REG_ESP].Long -= (ULONG)FrameSize;
+    else State->GeneralRegs[SOFT386_REG_ESP].LowWord -= FrameSize;
+
+    return TRUE;
 }
 
 SOFT386_OPCODE_HANDLER(Soft386OpcodeLeave)
@@ -4250,7 +4656,7 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeLeave)
         return FALSE;
     }
 
-    if (State->PrefixFlags == SOFT386_PREFIX_OPSIZE)
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
     {
         /* The OPSIZE prefix toggles the size */
         Size = !Size;
@@ -4363,26 +4769,291 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeInt)
 
 SOFT386_OPCODE_HANDLER(Soft386OpcodeIret)
 {
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
+    INT i;
+    ULONG InstPtr, CodeSel, StackPtr, StackSel;
+    SOFT386_FLAGS_REG NewFlags;
+    BOOLEAN Size = State->SegmentRegs[SOFT386_REG_CS].Size;
 
-    return FALSE;
+    /* Make sure this is the right instruction */
+    ASSERT(Opcode == 0xCF);
+
+    if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
+    {
+        /* Invalid prefix */
+        Soft386Exception(State, SOFT386_EXCEPTION_UD);
+        return FALSE;
+    }
+
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
+
+    /* Pop EIP */
+    if (!Soft386StackPop(State, &InstPtr))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    /* Pop CS */
+    if (!Soft386StackPop(State, &CodeSel))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    /* Pop EFLAGS */
+    if (!Soft386StackPop(State, &NewFlags.Long))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    /* Check for protected mode */
+    if (State->ControlRegisters[SOFT386_REG_CR0] & SOFT386_CR0_PE)
+    {
+        INT Cpl = Soft386GetCurrentPrivLevel(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 (!Soft386LoadSegment(State, SOFT386_REG_CS, CodeSel))
+                {
+                    /* Exception occurred */
+                    return FALSE;
+                }
+
+                /* 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 */
+                Soft386ExceptionWithErrorCode(State, SOFT386_EXCEPTION_GP, 0);
+                return FALSE;
+            }
+
+            return TRUE;
+        }
+
+        if (State->Flags.Nt)
+        {
+            /* Nested task return */
+
+            UNIMPLEMENTED;
+            return FALSE;
+        }
+
+        if (NewFlags.Vm)
+        {
+            /* Return to VM86 mode */
+            ULONG Es, Ds, Fs, Gs;
+
+            /* Pop ESP, SS, ES, FS, GS */
+            if (!Soft386StackPop(State, &StackPtr)) return FALSE;
+            if (!Soft386StackPop(State, &StackSel)) return FALSE;
+            if (!Soft386StackPop(State, &Es)) return FALSE;
+            if (!Soft386StackPop(State, &Ds)) return FALSE;
+            if (!Soft386StackPop(State, &Fs)) return FALSE;
+            if (!Soft386StackPop(State, &Gs)) return FALSE;
+
+            /* Set the new IP */
+            State->InstPtr.Long = LOWORD(InstPtr);
+
+            /* 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;
+
+            /* Load the new segments */
+            if (!Soft386LoadSegment(State, SOFT386_REG_CS, CodeSel)) return FALSE;
+            if (!Soft386LoadSegment(State, SOFT386_REG_SS, StackSel)) return FALSE;
+            if (!Soft386LoadSegment(State, SOFT386_REG_ES, Es)) return FALSE;
+            if (!Soft386LoadSegment(State, SOFT386_REG_DS, Ds)) return FALSE;
+            if (!Soft386LoadSegment(State, SOFT386_REG_FS, Fs)) return FALSE;
+            if (!Soft386LoadSegment(State, SOFT386_REG_GS, Gs)) return FALSE;
+
+            return TRUE;
+        }
+
+        /* Load the new CS */
+        if (!Soft386LoadSegment(State, SOFT386_REG_CS, CodeSel))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+
+        /* Set EIP */
+        if (Size) State->InstPtr.Long = InstPtr;
+        else State->InstPtr.LowWord = LOWORD(InstPtr);
+
+        if (GET_SEGMENT_RPL(CodeSel) > Cpl)
+        {
+            /* Pop ESP */
+            if (!Soft386StackPop(State, &StackPtr))
+            {
+                /* Exception */
+                return FALSE;
+            }
+
+            /* Pop SS */
+            if (!Soft386StackPop(State, &StackSel))
+            {
+                /* Exception */
+                return FALSE;
+            }
+
+            /* Load new SS */
+            if (!Soft386LoadSegment(State, SOFT386_REG_SS, StackSel))
+            {
+                /* Exception */
+                return FALSE;
+            }
+
+            /* Set ESP */
+            if (Size) State->GeneralRegs[SOFT386_REG_ESP].Long = StackPtr;
+            else State->GeneralRegs[SOFT386_REG_ESP].LowWord = LOWORD(StackPtr);
+        }
+
+        /* Set the new flags */
+        if (Size) State->Flags.Long = NewFlags.Long & PROT_MODE_FLAGS_MASK;
+        else State->Flags.LowWord = NewFlags.LowWord & PROT_MODE_FLAGS_MASK;
+        State->Flags.AlwaysSet = TRUE;
+
+        /* Set additional flags */
+        if (Cpl <= State->Flags.Iopl) State->Flags.If = NewFlags.If;
+        if (Cpl == 0) State->Flags.Iopl = NewFlags.Iopl;
+
+        if (GET_SEGMENT_RPL(CodeSel) > Cpl)
+        {
+            /* Update the CPL */
+            Cpl = Soft386GetCurrentPrivLevel(State);
+
+            /* Check segment security */
+            for (i = 0; i <= SOFT386_NUM_SEG_REGS; i++)
+            {
+                /* Don't check CS or SS */
+                if ((i == SOFT386_REG_CS) || (i == SOFT386_REG_SS)) continue;
+
+                if ((Cpl > State->SegmentRegs[i].Dpl)
+                    && (!State->SegmentRegs[i].Executable
+                    || !State->SegmentRegs[i].DirConf))
+                {
+                    /* Load the NULL descriptor in the segment */
+                    if (!Soft386LoadSegment(State, i, 0)) return FALSE;
+                }
+            }
+        }
+    }
+    else
+    {
+        if (Size && (InstPtr & 0xFFFF0000))
+        {
+            /* Invalid */
+            Soft386ExceptionWithErrorCode(State, SOFT386_EXCEPTION_GP, 0);
+            return FALSE;
+        }
+
+        /* Set new EIP */
+        State->InstPtr.Long = InstPtr;
+
+        /* Load new CS */
+        if (!Soft386LoadSegment(State, SOFT386_REG_CS, CodeSel))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+
+        /* 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;
+    }
+
+    return TRUE;
 }
 
 SOFT386_OPCODE_HANDLER(Soft386OpcodeAam)
 {
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
+    UCHAR Base;
+    UCHAR Value = State->GeneralRegs[SOFT386_REG_EAX].LowByte;
 
-    return FALSE;
+    if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
+    {
+        /* Invalid prefix */
+        Soft386Exception(State, SOFT386_EXCEPTION_UD);
+        return FALSE;
+    }
+
+    /* Fetch the base */
+    if (!Soft386FetchByte(State, &Base))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    /* Check if the base is zero */
+    if (Base == 0)
+    {
+        /* Divide error */
+        Soft386Exception(State, SOFT386_EXCEPTION_DE);
+        return FALSE;
+    }
+
+    /* Adjust */
+    State->GeneralRegs[SOFT386_REG_EAX].HighByte = Value / Base;
+    State->GeneralRegs[SOFT386_REG_EAX].LowByte = Value %= Base;
+
+    /* Update flags */
+    State->Flags.Zf = (Value == 0) ? TRUE : FALSE;
+    State->Flags.Sf = (Value & SIGN_FLAG_BYTE) ? TRUE : FALSE;
+    State->Flags.Pf = Soft386CalculateParity(Value);
+
+    return TRUE;
 }
 
 SOFT386_OPCODE_HANDLER(Soft386OpcodeAad)
 {
-    // TODO: NOT IMPLEMENTED
-    UNIMPLEMENTED;
+    UCHAR Base;
+    UCHAR Value = State->GeneralRegs[SOFT386_REG_EAX].LowByte;
 
-    return FALSE;
+    if (State->PrefixFlags & SOFT386_PREFIX_LOCK)
+    {
+        /* Invalid prefix */
+        Soft386Exception(State, SOFT386_EXCEPTION_UD);
+        return FALSE;
+    }
+
+    /* Fetch the base */
+    if (!Soft386FetchByte(State, &Base))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    /* Adjust */
+    Value += State->GeneralRegs[SOFT386_REG_EAX].HighByte * Base;
+    State->GeneralRegs[SOFT386_REG_EAX].LowByte = Value;
+
+    /* Update flags */
+    State->Flags.Zf = (Value == 0) ? TRUE : FALSE;
+    State->Flags.Sf = (Value & SIGN_FLAG_BYTE) ? TRUE : FALSE;
+    State->Flags.Pf = Soft386CalculateParity(Value);
+
+    return TRUE;
 }
 
 SOFT386_OPCODE_HANDLER(Soft386OpcodeXlat)
@@ -4409,7 +5080,7 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeLoop)
         return FALSE;
     }
 
-    if (State->PrefixFlags == SOFT386_PREFIX_OPSIZE)
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
     {
         /* The OPSIZE prefix toggles the size */
         Size = !Size;
@@ -4506,13 +5177,6 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeCall)
         return FALSE;
     }
 
-    /* Push the current value of the instruction pointer */
-    if (!Soft386StackPush(State, State->InstPtr.Long))
-    {
-        /* Exception occurred */
-        return FALSE;
-    }
-
     if (Size)
     {
         LONG Offset = 0;
@@ -4524,6 +5188,13 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeCall)
             return FALSE;
         }
 
+        /* Push the current value of the instruction pointer */
+        if (!Soft386StackPush(State, State->InstPtr.Long))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+
         /* Move the instruction pointer */        
         State->InstPtr.Long += Offset;
     }
@@ -4538,6 +5209,13 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeCall)
             return FALSE;
         }
 
+        /* Push the current value of the instruction pointer */
+        if (!Soft386StackPush(State, State->InstPtr.Long))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+
         /* Move the instruction pointer */        
         State->InstPtr.LowWord += Offset;
     }
@@ -4603,3 +5281,196 @@ SOFT386_OPCODE_HANDLER(Soft386OpcodeJmpAbs)
 
     return FALSE;
 }
+
+SOFT386_OPCODE_HANDLER(Soft386OpcodeMovAlOffset)
+{
+    BOOLEAN Size = State->SegmentRegs[SOFT386_REG_CS].Size;
+    ULONG Offset;
+
+    /* Make sure this is the right instruction */
+    ASSERT(Opcode == 0xA0);
+
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
+
+    if (Size)
+    {
+        if (!Soft386FetchDword(State, &Offset))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+    }
+    else
+    {
+        USHORT WordOffset;
+
+        if (!Soft386FetchWord(State, &WordOffset))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+
+        Offset = (ULONG)WordOffset;
+    }
+
+    /* Read from memory */
+    return Soft386ReadMemory(State,
+                             (State->PrefixFlags & SOFT386_PREFIX_SEG) ?
+                             State->SegmentOverride : SOFT386_REG_DS,
+                             Offset,
+                             FALSE,
+                             &State->GeneralRegs[SOFT386_REG_EAX].LowByte,
+                             sizeof(UCHAR));
+}
+
+SOFT386_OPCODE_HANDLER(Soft386OpcodeMovEaxOffset)
+{
+    BOOLEAN Size = State->SegmentRegs[SOFT386_REG_CS].Size;
+
+    /* Make sure this is the right instruction */
+    ASSERT(Opcode == 0xA1);
+
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
+
+    if (Size)
+    {
+        ULONG Offset;
+
+        if (!Soft386FetchDword(State, &Offset))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+
+        /* Read from memory */
+        return Soft386ReadMemory(State,
+                                 (State->PrefixFlags & SOFT386_PREFIX_SEG) ?
+                                 State->SegmentOverride : SOFT386_REG_DS,
+                                 Offset,
+                                 FALSE,
+                                 &State->GeneralRegs[SOFT386_REG_EAX].Long,
+                                 sizeof(ULONG));
+    }
+    else
+    {
+        USHORT Offset;
+
+        if (!Soft386FetchWord(State, &Offset))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+
+        /* Read from memory */
+        return Soft386ReadMemory(State,
+                                 (State->PrefixFlags & SOFT386_PREFIX_SEG) ?
+                                 State->SegmentOverride : SOFT386_REG_DS,
+                                 Offset,
+                                 FALSE,
+                                 &State->GeneralRegs[SOFT386_REG_EAX].LowWord,
+                                 sizeof(USHORT));
+    }
+}
+
+SOFT386_OPCODE_HANDLER(Soft386OpcodeMovOffsetAl)
+{
+    BOOLEAN Size = State->SegmentRegs[SOFT386_REG_CS].Size;
+    ULONG Offset;
+
+    /* Make sure this is the right instruction */
+    ASSERT(Opcode == 0xA2);
+
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
+
+    if (Size)
+    {
+        if (!Soft386FetchDword(State, &Offset))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+    }
+    else
+    {
+        USHORT WordOffset;
+
+        if (!Soft386FetchWord(State, &WordOffset))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+
+        Offset = (ULONG)WordOffset;
+    }
+
+    /* Write to memory */
+    return Soft386WriteMemory(State,
+                             (State->PrefixFlags & SOFT386_PREFIX_SEG) ?
+                             State->SegmentOverride : SOFT386_REG_DS,
+                             Offset,
+                             &State->GeneralRegs[SOFT386_REG_EAX].LowByte,
+                             sizeof(UCHAR));
+}
+
+SOFT386_OPCODE_HANDLER(Soft386OpcodeMovOffsetEax)
+{
+    BOOLEAN Size = State->SegmentRegs[SOFT386_REG_CS].Size;
+
+    /* Make sure this is the right instruction */
+    ASSERT(Opcode == 0xA3);
+
+    if (State->PrefixFlags & SOFT386_PREFIX_OPSIZE)
+    {
+        /* The OPSIZE prefix toggles the size */
+        Size = !Size;
+    }
+
+    if (Size)
+    {
+        ULONG Offset;
+
+        if (!Soft386FetchDword(State, &Offset))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+
+        /* Write to memory */
+        return Soft386WriteMemory(State,
+                                  (State->PrefixFlags & SOFT386_PREFIX_SEG) ?
+                                  State->SegmentOverride : SOFT386_REG_DS,
+                                  Offset,
+                                  &State->GeneralRegs[SOFT386_REG_EAX].Long,
+                                  sizeof(ULONG));
+    }
+    else
+    {
+        USHORT Offset;
+
+        if (!Soft386FetchWord(State, &Offset))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+
+        /* Write to memory */
+        return Soft386WriteMemory(State,
+                                  (State->PrefixFlags & SOFT386_PREFIX_SEG) ?
+                                  State->SegmentOverride : SOFT386_REG_DS,
+                                  Offset,
+                                  &State->GeneralRegs[SOFT386_REG_EAX].LowWord,
+                                  sizeof(USHORT));
+    }
+}