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 ***********************************************************/
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 (InstFetch
|| CachedDescriptor
->Executable
|| !CachedDescriptor
->DirConf
)
52 if ((Offset
+ Size
- 1) > CachedDescriptor
->Limit
)
54 /* Read beyond limit */
55 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
61 if (Offset
< CachedDescriptor
->Limit
)
63 /* Read beyond limit */
64 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
69 /* Check for protected mode */
70 if ((State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
) && !State
->Flags
.Vm
)
72 /* Privilege checks */
74 if (!CachedDescriptor
->Present
)
76 Fast486Exception(State
, FAST486_EXCEPTION_NP
);
80 if ((!InstFetch
&& (CachedDescriptor
->Rpl
> CachedDescriptor
->Dpl
))
81 || (Fast486GetCurrentPrivLevel(State
) > CachedDescriptor
->Dpl
))
83 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
89 if (!CachedDescriptor
->Executable
)
91 /* Data segment not executable */
92 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
98 if (CachedDescriptor
->Executable
&& (!CachedDescriptor
->ReadWrite
))
100 /* Code segment not readable */
101 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
107 /* Find the linear address */
108 LinearAddress
= CachedDescriptor
->Base
+ Offset
;
110 #ifndef FAST486_NO_PREFETCH
111 if (InstFetch
&& ((Offset
+ FAST486_CACHE_SIZE
- 1) <= CachedDescriptor
->Limit
))
113 State
->PrefetchAddress
= LinearAddress
;
115 if ((State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PG
)
116 && (PAGE_OFFSET(State
->PrefetchAddress
) > (FAST486_PAGE_SIZE
- FAST486_CACHE_SIZE
)))
118 /* We mustn't prefetch across a page boundary */
119 State
->PrefetchAddress
= PAGE_ALIGN(State
->PrefetchAddress
)
120 | (FAST486_PAGE_SIZE
- FAST486_CACHE_SIZE
);
122 if ((LinearAddress
- State
->PrefetchAddress
+ Size
) >= FAST486_CACHE_SIZE
)
124 /* We can't prefetch without possibly violating page permissions */
125 State
->PrefetchValid
= FALSE
;
126 return Fast486ReadLinearMemory(State
, LinearAddress
, Buffer
, Size
, TRUE
);
131 if (Fast486ReadLinearMemory(State
,
132 State
->PrefetchAddress
,
133 State
->PrefetchCache
,
137 State
->PrefetchValid
= TRUE
;
139 RtlMoveMemory(Buffer
,
140 &State
->PrefetchCache
[LinearAddress
- State
->PrefetchAddress
],
146 State
->PrefetchValid
= FALSE
;
153 /* Read from the linear address */
154 return Fast486ReadLinearMemory(State
, LinearAddress
, Buffer
, Size
, TRUE
);
159 Fast486WriteMemory(PFAST486_STATE State
,
160 FAST486_SEG_REGS SegmentReg
,
166 PFAST486_SEG_REG CachedDescriptor
;
168 ASSERT(SegmentReg
< FAST486_NUM_SEG_REGS
);
170 /* Get the cached descriptor */
171 CachedDescriptor
= &State
->SegmentRegs
[SegmentReg
];
173 if (CachedDescriptor
->Executable
|| !CachedDescriptor
->DirConf
)
175 if ((Offset
+ Size
- 1) > CachedDescriptor
->Limit
)
177 /* Write beyond limit */
178 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
184 if (Offset
< CachedDescriptor
->Limit
)
186 /* Read beyond limit */
187 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
192 /* Check for protected mode */
193 if ((State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
) && !State
->Flags
.Vm
)
195 /* Privilege checks */
197 if (!CachedDescriptor
->Present
)
199 Fast486Exception(State
, FAST486_EXCEPTION_NP
);
203 if ((CachedDescriptor
->Rpl
> CachedDescriptor
->Dpl
)
204 || (Fast486GetCurrentPrivLevel(State
) > CachedDescriptor
->Dpl
))
206 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
210 if (CachedDescriptor
->Executable
)
212 /* Code segment not writable */
213 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
216 else if (!CachedDescriptor
->ReadWrite
)
218 /* Data segment not writeable */
219 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
224 /* Find the linear address */
225 LinearAddress
= CachedDescriptor
->Base
+ Offset
;
227 #ifndef FAST486_NO_PREFETCH
228 if (State
->PrefetchValid
229 && (LinearAddress
>= State
->PrefetchAddress
)
230 && ((LinearAddress
+ Size
) <= (State
->PrefetchAddress
+ FAST486_CACHE_SIZE
)))
232 /* Update the prefetch */
233 RtlMoveMemory(&State
->PrefetchCache
[LinearAddress
- State
->PrefetchAddress
],
235 min(Size
, FAST486_CACHE_SIZE
+ State
->PrefetchAddress
- LinearAddress
));
239 /* Write to the linear address */
240 return Fast486WriteLinearMemory(State
, LinearAddress
, Buffer
, Size
, TRUE
);
243 static inline BOOLEAN
245 Fast486GetIntVector(PFAST486_STATE State
,
247 PFAST486_IDT_ENTRY IdtEntry
)
249 /* Check for protected mode */
250 if (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
)
252 /* Read from the IDT */
253 if (!Fast486ReadLinearMemory(State
,
255 + Number
* sizeof(*IdtEntry
),
260 /* Exception occurred */
266 /* Read from the real-mode IVT */
269 /* Paging is always disabled in real mode */
270 State
->MemReadCallback(State
,
272 + Number
* sizeof(FarPointer
),
276 /* Fill a fake IDT entry */
277 IdtEntry
->Offset
= LOWORD(FarPointer
);
278 IdtEntry
->Selector
= HIWORD(FarPointer
);
280 IdtEntry
->Type
= FAST486_IDT_INT_GATE
;
281 IdtEntry
->Storage
= FALSE
;
283 IdtEntry
->Present
= TRUE
;
284 IdtEntry
->OffsetHigh
= 0;
290 static inline BOOLEAN
292 Fast486InterruptInternal(PFAST486_STATE State
,
293 PFAST486_IDT_ENTRY IdtEntry
,
294 BOOLEAN PushErrorCode
,
297 BOOLEAN GateSize
= (IdtEntry
->Type
== FAST486_IDT_INT_GATE_32
) ||
298 (IdtEntry
->Type
== FAST486_IDT_TRAP_GATE_32
);
299 USHORT OldCs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
300 ULONG OldEip
= State
->InstPtr
.Long
;
301 ULONG OldFlags
= State
->Flags
.Long
;
302 UCHAR OldCpl
= State
->Cpl
;
304 /* Check for protected mode */
305 if (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
)
307 USHORT OldSs
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
308 ULONG OldEsp
= State
->GeneralRegs
[FAST486_REG_ESP
].Long
;
309 BOOLEAN OldVm
= State
->Flags
.Vm
;
311 if (IdtEntry
->Type
== FAST486_TASK_GATE_SIGNATURE
)
314 return Fast486TaskSwitch(State
, FAST486_TASK_CALL
, IdtEntry
->Selector
);
317 /* Check if the interrupt handler is more privileged or if we're in V86 mode */
318 if ((OldCpl
> GET_SEGMENT_RPL(IdtEntry
->Selector
)) || State
->Flags
.Vm
)
321 PFAST486_LEGACY_TSS LegacyTss
= (PFAST486_LEGACY_TSS
)&Tss
;
326 if (!Fast486ReadLinearMemory(State
,
329 State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1)
330 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
333 /* Exception occurred */
337 /* Switch to the new privilege level */
338 State
->Cpl
= GET_SEGMENT_RPL(IdtEntry
->Selector
);
340 /* Check the new (higher) privilege level */
345 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
352 NewSs
= LegacyTss
->Ss0
;
353 NewEsp
= LegacyTss
->Sp0
;
361 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
368 NewSs
= LegacyTss
->Ss1
;
369 NewEsp
= LegacyTss
->Sp1
;
377 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
384 NewSs
= LegacyTss
->Ss2
;
385 NewEsp
= LegacyTss
->Sp2
;
393 /* Should never reach here! */
398 if (!Fast486LoadSegment(State
, FAST486_REG_SS
, NewSs
))
400 /* Exception occurred */
404 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewEsp
;
408 /* Clear the VM flag */
409 State
->Flags
.Vm
= FALSE
;
414 if (!Fast486LoadSegment(State
, FAST486_REG_CS
, IdtEntry
->Selector
))
416 /* An exception occurred during the jump */
422 /* 32-bit code segment, use EIP */
423 State
->InstPtr
.Long
= MAKELONG(IdtEntry
->Offset
, IdtEntry
->OffsetHigh
);
427 /* 16-bit code segment, use IP */
428 State
->InstPtr
.LowWord
= IdtEntry
->Offset
;
433 /* Push GS, FS, DS and ES */
434 if (!Fast486StackPushInternal(State
,
436 State
->SegmentRegs
[FAST486_REG_GS
].Selector
))
440 if (!Fast486StackPushInternal(State
,
442 State
->SegmentRegs
[FAST486_REG_FS
].Selector
))
446 if (!Fast486StackPushInternal(State
,
448 State
->SegmentRegs
[FAST486_REG_DS
].Selector
))
452 if (!Fast486StackPushInternal(State
,
454 State
->SegmentRegs
[FAST486_REG_ES
].Selector
))
459 /* Now load them with NULL selectors, since they are useless in protected mode */
460 if (!Fast486LoadSegment(State
, FAST486_REG_GS
, 0)) return FALSE
;
461 if (!Fast486LoadSegment(State
, FAST486_REG_FS
, 0)) return FALSE
;
462 if (!Fast486LoadSegment(State
, FAST486_REG_DS
, 0)) return FALSE
;
463 if (!Fast486LoadSegment(State
, FAST486_REG_ES
, 0)) return FALSE
;
466 /* Check if the interrupt handler is more privileged or we're in VM86 mode (again) */
467 if ((OldCpl
> GET_SEGMENT_RPL(IdtEntry
->Selector
)) || OldVm
)
469 /* Push SS selector */
470 if (!Fast486StackPushInternal(State
, GateSize
, OldSs
)) return FALSE
;
472 /* Push the stack pointer */
473 if (!Fast486StackPushInternal(State
, GateSize
, OldEsp
)) return FALSE
;
479 if (!Fast486LoadSegment(State
, FAST486_REG_CS
, IdtEntry
->Selector
))
481 /* An exception occurred during the jump */
486 State
->InstPtr
.LowWord
= IdtEntry
->Offset
;
490 if (!Fast486StackPushInternal(State
, GateSize
, OldFlags
)) return FALSE
;
492 /* Push CS selector */
493 if (!Fast486StackPushInternal(State
, GateSize
, OldCs
)) return FALSE
;
495 /* Push the instruction pointer */
496 if (!Fast486StackPushInternal(State
, GateSize
, OldEip
)) return FALSE
;
500 /* Push the error code */
501 if (!Fast486StackPushInternal(State
, GateSize
, ErrorCode
)) return FALSE
;
504 if ((IdtEntry
->Type
== FAST486_IDT_INT_GATE
)
505 || (IdtEntry
->Type
== FAST486_IDT_INT_GATE_32
))
507 /* Disable interrupts after a jump to an interrupt gate handler */
508 State
->Flags
.If
= FALSE
;
516 Fast486PerformInterrupt(PFAST486_STATE State
,
519 FAST486_IDT_ENTRY IdtEntry
;
521 /* Get the interrupt vector */
522 if (!Fast486GetIntVector(State
, Number
, &IdtEntry
))
524 /* Exception occurred */
528 /* Perform the interrupt */
529 if (!Fast486InterruptInternal(State
, &IdtEntry
, FALSE
, 0))
531 /* Exception occurred */
540 Fast486ExceptionWithErrorCode(PFAST486_STATE State
,
541 FAST486_EXCEPTIONS ExceptionCode
,
544 FAST486_IDT_ENTRY IdtEntry
;
546 /* Increment the exception count */
547 State
->ExceptionCount
++;
549 /* Check if the exception occurred more than once */
550 if (State
->ExceptionCount
> 1)
552 /* Then this is a double fault */
553 ExceptionCode
= FAST486_EXCEPTION_DF
;
556 /* Check if this is a triple fault */
557 if (State
->ExceptionCount
== 3)
559 DPRINT("Fast486ExceptionWithErrorCode(%04X:%08X) -- Triple fault\n",
560 State
->SegmentRegs
[FAST486_REG_CS
].Selector
,
561 State
->InstPtr
.Long
);
568 /* Clear the prefix flags */
569 State
->PrefixFlags
= 0;
571 /* Restore the IP to the saved IP */
572 State
->InstPtr
= State
->SavedInstPtr
;
574 /* Get the interrupt vector */
575 if (!Fast486GetIntVector(State
, ExceptionCode
, &IdtEntry
))
578 * If this function failed, that means Fast486Exception
579 * was called again, so just return in this case.
584 /* Perform the interrupt */
585 if (!Fast486InterruptInternal(State
,
587 EXCEPTION_HAS_ERROR_CODE(ExceptionCode
)
588 && (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
),
592 * If this function failed, that means Fast486Exception
593 * was called again, so just return in this case.
598 /* Reset the exception count */
599 State
->ExceptionCount
= 0;
604 Fast486TaskSwitch(PFAST486_STATE State
, FAST486_TASK_SWITCH_TYPE Type
, USHORT Selector
)
608 FAST486_SYSTEM_DESCRIPTOR NewTssDescriptor
;
610 PFAST486_LEGACY_TSS OldLegacyTss
= (PFAST486_LEGACY_TSS
)&OldTss
;
612 PFAST486_LEGACY_TSS NewLegacyTss
= (PFAST486_LEGACY_TSS
)&NewTss
;
613 USHORT NewLdtr
, NewEs
, NewCs
, NewSs
, NewDs
;
615 if (State
->TaskReg
.Limit
< (sizeof(FAST486_TSS
) - 1)
616 && State
->TaskReg
.Limit
!= (sizeof(FAST486_LEGACY_TSS
) - 1))
618 /* Invalid task register limit */
619 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, State
->TaskReg
.Selector
);
623 /* Read the old TSS */
624 if (!Fast486ReadLinearMemory(State
,
627 State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1)
628 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
631 /* Exception occurred */
636 /* If this is a task return, use the linked previous selector */
637 if (Type
== FAST486_TASK_RETURN
)
639 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1)) Selector
= LOWORD(OldTss
.Link
);
640 else Selector
= OldLegacyTss
->Link
;
643 /* Make sure the entry exists in the GDT (not LDT!) */
644 if ((GET_SEGMENT_INDEX(Selector
) == 0)
645 || (Selector
& SEGMENT_TABLE_INDICATOR
)
646 || GET_SEGMENT_INDEX(Selector
) >= (State
->Gdtr
.Size
+ 1u))
648 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, Selector
);
652 /* Get the TSS descriptor from the GDT */
653 if (!Fast486ReadLinearMemory(State
,
654 State
->Gdtr
.Address
+ GET_SEGMENT_INDEX(Selector
),
656 sizeof(NewTssDescriptor
),
659 /* Exception occurred */
663 if (!NewTssDescriptor
.Present
)
665 /* Incoming task TSS not present */
666 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_NP
, Selector
);
670 /* Calculate the linear address of the new TSS */
671 NewTssAddress
= NewTssDescriptor
.Base
;
672 NewTssAddress
|= NewTssDescriptor
.BaseMid
<< 16;
673 NewTssAddress
|= NewTssDescriptor
.BaseHigh
<< 24;
675 /* Calculate the limit of the new TSS */
676 NewTssLimit
= NewTssDescriptor
.Limit
| (NewTssDescriptor
.LimitHigh
<< 16);
678 if (NewTssDescriptor
.Granularity
)
681 NewTssLimit
|= 0x00000FFF;
684 if (NewTssLimit
< (sizeof(FAST486_TSS
) - 1)
685 && NewTssLimit
!= (sizeof(FAST486_LEGACY_TSS
) - 1))
687 /* TSS limit invalid */
688 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, Selector
);
693 * The incoming task shouldn't be busy if we're executing it as a
694 * new task, and it should be busy if we're returning to it.
696 if ((((NewTssDescriptor
.Signature
!= FAST486_TSS_SIGNATURE
)
697 && (NewTssDescriptor
.Signature
!= FAST486_TSS_16_SIGNATURE
))
698 || (Type
== FAST486_TASK_RETURN
))
699 && (((NewTssDescriptor
.Signature
!= FAST486_BUSY_TSS_SIGNATURE
)
700 && (NewTssDescriptor
.Signature
!= FAST486_BUSY_TSS_16_SIGNATURE
))
701 || (Type
!= FAST486_TASK_RETURN
)))
703 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_GP
, Selector
);
707 /* Read the new TSS */
708 if (!Fast486ReadLinearMemory(State
,
711 NewTssLimit
>= (sizeof(FAST486_TSS
) - 1)
712 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
715 /* Exception occurred */
719 if (Type
!= FAST486_TASK_CALL
)
721 /* Clear the busy bit of the outgoing task */
722 FAST486_SYSTEM_DESCRIPTOR OldTssDescriptor
;
724 if (!Fast486ReadLinearMemory(State
,
726 + GET_SEGMENT_INDEX(State
->TaskReg
.Selector
),
728 sizeof(OldTssDescriptor
),
731 /* Exception occurred */
735 OldTssDescriptor
.Signature
= FAST486_TSS_SIGNATURE
;
737 if (!Fast486WriteLinearMemory(State
,
739 + GET_SEGMENT_INDEX(State
->TaskReg
.Selector
),
741 sizeof(OldTssDescriptor
),
744 /* Exception occurred */
751 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1)) NewTss
.Link
= State
->TaskReg
.Selector
;
752 else NewLegacyTss
->Link
= State
->TaskReg
.Selector
;
755 /* Save the current task into the TSS */
756 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
758 OldTss
.Cr3
= State
->ControlRegisters
[FAST486_REG_CR3
];
759 OldTss
.Eip
= State
->InstPtr
.Long
;
760 OldTss
.Eflags
= State
->Flags
.Long
;
761 OldTss
.Eax
= State
->GeneralRegs
[FAST486_REG_EAX
].Long
;
762 OldTss
.Ecx
= State
->GeneralRegs
[FAST486_REG_ECX
].Long
;
763 OldTss
.Edx
= State
->GeneralRegs
[FAST486_REG_EDX
].Long
;
764 OldTss
.Ebx
= State
->GeneralRegs
[FAST486_REG_EBX
].Long
;
765 OldTss
.Esp
= State
->GeneralRegs
[FAST486_REG_ESP
].Long
;
766 OldTss
.Ebp
= State
->GeneralRegs
[FAST486_REG_EBP
].Long
;
767 OldTss
.Esi
= State
->GeneralRegs
[FAST486_REG_ESI
].Long
;
768 OldTss
.Edi
= State
->GeneralRegs
[FAST486_REG_EDI
].Long
;
769 OldTss
.Es
= State
->SegmentRegs
[FAST486_REG_ES
].Selector
;
770 OldTss
.Cs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
771 OldTss
.Ss
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
772 OldTss
.Ds
= State
->SegmentRegs
[FAST486_REG_DS
].Selector
;
773 OldTss
.Fs
= State
->SegmentRegs
[FAST486_REG_FS
].Selector
;
774 OldTss
.Gs
= State
->SegmentRegs
[FAST486_REG_GS
].Selector
;
775 OldTss
.Ldtr
= State
->Ldtr
.Selector
;
779 OldLegacyTss
->Ip
= State
->InstPtr
.LowWord
;
780 OldLegacyTss
->Flags
= State
->Flags
.LowWord
;
781 OldLegacyTss
->Ax
= State
->GeneralRegs
[FAST486_REG_EAX
].LowWord
;
782 OldLegacyTss
->Cx
= State
->GeneralRegs
[FAST486_REG_ECX
].LowWord
;
783 OldLegacyTss
->Dx
= State
->GeneralRegs
[FAST486_REG_EDX
].LowWord
;
784 OldLegacyTss
->Bx
= State
->GeneralRegs
[FAST486_REG_EBX
].LowWord
;
785 OldLegacyTss
->Sp
= State
->GeneralRegs
[FAST486_REG_ESP
].LowWord
;
786 OldLegacyTss
->Bp
= State
->GeneralRegs
[FAST486_REG_EBP
].LowWord
;
787 OldLegacyTss
->Si
= State
->GeneralRegs
[FAST486_REG_ESI
].LowWord
;
788 OldLegacyTss
->Di
= State
->GeneralRegs
[FAST486_REG_EDI
].LowWord
;
789 OldLegacyTss
->Es
= State
->SegmentRegs
[FAST486_REG_ES
].Selector
;
790 OldLegacyTss
->Cs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
791 OldLegacyTss
->Ss
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
792 OldLegacyTss
->Ds
= State
->SegmentRegs
[FAST486_REG_DS
].Selector
;
793 OldLegacyTss
->Ldtr
= State
->Ldtr
.Selector
;
796 /* Write back the old TSS */
797 if (!Fast486WriteLinearMemory(State
,
800 State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1)
801 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
804 /* Exception occurred */
808 /* Mark the new task as busy */
809 NewTssDescriptor
.Signature
= FAST486_BUSY_TSS_SIGNATURE
;
811 /* Write back the new TSS descriptor */
812 if (!Fast486WriteLinearMemory(State
,
813 State
->Gdtr
.Address
+ GET_SEGMENT_INDEX(Selector
),
815 sizeof(NewTssDescriptor
),
818 /* Exception occurred */
822 /* Set the task switch bit */
823 State
->ControlRegisters
[FAST486_REG_CR0
] |= FAST486_CR0_TS
;
825 /* Load the task register with the new values */
826 State
->TaskReg
.Selector
= Selector
;
827 State
->TaskReg
.Base
= NewTssAddress
;
828 State
->TaskReg
.Limit
= NewTssLimit
;
830 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1))
832 /* Change the page directory */
833 State
->ControlRegisters
[FAST486_REG_CR3
] = NewTss
.Cr3
;
837 if (State
->Tlb
) RtlFillMemory(State
->Tlb
, NUM_TLB_ENTRIES
* sizeof(ULONG
), 0xFF);
840 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1)) State
->Cpl
= GET_SEGMENT_RPL(NewTss
.Cs
);
841 else State
->Cpl
= GET_SEGMENT_RPL(NewLegacyTss
->Cs
);
843 #ifndef FAST486_NO_PREFETCH
844 /* Context switching invalidates the prefetch */
845 State
->PrefetchValid
= FALSE
;
848 /* Load the registers */
849 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1))
851 State
->InstPtr
.Long
= State
->SavedInstPtr
.Long
= NewTss
.Eip
;
852 State
->Flags
.Long
= NewTss
.Eflags
;
853 State
->GeneralRegs
[FAST486_REG_EAX
].Long
= NewTss
.Eax
;
854 State
->GeneralRegs
[FAST486_REG_ECX
].Long
= NewTss
.Ecx
;
855 State
->GeneralRegs
[FAST486_REG_EDX
].Long
= NewTss
.Edx
;
856 State
->GeneralRegs
[FAST486_REG_EBX
].Long
= NewTss
.Ebx
;
857 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewTss
.Esp
;
858 State
->GeneralRegs
[FAST486_REG_EBP
].Long
= NewTss
.Ebp
;
859 State
->GeneralRegs
[FAST486_REG_ESI
].Long
= NewTss
.Esi
;
860 State
->GeneralRegs
[FAST486_REG_EDI
].Long
= NewTss
.Edi
;
865 NewLdtr
= NewTss
.Ldtr
;
869 State
->InstPtr
.LowWord
= State
->SavedInstPtr
.LowWord
= NewLegacyTss
->Ip
;
870 State
->Flags
.LowWord
= NewLegacyTss
->Flags
;
871 State
->GeneralRegs
[FAST486_REG_EAX
].LowWord
= NewLegacyTss
->Ax
;
872 State
->GeneralRegs
[FAST486_REG_ECX
].LowWord
= NewLegacyTss
->Cx
;
873 State
->GeneralRegs
[FAST486_REG_EDX
].LowWord
= NewLegacyTss
->Dx
;
874 State
->GeneralRegs
[FAST486_REG_EBX
].LowWord
= NewLegacyTss
->Bx
;
875 State
->GeneralRegs
[FAST486_REG_ESP
].LowWord
= NewLegacyTss
->Sp
;
876 State
->GeneralRegs
[FAST486_REG_EBP
].LowWord
= NewLegacyTss
->Bp
;
877 State
->GeneralRegs
[FAST486_REG_ESI
].LowWord
= NewLegacyTss
->Si
;
878 State
->GeneralRegs
[FAST486_REG_EDI
].LowWord
= NewLegacyTss
->Di
;
879 NewEs
= NewLegacyTss
->Es
;
880 NewCs
= NewLegacyTss
->Cs
;
881 NewSs
= NewLegacyTss
->Ss
;
882 NewDs
= NewLegacyTss
->Ds
;
883 NewLdtr
= NewLegacyTss
->Ldtr
;
886 /* Set the NT flag if nesting */
887 if (Type
== FAST486_TASK_CALL
) State
->Flags
.Nt
= TRUE
;
889 if (GET_SEGMENT_INDEX(NewLdtr
) != 0)
892 FAST486_SYSTEM_DESCRIPTOR GdtEntry
;
894 if (NewLdtr
& SEGMENT_TABLE_INDICATOR
)
896 /* This selector doesn't point to the GDT */
897 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
901 if (!Fast486ReadDescriptorEntry(State
, NewLdtr
, &Valid
, (PFAST486_GDT_ENTRY
)&GdtEntry
))
903 /* Exception occurred */
909 /* Invalid selector */
910 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
914 if (GdtEntry
.Signature
!= FAST486_LDT_SIGNATURE
)
916 /* This is not an LDT descriptor */
917 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
921 if (!GdtEntry
.Present
)
923 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
927 /* Update the LDTR */
928 State
->Ldtr
.Selector
= NewLdtr
;
929 State
->Ldtr
.Base
= GdtEntry
.Base
| (GdtEntry
.BaseMid
<< 16) | (GdtEntry
.BaseHigh
<< 24);
930 State
->Ldtr
.Limit
= GdtEntry
.Limit
| (GdtEntry
.LimitHigh
<< 16);
932 if (GdtEntry
.Granularity
)
934 State
->Ldtr
.Limit
<<= 12;
935 State
->Ldtr
.Limit
|= 0x00000FFF;
940 /* The LDT of this task is empty */
941 RtlZeroMemory(&State
->Ldtr
, sizeof(State
->Ldtr
));
944 /* Load the new segments */
945 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_CS
, NewCs
, FAST486_EXCEPTION_TS
))
950 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_SS
, NewSs
, FAST486_EXCEPTION_TS
))
955 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_ES
, NewEs
, FAST486_EXCEPTION_TS
))
960 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_DS
, NewDs
, FAST486_EXCEPTION_TS
))
965 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1))
967 if (!Fast486LoadSegmentInternal(State
,
970 FAST486_EXCEPTION_TS
))
975 if (!Fast486LoadSegmentInternal(State
,
978 FAST486_EXCEPTION_TS
))
989 Fast486CallGate(PFAST486_STATE State
,
990 PFAST486_CALL_GATE Gate
,
994 FAST486_GDT_ENTRY NewCodeSegment
;
995 BOOLEAN GateSize
= (Gate
->Type
== FAST486_CALL_GATE_SIGNATURE
);
997 PFAST486_LEGACY_TSS LegacyTss
= (PFAST486_LEGACY_TSS
)&Tss
;
998 USHORT OldCs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
999 ULONG OldEip
= State
->InstPtr
.Long
;
1000 USHORT OldCpl
= State
->Cpl
;
1001 USHORT OldSs
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
1002 ULONG OldEsp
= State
->GeneralRegs
[FAST486_REG_ESP
].Long
;
1003 ULONG ParamBuffer
[32]; /* Maximum possible size - 32 DWORDs */
1004 PULONG LongParams
= (PULONG
)ParamBuffer
;
1005 PUSHORT ShortParams
= (PUSHORT
)ParamBuffer
;
1007 if (!Gate
->Selector
)
1009 /* The code segment is NULL */
1010 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
1014 if (!Fast486ReadDescriptorEntry(State
, Gate
->Selector
, &Valid
, &NewCodeSegment
))
1016 /* Exception occurred */
1020 if (!Valid
|| (NewCodeSegment
.Dpl
> Fast486GetCurrentPrivLevel(State
)))
1022 /* Code segment invalid */
1023 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_GP
, Gate
->Selector
);
1027 if (Call
&& Gate
->ParamCount
)
1029 /* Read the parameters */
1030 if (!Fast486ReadMemory(State
,
1035 Gate
->ParamCount
* sizeof(ULONG
)))
1037 /* Exception occurred */
1042 /* Check if the new code segment is more privileged */
1043 if (NewCodeSegment
.Dpl
< OldCpl
)
1051 if (!Fast486ReadLinearMemory(State
,
1052 State
->TaskReg
.Base
,
1054 State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1)
1055 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
1058 /* Exception occurred */
1062 /* Switch to the new privilege level */
1063 State
->Cpl
= NewCodeSegment
.Dpl
;
1065 /* Check the new (higher) privilege level */
1070 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
1077 NewSs
= LegacyTss
->Ss0
;
1078 NewEsp
= LegacyTss
->Sp0
;
1086 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
1093 NewSs
= LegacyTss
->Ss1
;
1094 NewEsp
= LegacyTss
->Sp1
;
1102 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
1109 NewSs
= LegacyTss
->Ss2
;
1110 NewEsp
= LegacyTss
->Sp2
;
1118 /* Should never reach here! */
1123 if (!Fast486LoadSegment(State
, FAST486_REG_SS
, NewSs
))
1125 /* Exception occurred */
1129 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewEsp
;
1131 else if (!NewCodeSegment
.DirConf
)
1133 /* This is not allowed for jumps */
1134 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_GP
, Gate
->Selector
);
1140 if (!Fast486LoadSegment(State
, FAST486_REG_CS
, Gate
->Selector
))
1142 /* An exception occurred during the jump */
1146 /* Set the instruction pointer */
1147 if (GateSize
) State
->InstPtr
.Long
= MAKELONG(Gate
->Offset
, Gate
->OffsetHigh
);
1148 else State
->InstPtr
.Long
= Gate
->Offset
;
1154 /* Check if the new code segment is more privileged (again) */
1155 if (NewCodeSegment
.Dpl
< OldCpl
)
1157 /* Push SS selector */
1158 if (!Fast486StackPushInternal(State
, GateSize
, OldSs
)) return FALSE
;
1160 /* Push stack pointer */
1161 if (!Fast486StackPushInternal(State
, GateSize
, OldEsp
)) return FALSE
;
1164 /* Push the parameters in reverse order */
1165 for (i
= Gate
->ParamCount
- 1; i
>= 0; i
--)
1167 if (!Fast486StackPushInternal(State
,
1169 GateSize
? LongParams
[i
] : ShortParams
[i
]))
1171 /* Exception occurred */
1176 /* Push CS selector */
1177 if (!Fast486StackPushInternal(State
, GateSize
, OldCs
)) return FALSE
;
1179 /* Push the instruction pointer */
1180 if (!Fast486StackPushInternal(State
, GateSize
, OldEip
)) return FALSE
;