2 * Fast486 386/486 CPU Emulation Library
5 * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 /* INCLUDES *******************************************************************/
32 /* PUBLIC FUNCTIONS ***********************************************************/
35 Fast486ReadMemory(PFAST486_STATE State
,
36 FAST486_SEG_REGS SegmentReg
,
43 PFAST486_SEG_REG CachedDescriptor
;
45 ASSERT(SegmentReg
< FAST486_NUM_SEG_REGS
);
47 /* Get the cached descriptor */
48 CachedDescriptor
= &State
->SegmentRegs
[SegmentReg
];
50 if ((Offset
+ Size
- 1) > CachedDescriptor
->Limit
)
52 /* Read beyond limit */
53 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
57 /* Check for protected mode */
58 if (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
)
60 /* Privilege checks */
62 if (!CachedDescriptor
->Present
)
64 Fast486Exception(State
, FAST486_EXCEPTION_NP
);
68 if ((!InstFetch
&& (CachedDescriptor
->Rpl
> CachedDescriptor
->Dpl
))
69 || (Fast486GetCurrentPrivLevel(State
) > CachedDescriptor
->Dpl
))
71 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
77 if (!CachedDescriptor
->Executable
)
79 /* Data segment not executable */
80 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
86 if (CachedDescriptor
->Executable
&& (!CachedDescriptor
->ReadWrite
))
88 /* Code segment not readable */
89 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
95 /* Find the linear address */
96 LinearAddress
= CachedDescriptor
->Base
+ Offset
;
98 #ifndef FAST486_NO_PREFETCH
99 if (InstFetch
&& ((Offset
+ FAST486_CACHE_SIZE
- 1) <= CachedDescriptor
->Limit
))
101 State
->PrefetchAddress
= LinearAddress
;
103 if ((State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PG
)
104 && (PAGE_OFFSET(State
->PrefetchAddress
) > (FAST486_PAGE_SIZE
- FAST486_CACHE_SIZE
)))
106 /* We mustn't prefetch across a page boundary */
107 State
->PrefetchAddress
= PAGE_ALIGN(State
->PrefetchAddress
)
108 | (FAST486_PAGE_SIZE
- FAST486_CACHE_SIZE
);
112 if (Fast486ReadLinearMemory(State
,
113 State
->PrefetchAddress
,
114 State
->PrefetchCache
,
117 State
->PrefetchValid
= TRUE
;
119 RtlMoveMemory(Buffer
,
120 &State
->PrefetchCache
[LinearAddress
- State
->PrefetchAddress
],
126 State
->PrefetchValid
= FALSE
;
133 /* Read from the linear address */
134 return Fast486ReadLinearMemory(State
, LinearAddress
, Buffer
, Size
);
139 Fast486WriteMemory(PFAST486_STATE State
,
140 FAST486_SEG_REGS SegmentReg
,
146 PFAST486_SEG_REG CachedDescriptor
;
148 ASSERT(SegmentReg
< FAST486_NUM_SEG_REGS
);
150 /* Get the cached descriptor */
151 CachedDescriptor
= &State
->SegmentRegs
[SegmentReg
];
153 if ((Offset
+ Size
- 1) > CachedDescriptor
->Limit
)
155 /* Write beyond limit */
156 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
160 /* Check for protected mode */
161 if (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
)
163 /* Privilege checks */
165 if (!CachedDescriptor
->Present
)
167 Fast486Exception(State
, FAST486_EXCEPTION_NP
);
171 if ((CachedDescriptor
->Rpl
> CachedDescriptor
->Dpl
)
172 || (Fast486GetCurrentPrivLevel(State
) > CachedDescriptor
->Dpl
))
174 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
178 if (CachedDescriptor
->Executable
)
180 /* Code segment not writable */
181 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
184 else if (!CachedDescriptor
->ReadWrite
)
186 /* Data segment not writeable */
187 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
192 /* Find the linear address */
193 LinearAddress
= CachedDescriptor
->Base
+ Offset
;
195 #ifndef FAST486_NO_PREFETCH
196 if (State
->PrefetchValid
197 && (LinearAddress
>= State
->PrefetchAddress
)
198 && ((LinearAddress
+ Size
) <= (State
->PrefetchAddress
+ FAST486_CACHE_SIZE
)))
200 /* Update the prefetch */
201 RtlMoveMemory(&State
->PrefetchCache
[LinearAddress
- State
->PrefetchAddress
],
203 min(Size
, FAST486_CACHE_SIZE
+ State
->PrefetchAddress
- LinearAddress
));
207 /* Write to the linear address */
208 return Fast486WriteLinearMemory(State
, LinearAddress
, Buffer
, Size
);
211 static inline BOOLEAN
213 Fast486GetIntVector(PFAST486_STATE State
,
215 PFAST486_IDT_ENTRY IdtEntry
)
217 /* Check for protected mode */
218 if (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
)
220 /* Read from the IDT */
221 if (!Fast486ReadLinearMemory(State
,
223 + Number
* sizeof(*IdtEntry
),
227 /* Exception occurred */
233 /* Read from the real-mode IVT */
236 /* Paging is always disabled in real mode */
237 State
->MemReadCallback(State
,
239 + Number
* sizeof(FarPointer
),
243 /* Fill a fake IDT entry */
244 IdtEntry
->Offset
= LOWORD(FarPointer
);
245 IdtEntry
->Selector
= HIWORD(FarPointer
);
247 IdtEntry
->Type
= FAST486_IDT_INT_GATE
;
248 IdtEntry
->Storage
= FALSE
;
250 IdtEntry
->Present
= TRUE
;
251 IdtEntry
->OffsetHigh
= 0;
257 static inline BOOLEAN
259 Fast486InterruptInternal(PFAST486_STATE State
,
260 PFAST486_IDT_ENTRY IdtEntry
)
262 USHORT SegmentSelector
= IdtEntry
->Selector
;
263 ULONG Offset
= MAKELONG(IdtEntry
->Offset
, IdtEntry
->OffsetHigh
);
264 ULONG GateType
= IdtEntry
->Type
;
265 BOOLEAN GateSize
= (GateType
== FAST486_IDT_INT_GATE_32
) ||
266 (GateType
== FAST486_IDT_TRAP_GATE_32
);
268 BOOLEAN Success
= FALSE
;
269 ULONG OldPrefixFlags
= State
->PrefixFlags
;
271 /* Check for protected mode */
272 if (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
)
275 USHORT OldSs
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
276 ULONG OldEsp
= State
->GeneralRegs
[FAST486_REG_ESP
].Long
;
278 if (GateSize
!= (State
->SegmentRegs
[FAST486_REG_CS
].Size
))
281 * The gate size doesn't match the current operand size, so toggle
284 State
->PrefixFlags
^= FAST486_PREFIX_OPSIZE
;
287 /* Check if the interrupt handler is more privileged */
288 if (Fast486GetCurrentPrivLevel(State
) > GET_SEGMENT_RPL(SegmentSelector
))
291 if (!Fast486ReadLinearMemory(State
,
296 /* Exception occurred */
300 /* Check the new (higher) privilege level */
301 switch (GET_SEGMENT_RPL(SegmentSelector
))
305 if (!Fast486LoadSegment(State
, FAST486_REG_SS
, Tss
.Ss0
))
307 /* Exception occurred */
310 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= Tss
.Esp0
;
317 if (!Fast486LoadSegment(State
, FAST486_REG_SS
, Tss
.Ss1
))
319 /* Exception occurred */
322 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= Tss
.Esp1
;
329 if (!Fast486LoadSegment(State
, FAST486_REG_SS
, Tss
.Ss2
))
331 /* Exception occurred */
334 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= Tss
.Esp2
;
341 /* Should never reach here! */
346 /* Push SS selector */
347 if (!Fast486StackPush(State
, OldSs
)) goto Cleanup
;
349 /* Push stack pointer */
350 if (!Fast486StackPush(State
, OldEsp
)) goto Cleanup
;
355 if (State
->SegmentRegs
[FAST486_REG_CS
].Size
)
357 /* Set OPSIZE, because INT always pushes 16-bit values in real mode */
358 State
->PrefixFlags
|= FAST486_PREFIX_OPSIZE
;
363 if (!Fast486StackPush(State
, State
->Flags
.Long
)) goto Cleanup
;
365 /* Push CS selector */
366 if (!Fast486StackPush(State
, State
->SegmentRegs
[FAST486_REG_CS
].Selector
)) goto Cleanup
;
368 /* Push the instruction pointer */
369 if (!Fast486StackPush(State
, State
->InstPtr
.Long
)) goto Cleanup
;
371 if ((GateType
== FAST486_IDT_INT_GATE
) || (GateType
== FAST486_IDT_INT_GATE_32
))
373 /* Disable interrupts after a jump to an interrupt gate handler */
374 State
->Flags
.If
= FALSE
;
378 if (!Fast486LoadSegment(State
, FAST486_REG_CS
, SegmentSelector
))
380 /* An exception occurred during the jump */
386 /* 32-bit code segment, use EIP */
387 State
->InstPtr
.Long
= Offset
;
391 /* 16-bit code segment, use IP */
392 State
->InstPtr
.LowWord
= LOWORD(Offset
);
398 /* Restore the prefix flags */
399 State
->PrefixFlags
= OldPrefixFlags
;
406 Fast486PerformInterrupt(PFAST486_STATE State
,
409 FAST486_IDT_ENTRY IdtEntry
;
411 /* Get the interrupt vector */
412 if (!Fast486GetIntVector(State
, Number
, &IdtEntry
))
414 /* Exception occurred */
418 /* Perform the interrupt */
419 if (!Fast486InterruptInternal(State
, &IdtEntry
))
421 /* Exception occurred */
430 Fast486ExceptionWithErrorCode(PFAST486_STATE State
,
431 FAST486_EXCEPTIONS ExceptionCode
,
434 /* Increment the exception count */
435 State
->ExceptionCount
++;
437 /* Check if the exception occurred more than once */
438 if (State
->ExceptionCount
> 1)
440 /* Then this is a double fault */
441 ExceptionCode
= FAST486_EXCEPTION_DF
;
444 /* Check if this is a triple fault */
445 if (State
->ExceptionCount
== 3)
447 DPRINT("Fast486ExceptionWithErrorCode(%04X:%08X) -- Triple fault\n",
448 State
->SegmentRegs
[FAST486_REG_CS
].Selector
,
449 State
->InstPtr
.Long
);
456 /* Restore the IP to the saved IP */
457 State
->InstPtr
= State
->SavedInstPtr
;
459 /* Perform the interrupt */
460 if (!Fast486PerformInterrupt(State
, ExceptionCode
))
463 * If this function failed, that means Fast486Exception
464 * was called again, so just return in this case.
469 if (EXCEPTION_HAS_ERROR_CODE(ExceptionCode
)
470 && (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
))
472 /* Push the error code */
473 if (!Fast486StackPush(State
, ErrorCode
))
476 * If this function failed, that means Fast486Exception
477 * was called again, so just return in this case.
483 /* Reset the exception count */
484 State
->ExceptionCount
= 0;