* Fast486 386/486 CPU Emulation Library
* common.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
/* Get the cached descriptor */
CachedDescriptor = &State->SegmentRegs[SegmentReg];
- if ((Offset + Size - 1) > CachedDescriptor->Limit)
+ if (InstFetch || !CachedDescriptor->DirConf)
{
- /* Read beyond limit */
- Fast486Exception(State, FAST486_EXCEPTION_GP);
- return FALSE;
+ if ((Offset + Size - 1) > CachedDescriptor->Limit)
+ {
+ /* Read beyond limit */
+ Fast486Exception(State, FAST486_EXCEPTION_GP);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (Offset < CachedDescriptor->Limit)
+ {
+ /* Read beyond limit */
+ Fast486Exception(State, FAST486_EXCEPTION_GP);
+ return FALSE;
+ }
}
/* Check for protected mode */
/* We mustn't prefetch across a page boundary */
State->PrefetchAddress = PAGE_ALIGN(State->PrefetchAddress)
| (FAST486_PAGE_SIZE - FAST486_CACHE_SIZE);
+
+ if ((LinearAddress - State->PrefetchAddress + Size) >= FAST486_CACHE_SIZE)
+ {
+ /* We can't prefetch without possibly violating page permissions */
+ State->PrefetchValid = FALSE;
+ return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size);
+ }
}
/* Prefetch */
/* Get the cached descriptor */
CachedDescriptor = &State->SegmentRegs[SegmentReg];
- if ((Offset + Size - 1) > CachedDescriptor->Limit)
+ if (!CachedDescriptor->DirConf)
{
- /* Write beyond limit */
- Fast486Exception(State, FAST486_EXCEPTION_GP);
- return FALSE;
+ if ((Offset + Size - 1) > CachedDescriptor->Limit)
+ {
+ /* Write beyond limit */
+ Fast486Exception(State, FAST486_EXCEPTION_GP);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (Offset < CachedDescriptor->Limit)
+ {
+ /* Read beyond limit */
+ Fast486Exception(State, FAST486_EXCEPTION_GP);
+ return FALSE;
+ }
}
/* Check for protected mode */
static inline BOOLEAN
FASTCALL
Fast486InterruptInternal(PFAST486_STATE State,
- PFAST486_IDT_ENTRY IdtEntry)
+ PFAST486_IDT_ENTRY IdtEntry,
+ BOOLEAN PushErrorCode,
+ ULONG ErrorCode)
{
USHORT SegmentSelector = IdtEntry->Selector;
ULONG Offset = MAKELONG(IdtEntry->Offset, IdtEntry->OffsetHigh);
/* Task call */
return Fast486TaskSwitch(State, FAST486_TASK_CALL, IdtEntry->Selector);
}
-
+
if (GateSize != (State->SegmentRegs[FAST486_REG_CS].Size))
{
- /*
- * The gate size doesn't match the current operand size, so toggle
- * the OPSIZE flag.
- */
- State->PrefixFlags ^= FAST486_PREFIX_OPSIZE;
+ /* The gate size doesn't match the current operand size, so set the OPSIZE flag. */
+ State->PrefixFlags |= FAST486_PREFIX_OPSIZE;
}
- /* Check if the interrupt handler is more privileged */
- if (Fast486GetCurrentPrivLevel(State) > GET_SEGMENT_RPL(SegmentSelector))
+ /* Check if the interrupt handler is more privileged or if we're in V86 mode */
+ if ((Fast486GetCurrentPrivLevel(State) > GET_SEGMENT_RPL(SegmentSelector))
+ || State->Flags.Vm)
{
/* Read the TSS */
if (!Fast486ReadLinearMemory(State,
goto Cleanup;
}
+ /* Switch to the new privilege level */
+ State->Cpl = GET_SEGMENT_RPL(SegmentSelector);
+
+ if (State->Flags.Vm)
+ {
+ /* Clear the VM flag */
+ State->Flags.Vm = FALSE;
+
+ /* Push GS, FS, DS and ES */
+ if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_GS].Selector)) goto Cleanup;
+ if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_FS].Selector)) goto Cleanup;
+ if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_DS].Selector)) goto Cleanup;
+ if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_ES].Selector)) goto Cleanup;
+
+ /* Now load them with NULL selectors, since they are useless in protected mode */
+ if (!Fast486LoadSegment(State, FAST486_REG_GS, 0)) goto Cleanup;
+ if (!Fast486LoadSegment(State, FAST486_REG_FS, 0)) goto Cleanup;
+ if (!Fast486LoadSegment(State, FAST486_REG_DS, 0)) goto Cleanup;
+ if (!Fast486LoadSegment(State, FAST486_REG_ES, 0)) goto Cleanup;
+ }
+
/* Check the new (higher) privilege level */
- switch (GET_SEGMENT_RPL(SegmentSelector))
+ switch (State->Cpl)
{
case 0:
{
/* Push the instruction pointer */
if (!Fast486StackPush(State, State->InstPtr.Long)) goto Cleanup;
+ if (PushErrorCode)
+ {
+ /* Push the error code */
+ if (!Fast486StackPush(State, ErrorCode))
+ {
+ /* An exception occurred */
+ goto Cleanup;
+ }
+ }
+
if ((GateType == FAST486_IDT_INT_GATE) || (GateType == FAST486_IDT_INT_GATE_32))
{
/* Disable interrupts after a jump to an interrupt gate handler */
}
/* Perform the interrupt */
- if (!Fast486InterruptInternal(State, &IdtEntry))
+ if (!Fast486InterruptInternal(State, &IdtEntry, FALSE, 0))
{
/* Exception occurred */
return FALSE;
FAST486_EXCEPTIONS ExceptionCode,
ULONG ErrorCode)
{
+ FAST486_IDT_ENTRY IdtEntry;
+
/* Increment the exception count */
State->ExceptionCount++;
return;
}
+ /* Clear the prefix flags */
+ State->PrefixFlags = 0;
+
/* Restore the IP to the saved IP */
State->InstPtr = State->SavedInstPtr;
- /* Perform the interrupt */
- if (!Fast486PerformInterrupt(State, ExceptionCode))
+ /* Get the interrupt vector */
+ if (!Fast486GetIntVector(State, ExceptionCode, &IdtEntry))
{
/*
* If this function failed, that means Fast486Exception
return;
}
- if (EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
- && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE))
+ /* Perform the interrupt */
+ if (!Fast486InterruptInternal(State,
+ &IdtEntry,
+ EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
+ && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE),
+ ErrorCode))
{
- /* Push the error code */
- if (!Fast486StackPush(State, ErrorCode))
- {
- /*
- * If this function failed, that means Fast486Exception
- * was called again, so just return in this case.
- */
- return;
- }
+ /*
+ * If this function failed, that means Fast486Exception
+ * was called again, so just return in this case.
+ */
+ return;
}
/* Reset the exception count */
/* Make sure the entry exists in the GDT (not LDT!) */
if ((GET_SEGMENT_INDEX(Selector) == 0)
|| (Selector & SEGMENT_TABLE_INDICATOR)
- || GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1))
+ || GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1u))
{
Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
return FALSE;
/* Calculate the limit of the new TSS */
NewTssLimit = NewTssDescriptor.Limit | (NewTssDescriptor.LimitHigh << 16);
- if (NewTssDescriptor.Granularity) NewTssLimit <<= 12;
+
+ if (NewTssDescriptor.Granularity)
+ {
+ NewTssLimit <<= 12;
+ NewTssLimit |= 0x00000FFF;
+ }
if (NewTssLimit < sizeof(FAST486_TSS))
{
/* TSS limit too small */
- Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
+ Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
+ return FALSE;
}
/*
&& ((NewTssDescriptor.Signature != FAST486_BUSY_TSS_SIGNATURE)
|| (Type != FAST486_TASK_RETURN)))
{
- Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
+ Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
return FALSE;
}
/* Flush the TLB */
if (State->Tlb) RtlZeroMemory(State->Tlb, NUM_TLB_ENTRIES * sizeof(ULONG));
+ /* Update the CPL */
+ State->Cpl = GET_SEGMENT_RPL(NewTss.Cs);
+
#ifndef FAST486_NO_PREFETCH
/* Context switching invalidates the prefetch */
State->PrefetchValid = FALSE;
#endif
+ /* Update the CPL */
+ State->Cpl = GET_SEGMENT_RPL(NewTss.Cs);
+
/* Load the registers */
State->InstPtr.Long = State->SavedInstPtr.Long = NewTss.Eip;
State->Flags.Long = NewTss.Eflags;
return FALSE;
}
- if (Fast486ReadDescriptorEntry(State,
- NewTss.Ldtr,
- &Valid,
- (PFAST486_GDT_ENTRY)&GdtEntry))
+ if (!Fast486ReadDescriptorEntry(State,
+ NewTss.Ldtr,
+ &Valid,
+ (PFAST486_GDT_ENTRY)&GdtEntry))
{
/* Exception occurred */
return FALSE;
State->Ldtr.Selector = NewTss.Ldtr;
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;
+ }
}
else
{