2 * Fast486 386/486 CPU Emulation Library
5 * Copyright (C) 2015 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 ***********************************************************/
36 Fast486ReadMemory(PFAST486_STATE State
,
37 FAST486_SEG_REGS SegmentReg
,
44 PFAST486_SEG_REG CachedDescriptor
;
45 FAST486_EXCEPTIONS Exception
= SegmentReg
!= FAST486_REG_SS
46 ? FAST486_EXCEPTION_GP
: FAST486_EXCEPTION_SS
;
48 ASSERT(SegmentReg
< FAST486_NUM_SEG_REGS
);
50 /* Get the cached descriptor */
51 CachedDescriptor
= &State
->SegmentRegs
[SegmentReg
];
53 if (InstFetch
|| CachedDescriptor
->Executable
|| !CachedDescriptor
->DirConf
)
55 if ((Offset
+ Size
- 1) > CachedDescriptor
->Limit
)
57 /* Read beyond limit */
58 Fast486Exception(State
, Exception
);
64 if (Offset
< CachedDescriptor
->Limit
)
66 /* Read beyond limit */
67 Fast486Exception(State
, Exception
);
72 /* Check for protected mode */
73 if ((State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
) && !State
->Flags
.Vm
)
75 /* Privilege checks */
77 if (!CachedDescriptor
->Present
)
79 Fast486Exception(State
, Exception
);
83 if ((!InstFetch
&& (CachedDescriptor
->Rpl
> CachedDescriptor
->Dpl
))
84 || (Fast486GetCurrentPrivLevel(State
) > CachedDescriptor
->Dpl
))
86 Fast486Exception(State
, Exception
);
92 if (!CachedDescriptor
->Executable
)
94 /* Data segment not executable */
95 Fast486Exception(State
, Exception
);
101 if (CachedDescriptor
->Executable
&& (!CachedDescriptor
->ReadWrite
))
103 /* Code segment not readable */
104 Fast486Exception(State
, Exception
);
110 /* Find the linear address */
111 LinearAddress
= CachedDescriptor
->Base
+ Offset
;
113 #ifndef FAST486_NO_PREFETCH
114 if (InstFetch
&& ((Offset
+ FAST486_CACHE_SIZE
- 1) <= CachedDescriptor
->Limit
))
116 State
->PrefetchAddress
= LinearAddress
;
118 if ((State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PG
)
119 && (PAGE_OFFSET(State
->PrefetchAddress
) > (FAST486_PAGE_SIZE
- FAST486_CACHE_SIZE
)))
121 /* We mustn't prefetch across a page boundary */
122 State
->PrefetchAddress
= PAGE_ALIGN(State
->PrefetchAddress
)
123 | (FAST486_PAGE_SIZE
- FAST486_CACHE_SIZE
);
125 if ((LinearAddress
- State
->PrefetchAddress
+ Size
) >= FAST486_CACHE_SIZE
)
127 /* We can't prefetch without possibly violating page permissions */
128 State
->PrefetchValid
= FALSE
;
129 return Fast486ReadLinearMemory(State
, LinearAddress
, Buffer
, Size
, TRUE
);
134 if (Fast486ReadLinearMemory(State
,
135 State
->PrefetchAddress
,
136 State
->PrefetchCache
,
140 State
->PrefetchValid
= TRUE
;
142 RtlMoveMemory(Buffer
,
143 &State
->PrefetchCache
[LinearAddress
- State
->PrefetchAddress
],
149 State
->PrefetchValid
= FALSE
;
156 /* Read from the linear address */
157 return Fast486ReadLinearMemory(State
, LinearAddress
, Buffer
, Size
, TRUE
);
163 Fast486WriteMemory(PFAST486_STATE State
,
164 FAST486_SEG_REGS SegmentReg
,
170 PFAST486_SEG_REG CachedDescriptor
;
171 FAST486_EXCEPTIONS Exception
= SegmentReg
!= FAST486_REG_SS
172 ? FAST486_EXCEPTION_GP
: FAST486_EXCEPTION_SS
;
174 ASSERT(SegmentReg
< FAST486_NUM_SEG_REGS
);
176 /* Get the cached descriptor */
177 CachedDescriptor
= &State
->SegmentRegs
[SegmentReg
];
179 if (CachedDescriptor
->Executable
|| !CachedDescriptor
->DirConf
)
181 if ((Offset
+ Size
- 1) > CachedDescriptor
->Limit
)
183 /* Write beyond limit */
184 Fast486Exception(State
, Exception
);
190 if (Offset
< CachedDescriptor
->Limit
)
192 /* Write beyond limit */
193 Fast486Exception(State
, Exception
);
198 /* Check for protected mode */
199 if ((State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
) && !State
->Flags
.Vm
)
201 /* Privilege checks */
203 if (!CachedDescriptor
->Present
)
205 Fast486Exception(State
, Exception
);
209 if ((CachedDescriptor
->Rpl
> CachedDescriptor
->Dpl
)
210 || (Fast486GetCurrentPrivLevel(State
) > CachedDescriptor
->Dpl
))
212 Fast486Exception(State
, Exception
);
216 if (CachedDescriptor
->Executable
)
218 /* Code segment not writable */
219 Fast486Exception(State
, Exception
);
222 else if (!CachedDescriptor
->ReadWrite
)
224 /* Data segment not writeable */
225 Fast486Exception(State
, Exception
);
230 /* Find the linear address */
231 LinearAddress
= CachedDescriptor
->Base
+ Offset
;
233 #ifndef FAST486_NO_PREFETCH
234 if (State
->PrefetchValid
235 && (LinearAddress
>= State
->PrefetchAddress
)
236 && ((LinearAddress
+ Size
) <= (State
->PrefetchAddress
+ FAST486_CACHE_SIZE
)))
238 /* Update the prefetch */
239 RtlMoveMemory(&State
->PrefetchCache
[LinearAddress
- State
->PrefetchAddress
],
241 min(Size
, FAST486_CACHE_SIZE
+ State
->PrefetchAddress
- LinearAddress
));
245 /* Write to the linear address */
246 return Fast486WriteLinearMemory(State
, LinearAddress
, Buffer
, Size
, TRUE
);
249 static inline BOOLEAN
251 Fast486GetIntVector(PFAST486_STATE State
,
253 PFAST486_IDT_ENTRY IdtEntry
)
255 /* Check for protected mode */
256 if (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
)
258 /* Read from the IDT */
259 if (!Fast486ReadLinearMemory(State
,
261 + Number
* sizeof(*IdtEntry
),
266 /* Exception occurred */
272 /* Read from the real-mode IVT */
275 /* Paging is always disabled in real mode */
276 State
->MemReadCallback(State
,
278 + Number
* sizeof(FarPointer
),
282 /* Fill a fake IDT entry */
283 IdtEntry
->Offset
= LOWORD(FarPointer
);
284 IdtEntry
->Selector
= HIWORD(FarPointer
);
286 IdtEntry
->Type
= FAST486_IDT_INT_GATE
;
287 IdtEntry
->Storage
= FALSE
;
289 IdtEntry
->Present
= TRUE
;
290 IdtEntry
->OffsetHigh
= 0;
296 static inline BOOLEAN
298 Fast486InterruptInternal(PFAST486_STATE State
,
299 PFAST486_IDT_ENTRY IdtEntry
,
300 BOOLEAN PushErrorCode
,
303 BOOLEAN GateSize
= (IdtEntry
->Type
== FAST486_IDT_INT_GATE_32
) ||
304 (IdtEntry
->Type
== FAST486_IDT_TRAP_GATE_32
);
305 USHORT OldCs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
306 ULONG OldEip
= State
->InstPtr
.Long
;
307 ULONG OldFlags
= State
->Flags
.Long
;
308 UCHAR OldCpl
= State
->Cpl
;
310 /* Check for protected mode */
311 if (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
)
313 USHORT OldSs
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
314 ULONG OldEsp
= State
->GeneralRegs
[FAST486_REG_ESP
].Long
;
315 BOOLEAN OldVm
= State
->Flags
.Vm
;
317 if (IdtEntry
->Type
== FAST486_TASK_GATE_SIGNATURE
)
320 if (!Fast486TaskSwitch(State
, FAST486_TASK_CALL
, IdtEntry
->Selector
))
322 /* Exception occurred */
329 /* Check if the interrupt handler is more privileged or if we're in V86 mode */
330 if ((OldCpl
> GET_SEGMENT_RPL(IdtEntry
->Selector
)) || State
->Flags
.Vm
)
333 PFAST486_LEGACY_TSS LegacyTss
= (PFAST486_LEGACY_TSS
)&Tss
;
338 if (!Fast486ReadLinearMemory(State
,
341 State
->TaskReg
.Modern
342 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
345 /* Exception occurred */
349 /* Switch to the new privilege level */
350 State
->Cpl
= GET_SEGMENT_RPL(IdtEntry
->Selector
);
352 /* Clear the VM flag */
353 State
->Flags
.Vm
= FALSE
;
355 /* Check the new (higher) privilege level */
360 if (State
->TaskReg
.Modern
)
367 NewSs
= LegacyTss
->Ss0
;
368 NewEsp
= LegacyTss
->Sp0
;
376 if (State
->TaskReg
.Modern
)
383 NewSs
= LegacyTss
->Ss1
;
384 NewEsp
= LegacyTss
->Sp1
;
392 if (State
->TaskReg
.Modern
)
399 NewSs
= LegacyTss
->Ss2
;
400 NewEsp
= LegacyTss
->Sp2
;
408 /* Should never reach here! */
413 if (!Fast486LoadSegment(State
, FAST486_REG_SS
, NewSs
))
415 /* Exception occurred */
419 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewEsp
;
423 if (!Fast486LoadSegment(State
, FAST486_REG_CS
, IdtEntry
->Selector
))
425 /* An exception occurred during the jump */
431 /* 32-bit code segment, use EIP */
432 State
->InstPtr
.Long
= MAKELONG(IdtEntry
->Offset
, IdtEntry
->OffsetHigh
);
436 /* 16-bit code segment, use IP */
437 State
->InstPtr
.LowWord
= IdtEntry
->Offset
;
441 State
->Flags
.Nt
= FALSE
;
445 /* Push GS, FS, DS and ES */
446 if (!Fast486StackPushInternal(State
,
448 State
->SegmentRegs
[FAST486_REG_GS
].Selector
))
452 if (!Fast486StackPushInternal(State
,
454 State
->SegmentRegs
[FAST486_REG_FS
].Selector
))
458 if (!Fast486StackPushInternal(State
,
460 State
->SegmentRegs
[FAST486_REG_DS
].Selector
))
464 if (!Fast486StackPushInternal(State
,
466 State
->SegmentRegs
[FAST486_REG_ES
].Selector
))
471 /* Now load them with NULL selectors, since they are useless in protected mode */
472 if (!Fast486LoadSegment(State
, FAST486_REG_GS
, 0)) return FALSE
;
473 if (!Fast486LoadSegment(State
, FAST486_REG_FS
, 0)) return FALSE
;
474 if (!Fast486LoadSegment(State
, FAST486_REG_DS
, 0)) return FALSE
;
475 if (!Fast486LoadSegment(State
, FAST486_REG_ES
, 0)) return FALSE
;
478 /* Check if the interrupt handler is more privileged or we're in VM86 mode (again) */
479 if ((OldCpl
> GET_SEGMENT_RPL(IdtEntry
->Selector
)) || OldVm
)
481 /* Push SS selector */
482 if (!Fast486StackPushInternal(State
, GateSize
, OldSs
)) return FALSE
;
484 /* Push the stack pointer */
485 if (!Fast486StackPushInternal(State
, GateSize
, OldEsp
)) return FALSE
;
491 if (!Fast486LoadSegment(State
, FAST486_REG_CS
, IdtEntry
->Selector
))
493 /* An exception occurred during the jump */
498 State
->InstPtr
.LowWord
= IdtEntry
->Offset
;
502 if (!Fast486StackPushInternal(State
, GateSize
, OldFlags
)) return FALSE
;
504 /* Push CS selector */
505 if (!Fast486StackPushInternal(State
, GateSize
, OldCs
)) return FALSE
;
507 /* Push the instruction pointer */
508 if (!Fast486StackPushInternal(State
, GateSize
, OldEip
)) return FALSE
;
514 /* Push the error code */
515 if (!Fast486StackPushInternal(State
, GateSize
, ErrorCode
)) return FALSE
;
518 if ((IdtEntry
->Type
== FAST486_IDT_INT_GATE
)
519 || (IdtEntry
->Type
== FAST486_IDT_INT_GATE_32
))
521 /* Disable interrupts after a jump to an interrupt gate handler */
522 State
->Flags
.If
= FALSE
;
526 State
->Flags
.Tf
= FALSE
;
533 Fast486PerformInterrupt(PFAST486_STATE State
,
536 FAST486_IDT_ENTRY IdtEntry
;
538 /* Get the interrupt vector */
539 if (!Fast486GetIntVector(State
, Number
, &IdtEntry
))
541 /* Exception occurred */
545 /* Perform the interrupt */
546 if (!Fast486InterruptInternal(State
, &IdtEntry
, FALSE
, 0))
548 /* Exception occurred */
557 Fast486ExceptionWithErrorCode(PFAST486_STATE State
,
558 FAST486_EXCEPTIONS ExceptionCode
,
561 FAST486_IDT_ENTRY IdtEntry
;
563 /* Increment the exception count */
564 State
->ExceptionCount
++;
566 /* Check if the exception occurred more than once */
567 if (State
->ExceptionCount
> 1)
569 /* Then this is a double fault */
570 ExceptionCode
= FAST486_EXCEPTION_DF
;
573 /* Check if this is a triple fault */
574 if (State
->ExceptionCount
== 3)
576 DPRINT("Fast486ExceptionWithErrorCode(%04X:%08X) -- Triple fault\n",
577 State
->SegmentRegs
[FAST486_REG_CS
].Selector
,
578 State
->InstPtr
.Long
);
585 /* Clear the prefix flags */
586 State
->PrefixFlags
= 0;
588 /* Restore the IP to the saved IP */
589 State
->InstPtr
= State
->SavedInstPtr
;
591 /* Restore the SP to the saved SP */
592 State
->GeneralRegs
[FAST486_REG_ESP
] = State
->SavedStackPtr
;
594 /* Get the interrupt vector */
595 if (!Fast486GetIntVector(State
, ExceptionCode
, &IdtEntry
))
598 * If this function failed, that means Fast486Exception
599 * was called again, so just return in this case.
604 /* Perform the interrupt */
605 if (!Fast486InterruptInternal(State
,
607 EXCEPTION_HAS_ERROR_CODE(ExceptionCode
)
608 && (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
),
612 * If this function failed, that means Fast486Exception
613 * was called again, so just return in this case.
618 /* Reset the exception count */
619 State
->ExceptionCount
= 0;
624 Fast486TaskSwitch(PFAST486_STATE State
, FAST486_TASK_SWITCH_TYPE Type
, USHORT Selector
)
628 FAST486_SYSTEM_DESCRIPTOR NewTssDescriptor
;
630 PFAST486_LEGACY_TSS OldLegacyTss
= (PFAST486_LEGACY_TSS
)&OldTss
;
632 PFAST486_LEGACY_TSS NewLegacyTss
= (PFAST486_LEGACY_TSS
)&NewTss
;
633 USHORT NewLdtr
, NewEs
, NewCs
, NewSs
, NewDs
;
635 if ((State
->TaskReg
.Modern
&& State
->TaskReg
.Limit
< (sizeof(FAST486_TSS
) - 1))
636 || (!State
->TaskReg
.Modern
&& State
->TaskReg
.Limit
< (sizeof(FAST486_LEGACY_TSS
) - 1)))
638 /* Invalid task register limit */
639 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, State
->TaskReg
.Selector
);
643 /* Read the old TSS */
644 if (!Fast486ReadLinearMemory(State
,
647 State
->TaskReg
.Modern
648 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
651 /* Exception occurred */
656 /* If this is a task return, use the linked previous selector */
657 if (Type
== FAST486_TASK_RETURN
)
659 if (State
->TaskReg
.Modern
) Selector
= LOWORD(OldTss
.Link
);
660 else Selector
= OldLegacyTss
->Link
;
663 /* Make sure the entry exists in the GDT (not LDT!) */
664 if ((GET_SEGMENT_INDEX(Selector
) == 0)
665 || (Selector
& SEGMENT_TABLE_INDICATOR
)
666 || GET_SEGMENT_INDEX(Selector
) >= (State
->Gdtr
.Size
+ 1u))
668 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, Selector
);
672 /* Get the TSS descriptor from the GDT */
673 if (!Fast486ReadLinearMemory(State
,
674 State
->Gdtr
.Address
+ GET_SEGMENT_INDEX(Selector
),
676 sizeof(NewTssDescriptor
),
679 /* Exception occurred */
683 if (!NewTssDescriptor
.Present
)
685 /* Incoming task TSS not present */
686 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_NP
, Selector
);
690 /* Calculate the linear address of the new TSS */
691 NewTssAddress
= NewTssDescriptor
.Base
;
692 NewTssAddress
|= NewTssDescriptor
.BaseMid
<< 16;
693 NewTssAddress
|= NewTssDescriptor
.BaseHigh
<< 24;
695 /* Calculate the limit of the new TSS */
696 NewTssLimit
= NewTssDescriptor
.Limit
| (NewTssDescriptor
.LimitHigh
<< 16);
698 if (NewTssDescriptor
.Granularity
)
701 NewTssLimit
|= 0x00000FFF;
704 if (NewTssLimit
< (sizeof(FAST486_TSS
) - 1)
705 && NewTssLimit
!= (sizeof(FAST486_LEGACY_TSS
) - 1))
707 /* TSS limit invalid */
708 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, Selector
);
713 * The incoming task shouldn't be busy if we're executing it as a
714 * new task, and it should be busy if we're returning to it.
716 if ((((NewTssDescriptor
.Signature
!= FAST486_TSS_SIGNATURE
)
717 && (NewTssDescriptor
.Signature
!= FAST486_TSS_16_SIGNATURE
))
718 || (Type
== FAST486_TASK_RETURN
))
719 && (((NewTssDescriptor
.Signature
!= FAST486_BUSY_TSS_SIGNATURE
)
720 && (NewTssDescriptor
.Signature
!= FAST486_BUSY_TSS_16_SIGNATURE
))
721 || (Type
!= FAST486_TASK_RETURN
)))
723 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_GP
, Selector
);
727 /* Read the new TSS */
728 if (!Fast486ReadLinearMemory(State
,
731 (NewTssDescriptor
.Signature
== FAST486_TSS_SIGNATURE
)
732 || (NewTssDescriptor
.Signature
== FAST486_BUSY_TSS_SIGNATURE
)
733 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
736 /* Exception occurred */
740 if (Type
!= FAST486_TASK_CALL
)
742 /* Clear the busy bit of the outgoing task */
743 FAST486_SYSTEM_DESCRIPTOR OldTssDescriptor
;
745 if (!Fast486ReadLinearMemory(State
,
747 + GET_SEGMENT_INDEX(State
->TaskReg
.Selector
),
749 sizeof(OldTssDescriptor
),
752 /* Exception occurred */
756 OldTssDescriptor
.Signature
= FAST486_TSS_SIGNATURE
;
758 if (!Fast486WriteLinearMemory(State
,
760 + GET_SEGMENT_INDEX(State
->TaskReg
.Selector
),
762 sizeof(OldTssDescriptor
),
765 /* Exception occurred */
772 if ((NewTssDescriptor
.Signature
== FAST486_TSS_SIGNATURE
)
773 || (NewTssDescriptor
.Signature
== FAST486_BUSY_TSS_SIGNATURE
))
775 NewTss
.Link
= State
->TaskReg
.Selector
;
777 /* Write back the new TSS link */
778 if (!Fast486WriteLinearMemory(State
,
784 /* Exception occurred */
790 NewLegacyTss
->Link
= State
->TaskReg
.Selector
;
792 /* Write back the new legacy TSS link */
793 if (!Fast486WriteLinearMemory(State
,
796 sizeof(NewLegacyTss
->Link
),
799 /* Exception occurred */
805 /* Save the current task into the TSS */
806 if (State
->TaskReg
.Modern
)
808 OldTss
.Cr3
= State
->ControlRegisters
[FAST486_REG_CR3
];
809 OldTss
.Eip
= State
->InstPtr
.Long
;
810 OldTss
.Eflags
= State
->Flags
.Long
;
811 OldTss
.Eax
= State
->GeneralRegs
[FAST486_REG_EAX
].Long
;
812 OldTss
.Ecx
= State
->GeneralRegs
[FAST486_REG_ECX
].Long
;
813 OldTss
.Edx
= State
->GeneralRegs
[FAST486_REG_EDX
].Long
;
814 OldTss
.Ebx
= State
->GeneralRegs
[FAST486_REG_EBX
].Long
;
815 OldTss
.Esp
= State
->GeneralRegs
[FAST486_REG_ESP
].Long
;
816 OldTss
.Ebp
= State
->GeneralRegs
[FAST486_REG_EBP
].Long
;
817 OldTss
.Esi
= State
->GeneralRegs
[FAST486_REG_ESI
].Long
;
818 OldTss
.Edi
= State
->GeneralRegs
[FAST486_REG_EDI
].Long
;
819 OldTss
.Es
= State
->SegmentRegs
[FAST486_REG_ES
].Selector
;
820 OldTss
.Cs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
821 OldTss
.Ss
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
822 OldTss
.Ds
= State
->SegmentRegs
[FAST486_REG_DS
].Selector
;
823 OldTss
.Fs
= State
->SegmentRegs
[FAST486_REG_FS
].Selector
;
824 OldTss
.Gs
= State
->SegmentRegs
[FAST486_REG_GS
].Selector
;
825 OldTss
.Ldtr
= State
->Ldtr
.Selector
;
829 OldLegacyTss
->Ip
= State
->InstPtr
.LowWord
;
830 OldLegacyTss
->Flags
= State
->Flags
.LowWord
;
831 OldLegacyTss
->Ax
= State
->GeneralRegs
[FAST486_REG_EAX
].LowWord
;
832 OldLegacyTss
->Cx
= State
->GeneralRegs
[FAST486_REG_ECX
].LowWord
;
833 OldLegacyTss
->Dx
= State
->GeneralRegs
[FAST486_REG_EDX
].LowWord
;
834 OldLegacyTss
->Bx
= State
->GeneralRegs
[FAST486_REG_EBX
].LowWord
;
835 OldLegacyTss
->Sp
= State
->GeneralRegs
[FAST486_REG_ESP
].LowWord
;
836 OldLegacyTss
->Bp
= State
->GeneralRegs
[FAST486_REG_EBP
].LowWord
;
837 OldLegacyTss
->Si
= State
->GeneralRegs
[FAST486_REG_ESI
].LowWord
;
838 OldLegacyTss
->Di
= State
->GeneralRegs
[FAST486_REG_EDI
].LowWord
;
839 OldLegacyTss
->Es
= State
->SegmentRegs
[FAST486_REG_ES
].Selector
;
840 OldLegacyTss
->Cs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
841 OldLegacyTss
->Ss
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
842 OldLegacyTss
->Ds
= State
->SegmentRegs
[FAST486_REG_DS
].Selector
;
843 OldLegacyTss
->Ldtr
= State
->Ldtr
.Selector
;
846 /* Write back the old TSS */
847 if (!Fast486WriteLinearMemory(State
,
850 State
->TaskReg
.Modern
851 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
854 /* Exception occurred */
858 /* Mark the new task as busy */
859 if (NewTssDescriptor
.Signature
== FAST486_TSS_SIGNATURE
860 || NewTssDescriptor
.Signature
== FAST486_BUSY_TSS_SIGNATURE
)
863 NewTssDescriptor
.Signature
= FAST486_BUSY_TSS_SIGNATURE
;
868 NewTssDescriptor
.Signature
= FAST486_BUSY_TSS_16_SIGNATURE
;
871 /* Write back the new TSS descriptor */
872 if (!Fast486WriteLinearMemory(State
,
873 State
->Gdtr
.Address
+ GET_SEGMENT_INDEX(Selector
),
875 sizeof(NewTssDescriptor
),
878 /* Exception occurred */
882 /* Set the task switch bit */
883 State
->ControlRegisters
[FAST486_REG_CR0
] |= FAST486_CR0_TS
;
885 /* Load the task register with the new values */
886 State
->TaskReg
.Selector
= Selector
;
887 State
->TaskReg
.Base
= NewTssAddress
;
888 State
->TaskReg
.Limit
= NewTssLimit
;
889 State
->TaskReg
.Modern
= (NewTssDescriptor
.Signature
== FAST486_BUSY_TSS_SIGNATURE
);
891 if (NewTssDescriptor
.Signature
== FAST486_BUSY_TSS_SIGNATURE
)
893 /* Change the page directory */
894 State
->ControlRegisters
[FAST486_REG_CR3
] = NewTss
.Cr3
;
898 Fast486FlushTlb(State
);
901 if (NewTssDescriptor
.Signature
== FAST486_BUSY_TSS_SIGNATURE
)
903 State
->Cpl
= GET_SEGMENT_RPL(NewTss
.Cs
);
907 State
->Cpl
= GET_SEGMENT_RPL(NewLegacyTss
->Cs
);
910 #ifndef FAST486_NO_PREFETCH
911 /* Context switching invalidates the prefetch */
912 State
->PrefetchValid
= FALSE
;
915 /* Load the registers */
916 if (NewTssDescriptor
.Signature
== FAST486_BUSY_TSS_SIGNATURE
)
918 State
->InstPtr
.Long
= State
->SavedInstPtr
.Long
= NewTss
.Eip
;
919 State
->Flags
.Long
= NewTss
.Eflags
;
920 State
->GeneralRegs
[FAST486_REG_EAX
].Long
= NewTss
.Eax
;
921 State
->GeneralRegs
[FAST486_REG_ECX
].Long
= NewTss
.Ecx
;
922 State
->GeneralRegs
[FAST486_REG_EDX
].Long
= NewTss
.Edx
;
923 State
->GeneralRegs
[FAST486_REG_EBX
].Long
= NewTss
.Ebx
;
924 State
->GeneralRegs
[FAST486_REG_EBP
].Long
= NewTss
.Ebp
;
925 State
->GeneralRegs
[FAST486_REG_ESI
].Long
= NewTss
.Esi
;
926 State
->GeneralRegs
[FAST486_REG_EDI
].Long
= NewTss
.Edi
;
930 NewLdtr
= NewTss
.Ldtr
;
932 if (Type
== FAST486_TASK_CALL
&& State
->Cpl
< 3)
938 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewTss
.Esp0
;
945 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewTss
.Esp1
;
952 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewTss
.Esp2
;
960 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewTss
.Esp
;
966 State
->InstPtr
.LowWord
= State
->SavedInstPtr
.LowWord
= NewLegacyTss
->Ip
;
967 State
->Flags
.LowWord
= NewLegacyTss
->Flags
;
968 State
->GeneralRegs
[FAST486_REG_EAX
].LowWord
= NewLegacyTss
->Ax
;
969 State
->GeneralRegs
[FAST486_REG_ECX
].LowWord
= NewLegacyTss
->Cx
;
970 State
->GeneralRegs
[FAST486_REG_EDX
].LowWord
= NewLegacyTss
->Dx
;
971 State
->GeneralRegs
[FAST486_REG_EBX
].LowWord
= NewLegacyTss
->Bx
;
972 State
->GeneralRegs
[FAST486_REG_EBP
].LowWord
= NewLegacyTss
->Bp
;
973 State
->GeneralRegs
[FAST486_REG_ESI
].LowWord
= NewLegacyTss
->Si
;
974 State
->GeneralRegs
[FAST486_REG_EDI
].LowWord
= NewLegacyTss
->Di
;
975 NewEs
= NewLegacyTss
->Es
;
976 NewCs
= NewLegacyTss
->Cs
;
977 NewDs
= NewLegacyTss
->Ds
;
978 NewLdtr
= NewLegacyTss
->Ldtr
;
980 if (Type
== FAST486_TASK_CALL
&& State
->Cpl
< 3)
986 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewLegacyTss
->Sp0
;
987 NewSs
= NewLegacyTss
->Ss0
;
993 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewLegacyTss
->Sp1
;
994 NewSs
= NewLegacyTss
->Ss1
;
1000 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewLegacyTss
->Sp2
;
1001 NewSs
= NewLegacyTss
->Ss2
;
1008 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewLegacyTss
->Sp
;
1009 NewSs
= NewLegacyTss
->Ss
;
1013 /* Set the NT flag if nesting */
1014 if (Type
== FAST486_TASK_CALL
) State
->Flags
.Nt
= TRUE
;
1016 if (GET_SEGMENT_INDEX(NewLdtr
) != 0)
1019 FAST486_SYSTEM_DESCRIPTOR GdtEntry
;
1021 if (NewLdtr
& SEGMENT_TABLE_INDICATOR
)
1023 /* This selector doesn't point to the GDT */
1024 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
1028 if (!Fast486ReadDescriptorEntry(State
, NewLdtr
, &Valid
, (PFAST486_GDT_ENTRY
)&GdtEntry
))
1030 /* Exception occurred */
1036 /* Invalid selector */
1037 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
1041 if (GdtEntry
.Signature
!= FAST486_LDT_SIGNATURE
)
1043 /* This is not an LDT descriptor */
1044 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
1048 if (!GdtEntry
.Present
)
1050 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
1054 /* Update the LDTR */
1055 State
->Ldtr
.Selector
= NewLdtr
;
1056 State
->Ldtr
.Base
= GdtEntry
.Base
| (GdtEntry
.BaseMid
<< 16) | (GdtEntry
.BaseHigh
<< 24);
1057 State
->Ldtr
.Limit
= GdtEntry
.Limit
| (GdtEntry
.LimitHigh
<< 16);
1059 if (GdtEntry
.Granularity
)
1061 State
->Ldtr
.Limit
<<= 12;
1062 State
->Ldtr
.Limit
|= 0x00000FFF;
1067 /* The LDT of this task is empty */
1068 RtlZeroMemory(&State
->Ldtr
, sizeof(State
->Ldtr
));
1071 /* Load the new segments */
1072 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_CS
, NewCs
, FAST486_EXCEPTION_TS
))
1077 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_SS
, NewSs
, FAST486_EXCEPTION_TS
))
1082 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_ES
, NewEs
, FAST486_EXCEPTION_TS
))
1087 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_DS
, NewDs
, FAST486_EXCEPTION_TS
))
1092 if (NewTssDescriptor
.Signature
== FAST486_BUSY_TSS_SIGNATURE
)
1094 if (!Fast486LoadSegmentInternal(State
,
1097 FAST486_EXCEPTION_TS
))
1102 if (!Fast486LoadSegmentInternal(State
,
1105 FAST486_EXCEPTION_TS
))
1116 Fast486CallGate(PFAST486_STATE State
,
1117 PFAST486_CALL_GATE Gate
,
1121 FAST486_GDT_ENTRY NewCodeSegment
;
1122 BOOLEAN GateSize
= (Gate
->Type
== FAST486_CALL_GATE_SIGNATURE
);
1124 PFAST486_LEGACY_TSS LegacyTss
= (PFAST486_LEGACY_TSS
)&Tss
;
1125 USHORT OldCs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
1126 ULONG OldEip
= State
->InstPtr
.Long
;
1127 USHORT OldCpl
= State
->Cpl
;
1128 USHORT OldSs
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
1129 ULONG OldEsp
= State
->GeneralRegs
[FAST486_REG_ESP
].Long
;
1130 ULONG ParamBuffer
[32]; /* Maximum possible size - 32 DWORDs */
1131 PULONG LongParams
= (PULONG
)ParamBuffer
;
1132 PUSHORT ShortParams
= (PUSHORT
)ParamBuffer
;
1134 if (!Gate
->Selector
)
1136 /* The code segment is NULL */
1137 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
1141 if (!Fast486ReadDescriptorEntry(State
, Gate
->Selector
, &Valid
, &NewCodeSegment
))
1143 /* Exception occurred */
1147 if (!Valid
|| (NewCodeSegment
.Dpl
> Fast486GetCurrentPrivLevel(State
)))
1149 /* Code segment invalid */
1150 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_GP
, Gate
->Selector
);
1154 if (Call
&& Gate
->ParamCount
)
1156 /* Read the parameters */
1157 if (!Fast486ReadMemory(State
,
1162 Gate
->ParamCount
* (GateSize
? sizeof(ULONG
) : sizeof(USHORT
))))
1164 /* Exception occurred */
1169 /* Check if the new code segment is more privileged */
1170 if (NewCodeSegment
.Dpl
< OldCpl
)
1178 if (!Fast486ReadLinearMemory(State
,
1179 State
->TaskReg
.Base
,
1181 State
->TaskReg
.Modern
1182 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
1185 /* Exception occurred */
1189 /* Switch to the new privilege level */
1190 State
->Cpl
= NewCodeSegment
.Dpl
;
1192 /* Check the new (higher) privilege level */
1197 if (State
->TaskReg
.Modern
)
1204 NewSs
= LegacyTss
->Ss0
;
1205 NewEsp
= LegacyTss
->Sp0
;
1213 if (State
->TaskReg
.Modern
)
1220 NewSs
= LegacyTss
->Ss1
;
1221 NewEsp
= LegacyTss
->Sp1
;
1229 if (State
->TaskReg
.Modern
)
1236 NewSs
= LegacyTss
->Ss2
;
1237 NewEsp
= LegacyTss
->Sp2
;
1245 /* Should never reach here! */
1250 if (!Fast486LoadSegment(State
, FAST486_REG_SS
, NewSs
))
1252 /* Exception occurred */
1256 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewEsp
;
1258 else if (!NewCodeSegment
.DirConf
)
1260 /* This is not allowed for jumps */
1261 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_GP
, Gate
->Selector
);
1267 if (!Fast486LoadSegment(State
, FAST486_REG_CS
, Gate
->Selector
))
1269 /* An exception occurred during the jump */
1273 /* Set the instruction pointer */
1274 if (GateSize
) State
->InstPtr
.Long
= MAKELONG(Gate
->Offset
, Gate
->OffsetHigh
);
1275 else State
->InstPtr
.Long
= Gate
->Offset
;
1281 /* Check if the new code segment is more privileged (again) */
1282 if (NewCodeSegment
.Dpl
< OldCpl
)
1284 /* Push SS selector */
1285 if (!Fast486StackPushInternal(State
, GateSize
, OldSs
)) return FALSE
;
1287 /* Push stack pointer */
1288 if (!Fast486StackPushInternal(State
, GateSize
, OldEsp
)) return FALSE
;
1291 /* Push the parameters in reverse order */
1292 for (i
= Gate
->ParamCount
- 1; i
>= 0; i
--)
1294 if (!Fast486StackPushInternal(State
,
1296 GateSize
? LongParams
[i
] : ShortParams
[i
]))
1298 /* Exception occurred */
1303 /* Push CS selector */
1304 if (!Fast486StackPushInternal(State
, GateSize
, OldCs
)) return FALSE
;
1306 /* Push the instruction pointer */
1307 if (!Fast486StackPushInternal(State
, GateSize
, OldEip
)) return FALSE
;