/* * COPYRIGHT: GPL - See COPYING in the top level directory * PROJECT: ReactOS Virtual DOS Machine * FILE: bios32.c * PURPOSE: VDM 32-bit BIOS * PROGRAMMERS: Aleksandar Andrejevic */ /* INCLUDES *******************************************************************/ #define NDEBUG #include "emulator.h" #include "callback.h" #include "bios32.h" #include "io.h" #include "hardware/cmos.h" #include "hardware/pic.h" #include "hardware/timer.h" /* PRIVATE VARIABLES **********************************************************/ CALLBACK16 BiosContext; PBIOS_DATA_AREA Bda; /* PRIVATE FUNCTIONS **********************************************************/ static VOID WINAPI BiosException(LPWORD Stack) { /* Get the exception number and call the emulator API */ BYTE ExceptionNumber = LOBYTE(Stack[STACK_INT_NUM]); EmulatorException(ExceptionNumber, Stack); } static VOID WINAPI BiosEquipmentService(LPWORD Stack) { /* Return the equipment list */ setAX(Bda->EquipmentList); } static VOID WINAPI BiosGetMemorySize(LPWORD Stack) { /* Return the conventional memory size in kB, typically 640 kB */ setAX(Bda->MemorySize); } static VOID WINAPI BiosMiscService(LPWORD Stack) { switch (getAH()) { /* Wait */ case 0x86: { /* * Interval in microseconds in CX:DX * See Ralf Brown: http://www.ctyme.com/intr/rb-1525.htm * for more information. */ Sleep(MAKELONG(getDX(), getCX())); /* Clear CF */ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } /* Copy Extended Memory */ case 0x87: { DWORD Count = (DWORD)getCX() * 2; PFAST486_GDT_ENTRY Gdt = (PFAST486_GDT_ENTRY)SEG_OFF_TO_PTR(getES(), getSI()); DWORD SourceBase = Gdt[2].Base + (Gdt[2].BaseMid << 16) + (Gdt[2].BaseHigh << 24); DWORD SourceLimit = Gdt[2].Limit + (Gdt[2].LimitHigh << 16); DWORD DestBase = Gdt[3].Base + (Gdt[3].BaseMid << 16) + (Gdt[3].BaseHigh << 24); DWORD DestLimit = Gdt[3].Limit + (Gdt[3].LimitHigh << 16); /* Check for flags */ if (Gdt[2].Granularity) SourceLimit = (SourceLimit << 12) | 0xFFF; if (Gdt[3].Granularity) DestLimit = (DestLimit << 12) | 0xFFF; if ((Count > SourceLimit) || (Count > DestLimit)) { setAX(0x80); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } /* Copy */ RtlMoveMemory((PVOID)((ULONG_PTR)BaseAddress + DestBase), (PVOID)((ULONG_PTR)BaseAddress + SourceBase), Count); setAX(ERROR_SUCCESS); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } /* Get Extended Memory Size */ case 0x88: { UCHAR Low, High; /* * Return the (usable) extended memory (after 1 MB) * size in kB from CMOS. */ IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_LOW); Low = IOReadB(CMOS_DATA_PORT); IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_HIGH); High = IOReadB(CMOS_DATA_PORT); setAX(MAKEWORD(Low, High)); /* Clear CF */ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } default: { DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n", getAH()); } } } static VOID WINAPI BiosTimeService(LPWORD Stack) { switch (getAH()) { case 0x00: { /* Set AL to 1 if midnight had passed, 0 otherwise */ setAL(Bda->MidnightPassed ? 0x01 : 0x00); /* Return the tick count in CX:DX */ setCX(HIWORD(Bda->TickCounter)); setDX(LOWORD(Bda->TickCounter)); /* Reset the midnight flag */ Bda->MidnightPassed = FALSE; break; } case 0x01: { /* Set the tick count to CX:DX */ Bda->TickCounter = MAKELONG(getDX(), getCX()); /* Reset the midnight flag */ Bda->MidnightPassed = FALSE; break; } default: { DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n", getAH()); } } } static VOID WINAPI BiosSystemTimerInterrupt(LPWORD Stack) { /* Increase the system tick count */ Bda->TickCounter++; } // From SeaBIOS static VOID PicSetIRQMask(USHORT off, USHORT on) { UCHAR pic1off = off, pic1on = on, pic2off = off>>8, pic2on = on>>8; IOWriteB(PIC_MASTER_DATA, (IOReadB(PIC_MASTER_DATA) & ~pic1off) | pic1on); IOWriteB(PIC_SLAVE_DATA , (IOReadB(PIC_SLAVE_DATA ) & ~pic2off) | pic2on); } // From SeaBIOS VOID EnableHwIRQ(UCHAR hwirq, EMULATOR_INT32_PROC func) { UCHAR vector; PicSetIRQMask(1 << hwirq, 0); if (hwirq < 8) vector = BIOS_PIC_MASTER_INT + hwirq; else vector = BIOS_PIC_SLAVE_INT + hwirq - 8; RegisterBiosInt32(vector, func); } VOID PicIRQComplete(LPWORD Stack) { /* Get the interrupt number */ BYTE IntNum = LOBYTE(Stack[STACK_INT_NUM]); /* * If this was a PIC IRQ, send an End-of-Interrupt to the PIC. */ if (IntNum >= BIOS_PIC_MASTER_INT && IntNum < BIOS_PIC_MASTER_INT + 8) { /* It was an IRQ from the master PIC */ IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI); } else if (IntNum >= BIOS_PIC_SLAVE_INT && IntNum < BIOS_PIC_SLAVE_INT + 8) { /* It was an IRQ from the slave PIC */ IOWriteB(PIC_SLAVE_CMD , PIC_OCW2_EOI); IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI); } } static VOID WINAPI BiosHandleMasterPicIRQ(LPWORD Stack) { BYTE IrqNumber; IOWriteB(PIC_MASTER_CMD, PIC_OCW3_READ_ISR /* == 0x0B */); IrqNumber = IOReadB(PIC_MASTER_CMD); DPRINT("Master - IrqNumber = 0x%x\n", IrqNumber); PicIRQComplete(Stack); } static VOID WINAPI BiosHandleSlavePicIRQ(LPWORD Stack) { BYTE IrqNumber; IOWriteB(PIC_SLAVE_CMD, PIC_OCW3_READ_ISR /* == 0x0B */); IrqNumber = IOReadB(PIC_SLAVE_CMD); DPRINT("Slave - IrqNumber = 0x%x\n", IrqNumber); PicIRQComplete(Stack); } // Timer IRQ 0 static VOID WINAPI BiosTimerIrq(LPWORD Stack) { /* * Perform the system timer interrupt. * * Do not call directly BiosSystemTimerInterrupt(Stack); * because some programs may hook only BIOS_SYS_TIMER_INTERRUPT * for their purpose... */ /** EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT); **/ Int32Call(&BiosContext, BIOS_SYS_TIMER_INTERRUPT); PicIRQComplete(Stack); } static VOID BiosHwSetup(VOID) { /* Initialize the master and the slave PICs (cascade mode) */ IOWriteB(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4); IOWriteB(PIC_SLAVE_CMD , PIC_ICW1 | PIC_ICW1_ICW4); /* * Set the interrupt vector offsets for each PIC * (base IRQs: 0x08-0x0F for IRQ 0-7, 0x70-0x77 for IRQ 8-15) */ IOWriteB(PIC_MASTER_DATA, BIOS_PIC_MASTER_INT); IOWriteB(PIC_SLAVE_DATA , BIOS_PIC_SLAVE_INT ); /* Tell the master PIC that there is a slave PIC at IRQ 2 */ IOWriteB(PIC_MASTER_DATA, 1 << 2); /* Tell the slave PIC its cascade identity */ IOWriteB(PIC_SLAVE_DATA , 2); /* Make sure both PICs are in 8086 mode */ IOWriteB(PIC_MASTER_DATA, PIC_ICW4_8086); IOWriteB(PIC_SLAVE_DATA , PIC_ICW4_8086); /* Clear the masks for both PICs */ // IOWriteB(PIC_MASTER_DATA, 0x00); // IOWriteB(PIC_SLAVE_DATA , 0x00); /* Disable all IRQs */ IOWriteB(PIC_MASTER_DATA, 0xFF); IOWriteB(PIC_SLAVE_DATA , 0xFF); /* Initialize PIT Counter 0 */ IOWriteB(PIT_COMMAND_PORT, 0x34); IOWriteB(PIT_DATA_PORT(0), 0x00); IOWriteB(PIT_DATA_PORT(0), 0x00); /* Initialize PIT Counter 1 */ IOWriteB(PIT_COMMAND_PORT, 0x74); IOWriteB(PIT_DATA_PORT(1), 0x00); IOWriteB(PIT_DATA_PORT(1), 0x00); /* Initialize PIT Counter 2 */ IOWriteB(PIT_COMMAND_PORT, 0xB4); IOWriteB(PIT_DATA_PORT(2), 0x00); IOWriteB(PIT_DATA_PORT(2), 0x00); EnableHwIRQ(0, BiosTimerIrq); } static VOID InitializeBiosInt32(VOID) { USHORT i; // USHORT Offset = 0; /* Initialize the callback context */ InitializeContext(&BiosContext, BIOS_SEGMENT, 0x0000); /* Register the BIOS 32-bit Interrupts */ for (i = 0x00; i <= 0xFF; i++) { // Offset += RegisterInt32(MAKELONG(Offset, BIOS_SEGMENT), i, NULL, NULL); BiosContext.NextOffset += RegisterInt32(MAKELONG(BiosContext.NextOffset, BiosContext.Segment), i, NULL, NULL); } /* Initialize the exception vector interrupts to a default Exception handler */ for (i = 0; i < 8; i++) RegisterBiosInt32(i, BiosException); /* Initialize HW vector interrupts to a default HW handler */ for (i = BIOS_PIC_MASTER_INT; i < BIOS_PIC_MASTER_INT + 8; i++) RegisterBiosInt32(i, BiosHandleMasterPicIRQ); for (i = BIOS_PIC_SLAVE_INT ; i < BIOS_PIC_SLAVE_INT + 8; i++) RegisterBiosInt32(i, BiosHandleSlavePicIRQ); /* Initialize software vector handlers */ RegisterBiosInt32(BIOS_EQUIPMENT_INTERRUPT, BiosEquipmentService ); RegisterBiosInt32(BIOS_MEMORY_SIZE , BiosGetMemorySize ); RegisterBiosInt32(BIOS_MISC_INTERRUPT , BiosMiscService ); RegisterBiosInt32(BIOS_TIME_INTERRUPT , BiosTimeService ); RegisterBiosInt32(BIOS_SYS_TIMER_INTERRUPT, BiosSystemTimerInterrupt); /* Some interrupts are in fact addresses to tables */ ((PULONG)BaseAddress)[0x1E] = (ULONG)NULL; ((PULONG)BaseAddress)[0x41] = (ULONG)NULL; ((PULONG)BaseAddress)[0x46] = (ULONG)NULL; ((PULONG)BaseAddress)[0x48] = (ULONG)NULL; ((PULONG)BaseAddress)[0x49] = (ULONG)NULL; } /* PUBLIC FUNCTIONS ***********************************************************/ /* * The BIOS POST (Power On-Self Test) */ BOOLEAN Bios32Initialize(IN HANDLE ConsoleInput, IN HANDLE ConsoleOutput) { UCHAR Low, High; /* Initialize the BDA */ Bda = (PBIOS_DATA_AREA)SEG_OFF_TO_PTR(BDA_SEGMENT, 0); Bda->EquipmentList = BIOS_EQUIPMENT_LIST; /* * Retrieve the conventional memory size * in kB from CMOS, typically 640 kB. */ IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_LOW); Low = IOReadB(CMOS_DATA_PORT); IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_HIGH); High = IOReadB(CMOS_DATA_PORT); Bda->MemorySize = MAKEWORD(Low, High); /* Register the BIOS 32-bit Interrupts */ InitializeBiosInt32(); /* Initialize platform hardware (PIC/PIT chips, ...) */ BiosHwSetup(); /* Initialize the Keyboard BIOS */ if (!KbdBios32Initialize(ConsoleInput)) return FALSE; /* Set the console input mode */ SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT); /* Initialize the Video BIOS */ if (!VidBios32Initialize(ConsoleOutput)) return FALSE; /* Enable interrupts */ setIF(1); /* We are done */ return TRUE; } VOID Bios32Cleanup(VOID) { VidBios32Cleanup(); KbdBios32Cleanup(); } /* EOF */