2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: 386/486 CPU Emulation Library
5 * PURPOSE: Common functions used internally by Soft386.
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
11 // #define WIN32_NO_STATUS
12 // #define _INC_WINDOWS
21 /* PRIVATE FUNCTIONS **********************************************************/
23 static /* FORCEINLINE */
25 Soft386GetPageTableEntry(PSOFT386_STATE State
,
28 // TODO: NOT IMPLEMENTED
34 /* PUBLIC FUNCTIONS ***********************************************************/
37 Soft386ReadMemory(PSOFT386_STATE State
,
38 SOFT386_SEG_REGS SegmentReg
,
45 PSOFT386_SEG_REG CachedDescriptor
;
46 INT Cpl
= Soft386GetCurrentPrivLevel(State
);
48 ASSERT(SegmentReg
< SOFT386_NUM_SEG_REGS
);
50 /* Get the cached descriptor */
51 CachedDescriptor
= &State
->SegmentRegs
[SegmentReg
];
53 if ((Offset
+ Size
) >= CachedDescriptor
->Limit
)
55 /* Read beyond limit */
56 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
61 /* Check for protected mode */
62 if (State
->ControlRegisters
[0] & SOFT386_CR0_PE
)
64 /* Privilege checks */
66 if (!CachedDescriptor
->Present
)
68 Soft386Exception(State
, SOFT386_EXCEPTION_NP
);
72 if (GET_SEGMENT_RPL(CachedDescriptor
->Selector
) > CachedDescriptor
->Dpl
)
74 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
80 if (!CachedDescriptor
->Executable
)
82 /* Data segment not executable */
84 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
90 if (CachedDescriptor
->Executable
&& (!CachedDescriptor
->ReadWrite
))
92 /* Code segment not readable */
94 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
100 /* Find the linear address */
101 LinearAddress
= CachedDescriptor
->Base
+ Offset
;
103 /* Check if paging is enabled */
104 if (State
->ControlRegisters
[SOFT386_REG_CR0
] & SOFT386_CR0_PG
)
107 SOFT386_PAGE_TABLE TableEntry
;
109 for (Page
= PAGE_ALIGN(LinearAddress
);
110 Page
<= PAGE_ALIGN(LinearAddress
+ Size
- 1);
113 ULONG PageOffset
= 0, PageLength
= PAGE_SIZE
;
115 /* Get the table entry */
116 TableEntry
.Value
= Soft386GetPageTableEntry(State
, Page
);
118 if (!TableEntry
.Present
|| (!TableEntry
.Usermode
&& (Cpl
> 0)))
121 Soft386ExceptionWithErrorCode(State
,
122 SOFT386_EXCEPTION_PF
,
123 TableEntry
.Value
& 0x07);
127 /* Check if this is the first page */
128 if (Page
== PAGE_ALIGN(LinearAddress
))
130 /* Start copying from the offset from the beginning of the page */
131 PageOffset
= PAGE_OFFSET(LinearAddress
);
134 /* Check if this is the last page */
135 if (Page
== PAGE_ALIGN(LinearAddress
+ Size
- 1))
137 /* Copy only a part of the page */
138 PageLength
= PAGE_OFFSET(LinearAddress
+ Size
);
141 /* Did the host provide a memory hook? */
142 if (State
->MemReadCallback
)
144 /* Yes, call the host */
145 State
->MemReadCallback(State
,
146 (TableEntry
.Address
<< 12) | PageOffset
,
152 /* Read the memory directly */
153 RtlMoveMemory(Buffer
,
154 (PVOID
)((TableEntry
.Address
<< 12) | PageOffset
),
161 /* Did the host provide a memory hook? */
162 if (State
->MemReadCallback
)
164 /* Yes, call the host */
165 State
->MemReadCallback(State
, LinearAddress
, Buffer
, Size
);
169 /* Read the memory directly */
170 RtlMoveMemory(Buffer
, (PVOID
)LinearAddress
, Size
);
178 Soft386WriteMemory(PSOFT386_STATE State
,
179 SOFT386_SEG_REGS SegmentReg
,
185 PSOFT386_SEG_REG CachedDescriptor
;
186 INT Cpl
= Soft386GetCurrentPrivLevel(State
);
188 ASSERT(SegmentReg
< SOFT386_NUM_SEG_REGS
);
190 /* Get the cached descriptor */
191 CachedDescriptor
= &State
->SegmentRegs
[SegmentReg
];
193 if ((Offset
+ Size
) >= CachedDescriptor
->Limit
)
195 /* Write beyond limit */
196 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
201 /* Check for protected mode */
202 if (State
->ControlRegisters
[0] & SOFT386_CR0_PE
)
204 /* Privilege checks */
206 if (!CachedDescriptor
->Present
)
208 Soft386Exception(State
, SOFT386_EXCEPTION_NP
);
212 if (GET_SEGMENT_RPL(CachedDescriptor
->Selector
) > CachedDescriptor
->Dpl
)
214 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
218 if (CachedDescriptor
->Executable
)
220 /* Code segment not writable */
222 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
225 else if (!CachedDescriptor
->ReadWrite
)
227 /* Data segment not writeable */
229 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
234 /* Find the linear address */
235 LinearAddress
= CachedDescriptor
->Base
+ Offset
;
237 /* Check if paging is enabled */
238 if (State
->ControlRegisters
[SOFT386_REG_CR0
] & SOFT386_CR0_PG
)
241 SOFT386_PAGE_TABLE TableEntry
;
243 for (Page
= PAGE_ALIGN(LinearAddress
);
244 Page
<= PAGE_ALIGN(LinearAddress
+ Size
- 1);
247 ULONG PageOffset
= 0, PageLength
= PAGE_SIZE
;
249 /* Get the table entry */
250 TableEntry
.Value
= Soft386GetPageTableEntry(State
, Page
);
252 if ((!TableEntry
.Present
|| (!TableEntry
.Usermode
&& (Cpl
> 0)))
253 || ((State
->ControlRegisters
[SOFT386_REG_CR0
] & SOFT386_CR0_WP
)
254 && !TableEntry
.Writeable
))
257 Soft386ExceptionWithErrorCode(State
,
258 SOFT386_EXCEPTION_PF
,
259 TableEntry
.Value
& 0x07);
263 /* Check if this is the first page */
264 if (Page
== PAGE_ALIGN(LinearAddress
))
266 /* Start copying from the offset from the beginning of the page */
267 PageOffset
= PAGE_OFFSET(LinearAddress
);
270 /* Check if this is the last page */
271 if (Page
== PAGE_ALIGN(LinearAddress
+ Size
- 1))
273 /* Copy only a part of the page */
274 PageLength
= PAGE_OFFSET(LinearAddress
+ Size
);
277 /* Did the host provide a memory hook? */
278 if (State
->MemWriteCallback
)
280 /* Yes, call the host */
281 State
->MemWriteCallback(State
,
282 (TableEntry
.Address
<< 12) | PageOffset
,
288 /* Read the memory directly */
289 RtlMoveMemory((PVOID
)((TableEntry
.Address
<< 12) | PageOffset
),
297 /* Did the host provide a memory hook? */
298 if (State
->MemWriteCallback
)
300 /* Yes, call the host */
301 State
->MemWriteCallback(State
, LinearAddress
, Buffer
, Size
);
305 /* Write the memory directly */
306 RtlMoveMemory((PVOID
)LinearAddress
, Buffer
, Size
);
314 Soft386InterruptInternal(PSOFT386_STATE State
,
315 USHORT SegmentSelector
,
317 BOOLEAN InterruptGate
)
319 /* Check for protected mode */
320 if (State
->ControlRegisters
[SOFT386_REG_CR0
] & SOFT386_CR0_PE
)
323 USHORT OldSs
= State
->SegmentRegs
[SOFT386_REG_SS
].Selector
;
324 ULONG OldEsp
= State
->GeneralRegs
[SOFT386_REG_ESP
].Long
;
326 /* Check if the interrupt handler is more privileged */
327 if (Soft386GetCurrentPrivLevel(State
) > GET_SEGMENT_RPL(SegmentSelector
))
330 // FIXME: This code is only correct when paging is disabled!!!
331 if (State
->MemReadCallback
)
333 State
->MemReadCallback(State
,
340 RtlMoveMemory(&Tss
, (PVOID
)State
->Tss
.Address
, sizeof(Tss
));
343 /* Check the new (higher) privilege level */
344 switch (GET_SEGMENT_RPL(SegmentSelector
))
348 if (!Soft386LoadSegment(State
, SOFT386_REG_SS
, Tss
.Ss0
))
350 /* Exception occurred */
353 State
->GeneralRegs
[SOFT386_REG_ESP
].Long
= Tss
.Esp0
;
360 if (!Soft386LoadSegment(State
, SOFT386_REG_SS
, Tss
.Ss1
))
362 /* Exception occurred */
365 State
->GeneralRegs
[SOFT386_REG_ESP
].Long
= Tss
.Esp1
;
372 if (!Soft386LoadSegment(State
, SOFT386_REG_SS
, Tss
.Ss2
))
374 /* Exception occurred */
377 State
->GeneralRegs
[SOFT386_REG_ESP
].Long
= Tss
.Esp2
;
384 /* Should never reach here! */
389 /* Push SS selector */
390 if (!Soft386StackPush(State
, OldSs
)) return FALSE
;
392 /* Push stack pointer */
393 if (!Soft386StackPush(State
, OldEsp
)) return FALSE
;
398 if (!Soft386StackPush(State
, State
->Flags
.Long
)) return FALSE
;
400 /* Push CS selector */
401 if (!Soft386StackPush(State
, State
->SegmentRegs
[SOFT386_REG_CS
].Selector
)) return FALSE
;
403 /* Push the instruction pointer */
404 if (!Soft386StackPush(State
, State
->InstPtr
.Long
)) return FALSE
;
408 /* Disable interrupts after a jump to an interrupt gate handler */
409 State
->Flags
.If
= FALSE
;
413 if (!Soft386LoadSegment(State
, SOFT386_REG_CS
, SegmentSelector
))
415 /* An exception occurred during the jump */
419 if (State
->SegmentRegs
[SOFT386_REG_CS
].Size
)
421 /* 32-bit code segment, use EIP */
422 State
->InstPtr
.Long
= Offset
;
426 /* 16-bit code segment, use IP */
427 State
->InstPtr
.LowWord
= LOWORD(Offset
);
435 Soft386ExceptionWithErrorCode(PSOFT386_STATE State
,
439 SOFT386_IDT_ENTRY IdtEntry
;
441 /* Increment the exception count */
442 State
->ExceptionCount
++;
444 /* Check if the exception occurred more than once */
445 if (State
->ExceptionCount
> 1)
447 /* Then this is a double fault */
448 ExceptionCode
= SOFT386_EXCEPTION_DF
;
451 /* Check if this is a triple fault */
452 if (State
->ExceptionCount
== 3)
459 if (!Soft386GetIntVector(State
, ExceptionCode
, &IdtEntry
))
462 * If this function failed, that means Soft386Exception
463 * was called again, so just return in this case.
468 /* Perform the interrupt */
469 if (!Soft386InterruptInternal(State
,
471 MAKELONG(IdtEntry
.Offset
, IdtEntry
.OffsetHigh
),
475 * If this function failed, that means Soft386Exception
476 * was called again, so just return in this case.
481 if (EXCEPTION_HAS_ERROR_CODE(ExceptionCode
))
483 /* Push the error code */
484 Soft386StackPush(State
, ErrorCode
);