2 * Soft386 386/486 CPU Emulation Library
5 * Copyright (C) 2013 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 *******************************************************************/
24 // #define WIN32_NO_STATUS
25 // #define _INC_WINDOWS
34 /* PRIVATE FUNCTIONS **********************************************************/
38 Soft386GetPageTableEntry(PSOFT386_STATE State
,
41 // TODO: NOT IMPLEMENTED
47 /* PUBLIC FUNCTIONS ***********************************************************/
50 Soft386ReadMemory(PSOFT386_STATE State
,
51 SOFT386_SEG_REGS SegmentReg
,
58 PSOFT386_SEG_REG CachedDescriptor
;
59 INT Cpl
= Soft386GetCurrentPrivLevel(State
);
61 ASSERT(SegmentReg
< SOFT386_NUM_SEG_REGS
);
63 /* Get the cached descriptor */
64 CachedDescriptor
= &State
->SegmentRegs
[SegmentReg
];
66 if ((Offset
+ Size
- 1) > CachedDescriptor
->Limit
)
68 /* Read beyond limit */
69 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
74 /* Check for protected mode */
75 if (State
->ControlRegisters
[0] & SOFT386_CR0_PE
)
77 /* Privilege checks */
79 if (!CachedDescriptor
->Present
)
81 Soft386Exception(State
, SOFT386_EXCEPTION_NP
);
85 if (GET_SEGMENT_RPL(CachedDescriptor
->Selector
) > CachedDescriptor
->Dpl
)
87 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
93 if (!CachedDescriptor
->Executable
)
95 /* Data segment not executable */
97 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
103 if (CachedDescriptor
->Executable
&& (!CachedDescriptor
->ReadWrite
))
105 /* Code segment not readable */
107 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
113 /* Find the linear address */
114 LinearAddress
= CachedDescriptor
->Base
+ Offset
;
116 /* Check if paging is enabled */
117 if (State
->ControlRegisters
[SOFT386_REG_CR0
] & SOFT386_CR0_PG
)
120 SOFT386_PAGE_TABLE TableEntry
;
122 for (Page
= PAGE_ALIGN(LinearAddress
);
123 Page
<= PAGE_ALIGN(LinearAddress
+ Size
- 1);
126 ULONG PageOffset
= 0, PageLength
= PAGE_SIZE
;
128 /* Get the table entry */
129 TableEntry
.Value
= Soft386GetPageTableEntry(State
, Page
);
131 if (!TableEntry
.Present
|| (!TableEntry
.Usermode
&& (Cpl
> 0)))
134 Soft386ExceptionWithErrorCode(State
,
135 SOFT386_EXCEPTION_PF
,
136 TableEntry
.Value
& 0x07);
140 /* Check if this is the first page */
141 if (Page
== PAGE_ALIGN(LinearAddress
))
143 /* Start copying from the offset from the beginning of the page */
144 PageOffset
= PAGE_OFFSET(LinearAddress
);
147 /* Check if this is the last page */
148 if (Page
== PAGE_ALIGN(LinearAddress
+ Size
- 1))
150 /* Copy only a part of the page */
151 PageLength
= PAGE_OFFSET(LinearAddress
+ Size
);
154 /* Did the host provide a memory hook? */
155 if (State
->MemReadCallback
)
157 /* Yes, call the host */
158 State
->MemReadCallback(State
,
159 (TableEntry
.Address
<< 12) | PageOffset
,
165 /* Read the memory directly */
166 RtlMoveMemory(Buffer
,
167 (PVOID
)((TableEntry
.Address
<< 12) | PageOffset
),
174 /* Did the host provide a memory hook? */
175 if (State
->MemReadCallback
)
177 /* Yes, call the host */
178 State
->MemReadCallback(State
, LinearAddress
, Buffer
, Size
);
182 /* Read the memory directly */
183 RtlMoveMemory(Buffer
, (PVOID
)LinearAddress
, Size
);
191 Soft386WriteMemory(PSOFT386_STATE State
,
192 SOFT386_SEG_REGS SegmentReg
,
198 PSOFT386_SEG_REG CachedDescriptor
;
199 INT Cpl
= Soft386GetCurrentPrivLevel(State
);
201 ASSERT(SegmentReg
< SOFT386_NUM_SEG_REGS
);
203 /* Get the cached descriptor */
204 CachedDescriptor
= &State
->SegmentRegs
[SegmentReg
];
206 if ((Offset
+ Size
- 1) > CachedDescriptor
->Limit
)
208 /* Write beyond limit */
209 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
214 /* Check for protected mode */
215 if (State
->ControlRegisters
[0] & SOFT386_CR0_PE
)
217 /* Privilege checks */
219 if (!CachedDescriptor
->Present
)
221 Soft386Exception(State
, SOFT386_EXCEPTION_NP
);
225 if (GET_SEGMENT_RPL(CachedDescriptor
->Selector
) > CachedDescriptor
->Dpl
)
227 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
231 if (CachedDescriptor
->Executable
)
233 /* Code segment not writable */
235 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
238 else if (!CachedDescriptor
->ReadWrite
)
240 /* Data segment not writeable */
242 Soft386Exception(State
, SOFT386_EXCEPTION_GP
);
247 /* Find the linear address */
248 LinearAddress
= CachedDescriptor
->Base
+ Offset
;
250 /* Check if paging is enabled */
251 if (State
->ControlRegisters
[SOFT386_REG_CR0
] & SOFT386_CR0_PG
)
254 SOFT386_PAGE_TABLE TableEntry
;
256 for (Page
= PAGE_ALIGN(LinearAddress
);
257 Page
<= PAGE_ALIGN(LinearAddress
+ Size
- 1);
260 ULONG PageOffset
= 0, PageLength
= PAGE_SIZE
;
262 /* Get the table entry */
263 TableEntry
.Value
= Soft386GetPageTableEntry(State
, Page
);
265 if ((!TableEntry
.Present
|| (!TableEntry
.Usermode
&& (Cpl
> 0)))
266 || ((State
->ControlRegisters
[SOFT386_REG_CR0
] & SOFT386_CR0_WP
)
267 && !TableEntry
.Writeable
))
270 Soft386ExceptionWithErrorCode(State
,
271 SOFT386_EXCEPTION_PF
,
272 TableEntry
.Value
& 0x07);
276 /* Check if this is the first page */
277 if (Page
== PAGE_ALIGN(LinearAddress
))
279 /* Start copying from the offset from the beginning of the page */
280 PageOffset
= PAGE_OFFSET(LinearAddress
);
283 /* Check if this is the last page */
284 if (Page
== PAGE_ALIGN(LinearAddress
+ Size
- 1))
286 /* Copy only a part of the page */
287 PageLength
= PAGE_OFFSET(LinearAddress
+ Size
);
290 /* Did the host provide a memory hook? */
291 if (State
->MemWriteCallback
)
293 /* Yes, call the host */
294 State
->MemWriteCallback(State
,
295 (TableEntry
.Address
<< 12) | PageOffset
,
301 /* Read the memory directly */
302 RtlMoveMemory((PVOID
)((TableEntry
.Address
<< 12) | PageOffset
),
310 /* Did the host provide a memory hook? */
311 if (State
->MemWriteCallback
)
313 /* Yes, call the host */
314 State
->MemWriteCallback(State
, LinearAddress
, Buffer
, Size
);
318 /* Write the memory directly */
319 RtlMoveMemory((PVOID
)LinearAddress
, Buffer
, Size
);
327 Soft386InterruptInternal(PSOFT386_STATE State
,
328 USHORT SegmentSelector
,
330 BOOLEAN InterruptGate
)
332 /* Check for protected mode */
333 if (State
->ControlRegisters
[SOFT386_REG_CR0
] & SOFT386_CR0_PE
)
336 USHORT OldSs
= State
->SegmentRegs
[SOFT386_REG_SS
].Selector
;
337 ULONG OldEsp
= State
->GeneralRegs
[SOFT386_REG_ESP
].Long
;
339 /* Check if the interrupt handler is more privileged */
340 if (Soft386GetCurrentPrivLevel(State
) > GET_SEGMENT_RPL(SegmentSelector
))
343 // FIXME: This code is only correct when paging is disabled!!!
344 if (State
->MemReadCallback
)
346 State
->MemReadCallback(State
,
353 RtlMoveMemory(&Tss
, (PVOID
)State
->Tss
.Address
, sizeof(Tss
));
356 /* Check the new (higher) privilege level */
357 switch (GET_SEGMENT_RPL(SegmentSelector
))
361 if (!Soft386LoadSegment(State
, SOFT386_REG_SS
, Tss
.Ss0
))
363 /* Exception occurred */
366 State
->GeneralRegs
[SOFT386_REG_ESP
].Long
= Tss
.Esp0
;
373 if (!Soft386LoadSegment(State
, SOFT386_REG_SS
, Tss
.Ss1
))
375 /* Exception occurred */
378 State
->GeneralRegs
[SOFT386_REG_ESP
].Long
= Tss
.Esp1
;
385 if (!Soft386LoadSegment(State
, SOFT386_REG_SS
, Tss
.Ss2
))
387 /* Exception occurred */
390 State
->GeneralRegs
[SOFT386_REG_ESP
].Long
= Tss
.Esp2
;
397 /* Should never reach here! */
402 /* Push SS selector */
403 if (!Soft386StackPush(State
, OldSs
)) return FALSE
;
405 /* Push stack pointer */
406 if (!Soft386StackPush(State
, OldEsp
)) return FALSE
;
411 if (!Soft386StackPush(State
, State
->Flags
.Long
)) return FALSE
;
413 /* Push CS selector */
414 if (!Soft386StackPush(State
, State
->SegmentRegs
[SOFT386_REG_CS
].Selector
)) return FALSE
;
416 /* Push the instruction pointer */
417 if (!Soft386StackPush(State
, State
->InstPtr
.Long
)) return FALSE
;
421 /* Disable interrupts after a jump to an interrupt gate handler */
422 State
->Flags
.If
= FALSE
;
426 if (!Soft386LoadSegment(State
, SOFT386_REG_CS
, SegmentSelector
))
428 /* An exception occurred during the jump */
432 if (State
->SegmentRegs
[SOFT386_REG_CS
].Size
)
434 /* 32-bit code segment, use EIP */
435 State
->InstPtr
.Long
= Offset
;
439 /* 16-bit code segment, use IP */
440 State
->InstPtr
.LowWord
= LOWORD(Offset
);
448 Soft386ExceptionWithErrorCode(PSOFT386_STATE State
,
449 SOFT386_EXCEPTIONS ExceptionCode
,
452 SOFT386_IDT_ENTRY IdtEntry
;
454 /* Increment the exception count */
455 State
->ExceptionCount
++;
457 /* Check if the exception occurred more than once */
458 if (State
->ExceptionCount
> 1)
460 /* Then this is a double fault */
461 ExceptionCode
= SOFT386_EXCEPTION_DF
;
464 /* Check if this is a triple fault */
465 if (State
->ExceptionCount
== 3)
472 /* Restore the IP to the saved IP */
473 State
->InstPtr
= State
->SavedInstPtr
;
475 if (!Soft386GetIntVector(State
, ExceptionCode
, &IdtEntry
))
478 * If this function failed, that means Soft386Exception
479 * was called again, so just return in this case.
484 /* Perform the interrupt */
485 if (!Soft386InterruptInternal(State
,
487 MAKELONG(IdtEntry
.Offset
, IdtEntry
.OffsetHigh
),
491 * If this function failed, that means Soft386Exception
492 * was called again, so just return in this case.
497 if (EXCEPTION_HAS_ERROR_CODE(ExceptionCode
)
498 && (State
->ControlRegisters
[SOFT386_REG_CR0
] & SOFT386_CR0_PE
))
500 /* Push the error code */
501 Soft386StackPush(State
, ErrorCode
);