[NTVDM]
[reactos.git] / subsystems / ntvdm / bios.c
index f040147..a08e831 100644 (file)
 
 #define NDEBUG
 
-#include "bios.h"
 #include "emulator.h"
+#include "bios.h"
+
 #include "vga.h"
 #include "pic.h"
 #include "ps2.h"
 #include "timer.h"
 
+#include "int32.h"
 #include "registers.h"
 
 /* PRIVATE VARIABLES **********************************************************/
@@ -26,6 +28,7 @@ static BYTE BiosKeyboardMap[256];
 static HANDLE BiosConsoleInput  = INVALID_HANDLE_VALUE;
 static HANDLE BiosConsoleOutput = INVALID_HANDLE_VALUE;
 static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo;
+static HANDLE InputThread = NULL;
 
 /*
  * VGA Register Configurations for BIOS Video Modes
@@ -379,6 +382,8 @@ BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
     COORD Resolution;
     LPBYTE Values = VideoModes[ModeNumber];
 
+    DPRINT1("Switching to mode %Xh; Values = 0x%p\n", ModeNumber, Values);
+
     if (Values == NULL) return FALSE;
 
     /* Write the misc register */
@@ -412,6 +417,9 @@ BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
         VgaWritePort(VGA_AC_WRITE, *(Values++));
     }
 
+    /* Reset the palette */
+    VgaResetPalette();
+
     /* Update the values in the BDA */
     Bda->VideoMode = ModeNumber;
     Bda->VideoPage = 0;
@@ -424,7 +432,7 @@ BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
 
     Resolution = VgaGetDisplayResolution();
     Bda->ScreenColumns = Resolution.X;
-    Bda->ScreenRows = Resolution.Y - 1;
+    Bda->ScreenRows    = Resolution.Y - 1;
 
     return TRUE;
 }
