2 * Fast486 386/486 CPU Emulation Library
5 * Copyright (C) 2013 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 /* PUBLIC FUNCTIONS ***********************************************************/
26 Fast486Exception(PFAST486_STATE State,
27 FAST486_EXCEPTIONS ExceptionCode)
29 /* Call the internal function */
30 Fast486ExceptionWithErrorCode(State, ExceptionCode, 0);
35 Fast486StackPush(PFAST486_STATE State,
38 BOOLEAN Size = State->SegmentRegs[FAST486_REG_SS].Size;
40 /* The OPSIZE prefix toggles the size */
41 if (State->PrefixFlags & FAST486_PREFIX_OPSIZE) Size = !Size;
47 /* Check if ESP is between 1 and 3 */
48 if (State->GeneralRegs[FAST486_REG_ESP].Long >= 1
49 && State->GeneralRegs[FAST486_REG_ESP].Long <= 3)
51 Fast486Exception(State, FAST486_EXCEPTION_SS);
55 /* Subtract ESP by 4 */
56 State->GeneralRegs[FAST486_REG_ESP].Long -= 4;
58 /* Store the value in SS:ESP */
59 return Fast486WriteMemory(State,
61 State->GeneralRegs[FAST486_REG_ESP].Long,
68 USHORT ShortValue = LOWORD(Value);
70 /* Check if SP is 1 */
71 if (State->GeneralRegs[FAST486_REG_ESP].Long == 1)
73 Fast486Exception(State, FAST486_EXCEPTION_SS);
77 /* Subtract SP by 2 */
78 State->GeneralRegs[FAST486_REG_ESP].LowWord -= 2;
80 /* Store the value in SS:SP */
81 return Fast486WriteMemory(State,
83 State->GeneralRegs[FAST486_REG_ESP].LowWord,
91 Fast486StackPop(PFAST486_STATE State,
96 BOOLEAN Size = State->SegmentRegs[FAST486_REG_SS].Size;
98 /* The OPSIZE prefix toggles the size */
99 if (State->PrefixFlags & FAST486_PREFIX_OPSIZE) Size = !Size;
105 /* Check if ESP is 0xFFFFFFFF */
106 if (State->GeneralRegs[FAST486_REG_ESP].Long == 0xFFFFFFFF)
108 Fast486Exception(State, FAST486_EXCEPTION_SS);
112 /* Read the value from SS:ESP */
113 if (!Fast486ReadMemory(State,
115 State->GeneralRegs[FAST486_REG_ESP].Long,
120 /* An exception occurred */
124 /* Increment ESP by 4 */
125 State->GeneralRegs[FAST486_REG_ESP].Long += 4;
127 /* Store the value in the result */
134 /* Check if SP is 0xFFFF */
135 if (State->GeneralRegs[FAST486_REG_ESP].LowWord == 0xFFFF)
137 Fast486Exception(State, FAST486_EXCEPTION_SS);
141 /* Read the value from SS:SP */
142 if (!Fast486ReadMemory(State,
144 State->GeneralRegs[FAST486_REG_ESP].LowWord,
149 /* An exception occurred */
153 /* Increment SP by 2 */
154 State->GeneralRegs[FAST486_REG_ESP].Long += 2;
156 /* Store the value in the result */
165 Fast486LoadSegment(PFAST486_STATE State,
169 PFAST486_SEG_REG CachedDescriptor;
170 FAST486_GDT_ENTRY GdtEntry;
172 ASSERT(Segment < FAST486_NUM_SEG_REGS);
174 /* Get the cached descriptor */
175 CachedDescriptor = &State->SegmentRegs[Segment];
177 /* Check for protected mode */
178 if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) && !State->Flags.Vm)
180 /* Make sure the GDT contains the entry */
181 if (GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1))
183 Fast486Exception(State, FAST486_EXCEPTION_GP);
188 // FIXME: This code is only correct when paging is disabled!!!
189 if (State->MemReadCallback)
191 State->MemReadCallback(State,
193 + GET_SEGMENT_INDEX(Selector),
199 RtlMoveMemory(&GdtEntry,
200 (PVOID)(State->Gdtr.Address
201 + GET_SEGMENT_INDEX(Selector)),
205 if (Segment == FAST486_REG_SS)
207 /* Loading the stack segment */
209 if (GET_SEGMENT_INDEX(Selector) == 0)
211 Fast486Exception(State, FAST486_EXCEPTION_GP);
215 if (!GdtEntry.SystemType)
217 /* This is a special descriptor */
218 Fast486Exception(State, FAST486_EXCEPTION_GP);
222 if (GdtEntry.Executable || !GdtEntry.ReadWrite)
224 Fast486Exception(State, FAST486_EXCEPTION_GP);
228 if ((GET_SEGMENT_RPL(Selector) != Fast486GetCurrentPrivLevel(State))
229 || (GET_SEGMENT_RPL(Selector) != GdtEntry.Dpl))
231 Fast486Exception(State, FAST486_EXCEPTION_GP);
235 if (!GdtEntry.Present)
237 Fast486Exception(State, FAST486_EXCEPTION_SS);
241 else if (Segment == FAST486_REG_CS)
243 /* Loading the code segment */
244 // TODO: NOT IMPLEMENTED
248 /* Loading a data segment */
250 if (!GdtEntry.SystemType)
252 /* This is a special descriptor */
253 Fast486Exception(State, FAST486_EXCEPTION_GP);
257 if ((GET_SEGMENT_RPL(Selector) > GdtEntry.Dpl)
258 && (Fast486GetCurrentPrivLevel(State) > GdtEntry.Dpl))
260 Fast486Exception(State, FAST486_EXCEPTION_GP);
264 if (!GdtEntry.Present)
266 Fast486Exception(State, FAST486_EXCEPTION_NP);
271 /* Update the cache entry */
272 CachedDescriptor->Selector = Selector;
273 CachedDescriptor->Base = GdtEntry.Base | (GdtEntry.BaseHigh << 24);
274 CachedDescriptor->Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
275 CachedDescriptor->Accessed = GdtEntry.Accessed;
276 CachedDescriptor->ReadWrite = GdtEntry.ReadWrite;
277 CachedDescriptor->DirConf = GdtEntry.DirConf;
278 CachedDescriptor->Executable = GdtEntry.Executable;
279 CachedDescriptor->SystemType = GdtEntry.SystemType;
280 CachedDescriptor->Dpl = GdtEntry.Dpl;
281 CachedDescriptor->Present = GdtEntry.Present;
282 CachedDescriptor->Size = GdtEntry.Size;
284 /* Check for page granularity */
285 if (GdtEntry.Granularity) CachedDescriptor->Limit <<= 12;
289 /* Update the selector and base */
290 CachedDescriptor->Selector = Selector;
291 CachedDescriptor->Base = Selector << 4;
299 Fast486FetchByte(PFAST486_STATE State,
302 PFAST486_SEG_REG CachedDescriptor;
304 /* Get the cached descriptor of CS */
305 CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
307 /* Read from memory */
308 if (!Fast486ReadMemory(State,
310 (CachedDescriptor->Size) ? State->InstPtr.Long
311 : State->InstPtr.LowWord,
316 /* Exception occurred during instruction fetch */
320 /* Advance the instruction pointer */
321 if (CachedDescriptor->Size) State->InstPtr.Long++;
322 else State->InstPtr.LowWord++;
329 Fast486FetchWord(PFAST486_STATE State,
332 PFAST486_SEG_REG CachedDescriptor;
334 /* Get the cached descriptor of CS */
335 CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
337 /* Read from memory */
338 // FIXME: Fix byte order on big-endian machines
339 if (!Fast486ReadMemory(State,
341 (CachedDescriptor->Size) ? State->InstPtr.Long
342 : State->InstPtr.LowWord,
347 /* Exception occurred during instruction fetch */
351 /* Advance the instruction pointer */
352 if (CachedDescriptor->Size) State->InstPtr.Long += sizeof(USHORT);
353 else State->InstPtr.LowWord += sizeof(USHORT);
360 Fast486FetchDword(PFAST486_STATE State,
363 PFAST486_SEG_REG CachedDescriptor;
365 /* Get the cached descriptor of CS */
366 CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
368 /* Read from memory */
369 // FIXME: Fix byte order on big-endian machines
370 if (!Fast486ReadMemory(State,
372 (CachedDescriptor->Size) ? State->InstPtr.Long
373 : State->InstPtr.LowWord,
378 /* Exception occurred during instruction fetch */
382 /* Advance the instruction pointer */
383 if (CachedDescriptor->Size) State->InstPtr.Long += sizeof(ULONG);
384 else State->InstPtr.LowWord += sizeof(ULONG);
391 Fast486GetIntVector(PFAST486_STATE State,
393 PFAST486_IDT_ENTRY IdtEntry)
397 /* Check for protected mode */
398 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
400 /* Read from the IDT */
401 // FIXME: This code is only correct when paging is disabled!!!
402 if (State->MemReadCallback)
404 State->MemReadCallback(State,
406 + Number * sizeof(*IdtEntry),
412 RtlMoveMemory(IdtEntry,
413 (PVOID)(State->Idtr.Address
414 + Number * sizeof(*IdtEntry)),
420 /* Read from the real-mode IVT */
422 /* Paging is always disabled in real mode */
423 if (State->MemReadCallback)
425 State->MemReadCallback(State,
427 + Number * sizeof(FarPointer),
433 RtlMoveMemory(&FarPointer,
434 (PVOID)(State->Idtr.Address
435 + Number * sizeof(FarPointer)),
439 /* Fill a fake IDT entry */
440 IdtEntry->Offset = LOWORD(FarPointer);
441 IdtEntry->Selector = HIWORD(FarPointer);
443 IdtEntry->Type = FAST486_IDT_INT_GATE;
444 IdtEntry->Storage = FALSE;
446 IdtEntry->Present = TRUE;
447 IdtEntry->OffsetHigh = 0;
451 * Once paging support is implemented this function
452 * will not always return true
459 Fast486CalculateParity(UCHAR Number)
461 return (0x9669 >> ((Number & 0x0F) ^ (Number >> 4))) & 1;
466 Fast486ParseModRegRm(PFAST486_STATE State,
468 PFAST486_MOD_REG_RM ModRegRm)
470 UCHAR ModRmByte, Mode, RegMem;
472 /* Fetch the MOD REG R/M byte */
473 if (!Fast486FetchByte(State, &ModRmByte))
475 /* Exception occurred */
479 /* Unpack the mode and R/M */
480 Mode = ModRmByte >> 6;
481 RegMem = ModRmByte & 0x07;
483 /* Set the register operand */
484 ModRegRm->Register = (ModRmByte >> 3) & 0x07;
487 if ((ModRmByte >> 6) == 3)
489 /* The second operand is also a register */
490 ModRegRm->Memory = FALSE;
491 ModRegRm->SecondRegister = RegMem;
497 /* The second operand is memory */
498 ModRegRm->Memory = TRUE;
502 if (RegMem == FAST486_REG_ESP)
505 ULONG Scale, Index, Base;
507 /* Fetch the SIB byte */
508 if (!Fast486FetchByte(State, &SibByte))
510 /* Exception occurred */
514 /* Unpack the scale, index and base */
515 Scale = 1 << (SibByte >> 6);
516 Index = (SibByte >> 3) & 0x07;
517 if (Index != FAST486_REG_ESP) Index = State->GeneralRegs[Index].Long;
519 Base = State->GeneralRegs[SibByte & 0x07].Long;
521 /* Calculate the address */
522 ModRegRm->MemoryAddress = Base + Index * Scale;
524 else if (RegMem == FAST486_REG_EBP)
526 if (Mode) ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].Long;
527 else ModRegRm->MemoryAddress = 0;
531 /* Get the base from the register */
532 ModRegRm->MemoryAddress = State->GeneralRegs[RegMem].Long;
535 /* Check if there is no segment override */
536 if (!(State->PrefixFlags & FAST486_PREFIX_SEG))
538 /* Check if the default segment should be SS */
539 if ((RegMem == FAST486_REG_EBP) && Mode)
541 /* Add a SS: prefix */
542 State->PrefixFlags |= FAST486_PREFIX_SEG;
543 State->SegmentOverride = FAST486_REG_SS;
552 if (!Fast486FetchByte(State, (PUCHAR)&Offset))
554 /* Exception occurred */
558 /* Add the signed offset to the address */
559 ModRegRm->MemoryAddress += (LONG)Offset;
561 else if ((Mode == 2) || ((Mode == 0) && (RegMem == FAST486_REG_EBP)))
565 /* Fetch the dword */
566 if (!Fast486FetchDword(State, (PULONG)&Offset))
568 /* Exception occurred */
572 /* Add the signed offset to the address */
573 ModRegRm->MemoryAddress += Offset;
578 /* Check the operand */
585 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord
586 + State->GeneralRegs[FAST486_REG_ESI].LowWord;
595 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord
596 + State->GeneralRegs[FAST486_REG_EDI].LowWord;
604 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_ESI].LowWord;
612 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EDI].LowWord;
622 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord;
626 /* [constant] (added later) */
627 ModRegRm->MemoryAddress = 0;
636 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord;
642 /* Check if there is no segment override */
643 if (!(State->PrefixFlags & FAST486_PREFIX_SEG))
645 /* Check if the default segment should be SS */
646 if ((RegMem == 2) || (RegMem == 3) || ((RegMem == 6) && Mode))
648 /* Add a SS: prefix */
649 State->PrefixFlags |= FAST486_PREFIX_SEG;
650 State->SegmentOverride = FAST486_REG_SS;
659 if (!Fast486FetchByte(State, (PUCHAR)&Offset))
661 /* Exception occurred */
665 /* Add the signed offset to the address */
666 ModRegRm->MemoryAddress += (LONG)Offset;
668 else if ((Mode == 2) || ((Mode == 0) && (RegMem == 6)))
673 if (!Fast486FetchWord(State, (PUSHORT)&Offset))
675 /* Exception occurred */
679 /* Add the signed offset to the address */
680 ModRegRm->MemoryAddress += (LONG)Offset;
683 /* Clear the top 16 bits */
684 ModRegRm->MemoryAddress &= 0x0000FFFF;
692 Fast486ReadModrmByteOperands(PFAST486_STATE State,
693 PFAST486_MOD_REG_RM ModRegRm,
697 FAST486_SEG_REGS Segment = FAST486_REG_DS;
699 /* Get the register value */
700 if (ModRegRm->Register & 0x04)
703 *RegValue = State->GeneralRegs[ModRegRm->Register & 0x03].HighByte;
708 *RegValue = State->GeneralRegs[ModRegRm->Register & 0x03].LowByte;
711 if (!ModRegRm->Memory)
713 /* Get the second register value */
714 if (ModRegRm->SecondRegister & 0x04)
717 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister & 0x03].HighByte;
722 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister & 0x03].LowByte;
727 /* Check for the segment override */
728 if (State->PrefixFlags & FAST486_PREFIX_SEG)
730 /* Use the override segment instead */
731 Segment = State->SegmentOverride;
735 if (!Fast486ReadMemory(State,
737 ModRegRm->MemoryAddress,
742 /* Exception occurred */
752 Fast486ReadModrmWordOperands(PFAST486_STATE State,
753 PFAST486_MOD_REG_RM ModRegRm,
757 FAST486_SEG_REGS Segment = FAST486_REG_DS;
759 /* Get the register value */
760 *RegValue = State->GeneralRegs[ModRegRm->Register].LowWord;
762 if (!ModRegRm->Memory)
764 /* Get the second register value */
765 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister].LowWord;
769 /* Check for the segment override */
770 if (State->PrefixFlags & FAST486_PREFIX_SEG)
772 /* Use the override segment instead */
773 Segment = State->SegmentOverride;
777 if (!Fast486ReadMemory(State,
779 ModRegRm->MemoryAddress,
784 /* Exception occurred */
794 Fast486ReadModrmDwordOperands(PFAST486_STATE State,
795 PFAST486_MOD_REG_RM ModRegRm,
799 FAST486_SEG_REGS Segment = FAST486_REG_DS;
801 /* Get the register value */
802 *RegValue = State->GeneralRegs[ModRegRm->Register].Long;
804 if (!ModRegRm->Memory)
806 /* Get the second register value */
807 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister].Long;
811 /* Check for the segment override */
812 if (State->PrefixFlags & FAST486_PREFIX_SEG)
814 /* Use the override segment instead */
815 Segment = State->SegmentOverride;
819 if (!Fast486ReadMemory(State,
821 ModRegRm->MemoryAddress,
826 /* Exception occurred */
836 Fast486WriteModrmByteOperands(PFAST486_STATE State,
837 PFAST486_MOD_REG_RM ModRegRm,
838 BOOLEAN WriteRegister,
841 FAST486_SEG_REGS Segment = FAST486_REG_DS;
845 /* Store the value in the register */
846 if (ModRegRm->Register & 0x04)
849 State->GeneralRegs[ModRegRm->Register & 0x03].HighByte = Value;
854 State->GeneralRegs[ModRegRm->Register & 0x03].LowByte = Value;
859 if (!ModRegRm->Memory)
861 /* Store the value in the second register */
862 if (ModRegRm->SecondRegister & 0x04)
865 State->GeneralRegs[ModRegRm->SecondRegister & 0x03].HighByte = Value;
870 State->GeneralRegs[ModRegRm->SecondRegister & 0x03].LowByte = Value;
875 /* Check for the segment override */
876 if (State->PrefixFlags & FAST486_PREFIX_SEG)
878 /* Use the override segment instead */
879 Segment = State->SegmentOverride;
883 if (!Fast486WriteMemory(State,
885 ModRegRm->MemoryAddress,
889 /* Exception occurred */
900 Fast486WriteModrmWordOperands(PFAST486_STATE State,
901 PFAST486_MOD_REG_RM ModRegRm,
902 BOOLEAN WriteRegister,
905 FAST486_SEG_REGS Segment = FAST486_REG_DS;
909 /* Store the value in the register */
910 State->GeneralRegs[ModRegRm->Register].LowWord = Value;
914 if (!ModRegRm->Memory)
916 /* Store the value in the second register */
917 State->GeneralRegs[ModRegRm->SecondRegister].LowWord = Value;
921 /* Check for the segment override */
922 if (State->PrefixFlags & FAST486_PREFIX_SEG)
924 /* Use the override segment instead */
925 Segment = State->SegmentOverride;
929 if (!Fast486WriteMemory(State,
931 ModRegRm->MemoryAddress,
935 /* Exception occurred */
946 Fast486WriteModrmDwordOperands(PFAST486_STATE State,
947 PFAST486_MOD_REG_RM ModRegRm,
948 BOOLEAN WriteRegister,
951 FAST486_SEG_REGS Segment = FAST486_REG_DS;
955 /* Store the value in the register */
956 State->GeneralRegs[ModRegRm->Register].Long = Value;
960 if (!ModRegRm->Memory)
962 /* Store the value in the second register */
963 State->GeneralRegs[ModRegRm->SecondRegister].Long = Value;
967 /* Check for the segment override */
968 if (State->PrefixFlags & FAST486_PREFIX_SEG)
970 /* Use the override segment instead */
971 Segment = State->SegmentOverride;
975 if (!Fast486WriteMemory(State,
977 ModRegRm->MemoryAddress,
981 /* Exception occurred */