xref: /reactos/sdk/lib/fast486/common.inl (revision c2c66aff)
1/*
2 * Fast486 386/486 CPU Emulation Library
3 * common.inl
4 *
5 * Copyright (C) 2015 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
6 *
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.
11 *
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.
16 *
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.
20 */
21
22#include "common.h"
23#include "fpu.h"
24
25/* PUBLIC FUNCTIONS ***********************************************************/
26
27#if defined (__GNUC__)
28    #define CountLeadingZeros64(x) __builtin_clzll(x)
29
30/*
31#elif (_MSC_VER >= 1500) && defined(_WIN64)
32    #define CountLeadingZeros64(x) __lzcnt64(x)
33#elif (_MSC_VER >= 1500)
34    #define CountLeadingZeros64(x) ((x) > 0xFFFFFFFFULL) ? __lzcnt((x) >> 32) \
35                                                         : (__lzcnt(x) + 32)
36*/
37
38#else
39    FORCEINLINE
40    ULONG
41    CountLeadingZeros64(ULONGLONG Value)
42    {
43        ULONG Count = 0;
44        Value = ~Value;
45        while ((LONGLONG)Value < 0)
46        {
47            Count++;
48            Value <<= 1;
49        }
50        return Count;
51    }
52#endif
53
54FORCEINLINE
55UINT
56FASTCALL
57Fast486GetCurrentPrivLevel(PFAST486_STATE State)
58{
59    /* Return the CPL, or 3 if we're in virtual 8086 mode */
60    return (!State->Flags.Vm) ? State->Cpl : 3;
61}
62
63FORCEINLINE
64ULONG
65FASTCALL
66Fast486GetPageTableEntry(PFAST486_STATE State,
67                         ULONG VirtualAddress,
68                         BOOLEAN MarkAsDirty)
69{
70    ULONG PdeIndex = GET_ADDR_PDE(VirtualAddress);
71    ULONG PteIndex = GET_ADDR_PTE(VirtualAddress);
72    FAST486_PAGE_DIR DirectoryEntry;
73    FAST486_PAGE_TABLE TableEntry;
74    ULONG PageDirectory = State->ControlRegisters[FAST486_REG_CR3];
75
76    if ((State->Tlb != NULL)
77        && (State->Tlb[VirtualAddress >> 12] != INVALID_TLB_FIELD))
78    {
79        /* Return the cached entry */
80        return State->Tlb[VirtualAddress >> 12];
81    }
82
83    /* Read the directory entry */
84    State->MemReadCallback(State,
85                           PageDirectory + PdeIndex * sizeof(ULONG),
86                           &DirectoryEntry.Value,
87                           sizeof(DirectoryEntry));
88
89    /* Make sure it is present */
90    if (!DirectoryEntry.Present) return 0;
91
92    /* Was the directory entry accessed before? */
93    if (!DirectoryEntry.Accessed)
94    {
95        /* Well, it is now */
96        DirectoryEntry.Accessed = TRUE;
97
98        /* Write back the directory entry */
99        State->MemWriteCallback(State,
100                                PageDirectory + PdeIndex * sizeof(ULONG),
101                                &DirectoryEntry.Value,
102                                sizeof(DirectoryEntry));
103    }
104
105    /* Read the table entry */
106    State->MemReadCallback(State,
107                           (DirectoryEntry.TableAddress << 12)
108                           + PteIndex * sizeof(ULONG),
109                           &TableEntry.Value,
110                           sizeof(TableEntry));
111
112    /* Make sure it is present */
113    if (!TableEntry.Present) return 0;
114
115    /* Do we need to change any flags? */
116    if (!TableEntry.Accessed || (MarkAsDirty && !TableEntry.Dirty))
117    {
118        /* Mark it as accessed and optionally dirty too */
119        TableEntry.Accessed = TRUE;
120        if (MarkAsDirty) TableEntry.Dirty = TRUE;
121
122        /* Write back the table entry */
123        State->MemWriteCallback(State,
124                                (DirectoryEntry.TableAddress << 12)
125                                + PteIndex * sizeof(ULONG),
126                                &TableEntry.Value,
127                                sizeof(TableEntry));
128    }
129
130    /*
131     * The resulting permissions depend on the permissions
132     * in the page directory table too
133     */
134    TableEntry.Writeable &= DirectoryEntry.Writeable;
135    TableEntry.Usermode &= DirectoryEntry.Usermode;
136
137    if (State->Tlb != NULL)
138    {
139        /* Set the TLB entry */
140        State->Tlb[VirtualAddress >> 12] = TableEntry.Value;
141        State->TlbEmpty = FALSE;
142    }
143
144    /* Return the table entry */
145    return TableEntry.Value;
146}
147
148FORCEINLINE
149VOID
150FASTCALL
151Fast486FlushTlb(PFAST486_STATE State)
152{
153    if (!State->Tlb || State->TlbEmpty) return;
154    RtlFillMemory(State->Tlb, NUM_TLB_ENTRIES * sizeof(ULONG), 0xFF);
155    State->TlbEmpty = TRUE;
156}
157
158FORCEINLINE
159BOOLEAN
160FASTCALL
161Fast486ReadLinearMemory(PFAST486_STATE State,
162                        ULONG LinearAddress,
163                        PVOID Buffer,
164                        ULONG Size,
165                        BOOLEAN CheckPrivilege)
166{
167    /* Check if paging is enabled */
168    if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
169    {
170        ULONG Page;
171        FAST486_PAGE_TABLE TableEntry;
172        INT Cpl = Fast486GetCurrentPrivLevel(State);
173        ULONG BufferOffset = 0;
174
175        for (Page = PAGE_ALIGN(LinearAddress);
176             Page <= PAGE_ALIGN(LinearAddress + Size - 1);
177             Page += FAST486_PAGE_SIZE)
178        {
179            ULONG PageOffset = 0, PageLength = FAST486_PAGE_SIZE;
180
181            /* Get the table entry */
182            TableEntry.Value = Fast486GetPageTableEntry(State, Page, FALSE);
183
184            /* Check if this is the first page */
185            if (Page == PAGE_ALIGN(LinearAddress))
186            {
187                /* Start reading from the offset from the beginning of the page */
188                PageOffset = PAGE_OFFSET(LinearAddress);
189                PageLength -= PageOffset;
190            }
191
192            if (CheckPrivilege && (!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0))))
193            {
194                State->ControlRegisters[FAST486_REG_CR2] = Page + PageOffset;
195
196                /* Exception */
197                Fast486ExceptionWithErrorCode(State,
198                                              FAST486_EXCEPTION_PF,
199                                              TableEntry.Present | (State->Cpl ? 0x04 : 0));
200                return FALSE;
201            }
202
203            /* Check if this is the last page */
204            if (Page == PAGE_ALIGN(LinearAddress + Size - 1))
205            {
206                /* Read only a part of the page */
207                PageLength = PAGE_OFFSET(LinearAddress + Size - 1) - PageOffset + 1;
208            }
209
210            /* Read the memory */
211            State->MemReadCallback(State,
212                                   (TableEntry.Address << 12) | PageOffset,
213                                   (PVOID)((ULONG_PTR)Buffer + BufferOffset),
214                                   PageLength);
215
216            BufferOffset += PageLength;
217        }
218    }
219    else
220    {
221        /* Read the memory */
222        State->MemReadCallback(State, LinearAddress, Buffer, Size);
223    }
224
225    return TRUE;
226}
227
228FORCEINLINE
229BOOLEAN
230FASTCALL
231Fast486WriteLinearMemory(PFAST486_STATE State,
232                         ULONG LinearAddress,
233                         PVOID Buffer,
234                         ULONG Size,
235                         BOOLEAN CheckPrivilege)
236{
237    /* Check if paging is enabled */
238    if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
239    {
240        ULONG Page;
241        FAST486_PAGE_TABLE TableEntry;
242        INT Cpl = Fast486GetCurrentPrivLevel(State);
243        ULONG BufferOffset = 0;
244
245        for (Page = PAGE_ALIGN(LinearAddress);
246             Page <= PAGE_ALIGN(LinearAddress + Size - 1);
247             Page += FAST486_PAGE_SIZE)
248        {
249            ULONG PageOffset = 0, PageLength = FAST486_PAGE_SIZE;
250
251            /* Get the table entry */
252            TableEntry.Value = Fast486GetPageTableEntry(State, Page, TRUE);
253
254            /* Check if this is the first page */
255            if (Page == PAGE_ALIGN(LinearAddress))
256            {
257                /* Start writing from the offset from the beginning of the page */
258                PageOffset = PAGE_OFFSET(LinearAddress);
259                PageLength -= PageOffset;
260            }
261
262            if (CheckPrivilege
263                && ((!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0)))
264                || ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_WP)
265                && !TableEntry.Writeable)))
266            {
267                State->ControlRegisters[FAST486_REG_CR2] = Page + PageOffset;
268
269                /* Exception */
270                Fast486ExceptionWithErrorCode(State,
271                                              FAST486_EXCEPTION_PF,
272                                              TableEntry.Present | 0x02 | (State->Cpl ? 0x04 : 0));
273                return FALSE;
274            }
275
276            /* Check if this is the last page */
277            if (Page == PAGE_ALIGN(LinearAddress + Size - 1))
278            {
279                /* Write only a part of the page */
280                PageLength = PAGE_OFFSET(LinearAddress + Size - 1) - PageOffset + 1;
281            }
282
283            /* Write the memory */
284            State->MemWriteCallback(State,
285                                    (TableEntry.Address << 12) | PageOffset,
286                                    (PVOID)((ULONG_PTR)Buffer + BufferOffset),
287                                    PageLength);
288
289            BufferOffset += PageLength;
290        }
291    }
292    else
293    {
294        /* Write the memory */
295        State->MemWriteCallback(State, LinearAddress, Buffer, Size);
296    }
297
298    return TRUE;
299}
300
301FORCEINLINE
302VOID
303FASTCALL
304Fast486Exception(PFAST486_STATE State,
305                 FAST486_EXCEPTIONS ExceptionCode)
306{
307    /* Call the internal function */
308    Fast486ExceptionWithErrorCode(State, ExceptionCode, 0);
309}
310
311FORCEINLINE
312BOOLEAN
313FASTCALL
314Fast486StackPushInternal(PFAST486_STATE State, BOOLEAN Size, ULONG Value)
315{
316    ULONG StackPointer = State->GeneralRegs[FAST486_REG_ESP].Long;
317
318    if (Size)
319    {
320        /* Check if ESP is between 1 and 3 */
321        if (State->GeneralRegs[FAST486_REG_ESP].Long >= 1
322            && State->GeneralRegs[FAST486_REG_ESP].Long <= 3)
323        {
324            Fast486Exception(State, FAST486_EXCEPTION_SS);
325            return FALSE;
326        }
327
328        /* Store the value in SS:[ESP - 4] */
329        if (!Fast486WriteMemory(State,
330                                FAST486_REG_SS,
331                                State->SegmentRegs[FAST486_REG_SS].Size
332                                ? StackPointer - sizeof(ULONG)
333                                : LOWORD(StackPointer - sizeof(ULONG)),
334                                &Value,
335                                sizeof(ULONG)))
336        {
337            /* Exception occurred */
338            return FALSE;
339        }
340
341        if (State->SegmentRegs[FAST486_REG_SS].Size)
342        {
343            /* Subtract ESP by 4 */
344            State->GeneralRegs[FAST486_REG_ESP].Long -= sizeof(ULONG);
345        }
346        else
347        {
348            /* Subtract SP by 4 */
349            State->GeneralRegs[FAST486_REG_ESP].LowWord -= sizeof(ULONG);
350        }
351    }
352    else
353    {
354        /* Check if SP is 1 */
355        if (State->GeneralRegs[FAST486_REG_ESP].LowWord == 1)
356        {
357            Fast486Exception(State, FAST486_EXCEPTION_SS);
358            return FALSE;
359        }
360
361        /* Store the value in SS:[SP - 2] */
362        if (!Fast486WriteMemory(State,
363                                FAST486_REG_SS,
364                                State->SegmentRegs[FAST486_REG_SS].Size
365                                ? StackPointer - sizeof(USHORT)
366                                : LOWORD(StackPointer - sizeof(USHORT)),
367                                &Value,
368                                sizeof(USHORT)))
369        {
370            /* Exception occurred */
371            return FALSE;
372        }
373
374        if (State->SegmentRegs[FAST486_REG_SS].Size)
375        {
376            /* Subtract ESP by 2 */
377            State->GeneralRegs[FAST486_REG_ESP].Long -= sizeof(USHORT);
378        }
379        else
380        {
381            /* Subtract SP by 2 */
382            State->GeneralRegs[FAST486_REG_ESP].LowWord -= sizeof(USHORT);
383        }
384    }
385
386    return TRUE;
387}
388
389FORCEINLINE
390BOOLEAN
391FASTCALL
392Fast486StackPush(PFAST486_STATE State, ULONG Value)
393{
394    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
395
396    /* The OPSIZE prefix toggles the size */
397    TOGGLE_OPSIZE(Size);
398
399    /* Call the internal function */
400    return Fast486StackPushInternal(State, Size, Value);
401}
402
403FORCEINLINE
404BOOLEAN
405FASTCALL
406Fast486StackPop(PFAST486_STATE State,
407                PULONG Value)
408{
409    BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
410
411    /* The OPSIZE prefix toggles the size */
412    TOGGLE_OPSIZE(Size);
413
414    if (Size)
415    {
416        /* 32-bit size */
417        ULONG LongValue;
418
419        /* Check if ESP is 0xFFFFFFFF */
420        if (State->GeneralRegs[FAST486_REG_ESP].Long == 0xFFFFFFFF)
421        {
422            Fast486Exception(State, FAST486_EXCEPTION_SS);
423            return FALSE;
424        }
425
426        /* Read the value from SS:ESP */
427        if (!Fast486ReadMemory(State,
428                               FAST486_REG_SS,
429                               State->SegmentRegs[FAST486_REG_SS].Size
430                               ? State->GeneralRegs[FAST486_REG_ESP].Long
431                               : State->GeneralRegs[FAST486_REG_ESP].LowWord,
432                               FALSE,
433                               &LongValue,
434                               sizeof(LongValue)))
435        {
436            /* An exception occurred */
437            return FALSE;
438        }
439
440        if (State->SegmentRegs[FAST486_REG_SS].Size)
441        {
442            /* Increment ESP by 4 */
443            State->GeneralRegs[FAST486_REG_ESP].Long += sizeof(ULONG);
444        }
445        else
446        {
447            /* Increment SP by 4 */
448            State->GeneralRegs[FAST486_REG_ESP].LowWord += sizeof(ULONG);
449        }
450
451        /* Store the value in the result */
452        *Value = LongValue;
453    }
454    else
455    {
456        /* 16-bit size */
457        USHORT ShortValue;
458
459        /* Check if SP is 0xFFFF */
460        if (State->GeneralRegs[FAST486_REG_ESP].LowWord == 0xFFFF)
461        {
462            Fast486Exception(State, FAST486_EXCEPTION_SS);
463            return FALSE;
464        }
465
466        /* Read the value from SS:SP */
467        if (!Fast486ReadMemory(State,
468                               FAST486_REG_SS,
469                               State->SegmentRegs[FAST486_REG_SS].Size
470                               ? State->GeneralRegs[FAST486_REG_ESP].Long
471                               : State->GeneralRegs[FAST486_REG_ESP].LowWord,
472                               FALSE,
473                               &ShortValue,
474                               sizeof(ShortValue)))
475        {
476            /* An exception occurred */
477            return FALSE;
478        }
479
480        if (State->SegmentRegs[FAST486_REG_SS].Size)
481        {
482            /* Increment ESP by 2 */
483            State->GeneralRegs[FAST486_REG_ESP].Long += sizeof(USHORT);
484        }
485        else
486        {
487            /* Increment SP by 2 */
488            State->GeneralRegs[FAST486_REG_ESP].LowWord += sizeof(USHORT);
489        }
490
491        /* Store the value in the result */
492        *Value = ShortValue;
493    }
494
495    return TRUE;
496}
497
498FORCEINLINE
499BOOLEAN
500FASTCALL
501Fast486ReadDescriptorEntry(PFAST486_STATE State,
502                           USHORT Selector,
503                           PBOOLEAN EntryValid,
504                           PFAST486_GDT_ENTRY Entry)
505{
506    if (!(Selector & SEGMENT_TABLE_INDICATOR))
507    {
508        /* Make sure the GDT contains the entry */
509        if (GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1u))
510        {
511            *EntryValid = FALSE;
512            return TRUE;
513        }
514
515        /* Read the GDT */
516        if (!Fast486ReadLinearMemory(State,
517                                     State->Gdtr.Address
518                                     + GET_SEGMENT_INDEX(Selector),
519                                     Entry,
520                                     sizeof(*Entry),
521                                     FALSE))
522        {
523            /* Exception occurred */
524            *EntryValid = FALSE;
525            return FALSE;
526        }
527    }
528    else
529    {
530        /* Make sure the LDT contains the entry */
531        if (GET_SEGMENT_INDEX(Selector) >= (State->Ldtr.Limit + 1u))
532        {
533            *EntryValid = FALSE;
534            return TRUE;
535        }
536
537        /* Read the LDT */
538        if (!Fast486ReadLinearMemory(State,
539                                     State->Ldtr.Base
540                                     + GET_SEGMENT_INDEX(Selector),
541                                     Entry,
542                                     sizeof(*Entry),
543                                     FALSE))
544        {
545            /* Exception occurred */
546            *EntryValid = FALSE;
547            return FALSE;
548        }
549    }
550
551    *EntryValid = TRUE;
552    return TRUE;
553}
554
555FORCEINLINE
556BOOLEAN
557FASTCALL
558Fast486LoadSegmentInternal(PFAST486_STATE State,
559                           FAST486_SEG_REGS Segment,
560                           USHORT Selector,
561                           FAST486_EXCEPTIONS Exception)
562{
563    PFAST486_SEG_REG CachedDescriptor;
564    BOOLEAN Valid;
565    FAST486_GDT_ENTRY GdtEntry;
566
567    ASSERT(Segment < FAST486_NUM_SEG_REGS);
568
569    /* Get the cached descriptor */
570    CachedDescriptor = &State->SegmentRegs[Segment];
571
572    /* Check for protected mode */
573    if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
574    {
575        /* Check for VM86 mode */
576        if (State->Flags.Vm)
577        {
578            /* Update the cached descriptor with VM86 values */
579            CachedDescriptor->Selector = Selector;
580            CachedDescriptor->Base = Selector << 4;
581            CachedDescriptor->Limit = 0xFFFF;
582            CachedDescriptor->ReadWrite = TRUE;
583            CachedDescriptor->DirConf = FALSE;
584            CachedDescriptor->SystemType = TRUE;
585            CachedDescriptor->Dpl = CachedDescriptor->Rpl = 3;
586            CachedDescriptor->Present = TRUE;
587            CachedDescriptor->Size = FALSE;
588            return TRUE;
589        }
590
591        if (!Fast486ReadDescriptorEntry(State, Selector, &Valid, &GdtEntry))
592        {
593            /* Exception occurred */
594            return FALSE;
595        }
596
597        if (!Valid)
598        {
599            /* Invalid selector */
600            Fast486ExceptionWithErrorCode(State, Exception, Selector);
601            return FALSE;
602        }
603
604        if (Segment == FAST486_REG_SS)
605        {
606            /* Loading the stack segment */
607
608            if (!(Selector & SEGMENT_TABLE_INDICATOR) && GET_SEGMENT_INDEX(Selector) == 0)
609            {
610                Fast486Exception(State, Exception);
611                return FALSE;
612            }
613
614            if (!GdtEntry.SystemType)
615            {
616                /* This is a special descriptor */
617                Fast486ExceptionWithErrorCode(State, Exception, Selector);
618                return FALSE;
619            }
620
621            if (GdtEntry.Executable || !GdtEntry.ReadWrite)
622            {
623                Fast486ExceptionWithErrorCode(State, Exception, Selector);
624                return FALSE;
625            }
626
627            if ((GET_SEGMENT_RPL(Selector) != Fast486GetCurrentPrivLevel(State))
628                || (GET_SEGMENT_RPL(Selector) != GdtEntry.Dpl))
629            {
630                Fast486ExceptionWithErrorCode(State, Exception, Selector);
631                return FALSE;
632            }
633
634            if (!GdtEntry.Present)
635            {
636                Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_SS, Selector);
637                return FALSE;
638            }
639        }
640        else if (Segment == FAST486_REG_CS)
641        {
642            /* Loading the code segment */
643
644#ifndef FAST486_NO_PREFETCH
645            /* Invalidate the prefetch */
646            State->PrefetchValid = FALSE;
647#endif
648
649            if (!(Selector & SEGMENT_TABLE_INDICATOR) && GET_SEGMENT_INDEX(Selector) == 0)
650            {
651                Fast486Exception(State, Exception);
652                return FALSE;
653            }
654
655            if (!GdtEntry.SystemType)
656            {
657                /* Must be a segment descriptor */
658                Fast486ExceptionWithErrorCode(State, Exception, Selector);
659                return FALSE;
660            }
661
662            if (!GdtEntry.Present)
663            {
664                Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector);
665                return FALSE;
666            }
667
668            if (!GdtEntry.Executable)
669            {
670                Fast486ExceptionWithErrorCode(State, Exception, Selector);
671                return FALSE;
672            }
673
674            if (GdtEntry.DirConf)
675            {
676                /* Conforming Code Segment */
677
678                if (GdtEntry.Dpl > Fast486GetCurrentPrivLevel(State))
679                {
680                    /* Must be accessed from lower-privileged code */
681                    Fast486ExceptionWithErrorCode(State, Exception, Selector);
682                    return FALSE;
683                }
684            }
685            else
686            {
687                /* Regular code segment */
688
689                if ((GET_SEGMENT_RPL(Selector) < Fast486GetCurrentPrivLevel(State)))
690                {
691                    Fast486ExceptionWithErrorCode(State, Exception, Selector);
692                    return FALSE;
693                }
694            }
695        }
696        else
697        {
698            /* Loading a data segment */
699
700            if (GET_SEGMENT_INDEX(Selector) != 0 || (Selector & SEGMENT_TABLE_INDICATOR))
701            {
702                if (!GdtEntry.SystemType)
703                {
704                    /* This is a special descriptor */
705                    Fast486ExceptionWithErrorCode(State, Exception, Selector);
706                    return FALSE;
707                }
708
709                if ((GET_SEGMENT_RPL(Selector) > GdtEntry.Dpl)
710                    || (Fast486GetCurrentPrivLevel(State) > GdtEntry.Dpl))
711                {
712                    Fast486ExceptionWithErrorCode(State, Exception, Selector);
713                    return FALSE;
714                }
715
716                if (!GdtEntry.Present)
717                {
718                    Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector);
719                    return FALSE;
720                }
721            }
722            else
723            {
724                /* This is a NULL selector */
725                RtlZeroMemory(&GdtEntry, sizeof(GdtEntry));
726            }
727        }
728
729        /* Update the cache entry */
730        CachedDescriptor->Selector = Selector;
731        CachedDescriptor->Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24);
732        CachedDescriptor->Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
733        CachedDescriptor->Accessed = GdtEntry.Accessed;
734        CachedDescriptor->ReadWrite = GdtEntry.ReadWrite;
735        CachedDescriptor->DirConf = GdtEntry.DirConf;
736        CachedDescriptor->Executable = GdtEntry.Executable;
737        CachedDescriptor->SystemType = GdtEntry.SystemType;
738        CachedDescriptor->Rpl = GET_SEGMENT_RPL(Selector);
739        CachedDescriptor->Dpl = GdtEntry.Dpl;
740        CachedDescriptor->Present = GdtEntry.Present;
741        CachedDescriptor->Size = GdtEntry.Size;
742
743        /* Check for page granularity */
744        if (GdtEntry.Granularity)
745        {
746            CachedDescriptor->Limit <<= 12;
747            CachedDescriptor->Limit |= 0x00000FFF;
748        }
749    }
750    else
751    {
752        /* Update the selector and base */
753        CachedDescriptor->Selector = Selector;
754        CachedDescriptor->Base = Selector << 4;
755    }
756
757    return TRUE;
758}
759
760FORCEINLINE
761BOOLEAN
762FASTCALL
763Fast486LoadSegment(PFAST486_STATE State,
764                   FAST486_SEG_REGS Segment,
765                   USHORT Selector)
766{
767    return Fast486LoadSegmentInternal(State,
768                                      Segment,
769                                      Selector,
770                                      FAST486_EXCEPTION_GP);
771}
772
773FORCEINLINE
774BOOLEAN
775FASTCALL
776Fast486ProcessGate(PFAST486_STATE State, USHORT Selector, ULONG Offset, BOOLEAN Call)
777{
778    BOOLEAN Valid;
779    FAST486_SYSTEM_DESCRIPTOR Descriptor;
780
781    if (!Fast486ReadDescriptorEntry(State,
782                                    Selector,
783                                    &Valid,
784                                    (PFAST486_GDT_ENTRY)&Descriptor))
785    {
786        /* Exception occurred */
787        return FALSE;
788    }
789
790    if (!Valid)
791    {
792        /* Invalid selector */
793        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
794        return FALSE;
795    }
796
797    switch (Descriptor.Signature)
798    {
799        case FAST486_TASK_GATE_SIGNATURE:
800        {
801            Fast486TaskSwitch(State,
802                              Call ? FAST486_TASK_CALL : FAST486_TASK_JUMP,
803                              ((PFAST486_IDT_ENTRY)&Descriptor)->Selector);
804
805            return FALSE;
806        }
807
808        case FAST486_TSS_16_SIGNATURE:
809        case FAST486_BUSY_TSS_16_SIGNATURE:
810        case FAST486_TSS_SIGNATURE:
811        case FAST486_BUSY_TSS_SIGNATURE:
812        {
813            Fast486TaskSwitch(State,
814                              Call ? FAST486_TASK_CALL : FAST486_TASK_JUMP,
815                              Selector);
816
817            return FALSE;
818        }
819
820        case FAST486_CALL_GATE_16_SIGNATURE:
821        case FAST486_CALL_GATE_SIGNATURE:
822        {
823            if ((Descriptor.Dpl < Fast486GetCurrentPrivLevel(State))
824                && (Descriptor.Dpl < GET_SEGMENT_RPL(Selector)))
825            {
826                Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
827                return FALSE;
828            }
829
830            if (!Descriptor.Present)
831            {
832                Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector);
833                return FALSE;
834            }
835
836            Fast486CallGate(State, (PFAST486_CALL_GATE)&Descriptor, Call);
837
838            /* The gate has been processed here, so return FALSE */
839            return FALSE;
840        }
841
842        default:
843        {
844            /* Security check for jumps and calls only */
845            if (State->Cpl != Descriptor.Dpl)
846            {
847                Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
848                return FALSE;
849            }
850
851            return TRUE;
852        }
853    }
854}
855
856FORCEINLINE
857BOOLEAN
858FASTCALL
859Fast486FetchByte(PFAST486_STATE State,
860                 PUCHAR Data)
861{
862    PFAST486_SEG_REG CachedDescriptor;
863    ULONG Offset;
864#ifndef FAST486_NO_PREFETCH
865    ULONG LinearAddress;
866#endif
867
868    /* Get the cached descriptor of CS */
869    CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
870
871    Offset = (CachedDescriptor->Size) ? State->InstPtr.Long
872                                      : State->InstPtr.LowWord;
873#ifndef FAST486_NO_PREFETCH
874    LinearAddress = CachedDescriptor->Base + Offset;
875
876    if (State->PrefetchValid
877        && (LinearAddress >= State->PrefetchAddress)
878        && ((LinearAddress + sizeof(UCHAR)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
879    {
880        *Data = *(PUCHAR)&State->PrefetchCache[LinearAddress - State->PrefetchAddress];
881    }
882    else
883#endif
884    {
885        /* Read from memory */
886        if (!Fast486ReadMemory(State,
887                               FAST486_REG_CS,
888                               Offset,
889                               TRUE,
890                               Data,
891                               sizeof(UCHAR)))
892        {
893            /* Exception occurred during instruction fetch */
894            return FALSE;
895        }
896    }
897
898    /* Advance the instruction pointer */
899    if (CachedDescriptor->Size) State->InstPtr.Long++;
900    else State->InstPtr.LowWord++;
901
902    return TRUE;
903}
904
905FORCEINLINE
906BOOLEAN
907FASTCALL
908Fast486FetchWord(PFAST486_STATE State,
909                 PUSHORT Data)
910{
911    PFAST486_SEG_REG CachedDescriptor;
912    ULONG Offset;
913#ifndef FAST486_NO_PREFETCH
914    ULONG LinearAddress;
915#endif
916
917    /* Get the cached descriptor of CS */
918    CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
919
920    Offset = (CachedDescriptor->Size) ? State->InstPtr.Long
921                                      : State->InstPtr.LowWord;
922
923#ifndef FAST486_NO_PREFETCH
924    LinearAddress = CachedDescriptor->Base + Offset;
925
926    if (State->PrefetchValid
927        && (LinearAddress >= State->PrefetchAddress)
928        && ((LinearAddress + sizeof(USHORT)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
929    {
930        *Data = *(PUSHORT)&State->PrefetchCache[LinearAddress - State->PrefetchAddress];
931    }
932    else
933#endif
934    {
935        /* Read from memory */
936        // FIXME: Fix byte order on big-endian machines
937        if (!Fast486ReadMemory(State,
938                               FAST486_REG_CS,
939                               Offset,
940                               TRUE,
941                               Data,
942                               sizeof(USHORT)))
943        {
944            /* Exception occurred during instruction fetch */
945            return FALSE;
946        }
947    }
948
949    /* Advance the instruction pointer */
950    if (CachedDescriptor->Size) State->InstPtr.Long += sizeof(USHORT);
951    else State->InstPtr.LowWord += sizeof(USHORT);
952
953    return TRUE;
954}
955
956FORCEINLINE
957BOOLEAN
958FASTCALL
959Fast486FetchDword(PFAST486_STATE State,
960                  PULONG Data)
961{
962    PFAST486_SEG_REG CachedDescriptor;
963    ULONG Offset;
964#ifndef FAST486_NO_PREFETCH
965    ULONG LinearAddress;
966#endif
967
968    /* Get the cached descriptor of CS */
969    CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
970
971    Offset = (CachedDescriptor->Size) ? State->InstPtr.Long
972                                      : State->InstPtr.LowWord;
973
974#ifndef FAST486_NO_PREFETCH
975    LinearAddress = CachedDescriptor->Base + Offset;
976
977    if (State->PrefetchValid
978        && (LinearAddress >= State->PrefetchAddress)
979        && ((LinearAddress + sizeof(ULONG)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
980    {
981        *Data = *(PULONG)&State->PrefetchCache[LinearAddress - State->PrefetchAddress];
982    }
983    else
984#endif
985    {
986        /* Read from memory */
987        // FIXME: Fix byte order on big-endian machines
988        if (!Fast486ReadMemory(State,
989                               FAST486_REG_CS,
990                               Offset,
991                               TRUE,
992                               Data,
993                               sizeof(ULONG)))
994        {
995            /* Exception occurred during instruction fetch */
996            return FALSE;
997        }
998    }
999
1000    /* Advance the instruction pointer */
1001    if (CachedDescriptor->Size) State->InstPtr.Long += sizeof(ULONG);
1002    else State->InstPtr.LowWord += sizeof(ULONG);
1003
1004    return TRUE;
1005}
1006
1007FORCEINLINE
1008BOOLEAN
1009FASTCALL
1010Fast486CalculateParity(UCHAR Number)
1011{
1012    // See http://graphics.stanford.edu/~seander/bithacks.html#ParityLookupTable too...
1013    return (0x9669 >> ((Number & 0x0F) ^ (Number >> 4))) & 1;
1014}
1015
1016FORCEINLINE
1017BOOLEAN
1018FASTCALL
1019Fast486ParseModRegRm(PFAST486_STATE State,
1020                     BOOLEAN AddressSize,
1021                     PFAST486_MOD_REG_RM ModRegRm)
1022{
1023    UCHAR ModRmByte, Mode, RegMem;
1024
1025    /* Fetch the MOD REG R/M byte */
1026    if (!Fast486FetchByte(State, &ModRmByte))
1027    {
1028        /* Exception occurred */
1029        return FALSE;
1030    }
1031
1032    /* Unpack the mode and R/M */
1033    Mode = ModRmByte >> 6;
1034    RegMem = ModRmByte & 0x07;
1035
1036    /* Set the register operand */
1037    ModRegRm->Register = (ModRmByte >> 3) & 0x07;
1038
1039    /* Check the mode */
1040    if (Mode == 3)
1041    {
1042        /* The second operand is also a register */
1043        ModRegRm->Memory = FALSE;
1044        ModRegRm->SecondRegister = RegMem;
1045
1046        /* Done parsing */
1047        return TRUE;
1048    }
1049
1050    /* The second operand is memory */
1051    ModRegRm->Memory = TRUE;
1052
1053    if (AddressSize)
1054    {
1055        if (RegMem == FAST486_REG_ESP)
1056        {
1057            UCHAR SibByte;
1058            ULONG Scale, Index, Base;
1059
1060            /* Fetch the SIB byte */
1061            if (!Fast486FetchByte(State, &SibByte))
1062            {
1063                /* Exception occurred */
1064                return FALSE;
1065            }
1066
1067            /* Unpack the scale, index and base */
1068            Scale = 1 << (SibByte >> 6);
1069            Index = (SibByte >> 3) & 0x07;
1070            if (Index != FAST486_REG_ESP) Index = State->GeneralRegs[Index].Long;
1071            else Index = 0;
1072
1073            if (((SibByte & 0x07) != FAST486_REG_EBP) || (Mode != 0))
1074            {
1075                /* Use the register a base */
1076                Base = State->GeneralRegs[SibByte & 0x07].Long;
1077            }
1078            else
1079            {
1080                /* Fetch the base */
1081                if (!Fast486FetchDword(State, &Base))
1082                {
1083                    /* Exception occurred */
1084                    return FALSE;
1085                }
1086            }
1087
1088            if (((SibByte & 0x07) == FAST486_REG_ESP)
1089                || ((SibByte & 0x07) == FAST486_REG_EBP && Mode != 0))
1090            {
1091                /* Check if there is no segment override */
1092                if (!(State->PrefixFlags & FAST486_PREFIX_SEG))
1093                {
1094                    /* Add a SS: prefix */
1095                    State->PrefixFlags |= FAST486_PREFIX_SEG;
1096                    State->SegmentOverride = FAST486_REG_SS;
1097                }
1098            }
1099
1100            /* Calculate the address */
1101            ModRegRm->MemoryAddress = Base + Index * Scale;
1102        }
1103        else if (RegMem == FAST486_REG_EBP)
1104        {
1105            if (Mode) ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].Long;
1106            else ModRegRm->MemoryAddress = 0;
1107        }
1108        else
1109        {
1110            /* Get the base from the register */
1111            ModRegRm->MemoryAddress = State->GeneralRegs[RegMem].Long;
1112        }
1113
1114        /* Check if there is no segment override */
1115        if (!(State->PrefixFlags & FAST486_PREFIX_SEG))
1116        {
1117            /* Check if the default segment should be SS */
1118            if ((RegMem == FAST486_REG_EBP) && Mode)
1119            {
1120                /* Add a SS: prefix */
1121                State->PrefixFlags |= FAST486_PREFIX_SEG;
1122                State->SegmentOverride = FAST486_REG_SS;
1123            }
1124        }
1125
1126        if (Mode == 1)
1127        {
1128            CHAR Offset;
1129
1130            /* Fetch the byte */
1131            if (!Fast486FetchByte(State, (PUCHAR)&Offset))
1132            {
1133                /* Exception occurred */
1134                return FALSE;
1135            }
1136
1137            /* Add the signed offset to the address */
1138            ModRegRm->MemoryAddress += (LONG)Offset;
1139        }
1140        else if ((Mode == 2) || ((Mode == 0) && (RegMem == FAST486_REG_EBP)))
1141        {
1142            LONG Offset;
1143
1144            /* Fetch the dword */
1145            if (!Fast486FetchDword(State, (PULONG)&Offset))
1146            {
1147                /* Exception occurred */
1148                return FALSE;
1149            }
1150
1151            /* Add the signed offset to the address */
1152            ModRegRm->MemoryAddress += Offset;
1153        }
1154    }
1155    else
1156    {
1157        /* Check the operand */
1158        switch (RegMem)
1159        {
1160            case 0:
1161            {
1162                /* [BX + SI] */
1163                ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord
1164                                           + State->GeneralRegs[FAST486_REG_ESI].LowWord;
1165                break;
1166            }
1167
1168            case 1:
1169            {
1170                /* [BX + DI] */
1171                ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord
1172                                           + State->GeneralRegs[FAST486_REG_EDI].LowWord;
1173                break;
1174            }
1175
1176            case 2:
1177            {
1178                /* SS:[BP + SI] */
1179                ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord
1180                                           + State->GeneralRegs[FAST486_REG_ESI].LowWord;
1181                break;
1182            }
1183
1184            case 3:
1185            {
1186                /* SS:[BP + DI] */
1187                ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord
1188                                           + State->GeneralRegs[FAST486_REG_EDI].LowWord;
1189                break;
1190            }
1191
1192            case 4:
1193            {
1194                /* [SI] */
1195                ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_ESI].LowWord;
1196                break;
1197            }
1198
1199            case 5:
1200            {
1201                /* [DI] */
1202                ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EDI].LowWord;
1203                break;
1204            }
1205
1206            case 6:
1207            {
1208                if (Mode)
1209                {
1210                    /* [BP] */
1211                    ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord;
1212                }
1213                else
1214                {
1215                    /* [constant] (added later) */
1216                    ModRegRm->MemoryAddress = 0;
1217                }
1218
1219                break;
1220            }
1221
1222            case 7:
1223            {
1224                /* [BX] */
1225                ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord;
1226                break;
1227            }
1228        }
1229
1230        /* Check if there is no segment override */
1231        if (!(State->PrefixFlags & FAST486_PREFIX_SEG))
1232        {
1233            /* Check if the default segment should be SS */
1234            if ((RegMem == 2) || (RegMem == 3) || ((RegMem == 6) && Mode))
1235            {
1236                /* Add a SS: prefix */
1237                State->PrefixFlags |= FAST486_PREFIX_SEG;
1238                State->SegmentOverride = FAST486_REG_SS;
1239            }
1240        }
1241
1242        if (Mode == 1)
1243        {
1244            CHAR Offset;
1245
1246            /* Fetch the byte */
1247            if (!Fast486FetchByte(State, (PUCHAR)&Offset))
1248            {
1249                /* Exception occurred */
1250                return FALSE;
1251            }
1252
1253            /* Add the signed offset to the address */
1254            ModRegRm->MemoryAddress += (LONG)Offset;
1255        }
1256        else if ((Mode == 2) || ((Mode == 0) && (RegMem == 6)))
1257        {
1258            SHORT Offset;
1259
1260            /* Fetch the word */
1261            if (!Fast486FetchWord(State, (PUSHORT)&Offset))
1262            {
1263                /* Exception occurred */
1264                return FALSE;
1265            }
1266
1267            /* Add the signed offset to the address */
1268            ModRegRm->MemoryAddress += (LONG)Offset;
1269        }
1270
1271        /* Clear the top 16 bits */
1272        ModRegRm->MemoryAddress &= 0x0000FFFF;
1273    }
1274
1275    return TRUE;
1276}
1277
1278FORCEINLINE
1279BOOLEAN
1280FASTCALL
1281Fast486ReadModrmByteOperands(PFAST486_STATE State,
1282                             PFAST486_MOD_REG_RM ModRegRm,
1283                             PUCHAR RegValue,
1284                             PUCHAR RmValue)
1285{
1286    FAST486_SEG_REGS Segment = FAST486_REG_DS;
1287
1288    if (RegValue)
1289    {
1290        /* Get the register value */
1291        if (ModRegRm->Register & 0x04)
1292        {
1293            /* AH, CH, DH, BH */
1294            *RegValue = State->GeneralRegs[ModRegRm->Register & 0x03].HighByte;
1295        }
1296        else
1297        {
1298            /* AL, CL, DL, BL */
1299            *RegValue = State->GeneralRegs[ModRegRm->Register & 0x03].LowByte;
1300        }
1301    }
1302
1303    if (RmValue)
1304    {
1305        if (!ModRegRm->Memory)
1306        {
1307            /* Get the second register value */
1308            if (ModRegRm->SecondRegister & 0x04)
1309            {
1310                /* AH, CH, DH, BH */
1311                *RmValue = State->GeneralRegs[ModRegRm->SecondRegister & 0x03].HighByte;
1312            }
1313            else
1314            {
1315                /* AL, CL, DL, BL */
1316                *RmValue = State->GeneralRegs[ModRegRm->SecondRegister & 0x03].LowByte;
1317            }
1318        }
1319        else
1320        {
1321            /* Check for the segment override */
1322            if (State->PrefixFlags & FAST486_PREFIX_SEG)
1323            {
1324                /* Use the override segment instead */
1325                Segment = State->SegmentOverride;
1326            }
1327
1328            /* Read memory */
1329            if (!Fast486ReadMemory(State,
1330                                   Segment,
1331                                   ModRegRm->MemoryAddress,
1332                                   FALSE,
1333                                   RmValue,
1334                                   sizeof(UCHAR)))
1335            {
1336                /* Exception occurred */
1337                return FALSE;
1338            }
1339        }
1340    }
1341
1342    return TRUE;
1343}
1344
1345FORCEINLINE
1346BOOLEAN
1347FASTCALL
1348Fast486ReadModrmWordOperands(PFAST486_STATE State,
1349                             PFAST486_MOD_REG_RM ModRegRm,
1350                             PUSHORT RegValue,
1351                             PUSHORT RmValue)
1352{
1353    FAST486_SEG_REGS Segment = FAST486_REG_DS;
1354
1355    if (RegValue)
1356    {
1357        /* Get the register value */
1358        *RegValue = State->GeneralRegs[ModRegRm->Register].LowWord;
1359    }
1360
1361    if (RmValue)
1362    {
1363        if (!ModRegRm->Memory)
1364        {
1365            /* Get the second register value */
1366            *RmValue = State->GeneralRegs[ModRegRm->SecondRegister].LowWord;
1367        }
1368        else
1369        {
1370            /* Check for the segment override */
1371            if (State->PrefixFlags & FAST486_PREFIX_SEG)
1372            {
1373                /* Use the override segment instead */
1374                Segment = State->SegmentOverride;
1375            }
1376
1377            /* Read memory */
1378            if (!Fast486ReadMemory(State,
1379                                   Segment,
1380                                   ModRegRm->MemoryAddress,
1381                                   FALSE,
1382                                   RmValue,
1383                                   sizeof(USHORT)))
1384            {
1385                /* Exception occurred */
1386                return FALSE;
1387            }
1388        }
1389    }
1390
1391    return TRUE;
1392}
1393
1394FORCEINLINE
1395BOOLEAN
1396FASTCALL
1397Fast486ReadModrmDwordOperands(PFAST486_STATE State,
1398                              PFAST486_MOD_REG_RM ModRegRm,
1399                              PULONG RegValue,
1400                              PULONG RmValue)
1401{
1402    FAST486_SEG_REGS Segment = FAST486_REG_DS;
1403
1404    if (RegValue)
1405    {
1406        /* Get the register value */
1407        *RegValue = State->GeneralRegs[ModRegRm->Register].Long;
1408    }
1409
1410    if (RmValue)
1411    {
1412        if (!ModRegRm->Memory)
1413        {
1414            /* Get the second register value */
1415            *RmValue = State->GeneralRegs[ModRegRm->SecondRegister].Long;
1416        }
1417        else
1418        {
1419            /* Check for the segment override */
1420            if (State->PrefixFlags & FAST486_PREFIX_SEG)
1421            {
1422                /* Use the override segment instead */
1423                Segment = State->SegmentOverride;
1424            }
1425
1426            /* Read memory */
1427            if (!Fast486ReadMemory(State,
1428                                   Segment,
1429                                   ModRegRm->MemoryAddress,
1430                                   FALSE,
1431                                   RmValue,
1432                                   sizeof(ULONG)))
1433            {
1434                /* Exception occurred */
1435                return FALSE;
1436            }
1437        }
1438    }
1439
1440    return TRUE;
1441}
1442
1443FORCEINLINE
1444BOOLEAN
1445FASTCALL
1446Fast486WriteModrmByteOperands(PFAST486_STATE State,
1447                              PFAST486_MOD_REG_RM ModRegRm,
1448                              BOOLEAN WriteRegister,
1449                              UCHAR Value)
1450{
1451    FAST486_SEG_REGS Segment = FAST486_REG_DS;
1452
1453    if (WriteRegister)
1454    {
1455        /* Store the value in the register */
1456        if (ModRegRm->Register & 0x04)
1457        {
1458            /* AH, CH, DH, BH */
1459            State->GeneralRegs[ModRegRm->Register & 0x03].HighByte = Value;
1460        }
1461        else
1462        {
1463            /* AL, CL, DL, BL */
1464            State->GeneralRegs[ModRegRm->Register & 0x03].LowByte = Value;
1465        }
1466    }
1467    else
1468    {
1469        if (!ModRegRm->Memory)
1470        {
1471            /* Store the value in the second register */
1472            if (ModRegRm->SecondRegister & 0x04)
1473            {
1474                /* AH, CH, DH, BH */
1475                State->GeneralRegs[ModRegRm->SecondRegister & 0x03].HighByte = Value;
1476            }
1477            else
1478            {
1479                /* AL, CL, DL, BL */
1480                State->GeneralRegs[ModRegRm->SecondRegister & 0x03].LowByte = Value;
1481            }
1482        }
1483        else
1484        {
1485            /* Check for the segment override */
1486            if (State->PrefixFlags & FAST486_PREFIX_SEG)
1487            {
1488                /* Use the override segment instead */
1489                Segment = State->SegmentOverride;
1490            }
1491
1492            /* Write memory */
1493            if (!Fast486WriteMemory(State,
1494                                    Segment,
1495                                    ModRegRm->MemoryAddress,
1496                                    &Value,
1497                                    sizeof(UCHAR)))
1498            {
1499                /* Exception occurred */
1500                return FALSE;
1501            }
1502        }
1503    }
1504
1505    return TRUE;
1506}
1507
1508FORCEINLINE
1509BOOLEAN
1510FASTCALL
1511Fast486WriteModrmWordOperands(PFAST486_STATE State,
1512                              PFAST486_MOD_REG_RM ModRegRm,
1513                              BOOLEAN WriteRegister,
1514                              USHORT Value)
1515{
1516    FAST486_SEG_REGS Segment = FAST486_REG_DS;
1517
1518    if (WriteRegister)
1519    {
1520        /* Store the value in the register */
1521        State->GeneralRegs[ModRegRm->Register].LowWord = Value;
1522    }
1523    else
1524    {
1525        if (!ModRegRm->Memory)
1526        {
1527            /* Store the value in the second register */
1528            State->GeneralRegs[ModRegRm->SecondRegister].LowWord = Value;
1529        }
1530        else
1531        {
1532            /* Check for the segment override */
1533            if (State->PrefixFlags & FAST486_PREFIX_SEG)
1534            {
1535                /* Use the override segment instead */
1536                Segment = State->SegmentOverride;
1537            }
1538
1539            /* Write memory */
1540            if (!Fast486WriteMemory(State,
1541                                    Segment,
1542                                    ModRegRm->MemoryAddress,
1543                                    &Value,
1544                                    sizeof(USHORT)))
1545            {
1546                /* Exception occurred */
1547                return FALSE;
1548            }
1549        }
1550    }
1551
1552    return TRUE;
1553}
1554
1555FORCEINLINE
1556BOOLEAN
1557FASTCALL
1558Fast486WriteModrmDwordOperands(PFAST486_STATE State,
1559                               PFAST486_MOD_REG_RM ModRegRm,
1560                               BOOLEAN WriteRegister,
1561                               ULONG Value)
1562{
1563    FAST486_SEG_REGS Segment = FAST486_REG_DS;
1564
1565    if (WriteRegister)
1566    {
1567        /* Store the value in the register */
1568        State->GeneralRegs[ModRegRm->Register].Long = Value;
1569    }
1570    else
1571    {
1572        if (!ModRegRm->Memory)
1573        {
1574            /* Store the value in the second register */
1575            State->GeneralRegs[ModRegRm->SecondRegister].Long = Value;
1576        }
1577        else
1578        {
1579            /* Check for the segment override */
1580            if (State->PrefixFlags & FAST486_PREFIX_SEG)
1581            {
1582                /* Use the override segment instead */
1583                Segment = State->SegmentOverride;
1584            }
1585
1586            /* Write memory */
1587            if (!Fast486WriteMemory(State,
1588                                    Segment,
1589                                    ModRegRm->MemoryAddress,
1590                                    &Value,
1591                                    sizeof(ULONG)))
1592            {
1593                /* Exception occurred */
1594                return FALSE;
1595            }
1596        }
1597    }
1598
1599    return TRUE;
1600}
1601
1602FORCEINLINE
1603BOOLEAN
1604FASTCALL
1605Fast486IoPrivilegeCheck(PFAST486_STATE State, USHORT Port)
1606{
1607    UCHAR Bits;
1608    ULONG Location;
1609    FAST486_TSS Tss;
1610
1611    /* Access is always allowed if the CPL is less than or equal to the IOPL */
1612    if (State->Cpl <= State->Flags.Iopl) return TRUE;
1613
1614    /* Legacy Task State Segments have no IOPB */
1615    if (!State->TaskReg.Modern) return FALSE;
1616
1617    /* Read the TSS */
1618    if (!Fast486ReadLinearMemory(State, State->TaskReg.Base, &Tss, sizeof(FAST486_TSS), FALSE))
1619    {
1620        /* Exception occurred */
1621        return FALSE;
1622    }
1623
1624    Location = State->TaskReg.Base + HIWORD(Tss.IopbOffset) + (Port >> 3);
1625
1626    if (Location > State->TaskReg.Limit)
1627    {
1628        /* Access denied */
1629        Fast486Exception(State, FAST486_EXCEPTION_GP);
1630        return FALSE;
1631    }
1632
1633    /* Read the appropriate bit from the TSS IOPB */
1634    if (!Fast486ReadLinearMemory(State, Location, &Bits, sizeof(UCHAR), FALSE))
1635    {
1636        /* Exception occurred */
1637        return FALSE;
1638    }
1639
1640    if (Bits & (1 << (Port & 0x07)))
1641    {
1642        /* Access denied */
1643        Fast486Exception(State, FAST486_EXCEPTION_GP);
1644        return FALSE;
1645    }
1646
1647    return TRUE;
1648}
1649
1650#ifndef FAST486_NO_FPU
1651
1652FORCEINLINE
1653VOID
1654FASTCALL
1655Fast486FpuExceptionCheck(PFAST486_STATE State)
1656{
1657    /* Check if an unmasked exception occurred */
1658    if ((State->FpuStatus.Ie && !State->FpuControl.Im)
1659        || (State->FpuStatus.De && !State->FpuControl.Dm)
1660        || (State->FpuStatus.Ze && !State->FpuControl.Zm)
1661        || (State->FpuStatus.Oe && !State->FpuControl.Om)
1662        || (State->FpuStatus.Ue && !State->FpuControl.Um)
1663        || (State->FpuStatus.Pe && !State->FpuControl.Pm))
1664    {
1665        if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_NE)
1666        {
1667            /* Call the #MF handler */
1668            Fast486Exception(State, FAST486_EXCEPTION_MF);
1669        }
1670        else
1671        {
1672            /* Use the external interrupt */
1673            State->FpuCallback(State);
1674        }
1675    }
1676}
1677
1678FORCEINLINE
1679BOOLEAN
1680FASTCALL
1681Fast486FpuNormalize(PFAST486_STATE State,
1682                    PFAST486_FPU_DATA_REG Data)
1683{
1684    UINT LeadingZeros;
1685
1686    if (FPU_IS_ZERO(Data))
1687    {
1688        Data->Exponent = 0;
1689        return TRUE;
1690    }
1691
1692    if (FPU_IS_NORMALIZED(Data)) return TRUE;
1693
1694    LeadingZeros = CountLeadingZeros64(Data->Mantissa);
1695
1696    if (LeadingZeros < Data->Exponent)
1697    {
1698        Data->Mantissa <<= LeadingZeros;
1699        Data->Exponent -= LeadingZeros;
1700    }
1701    else
1702    {
1703        /* Raise the underflow exception */
1704        State->FpuStatus.Ue = TRUE;
1705
1706        if (State->FpuControl.Um)
1707        {
1708            /* Make it denormalized */
1709            Data->Mantissa <<= Data->Exponent - 1;
1710            Data->Exponent = 1;
1711        }
1712        else
1713        {
1714            return FALSE;
1715        }
1716    }
1717
1718    return TRUE;
1719}
1720
1721FORCEINLINE
1722USHORT
1723FASTCALL
1724Fast486FpuGetValueTag(PFAST486_FPU_DATA_REG Data)
1725{
1726    if (FPU_IS_ZERO(Data)) return FPU_TAG_ZERO;
1727    else if (FPU_IS_NAN(Data)) return FPU_TAG_SPECIAL;
1728    else return FPU_TAG_VALID;
1729}
1730
1731FORCEINLINE
1732BOOLEAN
1733FASTCALL
1734Fast486FpuPush(PFAST486_STATE State,
1735               PCFAST486_FPU_DATA_REG Data)
1736{
1737    State->FpuStatus.Top--;
1738
1739    if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
1740    {
1741        FPU_ST(0) = *Data;
1742        FPU_UPDATE_TAG(0);
1743
1744        return TRUE;
1745    }
1746    else
1747    {
1748        /* Raise the stack fault and invalid operation exception */
1749        State->FpuStatus.Sf = State->FpuStatus.Ie = TRUE;
1750
1751        /* Set the C1 condition code bit (stack overflow) */
1752        State->FpuStatus.Code1 = TRUE;
1753
1754        return FALSE;
1755    }
1756}
1757
1758FORCEINLINE
1759BOOLEAN
1760FASTCALL
1761Fast486FpuPop(PFAST486_STATE State)
1762{
1763    if (FPU_GET_TAG(0) != FPU_TAG_EMPTY)
1764    {
1765        FPU_SET_TAG(0, FPU_TAG_EMPTY);
1766        State->FpuStatus.Top++;
1767
1768        return TRUE;
1769    }
1770    else
1771    {
1772        /* Raise the stack fault and invalid operation exception */
1773        State->FpuStatus.Sf = State->FpuStatus.Ie = TRUE;
1774
1775        /* Clear the C1 condition code bit (stack underflow) */
1776        State->FpuStatus.Code1 = FALSE;
1777
1778        return FALSE;
1779    }
1780}
1781
1782#endif
1783
1784/* EOF */
1785