@@ -453,53 +461,30 @@ BOOLEAN BiosSetVideoPage(BYTE PageNumber)
 
 BOOLEAN BiosInitialize(VOID)
 {
-    INT i;
-    WORD Offset = 0;
-    LPWORD IntVecTable = (LPWORD)BaseAddress;
-    LPBYTE BiosCode = (LPBYTE)SEG_OFF_TO_PTR(BIOS_SEGMENT, 0);
-
     /* Initialize the BDA */
     Bda = (PBIOS_DATA_AREA)SEG_OFF_TO_PTR(BDA_SEGMENT, 0);
     Bda->EquipmentList = BIOS_EQUIPMENT_LIST;
+    /*
+     * Conventional memory size is 640 kB,
+     * see: http://webpages.charter.net/danrollins/techhelp/0184.HTM
+     * and see Ralf Brown: http://www.ctyme.com/intr/rb-0598.htm
+     * for more information.
+     */
+    Bda->MemorySize = 0x0280;
     Bda->KeybdBufferStart = FIELD_OFFSET(BIOS_DATA_AREA, KeybdBuffer);
     Bda->KeybdBufferEnd = Bda->KeybdBufferStart + BIOS_KBD_BUFFER_SIZE * sizeof(WORD);
 
-    /* Generate ISR stubs and fill the IVT */
-    for (i = 0; i < 256; i++)
-    {
-        IntVecTable[i * 2] = Offset;
-        IntVecTable[i * 2 + 1] = BIOS_SEGMENT;
-
-        BiosCode[Offset++] = 0xFB; // sti
+    /* Initialize the 32-bit Interrupt system */
+    InitializeInt32(BIOS_SEGMENT);
 
-        BiosCode[Offset++] = 0x6A; // push i
-        BiosCode[Offset++] = (BYTE)i;
-
-        BiosCode[Offset++] = 0x6A; // push 0
-        BiosCode[Offset++] = 0x00;
-
-        BiosCode[Offset++] = 0xF8; // clc
-
-        BiosCode[Offset++] = LOBYTE(EMULATOR_BOP); // BOP sequence
-        BiosCode[Offset++] = HIBYTE(EMULATOR_BOP);
-        BiosCode[Offset++] = LOBYTE(EMULATOR_INT_BOP);
-        BiosCode[Offset++] = HIBYTE(EMULATOR_INT_BOP);
-
-        BiosCode[Offset++] = 0x73; // jnc +3
-        BiosCode[Offset++] = 0x03;
-
-        // HACK: The following instruction should be HLT!
-        BiosCode[Offset++] = 0x90; // nop
-
-        BiosCode[Offset++] = 0xEB; // jmp -10
-        BiosCode[Offset++] = 0xF6;
-
-        BiosCode[Offset++] = 0x83; // add sp, 4
-        BiosCode[Offset++] = 0xC4;
-        BiosCode[Offset++] = 0x04;
-
-        BiosCode[Offset++] = 0xCF; // iret
-    }
+    /* Register the BIOS 32-bit Interrupts */
+    RegisterInt32(BIOS_VIDEO_INTERRUPT    , BiosVideoService        );
+    RegisterInt32(BIOS_EQUIPMENT_INTERRUPT, BiosEquipmentService    );
+    RegisterInt32(BIOS_MEMORY_SIZE        , BiosGetMemorySize       );
+    RegisterInt32(BIOS_MISC_INTERRUPT     , BiosMiscService         );
+    RegisterInt32(BIOS_KBD_INTERRUPT      , BiosKeyboardService     );
+    RegisterInt32(BIOS_TIME_INTERRUPT     , BiosTimeService         );
+    RegisterInt32(BIOS_SYS_TIMER_INTERRUPT, BiosSystemTimerInterrupt);
 
     /* Get the input handle to the real console, and check for success */
     BiosConsoleInput = CreateFileW(L"CONIN$",
@@ -552,6 +537,9 @@ BOOLEAN BiosInitialize(VOID)
     /* Set the console input mode */
     SetConsoleMode(BiosConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
 
+    /* Start the input thread */
+    InputThread = CreateThread(NULL, 0, &InputThreadProc, BiosConsoleInput, 0, NULL);
+
     /* Initialize the PIC */
     PicWriteCommand(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
     PicWriteCommand(PIC_SLAVE_CMD , PIC_ICW1 | PIC_ICW1_ICW4);
@@ -581,6 +569,9 @@ BOOLEAN BiosInitialize(VOID)
 
 VOID BiosCleanup(VOID)
 {
+    /* Close the input thread handle */
+    if (InputThread != NULL) CloseHandle(InputThread);
+
     /* Restore the old screen buffer */
     SetConsoleActiveScreenBuffer(BiosConsoleOutput);
 
@@ -621,13 +612,23 @@ WORD BiosGetCharacter(VOID)
     return CharacterData;
 }
 
+VOID BiosGetCursorPosition(PBYTE Row, PBYTE Column, BYTE Page)
+{
+    /* Make sure the selected video page is valid */
+    if (Page >= BIOS_MAX_PAGES) return;
+
+    /* Get the cursor location */
+    *Row    = HIBYTE(Bda->CursorPosition[Page]);
+    *Column = LOBYTE(Bda->CursorPosition[Page]);
+}
+
 VOID BiosSetCursorPosition(BYTE Row, BYTE Column, BYTE Page)
 {
     /* Make sure the selected video page is valid */
     if (Page >= BIOS_MAX_PAGES) return;
 
     /* Update the position in the BDA */
-    Bda->CursorPosition[Page] = (Row << 8) | Column;
+    Bda->CursorPosition[Page] = MAKEWORD(Column, Row);
 
     /* Check if this is the current video page */
     if (Page == Bda->VideoPage)
@@ -667,7 +668,7 @@ BOOLEAN BiosScrollWindow(INT Direction,
         /* Fill the window */
         for (i = 0; i < WindowSize; i++)
         {
-            WindowData[i] = ' ' | (FillAttribute << 8);
+            WindowData[i] = MAKEWORD(' ', FillAttribute);
         }
 
         goto Done;
@@ -687,15 +688,14 @@ Done:
 
 VOID BiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
 {
-    WORD CharData = (Attribute << 8) | Character;
+    WORD CharData = MAKEWORD(Character, Attribute);
     BYTE Row, Column;
 
     /* Make sure the page exists */
     if (Page >= BIOS_MAX_PAGES) return;
 
     /* Get the cursor location */
-    Row = HIBYTE(Bda->CursorPosition[Page]);
-    Column = LOBYTE(Bda->CursorPosition[Page]);
+    BiosGetCursorPosition(&Row, &Column, Page);
 
     if (Character == '\a')
     {
@@ -718,12 +718,23 @@ VOID BiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
         }
 
         /* Erase the existing character */
-        CharData = (Attribute << 8) | ' ';
-        VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG,
-                       Page * Bda->VideoPageSize
-                       + (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
-                       (LPVOID)&CharData,
-                       sizeof(WORD));
+        CharData = MAKEWORD(' ', Attribute);
+        EmulatorWriteMemory(&EmulatorContext,
+                            TO_LINEAR(TEXT_VIDEO_SEG,
+                                Page * Bda->VideoPageSize +
+                                (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
+                            (LPVOID)&CharData,
+                            sizeof(WORD));
+    }
+    else if (Character == '\t')
+    {
+        /* Horizontal Tabulation control character */
+        do
+        {
+            // Taken from DosBox
+            BiosPrintCharacter(' ', Attribute, Page);
+            BiosGetCursorPosition(&Row, &Column, Page);
+        } while (Column % 8);
     }
     else if (Character == '\n')
     {
@@ -740,11 +751,12 @@ VOID BiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
         /* Default character */
 
         /* Write the character */
-        VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG,
-                       Page * Bda->VideoPageSize
-                       + (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
-                       (LPVOID)&CharData,
-                       sizeof(WORD));
+        EmulatorWriteMemory(&EmulatorContext,
+                            TO_LINEAR(TEXT_VIDEO_SEG,
+                                Page * Bda->VideoPageSize +
+                                (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
+                            (LPVOID)&CharData,
+                            sizeof(WORD));
 
         /* Advance the cursor */
         Column++;
@@ -769,13 +781,15 @@ VOID BiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
                          Rectangle,
                          Page,
                          DEFAULT_ATTRIBUTE);
+
+        Row--;
     }
 
     /* Set the cursor position */
     BiosSetCursorPosition(Row, Column, Page);
 }
 
-VOID BiosVideoService(LPWORD Stack)
+VOID WINAPI BiosVideoService(LPWORD Stack)
 {
     switch (getAH())
     {
@@ -911,6 +925,205 @@ VOID BiosVideoService(LPWORD Stack)
             break;
         }
 
+        /* Palette Control */
+        case 0x10:
+        {
+            switch (getAL())
+            {
+                /* Set Single Palette Register */
+                case 0x00:
+                {
+                    /* Reset the flip-flop */
+                    VgaReadPort(VGA_STAT_COLOR);
+
+                    /* Write the index */
+                    VgaWritePort(VGA_AC_INDEX, getBL());
+
+                    /* Write the data */
+                    VgaWritePort(VGA_AC_WRITE, getBH());
+
+                    break;
+                }
+
+                /* Set Overscan Color */
+                case 0x01:
+                {
+                    /* Reset the flip-flop */
+                    VgaReadPort(VGA_STAT_COLOR);
+
+                    /* Write the index */
+                    VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
+
+                    /* Write the data */
+                    VgaWritePort(VGA_AC_WRITE, getBH());
+
+                    break;
+                }
+
+                /* Set All Palette Registers */
+                case 0x02:
+                {
+                    INT i;
+                    LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
+
+                    /* Set the palette registers */
+                    for (i = 0; i <= VGA_AC_PAL_F_REG; i++)
+                    {
+                        /* Reset the flip-flop */
+                        VgaReadPort(VGA_STAT_COLOR);
+
+                        /* Write the index */
+                        VgaWritePort(VGA_AC_INDEX, i);
+
+                        /* Write the data */
+                        VgaWritePort(VGA_AC_WRITE, Buffer[i]);
+                    }
+
+                    /* Set the overscan register */
+                    VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
+                    VgaWritePort(VGA_AC_WRITE, Buffer[VGA_AC_PAL_F_REG + 1]);
+
+                    break;
+                }
+
+                /* Get Single Palette Register */
+                case 0x07:
+                {
+                    /* Reset the flip-flop */
+                    VgaReadPort(VGA_STAT_COLOR);
+
+                    /* Write the index */
+                    VgaWritePort(VGA_AC_INDEX, getBL());
+
+                    /* Read the data */
+                    setBH(VgaReadPort(VGA_AC_READ));
+
+                    break;
+                }
+
+                /* Get Overscan Color */
+                case 0x08:
+                {
+                    /* Reset the flip-flop */
+                    VgaReadPort(VGA_STAT_COLOR);
+
+                    /* Write the index */
+                    VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
+
+                    /* Read the data */
+                    setBH(VgaReadPort(VGA_AC_READ));
+
+                    break;
+                }
+
+                /* Get All Palette Registers */
+                case 0x09:
+                {
+                    INT i;
+                    LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
+
+                    /* Get the palette registers */
+                    for (i = 0; i <= VGA_AC_PAL_F_REG; i++)
+                    {
+                        /* Reset the flip-flop */
+                        VgaReadPort(VGA_STAT_COLOR);
+
+                        /* Write the index */
+                        VgaWritePort(VGA_AC_INDEX, i);
+
+                        /* Read the data */
+                        Buffer[i] = VgaReadPort(VGA_AC_READ);
+                    }
+
+                    /* Get the overscan register */
+                    VgaWritePort(VGA_AC_INDEX, VGA_AC_OVERSCAN_REG);
+                    Buffer[VGA_AC_PAL_F_REG + 1] = VgaReadPort(VGA_AC_READ);
+
+                    break;
+                }
+
+                /* Set Individual DAC Register */
+                case 0x10:
+                {
+                    /* Write the index */
+                    // Certainly in BL and not in BX as said by Ralf Brown...
+                    VgaWritePort(VGA_DAC_WRITE_INDEX, getBL());
+
+                    /* Write the data in this order: Red, Green, Blue */
+                    VgaWritePort(VGA_DAC_DATA, getDH());
+                    VgaWritePort(VGA_DAC_DATA, getCH());
+                    VgaWritePort(VGA_DAC_DATA, getCL());
+
+                    break;
+                }
+
+                /* Set Block of DAC Registers */
+                case 0x12:
+                {
+                    INT i;
+                    LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
+
+                    /* Write the index */
+                    // Certainly in BL and not in BX as said by Ralf Brown...
+                    VgaWritePort(VGA_DAC_WRITE_INDEX, getBL());
+
+                    for (i = 0; i < getCX(); i++)
+                    {
+                        /* Write the data in this order: Red, Green, Blue */
+                        VgaWritePort(VGA_DAC_DATA, *Buffer++);
+                        VgaWritePort(VGA_DAC_DATA, *Buffer++);
+                        VgaWritePort(VGA_DAC_DATA, *Buffer++);
+                    }
+
+                    break;
+                }
+
+                /* Get Individual DAC Register */
+                case 0x15:
+                {
+                    /* Write the index */
+                    VgaWritePort(VGA_DAC_READ_INDEX, getBL());
+
+                    /* Read the data in this order: Red, Green, Blue */
+                    setDH(VgaReadPort(VGA_DAC_DATA));
+                    setCH(VgaReadPort(VGA_DAC_DATA));
+                    setCL(VgaReadPort(VGA_DAC_DATA));
+
+                    break;
+                }
+
+                /* Get Block of DAC Registers */
+                case 0x17:
+                {
+                    INT i;
+                    LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
+
+                    /* Write the index */
+                    // Certainly in BL and not in BX as said by Ralf Brown...
+                    VgaWritePort(VGA_DAC_READ_INDEX, getBL());
+
+                    for (i = 0; i < getCX(); i++)
+                    {
+                        /* Write the data in this order: Red, Green, Blue */
+                        *Buffer++ = VgaReadPort(VGA_DAC_DATA);
+                        *Buffer++ = VgaReadPort(VGA_DAC_DATA);
+                        *Buffer++ = VgaReadPort(VGA_DAC_DATA);
+                    }
+
+                    break;
+                }
+
+                default:
+                {
+                    DPRINT1("BIOS Palette Control Sub-command AL = 0x%02X NOT IMPLEMENTED\n",
+                            getAL());
+                    break;
+                }
+            }
+
+            break;
+        }
+
         /* Scroll Window */
         case 0x12:
         {
@@ -952,7 +1165,43 @@ VOID BiosVideoService(LPWORD Stack)
     }
 }
 
-VOID BiosKeyboardService(LPWORD Stack)
+VOID WINAPI BiosEquipmentService(LPWORD Stack)
+{
+    /* Return the equipment list */
+    setAX(Bda->EquipmentList);
+}
+
+VOID WINAPI BiosGetMemorySize(LPWORD Stack)
+{
+    /* Return the conventional memory size in kB, typically 640 kB */
+    setAX(Bda->MemorySize);
+}
+
+VOID WINAPI BiosMiscService(LPWORD Stack)
+{
+    switch (getAH())
+    {
+        /* Get Extended Memory Size */
+        case 0x88:
+        {
+            /* Return the number of KB of RAM after 1 MB */
+            setAX((MAX_ADDRESS - 0x100000) / 1024);
+
+            /* Clear CF */
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+
+            break;
+        }
+
+        default:
+        {
+            DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
+                    getAH());
+        }
+    }
+}
+
+VOID WINAPI BiosKeyboardService(LPWORD Stack)
 {
     switch (getAH())
     {
@@ -1034,7 +1283,7 @@ VOID BiosKeyboardService(LPWORD Stack)
     }
 }
 
-VOID BiosTimeService(LPWORD Stack)
+VOID WINAPI BiosTimeService(LPWORD Stack)
 {
     switch (getAH())
     {
@@ -1072,18 +1321,12 @@ VOID BiosTimeService(LPWORD Stack)
     }
 }
 
-VOID BiosSystemTimerInterrupt(LPWORD Stack)
+VOID WINAPI BiosSystemTimerInterrupt(LPWORD Stack)
 {
     /* Increase the system tick count */
     Bda->TickCounter++;
 }
 
-VOID BiosEquipmentService(LPWORD Stack)
-{
-    /* Return the equipment list */
-    setAX(Bda->EquipmentList);
-}
-
 VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack)
 {
     switch (IrqNumber)