* Fast486 386/486 CPU Emulation Library
* opgroups.c
*
- * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * 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
case 1:
{
Result = FirstValue | SecondValue;
+ State->Flags.Cf = State->Flags.Of = FALSE;
break;
}
case 4:
{
Result = FirstValue & SecondValue;
+ State->Flags.Cf = State->Flags.Of = FALSE;
break;
}
case 6:
{
Result = FirstValue ^ SecondValue;
+ State->Flags.Cf = State->Flags.Of = FALSE;
break;
}
case 2:
{
Result = (Value << Count) | (State->Flags.Cf << (Count - 1));
-
+
/* Complete the calculation, but make sure we don't shift by too much */
if ((Bits - Count) < 31) Result |= Value >> (Bits - Count + 1);
if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
{
- /* Exception occurred - restore SP */
- if (OperandSize) State->GeneralRegs[FAST486_REG_ESP].Long -= sizeof(ULONG);
- else State->GeneralRegs[FAST486_REG_ESP].LowWord -= sizeof(USHORT);
-
+ /* Exception occurred */
return;
}
/* DIV */
case 6:
{
- UCHAR Quotient, Remainder;
+ USHORT Quotient;
+ UCHAR Remainder;
if (Value == 0)
{
Quotient = State->GeneralRegs[FAST486_REG_EAX].LowWord / Value;
Remainder = State->GeneralRegs[FAST486_REG_EAX].LowWord % Value;
+ if (Quotient > 0xFF)
+ {
+ /* Divide error */
+ Fast486Exception(State, FAST486_EXCEPTION_DE);
+ return;
+ }
+
/* Write back the results */
- State->GeneralRegs[FAST486_REG_EAX].LowByte = Quotient;
+ State->GeneralRegs[FAST486_REG_EAX].LowByte = (UCHAR)Quotient;
State->GeneralRegs[FAST486_REG_EAX].HighByte = Remainder;
break;
/* IDIV */
case 7:
{
- CHAR Quotient, Remainder;
+ SHORT Quotient;
+ CHAR Remainder;
if (Value == 0)
{
Quotient = (SHORT)State->GeneralRegs[FAST486_REG_EAX].LowWord / (CHAR)Value;
Remainder = (SHORT)State->GeneralRegs[FAST486_REG_EAX].LowWord % (CHAR)Value;
+ if (Quotient > 127 || Quotient < -128)
+ {
+ /* Divide error */
+ Fast486Exception(State, FAST486_EXCEPTION_DE);
+ return;
+ }
+
/* Write back the results */
- State->GeneralRegs[FAST486_REG_EAX].LowByte = (UCHAR)Quotient;
+ State->GeneralRegs[FAST486_REG_EAX].LowByte = (UCHAR)((CHAR)Quotient);
State->GeneralRegs[FAST486_REG_EAX].HighByte = (UCHAR)Remainder;
break;
case 3:
{
/* Calculate the result */
- ULONG Result = -Value;
+ ULONG Result = -(LONG)Value;
if (!OperandSize) Result &= 0xFFFF;
/* Update the flags */
{
ULONGLONG Dividend = (ULONGLONG)State->GeneralRegs[FAST486_REG_EAX].Long
| ((ULONGLONG)State->GeneralRegs[FAST486_REG_EDX].Long << 32);
- ULONG Quotient = Dividend / Value;
+ ULONGLONG Quotient = Dividend / Value;
ULONG Remainder = Dividend % Value;
+ if (Quotient > 0xFFFFFFFFULL)
+ {
+ /* Divide error */
+ Fast486Exception(State, FAST486_EXCEPTION_DE);
+ return;
+ }
+
/* Write back the results */
- State->GeneralRegs[FAST486_REG_EAX].Long = Quotient;
+ State->GeneralRegs[FAST486_REG_EAX].Long = (ULONG)Quotient;
State->GeneralRegs[FAST486_REG_EDX].Long = Remainder;
}
else
{
ULONG Dividend = (ULONG)State->GeneralRegs[FAST486_REG_EAX].LowWord
| ((ULONG)State->GeneralRegs[FAST486_REG_EDX].LowWord << 16);
- USHORT Quotient = Dividend / Value;
+ ULONG Quotient = Dividend / Value;
USHORT Remainder = Dividend % Value;
+ if (Quotient > 0xFFFF)
+ {
+ /* Divide error */
+ Fast486Exception(State, FAST486_EXCEPTION_DE);
+ return;
+ }
+
/* Write back the results */
- State->GeneralRegs[FAST486_REG_EAX].LowWord = Quotient;
+ State->GeneralRegs[FAST486_REG_EAX].LowWord = (USHORT)Quotient;
State->GeneralRegs[FAST486_REG_EDX].LowWord = Remainder;
}
{
LONGLONG Dividend = (LONGLONG)State->GeneralRegs[FAST486_REG_EAX].Long
| ((LONGLONG)State->GeneralRegs[FAST486_REG_EDX].Long << 32);
- LONG Quotient = Dividend / (LONG)Value;
+ LONGLONG Quotient = Dividend / (LONG)Value;
LONG Remainder = Dividend % (LONG)Value;
+ if (Quotient > 2147483647LL || Quotient < -2147483648LL)
+ {
+ /* Divide error */
+ Fast486Exception(State, FAST486_EXCEPTION_DE);
+ return;
+ }
+
/* Write back the results */
- State->GeneralRegs[FAST486_REG_EAX].Long = (ULONG)Quotient;
+ State->GeneralRegs[FAST486_REG_EAX].Long = (ULONG)((LONG)Quotient);
State->GeneralRegs[FAST486_REG_EDX].Long = (ULONG)Remainder;
}
else
{
LONG Dividend = (LONG)State->GeneralRegs[FAST486_REG_EAX].LowWord
| ((LONG)State->GeneralRegs[FAST486_REG_EDX].LowWord << 16);
- SHORT Quotient = Dividend / (SHORT)LOWORD(Value);
+ LONG Quotient = Dividend / (SHORT)LOWORD(Value);
SHORT Remainder = Dividend % (SHORT)LOWORD(Value);
+ if (Quotient > 32767 || Quotient < -32768)
+ {
+ /* Divide error */
+ Fast486Exception(State, FAST486_EXCEPTION_DE);
+ return;
+ }
+
/* Write back the results */
- State->GeneralRegs[FAST486_REG_EAX].LowWord = (USHORT)Quotient;
+ State->GeneralRegs[FAST486_REG_EAX].LowWord = (USHORT)((SHORT)Quotient);
State->GeneralRegs[FAST486_REG_EDX].LowWord = (USHORT)Remainder;
}
return;
}
- if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
+ if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
+ && !State->Flags.Vm)
{
if (!Fast486ProcessGate(State, Selector, Value, TRUE))
{
return;
}
- if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
+ if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
+ && !State->Flags.Vm)
{
if (!Fast486ProcessGate(State, Selector, Value, FALSE))
{
return;
}
+ if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
+ && !State->Flags.Vm)
+ {
+ if (!Fast486ProcessGate(State, Selector, Value, TRUE))
+ {
+ /* Gate processed or exception occurred */
+ return;
+ }
+ }
+
/* Push the current value of CS */
if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector))
{
return;
}
+ if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
+ && !State->Flags.Vm)
+ {
+ if (!Fast486ProcessGate(State, Selector, Value, FALSE))
+ {
+ /* Gate processed or exception occurred */
+ return;
+ }
+ }
+
/* Load the new code segment */
if (!Fast486LoadSegment(State, FAST486_REG_CS, Selector))
{
return;
}
- if (Fast486ReadDescriptorEntry(State,
- Selector,
- &Valid,
- (PFAST486_GDT_ENTRY)&GdtEntry))
+ if (!Fast486ReadDescriptorEntry(State,
+ Selector,
+ &Valid,
+ (PFAST486_GDT_ENTRY)&GdtEntry))
{
/* Exception occurred */
return;
State->Ldtr.Selector = Selector;
State->Ldtr.Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24);
State->Ldtr.Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
- if (GdtEntry.Granularity) State->Ldtr.Limit <<= 12;
+
+ if (GdtEntry.Granularity)
+ {
+ State->Ldtr.Limit <<= 12;
+ State->Ldtr.Limit |= 0x00000FFF;
+ }
break;
}
return;
}
- if (Fast486ReadDescriptorEntry(State,
- Selector,
- &Valid,
- (PFAST486_GDT_ENTRY)&GdtEntry))
+ if (!Fast486ReadDescriptorEntry(State,
+ Selector,
+ &Valid,
+ (PFAST486_GDT_ENTRY)&GdtEntry))
{
/* Exception occurred */
return;
return;
}
- if (GdtEntry.Signature != FAST486_TSS_SIGNATURE)
+ if (GdtEntry.Signature != FAST486_TSS_SIGNATURE
+ && GdtEntry.Signature != FAST486_BUSY_TSS_SIGNATURE
+ && GdtEntry.Signature != FAST486_TSS_16_SIGNATURE
+ && GdtEntry.Signature != FAST486_BUSY_TSS_16_SIGNATURE)
{
/* This is not a TSS descriptor */
Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
State->TaskReg.Selector = Selector;
State->TaskReg.Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24);
State->TaskReg.Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
- if (GdtEntry.Granularity) State->TaskReg.Limit <<= 12;
+
+ if (GdtEntry.Granularity)
+ {
+ State->TaskReg.Limit <<= 12;
+ State->TaskReg.Limit |= 0x00000FFF;
+ }
+
+ if (GdtEntry.Signature != FAST486_BUSY_TSS_SIGNATURE
+ && GdtEntry.Signature != FAST486_BUSY_TSS_16_SIGNATURE)
+ {
+ /* Set the busy bit of this TSS descriptor and write it back */
+ GdtEntry.Signature |= 2;
+
+ Fast486WriteLinearMemory(State,
+ State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
+ &GdtEntry,
+ sizeof(GdtEntry),
+ FALSE /* We already made sure CPL is 0 */);
+ }
break;
}
return;
}
- /* This is a privileged instruction */
- if (Fast486GetCurrentPrivLevel(State) != 0)
- {
- Fast486Exception(State, FAST486_EXCEPTION_GP);
- return;
- }
-
if (!Fast486ReadModrmWordOperands(State,
&ModRegRm,
NULL,
/* Set ZF if it is valid and accessible */
State->Flags.Zf = GdtEntry.Present // must be present
- && GdtEntry.SystemType // must be a segment
- && (((ModRegRm.Register == 4)
- /* code segments are only readable if the RW bit is set */
- && (!GdtEntry.Executable || GdtEntry.ReadWrite))
- || ((ModRegRm.Register == 5)
- /* code segments are never writable, data segments are writable when RW is set */
- && (!GdtEntry.Executable && GdtEntry.ReadWrite)))
- /*
- * for segments other than conforming code segments,
- * both RPL and CPL must be less than or equal to DPL
- */
- && ((!GdtEntry.Executable || !GdtEntry.DirConf)
- && ((GET_SEGMENT_RPL(Selector) <= GdtEntry.Dpl)
- && (Fast486GetCurrentPrivLevel(State) <= GdtEntry.Dpl)))
- /* for conforming code segments, DPL must be less than or equal to CPL */
- && ((GdtEntry.Executable && GdtEntry.DirConf)
- && (GdtEntry.Dpl <= Fast486GetCurrentPrivLevel(State)));
+ && GdtEntry.SystemType // must be a segment
+ && (((ModRegRm.Register == 4)
+ /* code segments are only readable if the RW bit is set */
+ && (!GdtEntry.Executable || GdtEntry.ReadWrite))
+ || ((ModRegRm.Register == 5)
+ /* code segments are never writable, data segments are writable when RW is set */
+ && (!GdtEntry.Executable && GdtEntry.ReadWrite)))
+ /*
+ * for segments other than conforming code segments,
+ * both RPL and CPL must be less than or equal to DPL
+ */
+ && (((!GdtEntry.Executable || !GdtEntry.DirConf)
+ && (GET_SEGMENT_RPL(Selector) <= GdtEntry.Dpl)
+ && (Fast486GetCurrentPrivLevel(State) <= GdtEntry.Dpl))
+ /* for conforming code segments, DPL must be less than or equal to CPL */
+ || ((GdtEntry.Executable && GdtEntry.DirConf)
+ && (GdtEntry.Dpl <= Fast486GetCurrentPrivLevel(State))));
break;
{
USHORT MachineStatusWord;
- /* This is a privileged instruction */
- if (Fast486GetCurrentPrivLevel(State) != 0)
+ if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
{
- Fast486Exception(State, FAST486_EXCEPTION_GP);
- return;
+ /* This is a privileged instruction */
+ if (Fast486GetCurrentPrivLevel(State) != 0)
+ {
+ Fast486Exception(State, FAST486_EXCEPTION_GP);
+ return;
+ }
}
/* Read the new Machine Status Word */
return;
}
- /* This instruction cannot be used to return to real mode */
- if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
- && !(MachineStatusWord & FAST486_CR0_PE))
- {
- Fast486Exception(State, FAST486_EXCEPTION_GP);
- return;
- }
-
- /* Set the lowest 4 bits */
- State->ControlRegisters[FAST486_REG_CR0] &= 0xFFFFFFF0;
+ /* Set the lowest 4 bits, but never clear bit 0 */
+ State->ControlRegisters[FAST486_REG_CR0] &= 0xFFFFFFF1;
State->ControlRegisters[FAST486_REG_CR0] |= MachineStatusWord & 0x0F;
break;